日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

自己动手写CPU(7)转移指令的实现

發布時間:2023/12/19 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自己动手写CPU(7)转移指令的实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

自己動手寫CPU(7)轉移指令的實現

分支延遲槽

在MIPS五級流水線中,一條指令被分成了五個階段:取指、譯指、執行、仿存、回寫。當第一條指令的執行階段結束時,第二條指令的譯指階段也已經結束了。

那么如果第一條指令是分支跳轉指令,那么在執行階段才會知道要不要跳轉以及跳轉的目標指令地址是多少。而此時第二條指令已結束譯指階段,第三條指令已結束取指階段。如果這個時候CPU直接跳轉到目標指令地址去執行,那么就需要清空現有流水線,從新的指令地址開始取指、譯指。這是因為分支跳轉后面的指令不能被執行啊,程序已經跳轉了,也就相當于原先第二條指令的取指和譯指操作,第三條指令的取指操作,這些CPU已經做過的工作,都白做了。因為CPU此時是從的新的地址重新開始的!這叫流水線“冒泡”。

但做上述的工作也是需要耗費CPU時間的,MIPS設計者們覺得十分浪費,所以發明了“分支延遲槽”的東西。

但是,即使引入延遲槽,在轉移發生時仍然會導致已經進入取指階段的指令無效,也就是說,仍浪費一個時鐘周期,要解決這個問題,可以在譯碼階段進行轉移判斷,這樣就可以避免浪費時鐘周期。OpenMIPS處理器就設計為在譯碼階段進行轉移判斷。

需注意的幾件事情:

  • “分支延遲槽”里面的指令,在目標跳轉指令前面執行,所以**“分支延遲槽”里面的指令不能修改目標跳轉指令會用到的寄存器或者變量的內容**,否則程序很容易搞錯。
  • “分支延遲槽”里面的指令,通常可以被加以利用會做一些比較意義的事情,例如清零內存之類的。

指令介紹

  • 跳轉指令:jr、jalr、j、jar
  • 分支指令b、bal、beq、bgez、bgezal、bgtz、blez、bltz、bltzal、bne

跳轉指令

指令格式

  • jr(功能碼為6’b001000):用法:jr rs,作用:pc <- rs 將地址為rs的通用寄存器的值賦給寄存器PC,作為新的指令地址

  • jalr(功能碼為6’b001001):用法:jalr rs 或者 jalr rd,rs 作用:rd <- return_address,pc <- rs,將地址為rs的通用寄存器的值賦給寄存器PC,作為新的指令地址,同時將跳轉指令后面第2條指令的地址作為返回地址保存到地址為rd的通用寄存器,如果沒有在指令中指明rd,那么默認將返回地址保存到寄存器$31

  • j(指令碼為6’b000010):用法:j target,作用:pc <- (pc+4)[31,28]||target||‘00’,轉移到新的指令地址,其中新地址的低28位是target左移兩位后的值,新指令地址高4位是后一指令的高四位

    因為處理器按照字節尋址,二指令存儲器每個地址是一個32bit字,所以要給指令中的立即數乘4,即左移兩位

  • jal(指令碼為6’b000011):用法:jal target,作用:pc <- (pc+4)[31,28]||target||‘00’,轉移到新的指令地址,其中新地址的低28位是target左移兩位后的值,新指令地址高4位是后一指令的高四位,jal指令要將跳轉指令后面的一條指令地址(pc+4)寫入$31寄存器

分支指令

指令格式

  • 由指令格式可以看出:

    beq、b、bgtz、blez、bne這5條指令可以直接依據指令中的指令碼進行判斷是哪一條指令,bltz、bltzal、bgez、bgezal、bal這5條指令指令碼相同,依據指令中16~20bit的值進一步判斷是哪一條指令

    所有分支指令的第0~15bit存儲的都是offset,如果發生轉移,那么將offset左移2位,并符號擴展至32位

    轉移目標地址 = (signed_extend)(offset||‘00’)+(pc+4)

  • beq(指令碼為6’b000100):用法:beq rs,rt,offset,作用:if rs = rt then branch,將地址為rs的通用寄存器的值與地址為rt的通用寄存器的值進行比較,如果相等,則發生轉移

  • b(指令碼為6’b000100,且16~25bit為0):用法:b offset,作用:無條件轉移,即beq指令的rs,rt都為0時的情況,實現時不需要特意實現b指令,只需要實現beq即可

  • bgtz(指令碼為6’b000111):用法:bgtz rs,offset,作用:if rs > 0 then branch

  • blez(指令碼6’b000110):用法:blez rs,offset,作用:if rs <= 0 then branch

  • bne(指令碼6’b000101):用法:bne rs,rt,offset,作用:if rs != rt then branch

  • bltz(指令碼為REGIMM,且第16~20bit為5’b00000):用法:bltz rs,offset,作用:if rs < 0 then branch

  • bltzal(指令碼為REGIMM,且第16~20bit為5’b10000):用法:bltzal rs,offset,作用:if rs < 0 then branch,并且將指令后面的指令地址作為返回地址,保存到通用寄存器$31

  • bgez(指令碼為REGIMM,且第16~20bit為5’b00001):用法:bgez rs,offset,作用:if rs >= 0 then branch

  • bgezal(指令碼為REGIMM,且第16~20bit為5’b10001):用法:bgezal rs,offset,作用:if rs >= 0 then branch,并且將指令后面的指令地址作為返回地址,保存到通用寄存器$31

  • bal(指令碼為REGIMM,且第2125bit為0,第1620bit為5’b10001):用法:bal offset,作用:無條件轉移,并且將指令后面的指令地址作為返回地址,保存到通用寄存器$31,bal是bgezal指令的特殊情況,即bgezal指令的rs為0,不用特意實現這個指令

調整系統結構

  • 如果處于譯碼階段的指令是轉移指令,并且滿足轉移條件,那么ID模塊設置轉移發生標志 branch_flag_o為 Branch,同時通過branch_target_address_o接口給出轉移目的地址,送到PC模塊,后者據此修改取指地址。
  • 如果處于譯碼階段的指令是轉移指令,并且滿足轉移條件,那么ID模塊還會設置next_inst_in_delayslot_o為 InDelaySlot,表示下一條指令是延遲槽指令,其中 InDelaySlot是一個宏定義。next_inst _in_delayslot_o信號會送入ID/EX模塊,并在下一個時鐘周期通過ID/EX模塊的is_in_delayslot_o接口送回到ID模塊,ID模塊可以據此判斷當前處于譯碼階段的指令是否是延遲槽指令
  • 如果轉移指令需要保存返回地址,那么ID模塊還要計算返回地址,并通過link_addr_o接口輸出,該值最終會傳遞到EX模塊,作為要寫入目的寄存器的值。
  • 代碼修改

    1、增加宏定義

    `define Branch 1'b1 //發生轉移 `define NotBranch 1'b0 //不發生轉移 `define InDelaySlot 1'b1 //是延遲槽指令 `define NotInDelaySlot 1'b0 //不是延遲槽指令`define EXE_J 6'b000010 //指令J的功能碼 `define EXE_JAL 6'b000011 //指令JAL的功能碼 `define EXE_JALR 6'b001001 //指令JALR的功能碼 `define EXE_JR 6'b001000 //指令JR的功能碼 `define EXE_BEQ 6'b000100 //指令BEQ的指令碼 `define EXE_BGEZ 5'b00001 //指令BGEZ第16~20bit `define EXE_BGEZAL 5'b10001 //指令BGEZAL第16~20bit `define EXE_BGTZ 6'b000111 //指令BGTZ的指令碼 `define EXE_BLEZ 6'b000110 //指令BLEZ的指令碼 `define EXE_BLTZ 5'b00000 //指令BLTZ第16~20bit `define EXE_BLTZAL 5'b10000 //指令BLTZAL第16~20bit `define EXE_BNE 6'b000101 //指令BNE的指令碼`define EXE_REGIMM_INST 6'b000001 //REGIMM類的指令碼`define EXE_J_OP 8'b01001111 `define EXE_JAL_OP 8'b01010000 `define EXE_JALR_OP 8'b00001001 `define EXE_JR_OP 8'b00001000 `define EXE_BEQ_OP 8'b01010001 `define EXE_BGEZ_OP 8'b01000001 `define EXE_BGEZAL_OP 8'b01001011 `define EXE_BGTZ_OP 8'b01010100 `define EXE_BLEZ_OP 8'b01010011 `define EXE_BLTZ_OP 8'b01000000 `define EXE_BLTZAL_OP 8'b01001010 `define EXE_BNE_OP 8'b01010010`define EXE_RES_JUMP_BRANCH 3'b110

    2、修改取指階段的PC模塊

    修改取指階段的PC模塊如下,主要修改一點:如果 branch_flag _i為 Branch,那么設置新的PC值為 branch_target _address_i。

    always @ (posedge clk) beginif (ce == `ChipDisable) beginpc <= 32'h00000000;end else if(stall[0] == `NoStop) beginif(branch_flag_i == `Branch) beginpc <= branch_target_address_i;end else beginpc <= pc + 4'h4;endend end

    3、修改譯碼階段ID模塊

    根據指令的指令碼和功能碼,以及指令有關bit位的特點來判斷是哪一條指令

    4、修改執行階段EX模塊

    如果alusel_o為EXE_RES_JUMP_BRANCH,那么就將返回地址link_address_i作為要寫入目的寄存器的值賦給wdata_o。

    測試

    JUMP指令

    BRANCH指令


    項目鏈接

    總結

    以上是生活随笔為你收集整理的自己动手写CPU(7)转移指令的实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。