当前位置:Gxlcms > mysql > 自己动手写CPU之第八阶段(4)转移指令实现过程2

自己动手写CPU之第八阶段(4)转移指令实现过程2

时间:2021-07-01 10:21:17 帮助过:50人阅读

将陆续上传本人写的新书《 自己动手写CPU 》,今天是第36篇,我尽量每周四篇 开展晒书评送书活动,在亚马逊、京东、当当三大图书网站上,发表《自己动手写CPU》书评的前十名读者,均可获赠《步步惊芯——软核 处理器 内部 设计 分析 》一书,大家踊跃参与吧

将陆续上传本人写的新书《自己动手写CPU》,今天是第36篇,我尽量每周四篇

开展晒书评送书活动,在亚马逊、京东、当当三大图书网站上,发表《自己动手写CPU》书评的前十名读者,均可获赠《步步惊芯——软核处理器内部设计分析》一书,大家踊跃参与吧!活动时间:2014-9-11至2014-10-20


8.4.3 修改执行阶段的EX 模块

参考图8-6可知,EX模块需要增加一些接口,增加的接口描述如表8-4所示。


EX模块的代码主要修改如下,完整代码请参考本书附带光盘Code\Chapter8目录下的ex.v文件。

module ex(

  ......

  // 处于执行阶段的转移指令要保存的返回地址
  input wire[`RegBus]           link_address_i,

  // 当前执行阶段的指令是否位于延迟槽
  input wire                    is_in_delayslot_i,	
	
  ...... 
	
);
	
  ......

  always @ (*) begin

  ......
	 
    case ( alusel_i ) 
      `EXE_RES_LOGIC:		begin
        wdata_o <= logicout;
      end
      `EXE_RES_SHIFT:		begin
        wdata_o <= shiftres;
      end	 	
      `EXE_RES_MOVE:		begin
        wdata_o <= moveres;
      end	 	
      `EXE_RES_ARITHMETIC:	begin
        wdata_o <= arithmeticres;
      end
      `EXE_RES_MUL:		begin
        wdata_o <= mulres[31:0];
      end	 	
      `EXE_RES_JUMP_BRANCH:	begin
        wdata_o <= link_address_i;
      end
      default:					begin
        wdata_o <= `ZeroWord;
      end
    endcase
end	

	......			

endmodule

如果alusel_o为EXE_RES_JUMP_BRANCH,那么就将返回地址link_address_i作为要写入目的寄存器的值赋给wdata_o。

注意一点,此处并没有利用输入的信号is_in_delayslot_i,该信号表示当前处于执行阶段的指令是否是延迟槽指令,这个信号会在异常处理过程中使用到,本章暂时不需要。

8.4.4 修改OpenMIPS模块

因为有一些模块添加了接口,所以需要修改顶层模块OpenMIPS,以将这些新增加的接口按照图8-6所示的关系连接起来。具体修改也很简单,不在书中列出,读者可以参考本书附带光盘Code\Chapter8目录下的openmips.v文件。

8.5 测试转移指令的实现效果

本节将通过两个测试程序验证转移指令是否实现正确,这两个测试程序分别验证跳转指令、分支指令。

8.5.1 测试跳转指令

测试代码如下,源文件是本书光盘Code\Chapter8\AsmTest\Test1目录下的inst_rom.S文件。

.org 0x0
.set noat
.set noreorder      # 添加这个伪操作,指示编译器不要对程序做出任何优化或是改动
.set nomacro        
.global _start
_start:
   ori  $1,$0,0x0001   # (1)$1 = 0x1                
   j    0x20           #  转移到0x20处
   ori  $1,$0,0x0002   # (2)$1 = 0x2,这是延迟槽指令
   ori  $1,$0,0x1111
   ori  $1,$0,0x1100

   .org 0x20
   ori  $1,$0,0x0003   # (3)$1 = 0x3               
   jal  0x40           #  转移到0x40处,同时设置$31为0x2c
   div  $zero,$31,$1   # (4)此时$31 = 0x2c, $1 = 0x3,所以得到除法结果
                       #     HI = 0x2, LO = 0xe,这是延迟槽指令

   ori  $1,$0,0x0005   # (6)$1 = 0x5
   ori  $1,$0,0x0006   # (7)$1 = 0x6
   j    0x60           #  转移到0x60处
   nop

   .org 0x40
   jalr $2,$31          #  此时$31为0x2c,所以转移到0x2c,同时设置$2为0x48
   or   $1,$2,$0        # (5)$1 = 0x48,这是延迟槽指令

   ori  $1,$0,0x0009    # (10)$1 = 0x9
   ori  $1,$0,0x000a    # (11)$1 = 0xa
   j 0x80               #  转移到0x80处
   nop

   .org 0x60
   ori  $1,$0,0x0007    # (8)$1 = 0x7 
   jr   $2              #  此时$2为0x48,所以转移到0x48处
   ori  $1,$0,0x0008    # (9)$1 = 0x8,这是延迟槽指令
   ori  $1,$0,0x1111
   ori  $1,$0,0x1100

   .org 0x80
   nop
    
_loop:
   j _loop
   nop


8.5.2 测试分支指令

测试代码如下,源文件是本书光盘Code\Chapter8\AsmTest\Test2目录下的inst_rom.S文件。

.org 0x0
   .set noat
   .set noreorder
   .set nomacro
   .global _start
_start:
   ori  $3,$0,0x8000
   sll  $3,16               # 设置$3 = 0x80000000
   ori  $1,$0,0x0001        #(1)$1 = 0x1                
   b    s1                  # 转移到s1处
   ori  $1,$0,0x0002        #(2)$1 = 0x2,这是延迟槽指令
1:
   ori  $1,$0,0x1111
   ori  $1,$0,0x1100

   .org 0x20
s1:
   ori  $1,$0,0x0003        #(3)$1 = 0x3
   bal  s2                  # 转移到s2处,同时设置$31为0x2c
   div  $zero,$31,$1        #(4)此时$31 = 0x2c, $1 = 0x3,所以除法
结果为 # HI = 0x2, LO = 0xe,这是延迟槽指令 ori $1,$0,0x1100 ori $1,$0,0x1111 bne $1,$0,s3 nop ori $1,$0,0x1100 ori $1,$0,0x1111 .org 0x50 s2: ori $1,$0,0x0004 #(5)$1 = 0x4 beq $3,$3,s3 # $3等于$3,所以会发生转移,目的地址是s3 or $1,$31,$0 #(6)$1 = 0x2c,这是延迟槽指令 ori $1,$0,0x1111 ori $1,$0,0x1100 2: ori $1,$0,0x0007 #(9)$1 = 0x7 ori $1,$0,0x0008 #(10)$1 = 0x8 bgtz $1,s4 # 此时$1为0x8,大于0,所以转移至标号s4处 ori $1,$0,0x0009 #(11)$1 = 0x9,这是延迟槽指令 ori $1,$0,0x1111 ori $1,$0,0x1100 .org 0x80 s3: ori $1,$0,0x0005 #(7)$1 = 0x5 bgez $1,2b # 此时$1为0x5,大于0,所以转移至前面的标号2处 ori $1,$0,0x0006 #(8)$1 = 0x6,这是延迟槽指令 ori $1,$0,0x1111 ori $1,$0,0x1100 .org 0x100 s4: ori $1,$0,0x000a #(12)$1 = 0xa bgezal $3,s3 # 此时$3为0x80000000,小于0,所以不发生转移 or $1,$0,$31 #(13)$1 = 0x10c ori $1,$0,0x000b #(14)$1 = 0xb ori $1,$0,0x000c #(15)$1 = 0xc ori $1,$0,0x000d #(16)$1 = 0xd ori $1,$0,0x000e #(17)$1 = 0xe bltz $3,s5 # 此时$3为0x80000000,小于0,所以发生转移,转移至s5处 ori $1,$0,0x000f #(18)$1 = 0xf,这是延迟槽指令 ori $1,$0,0x1100 .org 0x130 s5: ori $1,$0,0x0010 #(19)$1 = 0x10 blez $1,2b # 此时$1为0x10,大于0,所以不发生转移 ori $1,$0,0x0011 #(20)$1 = 0x11 ori $1,$0,0x0012 #(21)$1 = 0x12 ori $1,$0,0x0013 #(22)$1 = 0x13 bltzal $3,s6 # 此时$3为0x80000000,小于0,所以发生转移,转移到s6处 or $1,$0,$31 #(23)$1 = 0x14c,这是延迟槽指令 ori $1,$0,0x1100 .org 0x160 s6: ori $1,$0,0x0014 #(24)$1 = 0x14 nop _loop: j _loop nop

上面的测试程序使用到了所有的分支指令,程序的注释给出了寄存器$1的变化情况,及指令执行顺序,注意寄存器$1的变化是按照注释中的序号顺序进行的。ModelSim仿真结果如图8-9所示,观察$1的变化可知OpenMIPS正确实现了分支指令。



至此,转移指令也实现完毕了,下一步将实现加载存储指令,敬请关注!

人气教程排行