本项目总体上资源参考雷思磊老师的《自己动手写CPU》,在此向雷老师致敬。
该项目本为学校课程要求,但就我校学生来说难度较大,加之对硬件感兴趣学生较少, 导致完成量较少,故此公布自己验收后的源码,给后续同学做一个参考。
由于本学校对学生要求难度还不是很高,仅要求实现MIPS的32条简单指令。
主要有以下几类:
- 逻辑操作指令: and、andi、or、ori、xor、xori、nor、lui
- 移位操作指令: sll、sllv、sra、srav、srl、srlv
- 算数操作指令: add、addi、addiu、addu、sub、subu、slt、slti、sltiu、sltu
- 转移操作指令: j、jr、jal、beq、bne
- 加载操作指令: lw、sw
具体指令被内容参照文件MIPS32条指令.pdf
##主要功能模块 由于只需要实现32条指令,其中还不包括“乘法“、“除法”等复杂的运算指令,故只需要基本模块即可实现要求。
模块连接图参照openmips模块连接关系图.png
。
在此只介绍主要功能模块,if_id、id_ex、ex_mem、mem_wb模块只起两个模块之间的传值 作用,故此不做介绍。
本CPU设计时加入了流水线的设计,为了解决数据相关会导致的错误,加入了数据前推的操作, 所以在译码和访存模块,会见到有数据回传至译码模块,这样做的目的是在,提升效率的同时,保证数据的正确性。
也即程序计数器,对应文件为pc_reg.v
,主要实现的细节很简单:
rst
为重置输入,rst使能时禁止当前重置当前的程序计数器,芯片使能停止。branch_flag_i
、branch_target_address_i
在转移指令时才会使用,使能时 将程序计数器的值置为目标地址,branch_flag_i为使能标志,branch_target_address_i为目标地址。
pc
为指令地址,输出至rom中取值。ce
为芯片使能。
根据PC模块传入的地址取得目标指令,并送入译码模块进行译码。
addr
为pc传进来的要执行的指令的地址。
inst
为取到的指令(32位)。
众所周知为译码模块,对取得的指令进行分析。
pc_i
从pc模块传入的指令地址,在移动指令中才会用到,根据目前的指令地址,进行操作得出新指令。inst_i
为取到的指令。ex_aluop_i
为执行模块执行的运算类型ex_wreg_i
执行模块执行指令是否要写寄存器ex_wdata_i
执行模块执行指令要写寄存器的值ex_wd_i
执行模块要执行指令要写寄存器的地址mem_wreg_i
回写模块执行指令是否要写寄存器mem_wdata_i
回写模块执行指令要写寄存器的值mem_wd_i
回写模块要执行指令要写寄存器的地址reg1_data_i
从寄存器中取得的reg1对应的值reg2_data_i
从寄存器中取得的reg2对应的值
reg1_read_o
为第一个读寄存器端口的使能信号reg2_read_o
为第二个读寄存器端口的使能信号reg1_addr_o
为第一个读寄存器端口的使能信号reg2_read_o
为第二个读寄存器端口的地址信号aluop_o
为运算子类型alusel_o
为运算类型reg1_o
译码阶段源操作数1reg2_o
译码阶段源操作数2wd_o
译码阶段要写入寄存器的地址wreg_o
译码阶段是否有要写入的寄存器inst_o
指令branch_flag_o
上文提到的,只有在转移指令执行时才会用到,标识变量branch_target_address_o
目标转移地址link_addr_o
译码阶段需要保存的当前指令地址(用于转移指令时保存返回地址)
###Regfile 即模拟的寄存器模块,MIPS拥有32个32位寄存器,此处用32*32的二维数组表示该寄存器。 寄存器模块一般由译码模块请求,根据读寄存器端口的地址信号,读出寄存器中存储的指令,经执行-访存-回写后,由回写模块将需要保存的值 写入对应地址。
waddr
要写入的寄存器的地址wdata
要写入的数据we
写使能信号raddr1
第一个读寄存器端口要读取的地址raddr2
第二个读寄存器端口要读取的地址re1
第一个读寄存器使能信号re2
第二个读寄存器使能信号
rdata1
第一个读寄存器端口输出的寄存器的值rdata2
第二个读寄存器端口输出的寄存器的值
根据译码模块的译码结果进行相应的运算,将运算结果传给下一个模块。
alusel_i
执行阶段要执行的运算类型aluop_i
执行阶段要执行的运算子类型reg1_i
参与运算的源操作数1reg2_i
参与运算的源操作数2wd_i
指令执行要写入的寄存器地址wreg_i
是否要写入寄存器的使能变量inst_i
取得的指令link_address_i
译码阶段需要保存的指令地址
wd_o
同输入wreg_o
同输入wdata_o
执行阶段运算后要写入的寄存器的值mem_addr_o
加载、存储指令对应的ram
地址aluop_o
reg2_o
该模块为访存模块,即为将执行模块的结果,根据需要写入寄存器或内存。
wd_i
访存阶段的指令要写入的寄存器地址wreg_i
访存阶段的指令要写入的寄存器的使能变量wdata_i
访存阶段的指令要写入的寄存器的值mem_addr_i
访存阶段的指令要写入的ram地址reg2_i
访存阶段,存储、加载指令要存储的数据mem_data_i
从存储器中读出的值
wd_o
同输入wreg_o
同输入wdata_o
同输入mem_addr_o
同输入mem_we_o
是否为写操作mem_sel_o
字节选择信号mem_data_o
要写入存储器的值mem_ce_o
存储器使能信号
ce
芯片使能信号we
写入存储器的使能信号addr
写入存储器的地址sel
字节选择信号data_i
最终写入存储器的值
data_o
从存储器中读出的值
openmips.v
为顶层模块,对除ROM部分的所有模块例化连接,连接关系见openMips连接关系图
, 形成一个最简易CPU。openmips_min_sopc.v
加入ROM部分,形成一个SOPC,使用initial指令初始化ROM,OpenMIPS从指令存储器读取指令,进入OpenMIPS执行。opmips_min_sopc_tb.v
仿真文件inst_rom.data.v
指令文件