MIPS五段流水处理器I 顺序实现

尝试使用 Verilog 实现一个 mips 五段流水处理器。

MIPS 指令集

MIPS 指令集分为三类,总共 31 条,分别是

  • I 型
    • 指令内容中带有立即数
    • 最多使用两个寄存器
    • Op 字段用于区别不同指令
  • J 型
    • 长跳转类型
    • 有且仅有一个立即数
    • Op 字段用于区别不同指令
  • R 型
    • 仅使用寄存器的指令
    • Op 字段为 0,使用 funct 字段区别

这三种指令的具体划分以及内容参照这篇博客, 本文中不再赘述。

流水线划分

经典的五段流水线划分为

  1. 取指 Fetch
    • 从 PC 程序计数器指向的地址中取出指令
    • 放入 IR 指令寄存器中
    • PC += 指令长度
  2. 译码 Decode
    • 根据 IR 中的指令内容,得到其所需要的
      • 源操作数(寄存器)
      • 目标操作数寄存器
  3. 执行 Execute
    • 将源操作数(对应的寄存器)加载进 ALU,然后将对应的运算结果保存进目标操作数寄存器
  4. 访存 Memory (仅针对 load/store 指令)
    • load: 从存储器中读出 ALU 计算出的有效地址存储的数据
    • store: 将寄存器中的数据输入 ALU 计算出的有效地址中
  5. 写回 Write Back (仅针对除 store 以外的指令)
    • load: 将读出的数据存储到目标寄存器中
    • 其他指令: 将 ALU 计算出的结果输出到目标寄存器中

稍微多提一句的是,由于我们想要首先制作的是顺序处理器,因此这 5 个步骤中很多地方是没有必要的,比如更新 PC 可以放在最后一步,但是因为我们希望能给流水线寄存器预留拓展,因此还是采用这样符合流水线的设计来制造我们的 cpu。

基本设计图

我们使用自顶向下的设计方法来设计我们的系统,得到的草图如下: sB8HmT.png 图片是我自己画的,可能比较草率,具体的设计可以参照《CSAPP》一书 P302 的这张图

sBGnjP.jpg

因此我们可以得到需要的几个基本模块的划分:

  • 控制器
    • 用于控制传入、传出信号
    • 作为顶层原件存在
  • RAM(严格来说不能算为 cpu 的一部分)
    • 使用冯诺依曼体系结构,同时存储数据和指令
  • 寄存器组
    • 流水线寄存器组 用于存储流水线过程中产生的各种信号和数据,对于用户不可见
    • 内部寄存器组 用于存储对于用户可见的寄存器
  • 五大单元,用于处理各个阶段
    • 取指单元
    • 译码单元
    • 执行单元(主体构成为 ALU)
    • 访存单元
    • 写回单元

使用 verilog 的模块化设计,我们正好得到了一共 9 个模块,我们接下来的任务即设计这几个模块的具体输入以及输出,这里我们使用大写字母来标识某个阶段的输出,小写字母来标识上个阶段的输入。

模块设计

模块名称 输入 输出
F 取指单元 ram_in: 从 ram 接入的指令数据 F_IR: 指令
F_PC: 新的程序计数器值
D 译码单元 f_ir: 指令 D_src: 源寄存器组
D_dst: 目标寄存器组
D_alufun: 运算种类
D_icode: 指令类型
E 执行单元 d_src: 译码得到的输入
d_alufun: 译码得到的运算种类
d_dst
d_icode
E_val: 执行单元得到的运算结果
D_dst: 目标寄存器组
D_icode
M 访存单元 e_val: 执行单元得到的运算结果
d_src (store 指令): 放入存储器的数据
d_dst: 传给下一步
d_icode
M_val: 从内存中读出的数据
E_val: 传给下一步
D_dst: 目标寄存器组
D_icode
W 写回单元 e_val: ALU 运算结果
d_dst: 目标寄存器组
m_val: 内存中读出的数据
d_icode

根据上文的分析,发现由于有流水线寄存器这一机制的存在,不需要设置一个专门的控制器来进行相关的控制,每一阶段的运算只取决于上一阶段流水线寄存器的结果,这样一方面简化了设计,另一方面也让我们以后从顺序向流水线转化更加方便,下一步则是各个模块的 verilog 具体实现。

To be continue...

作者

Hyiker Hu

发布于

2021-01-14

更新于

2021-09-26

许可协议

评论