自己动手写CPU之第四阶段(2)——验证第一条指令ori的实现效果

将陆续上传本人写的新书《自己动手写CPU》(尚未出版),今天是第12篇,我尽量每周四篇

书名又之前的《自己动手写处理器》改为《自己动手写CPU》

4.3 验证OpenMIPS实现效果

4.3.1指令存储器ROM的实现

本节将验证我们的OpenMIPS是否实现正确,包含:流水线是否正确、ori指令是否实现正确。在验证之前,需要首先实现指令存储器,以便OpenMIPS从中读取指令。

指令存储器模块是只读的,其接口如图4-7所示,还是采用左边是输入接口,右边是输出接口的方式绘制,这样便于理解。接口含义如表4-12所示。

指令存储器ROM模块在文件inst_rom.v中实现,代码如下,可以在本书附带光盘的Code\Chapter4\目录下找到源文件。

module inst_rom(

	input wire			ce,
	input wire[`InstAddrBus]	addr,
	output reg[`InstBus]		inst

);
       // 定义一个数组,大小是InstMemNum,元素宽度是InstBus
	reg[`InstBus]  inst_mem[0:`InstMemNum-1];

       // 使用文件inst_rom.data初始化指令存储器
	initial $readmemh ( "inst_rom.data", inst_mem );

       // 当复位信号无效时,依据输入的地址,给出指令存储器ROM中对应的元素
	always @ (*) begin
	  if (ce == `ChipDisable) begin
	    inst <= `ZeroWord;
	  end else begin
	    inst <= inst_mem[addr[`InstMemNumLog2+1:2]];
	  end
	end

endmodule

代码很好理解,有以下几点说明。

(1)在初始化指令存储器时,使用了initial过程语句。initial过程语句只执行一次,通常用于仿真模块中对激励向量的描述,或用于给变量赋初值,是面向模拟仿真的过程语句,通常不能被综合工具支持。所以如果要将本章实现的OpenMIPS处理器使用综合工具进行综合,那么需要修改这里初始化指令存储器的方法。

(2)在初始化指令存储器时,使用了系统函数$readmemh,表示从inst_rom.data文件中读取数据以初始化inst_mem,而inst_mem正是之前定义的数组。inst_rom.data是一个文本文件,里面存储的是指令,其每行存储一条32位宽度的指令(使用十六进制表示),系统函数$readmemh会将inst_rom.data中的数据依次填写到inst_mem数组中。

(3)OpenMIPS是按照字节寻址的,而此处定义的指令存储器的每个地址是一个32bit的字,所以要将OpenMIPS给出的指令地址除以4再使用,比如:要读取地址0xC处的指令,那么实际就是对应ROM的inst_mem[3],如图4-8所示。

除以4也就是将指令地址右移2位,所以在读取的时候给出的地址是addr[`InstMemNumLog2+1:2],其中InstMemNumLog2是指令存储器的实际地址宽度,比如:如果inst_mem有1024个元素,那么InstMemNum等于1024,InstMemNumLog2等于10,表示实际地址宽度为10。

4.3.2 最小SOPC的实现

为了验证,需要建立一个SOPC,其中仅包含OpenMIPS、指令存储器ROM,所以是一个最小SOPC。OpenMIPS从指令存储器中读取指令,指令进入OpenMIPS开始执行。最小SOPC的结构如图4-9所示。

最小SOPC对应的模块是openmips_min_sopc,位于文件openmips_min_sopc.v中,读者可以在本书附带光盘的Code\Chapter4\目录下找到该文件,主要内容如下。在其中例化了处理器OpenMIPS、指令存储器ROM,并将两者按照图4-9的方式连接。

module openmips_min_sopc(

	input	wire		clk,
	input  wire		rst

);

  	// 连接指令存储器
  	wire[`InstAddrBus] inst_addr;
  	wire[`InstBus]     inst;
       wire               rom_ce;

       // 例化处理器OpenMIPS
 	openmips openmips0(
                 .clk(clk),			.rst(rst),
                 .rom_addr_o(inst_addr),	.rom_data_i(inst),
                 .rom_ce(rom_ce)
	);

       // 例化指令存储器ROM
	inst_rom inst_rom0(
		.ce(rom_ce),
		.addr(inst_addr),		.inst(inst)
	);

endmodule

4.3.3 编写测试程序

我们需要写一段测试程序,并将其存储到指令存储器ROM,这样当上一节建立的最小SOPC开始运行的时候,就会从ROM中取出我们的程序,送入OpenMIPS处理器执行。由于目前的OpenMIPS只实现了一条ori指令,所以测试程序很简单,如下,对应本书附带光盘Code\Chapter4\TestAsm目录下的inst_rom.S文件。

  ori $1,$0,0x1100        # $1 = $0 | 0x1100 = 0x1100
  ori $2,$0,0x0020        # $2 = $0 | 0x0020 = 0x0020
  ori $3,$0,0xff00        # $3 = $0 | 0xff00 = 0xff00
  ori $4,$0,0xffff        # $4 = $0 | 0xffff = 0xffff

共有4条指令,都是ori指令。

第1条指令将0x1100进行零扩展后与寄存器$0进行逻辑“或”运算,结果保存在寄存器$1中。

第2条指令将0x0020进行零扩展后与寄存器$0进行逻辑“或”运算,结果保存在寄存器$2中。

第3条指令将0xff00进行零扩展后与寄存器$0进行逻辑“或”运算,结果保存在寄存器$3中。

第4条指令将0xffff进行零扩展后与寄存器$0进行逻辑“或”运算,结果保存在寄存器$4中。

指令的注释说明了指令的执行结果。接下来,按照正常的顺序应该是使用编译器编译我们的测试程序,但由于GCC编译器的安装、使用、Makefile文件的制作等内容还需要不少篇幅讲解,而想必各位读者和笔者一样,急切地想知道OpenMIPS是否实现正确,所以本节采用手工编译的方式编译测试程序,4.4节将专题介绍GCC编译器的使用。

手工编译只需按照指令内容填充进图4-1所示的ori指令格式中,即可得到对应的二进制字,比如:对于指令ori $1,$0,0x1100,对应的二进制字如图4-10所示。

转化为十六进制即0x34011100,其余3条指令按照同样的方式可以得到对应的二进制字,按照$readmemh函数的要求,一行放一条指令,得到测试程序对应的isnt_rom.data文件如下,可在本书附带光盘的Code\Chapter4\TestAsm目录下找到同名文件。

34011100
34020020
3403ff00
3404ffff

4.3.4 建立Test Bench文件

本小节将建立Test Bench文件,其中给出最小SOPC运行所需的时钟信号、复位信号。代码如下,对应本书附带光盘Code\Chapter4\目录下的openmips_min_sopc_tb.v文件。

// 时间单位是1ns,精度是1ps
`timescale 1ns/1ps

module openmips_min_sopc_tb();

  reg     CLOCK_50;
  reg     rst;

  // 每隔10ns,CLOCK_50信号翻转一次,所以一个周期是20ns,对应50MHz
  initial begin
    CLOCK_50 = 1'b0;
    forever #10 CLOCK_50 = ~CLOCK_50;
  end

  // 最初时刻,复位信号有效,在第195ns,复位信号无效,最小SOPC开始运行
  // 运行1000ns后,暂停仿真
  initial begin
    rst = `RstEnable;
    #195 rst= `RstDisable;
    #1000 $stop;
  end

  // 例化最小SOPC
  openmips_min_sopc openmips_min_sopc0(
		.clk(CLOCK_50),
		.rst(rst)
	);

endmodule

4.3.5使用ModelSim检验OpenMIPS实现效果

万事俱备,只欠东风了,本节是验证前的最后一步——建立ModelSim工程,进行仿真。参考第2章的介绍,新建一个ModelSim工程,工程名可以为openmips_min_sopc,将上文创建的OpenMIPS所有源文件、Test Bench文件、指令存储器的源文件等(也就是本书附带光盘Code\Chapter4目录下所有.v文件)添加到工程中,然后编译。

注意:还需要将上一小节制作的inst_rom.data文件复制到工程目录下。

编译通过后,将workspace切换到Library选项卡,打开work这个library,选中openmips_min_sopc_tb,右键点击,选择Simulate,如图4-11所示。

在出现的波形显示界面中,添加要观察的信号,即可开始仿真。此处我们选择寄存器$1-$4作为观察对象,如图4-12所示,通过观察寄存器$1-$4的最终值,可知OpenMIPS正确执行了测试程序,也就是正确实现了ori指令。

添加更多要观察的信号,可以了解流水线执行情况,如图4-13所示。为了使流水线情况显示的更加直观,此处以第一条指令在流水线中的执行过程为例,并且图中去掉了其它指令执行时引起的信号变化。

(1)在复位结束后的第一个时钟周期上升沿,rom_ce_o变为ChipEnable,表示指令存储器使能,开始取指,进入取指阶段,从指令存储器中取出第一条指令0x34011100,赋给IF/ID模块的输入端口if_inst。下一个时钟周期,第一条指令进入译码阶段。

(2)观察译码阶段。

  • 此时译码阶段的指令id_inst正是第一条指令0x34011100
  • 指令地址id_pc是0x00000000
  • 在ID模块对指令进行译码,得到指令运算类型alusel_o是3‘b001,查询defines.h文件中的宏定义可知,对应宏EXE_RES_LOGIC,表示是逻辑运算
  • 得到运算子类型aluop_o是8‘b00100101,查询defines.h文件中的宏定义可知,对应宏EXE_OR_OP,表示逻辑“或”运算
  • 译码得到参与运算的源操作数1是0x00000000,正是$0寄存器的值
  • 译码得到参与运算的源操作数2是0x00001100,正是指令中立即数零扩展后的值
  • 译码得到wreg_o的值为1,表示要写目的寄存器
  • 译码得到要写入的目的寄存器wd_o是5‘b00001,正是$1寄存器

(3)观察执行阶段。

  • 进行指定的运算,得到wdata_o为0x00001100,就是要写到目的寄存器的数据
  • 传递译码阶段wreg_o的值,为1,表示要写目的寄存器
  • 传递译码阶段wd_o的值,为5‘b00001,表示要写入的目的寄存器是$1寄存器

(4)观察访存阶段

  • 传递执行阶段wdata_o的值,为0x00001100,表示要写到目的寄存器的数据
  • 传递执行阶段wreg_o的值,为1,表示要写目的寄存器
  • 传递执行阶段wd_o的值,为5‘b00001,表示要写入的目的寄存器是$1寄存器

(5)观察回写阶段

  • 得到访存阶段wdata_o的值,为0x00001100,表示要写到目的寄存器的数据
  • 得到访存阶段wreg_o的值,为1,表示要写目的寄存器
  • 得到访存阶段wd_o的值,为5‘b00001,表示要写入的目的寄存器是$1寄存器

在回写阶段的最后,将按照要求写目的寄存器$1,使得$1的值为0x00001100。通过上面的观察,可知原始的OpenMIPS五级流水线实现正确。接下来,我们就可以以此为基础,不断充实,添加实现更多的MIPS指令,不过,在此之前,我们要先学习使用GNU工具链,本节的例子只有4条指令,可以手工编译,以后会遇到比较复杂,拥有较多指令的程序,届时,手工编译就显得效率低下了,所以要使用GNU工具链。

未完待续!

自己动手写CPU之第四阶段(2)——验证第一条指令ori的实现效果

时间: 2024-08-01 10:45:00

自己动手写CPU之第四阶段(2)——验证第一条指令ori的实现效果的相关文章

自己动手写CPU之第四阶段(4)——Makefile文件建立

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第14篇,我尽量每周四篇 4.4.6 编写Makefile文件 为了得到指令存储器初始化文件,我们需要输入4条命令,有点麻烦,最好只输入一条命令就可以了,这需要使用到Makefile文件.在汇编程序inst_rom.S所在目录下新建一个Document,文件名为Makefile,内容如下. ifndef CROSS_COMPILE CROSS_COMPILE = mips-sde-elf- endif CC = $(CROSS_CO

自己动手写CPU之第六阶段(2)——移动操作指令实现思路

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第21篇,我尽量每周四篇 6.2 移动操作指令实现思路 6.2.1 实现思路 这6条移动操作指令可以分为两类:一类是不涉及特殊寄存器HI.LO的指令,包括movn.movz:另一类是涉及特殊寄存器HI.LO的指令,包括mfhi.mflo.mthi.mtlo.前一类很好实现,基本思路与第5章实现逻辑.移位操作指令时类似,只需要修改ID.EX模块即可.后一类涉及到特殊寄存器HI.LO,需要为OpenMIPS添加HI.LO寄存器,以及相应

自己动手写CPU之第六阶段(1)——移动操作指令说明

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第20篇,我尽量每周四篇 本章将实现移动操作指令,首先在6.1节介绍了MIPS32指令集架构中定义的移动操作指令的格式.作用,接着在6.2节给出移动操作指令实现思路,介绍了修改后的数据流图.新出现的数据相关问题及其解决措施,并给出了修改后的OpenMIPS系统结构图.在6.3节列出了详细的修改过程.本章最后通过一个测试程序验证移动操作指令是否实现正确. 6.1 移动操作指令说明 MIPS32指令集架构中定义的移动操作指令共有6条:m

自己动手写CPU之第五阶段(3)——MIPS指令集中的逻辑、移位与空指令

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第17篇,我尽量每周四篇 5.4 逻辑.移位操作与空指令说明 MIPS32指令集架构中定义的逻辑操作指令有8条:and.andi.or.ori.xor.xori.nor.lui,其中ori指令已经实现了,本章要实现其余7条指令. MIPS32指令集架构中定义的移位操作指令有6条:sll.sllv.sra.srav.srl.srlv. MIPS32指令集架构中定义的空指令有2条:nop.ssnop.其中ssnop是一种特殊类型的空操作

自己动手写CPU之第六阶段(3)——移动操作指令的实现

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第22篇,我尽量每周四篇 6.3 修改OpenMIPS以实现移动操作指令 6.3.1 HI.LO寄存器的实现 在HILO模块中实现HI.LO寄存器,HILO模块的接口描述如表6-1所示. HILO模块的代码如下,源文件是本书附带光盘Code\Chapter6目录下的hilo_reg.v.整个代码很简单:在时钟上升沿,如果复位信号无效,那么就判断输入的写使能信号we是否为WriteEnable,如果是WriteEnable,那么就将输

自己动手写CPU之第四阶段(3)——MIPS编译环境的建立

将陆续上传本人写的新书<自己动手写CPU>(尚未出版).今天是第13篇.我尽量每周四篇 4.4 MIPS编译环境的建立 OpenMIPS处理器在设计的时候就计划与MIPS32指令集架构兼容,所以能够使用MIPS32架构下已有的GNU开发工具链.本节将说明怎样安装使用GNU开发工具链以及怎样制作Makefile文件.从而以更加方便.快捷.自己主动的方式对測试程序进行编译.并得到指令存储器ROM的初始化文件inst_rom.data. 4.4.1 VisualBox的安装与设置 GNU工具链要安装

自己动手写CPU之第七阶段(9)——除法指令说明及实现思路

将陆续上传本人写的新书<自己动手写CPU>,今天是第32篇,我尽量每周四篇 亚马逊的销售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 China-pub的销售地址如下: http://product.china-pub.com/3804025 北发的销售地址如下: http://book.beifabook.com/Product/BookDetail.aspx?Plucode=

自己动手写CPU之第七阶段(10)——除法指令实现过程1

将陆续上传本人写的新书<自己动手写CPU>,今天是第33篇,我尽量每周四篇 亚马逊的销售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 China-pub的销售地址如下: http://product.china-pub.com/3804025 在京东.北发等网上书店均有! 除法指令的实现过程有点长,分两篇博文介绍,今天是第一篇. 7.12 修改OpenMIPS以实现除法指令 7.

自己动手写CPU之第七阶段(11)——除法指令实现过程2

将陆续上传本人写的新书<自己动手写CPU>,今天是第33篇,我尽量每周四篇 亚马逊的销售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 在当当.京东.互动.北发等网上书店均有! 除法指令的实现过程有点长,分两篇博文介绍,今天是第二篇. 7.12.2 修改译码阶段的ID模块 译码阶段的ID模块要增加对除法指令的分析,根据图7-15给出的指令格式可知,除法指令都是SPECIAL类指令,