跳转至

实验 4:流水线处理器设计

任务 1:完成流水线数据通路搭建

请参考单周期的数据通路,正确添加段间寄存器、前递单元、冒险控制单元。

本次实验需要实现的指令包括:

  • 移位指令 sll、slli、srl、srli、sra、srai
  • 算数与逻辑指令 add、addi、sub、xor、xori、or、ori、and、andi
  • 置数指令 slt、sltu、slti、sltiu、auipc、lui
  • 分支指令 beq、blt、bne、bge、bltu、bgeu、jal、jalr
  • 访存指令 lw、lb、lh、lbu、lhu、sw、sb、sh

我们建议大家按照下面的步骤逐步完成从单周期到流水线的改造。

Task 1-1

依据所学的知识点正确添加段间寄存器。

段间寄存器是为了缩短处理器的关键路径长度,以允许不同阶段的模块执行不同的指令。为此,我们将流水线处理器划分为五个不同的阶段:IF、ID、EX、MEM、WB,需要添加四个段间寄存器。

从最简单的角度来说,段间寄存器只需要在每个时钟上升沿将输入传递到输出,不过,为了方便后续的操作,我们需要额外添加四个接口,即 rst、en、stall 和 flush:

  • rst 的效果为同步清空,当此信号高电平时段间寄存器的所有寄存器都将被清空,与 CPU 的 rst 信号连接。
  • en 的作用是让段间寄存器受到 PDU 的控制,保证其与 PC 寄存器 en 端口的同步。段间寄存器的 en 端口同样连接到 global_en。
  • stall 的效果为停驻,若时钟上升沿此信号高电平,输出仍保持之前的值不变,而非接收输入(也即其是反向的写使能信号)。
  • flush 的效果为同步清空,若时钟上升沿此信号高电平,段间寄存器的所有寄存器都将被清空。

一个可以参考的框架如下:

1
2
3
4
5
6
7
8
always @(posedge clk) begin
    if (rst) begin
        // rst 操作的逻辑。如果你使用的是 rstn,请注意替换条件
    end
    else if (en) begin
        // flush 和 stall 操作的逻辑, flush 的优先级更高
    end
end

你需要对 PC 寄存器做类似的处理,因为其也相当于在 IF 段之前的 “段间寄存器”。

什么是清空?

很值得一提的是,清空未必是清零,例如 PC 清空就是回到初始位置。事实上,清空段间寄存器意味着将该段变为(也即调整所有的数据与控制信号)执行一条“什么都不做”的指令,也就是 nop 指令。

除此以外,段间寄存器其实有两种实现方式。直观的想法是,如数据通路一样,每段将该段需要传递的信号传递给下一段,忽略其他信号。然而,这么做会产生两个问题:

  1. 每段的段间寄存器需要重新设计。但有些信号(如寄存器堆写使能 rf_we)事实上需要多次传递, 而有些信号(如寄存器堆的读地址 rf_ra1)则在最初设计时看似不要传递,在处理数据相关时却发现需要传递。设计过程中对模块接口不断的复制、修改容易引起混乱。

  2. 如果信号只保留有限的生命周期,则调试时需要反复查看不同段落正在执行的指令, 这加大了调试的难度。

因此,我们推荐使用统一的段间寄存器模块,让信号一经产生就传递到最后,从而简化了段间寄存器的设计。 对于在上一阶段尚未产生的信号,直接在输入处接 0,忽略其输出,即只保留有效端口的使用——当然,这样也就会导致我们的段间寄存器十分庞大(在 2023 年的实验中,它共有 73 个输入输出端口)。

端口空置

在 Verilog 中,模块的输出端口允许空置,但输入端口不允许,否则会发生错误。因此对于无用的输入,可以将其端口置 0。例如

.useless_input(32'b0)  // A 32-bits useless input port

与框架的适配

  1. commit 信号在 IF 段产生,同样经过整个流水段后连入 commit_reg。为此,你可以在 IF 段定义

    assign commit_if = 1'H1;
    

    值得注意的是,在段间寄存器 flush 时,不要忘记将指令的 commit 信号清空。否则仿真框架会认为你提交了错误的指令!

  2. commit 系列的信号需要从其产生开始,经过段间寄存器一直流水到最后。

  3. debug 端口不需要经过段间寄存器,从寄存器堆接出后直接连到 CPU 外侧。

此时,你的处理器应当能够正常运行——如果指令序列中不存在任何相关的话。

Task 1-2

正确设计前递模块。

在本次实验中,所有的数据前递仅考虑两种情况:相邻指令前递(MEM 段前递到 EX 段);间隔一条指令前递(WB 段前递到 EX 段)。对于间隔两条指令的前递(WB 段前递到 ID 段),我们可以使用寄存器堆的写优先解决。此时,前递模块的示意图如下:

Image title
前递模块

这里对端口进行简单解释:

  • rf_we 指寄存器堆写使能信号,WB 与 MEM 段都需传入前递单元。一个有意思的思考点:为什么需要写使能信号呢?
  • rf_wa 指寄存器堆写地址,WB 与 MEM 段都需传入前递单元,用于比对。
  • rf_wd 指寄存器堆写数据,在 WB 段直接传入即可,在 MEM 段可将 alu_res 传入(见下方解释)。
  • rf_ra0_ex 与 rf_ra1_ex 为本次实验中要求传入 EX 段的信号,用于与 MEM 段、WB 段信号比对确定前递是否发生。
  • rf_rd0_fe 与 rf_rd1_fe 为前递使能信号,为 1 代表前递发生。
  • rf_rd0_fd 与 rf_rd1_fd 为前递数据信号,当前递使能为 1 时即代表前递的数据。

此时,ALU 的源操作数选择会在单周期的基础上,额外增加一级由 rf_rdx_fe 控制的选择。这一级选择应当在 pcadd4_ex/rf_rd0_ex、imm_ex/rf_rd1_ex 的选择之后进行。不要忘记,分支模块的源操作数也需要考虑前递!

前递模块的简化

你可能注意到了,我们并没有让 rf_wd_sel_mem 与 pcadd4_mem 传入前递模块,这是因为流水线发生前递时直接前递 alu_res,结果仍然是正确的,因为 jalr 指令会使得接下来两条指令被冲刷掉,即使检测到了前递也是无效的;而针对 load 指令我们会进行特判,不会出现 MEM 段前递到 EX 段的情况。

现在,你的处理器应当能够处理非 Load-Use 的数据相关。

Task 1-3

正确处理 flush 与 stall。

在本次实验中,所有的控制冒险以及 Load-Use 冒险 均在 ID - EX 段进行判断。如果 EX 段为 load 指令且与 ID 段的指令数据相关;或 EX 段为需要跳转的指令,则需要产生相应的段间寄存器控制信号。该模块的示意图如下:

Image title
段间寄存器控制模块

这里对端口进行简单解释:

  • rf_we 指寄存器堆写使能信号,EX 段需要传入以判断 Load-Use 冒险。
  • rf_wd_sel 指寄存器堆写数据的选择,EX 段需要传入以判断 Load-Use 冒险。
  • rf_wa 指寄存器堆写地址,EX 段需要传入以判断 Load-Use 冒险。
  • rf_ra0_id 与 rf_ra1_id 用于比对确定 Load-Use 冒险是否发生。
  • npc_sel 为下个 PC 选择器的控制信号,用于确定控制冒险是否发生。
  • 输出信号为各段间寄存器对应的 stall 与 flush 信号。

你需要仔细考虑:不同情况下应当 stall/flush 哪些寄存器。

在最终完成的 CPU 里,除了跳转指令需要三周期、Load-Use 冒险发生的情况需要两周期外,其他情况可以做到一周期一条指令,大大改进了执行速度。

任务 2:验证正确性

Task 2

我们准备了一些测试文件,用于检测处理器执行指令的正确性。

你可以使用 Vivado 的波形图,对比处理器运行完成后寄存器堆的数值是否与 rars 一致;也可以将上面的指令载入仿真框架,由框架自动评测正确性(推荐后者)。

任务 3:上板运行

Task 3-1

使用 PDU 上板框架在开发板上部署单周期处理器。v2 版本的PDU 的源文件在这里,v3 版本的 PDU 源文件在这里

需要注意的是,v2 版本的 PDU 波特率为 9600,需要考虑 rstn 信号取反;v3 版本的 PDU 波特率为 115200,需要正确设置 global_config.vh 中的内容。

提醒

如果你始终无法在物理开发板上部署处理器,可以在 FPGAOL 上进行部署,但会损失小部分分数。此外,我们强烈建议大家在仿真阶段确定 CPU 功能正确性后,再上板验证时序正确性

Task 3-2

上板运行汇编排序程序、指令测试程序,并与 rars 的结果进行对比。

选做任务 1:乘除法扩展

Task X-1

在实现上述指令的基础上,额外实现 mul, mulh, mulhu, div, divu, rem, remu 指令。请自行设计指令测试程序验证其正确性,并进行仿真与上板测试。注意:不允许直接使用 *%\ 运算符。

选做任务 2:分支前移

Task X-2

我们可以在 ID 段判断分支是否执行,从而在分支预测失败时只冲刷 2 条指令。请据此设计对应的前递与停顿策略,并对比与在 EX 段判断分支可能的优缺点。如果你选择完成此项,可以不做 EX 段判断分支的版本。

实验检查与提交

本次实验布置时间为 2025-04-16,持续两周。相应的 DDL 节点如下:

检查 DDL 报告提交 DDL
2025-04-30 21:00 2025-05-07 23:59

检查与报告延迟一周以内(含)的,至多只能得到 80% 分数;延迟一周以上、两周以内(含)的,至多只能得到 60% 分数;延迟超过两周的不得分。

提醒

实验的 DDL 为当天晚上 21:00。助教有权利在 21:00 准时停止检查,请大家合理安排好自己的时间。

关于实验报告

实验报告需要大家提交 PDF 格式。我们推荐大家使用 Markdown 或者 Latex 撰写实验报告,这样可以直接导出为 PDF。大家也可以使用 Word、WPS 等进行报告撰写,最后转换为 PDF 格式。我们不推荐也不拒绝大家手写实验报告,但请将其拍照扫描成 PDF 文件后再上传。我们不接受任何因为文件格式错误而导致成绩异常的申诉请求!

在实验报告中,你需要给出每一项任务的答案,并附上必要的说明过程(或截图)。

特别地:实验报告的字数和排版与最终得分无关。影响得分的仅有内容正确性与完整性。

实验报告请提交至 BB 平台。

评论