Skip to content

Hanmengnan/MIPS-CPU-implementation

Repository files navigation

前言

本项目总体上资源参考雷思磊老师的《自己动手写CPU》,在此向雷老师致敬。

该项目本为学校课程要求,但就我校学生来说难度较大,加之对硬件感兴趣学生较少, 导致完成量较少,故此公布自己验收后的源码,给后续同学做一个参考。

由于本学校对学生要求难度还不是很高,仅要求实现MIPS的32条简单指令。

主要有以下几类:

  1. 逻辑操作指令: and、andi、or、ori、xor、xori、nor、lui
  2. 移位操作指令: sll、sllv、sra、srav、srl、srlv
  3. 算数操作指令: add、addi、addiu、addu、sub、subu、slt、slti、sltiu、sltu
  4. 转移操作指令: j、jr、jal、beq、bne
  5. 加载操作指令: lw、sw

具体指令被内容参照文件MIPS32条指令.pdf

##主要功能模块 由于只需要实现32条指令,其中还不包括“乘法“、“除法”等复杂的运算指令,故只需要基本模块即可实现要求。

模块连接图参照openmips模块连接关系图.png

在此只介绍主要功能模块,if_id、id_ex、ex_mem、mem_wb模块只起两个模块之间的传值 作用,故此不做介绍。

本CPU设计时加入了流水线的设计,为了解决数据相关会导致的错误,加入了数据前推的操作, 所以在译码和访存模块,会见到有数据回传至译码模块,这样做的目的是在,提升效率的同时,保证数据的正确性。

PC

也即程序计数器,对应文件为pc_reg.v,主要实现的细节很简单:

输入:

  1. rst为重置输入,rst使能时禁止当前重置当前的程序计数器,芯片使能停止。
  2. branch_flag_ibranch_target_address_i在转移指令时才会使用,使能时 将程序计数器的值置为目标地址,branch_flag_i为使能标志,branch_target_address_i为目标地址。

输出:

  1. pc为指令地址,输出至rom中取值。
  2. ce为芯片使能。

ROM

根据PC模块传入的地址取得目标指令,并送入译码模块进行译码。

输入:

  1. addr为pc传进来的要执行的指令的地址。

输出:

  1. inst为取到的指令(32位)。

ID

众所周知为译码模块,对取得的指令进行分析。

输入:

  1. pc_i 从pc模块传入的指令地址,在移动指令中才会用到,根据目前的指令地址,进行操作得出新指令。
  2. inst_i为取到的指令。
  3. ex_aluop_i 为执行模块执行的运算类型
  4. ex_wreg_i 执行模块执行指令是否要写寄存器
  5. ex_wdata_i 执行模块执行指令要写寄存器的值
  6. ex_wd_i执行模块要执行指令要写寄存器的地址
  7. mem_wreg_i 回写模块执行指令是否要写寄存器
  8. mem_wdata_i 回写模块执行指令要写寄存器的值
  9. mem_wd_i回写模块要执行指令要写寄存器的地址
  10. reg1_data_i 从寄存器中取得的reg1对应的值
  11. reg2_data_i 从寄存器中取得的reg2对应的值

输出:

  1. reg1_read_o为第一个读寄存器端口的使能信号
  2. reg2_read_o为第二个读寄存器端口的使能信号
  3. reg1_addr_o为第一个读寄存器端口的使能信号
  4. reg2_read_o为第二个读寄存器端口的地址信号
  5. aluop_o为运算子类型
  6. alusel_o为运算类型
  7. reg1_o译码阶段源操作数1
  8. reg2_o译码阶段源操作数2
  9. wd_o译码阶段要写入寄存器的地址
  10. wreg_o译码阶段是否有要写入的寄存器
  11. inst_o指令
  12. branch_flag_o上文提到的,只有在转移指令执行时才会用到,标识变量
  13. branch_target_address_o目标转移地址
  14. link_addr_o译码阶段需要保存的当前指令地址(用于转移指令时保存返回地址)

###Regfile 即模拟的寄存器模块,MIPS拥有32个32位寄存器,此处用32*32的二维数组表示该寄存器。 寄存器模块一般由译码模块请求,根据读寄存器端口的地址信号,读出寄存器中存储的指令,经执行-访存-回写后,由回写模块将需要保存的值 写入对应地址。

输入:

  1. waddr 要写入的寄存器的地址
  2. wdata要写入的数据
  3. we写使能信号
  4. raddr1第一个读寄存器端口要读取的地址
  5. raddr2第二个读寄存器端口要读取的地址
  6. re1第一个读寄存器使能信号
  7. re2第二个读寄存器使能信号

输出:

  1. rdata1第一个读寄存器端口输出的寄存器的值
  2. rdata2第二个读寄存器端口输出的寄存器的值

EX

根据译码模块的译码结果进行相应的运算,将运算结果传给下一个模块。

输入:

  1. alusel_i执行阶段要执行的运算类型
  2. aluop_i执行阶段要执行的运算子类型
  3. reg1_i参与运算的源操作数1
  4. reg2_i参与运算的源操作数2
  5. wd_i指令执行要写入的寄存器地址
  6. wreg_i是否要写入寄存器的使能变量
  7. inst_i取得的指令
  8. link_address_i译码阶段需要保存的指令地址

输出:

  1. wd_o同输入
  2. wreg_o同输入
  3. wdata_o执行阶段运算后要写入的寄存器的值
  4. mem_addr_o加载、存储指令对应的ram地址
  5. aluop_o
  6. reg2_o

MEM

该模块为访存模块,即为将执行模块的结果,根据需要写入寄存器或内存。

输入:

  1. wd_i访存阶段的指令要写入的寄存器地址
  2. wreg_i访存阶段的指令要写入的寄存器的使能变量
  3. wdata_i访存阶段的指令要写入的寄存器的值
  4. mem_addr_i访存阶段的指令要写入的ram地址
  5. reg2_i访存阶段,存储、加载指令要存储的数据
  6. mem_data_i从存储器中读出的值

输出:

  1. wd_o同输入
  2. wreg_o同输入
  3. wdata_o同输入
  4. mem_addr_o同输入
  5. mem_we_o是否为写操作
  6. mem_sel_o字节选择信号
  7. mem_data_o要写入存储器的值
  8. mem_ce_o存储器使能信号

RAM

输入:

  1. ce芯片使能信号
  2. we写入存储器的使能信号
  3. addr写入存储器的地址
  4. sel字节选择信号
  5. data_i最终写入存储器的值

输出:

  1. data_o从存储器中读出的值

模块调用

  1. openmips.v为顶层模块,对除ROM部分的所有模块例化连接,连接关系见openMips连接关系图, 形成一个最简易CPU。
  2. openmips_min_sopc.v加入ROM部分,形成一个SOPC,使用initial指令初始化ROM,OpenMIPS从指令存储器读取指令,进入OpenMIPS执行。
  3. opmips_min_sopc_tb.v仿真文件
  4. inst_rom.data.v指令文件

About

Use verilog to implement MIPS’s 32 simple instructions

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published