自己动手写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,不用特意實現這個指令
調整系統結構
代碼修改
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'b1102、修改取指階段的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 end3、修改譯碼階段ID模塊
根據指令的指令碼和功能碼,以及指令有關bit位的特點來判斷是哪一條指令
4、修改執行階段EX模塊
如果alusel_o為EXE_RES_JUMP_BRANCH,那么就將返回地址link_address_i作為要寫入目的寄存器的值賦給wdata_o。
測試
JUMP指令
BRANCH指令
項目鏈接
總結
以上是生活随笔為你收集整理的自己动手写CPU(7)转移指令的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自己动手写CPU(6)流水线暂停、乘累加
- 下一篇: HLS中数据的合并与拆分