北航co计算机组成P5设计文档

写在前面:
本人 P5 课上其实有bug,也没有写好一些扩展性很好的接口,
还有一些比较奇怪的设计,比如 lui 指令偷了一个周期,
GRF也没有设计内部转发,
流水线寄存器传递的也太多太乱,
所以本人在 P6 几乎进行了一整个重构
P6 设计文档指路链接在下面。
(一周写 P5 确实很不合理,如果时间紧张写不完不妨主动gap一周。)
P5CPU设计文档及思考题
设计草稿
1.指令详解
- ADD:add RD, RS, RT
编码:000000 sssss ttttt ddddd 00000 100000
描述:GPR[rd] = GPR[rs] + GPR[rt] - SUB:sub RD, RS, RT
编码:000000 sssss ttttt ddddd 00000 100010
描述:GPR[rd] = GPR[rs] - GPR[rt] - ORI:ori RT, RS, IMM
编码:001101 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = GPR[rs] | zero_extend(IMM) - LW:lw RT, OFFSET(RS)
编码:100011 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = MEM[GPR[rs] + sign_ext(OFFSET)] - SW:sw RT, OFFSET(RS)
编码:101011 sssss ttttt iiiii iiiii iiiiii
描述:MEM[GPR[rs] + sign_ext(OFFSET)] = GPR[rt] - BEQ:beq RS, RT, LABEL
编码:000100 sssss ttttt iiiii iiiii iiiiii
描述:if GPR[rs] == GPR[rt], PC = PC + 4 + sign_ext(LABEL); else PC = PC + 4; - lui:lui RT, IMM
编码:001111 00000 ttttt iiiii iiiii iiiiii
描述:GPR[rt] = sign_ext(IMM) << 16; - jal: jal target
编码:000011 iiiii iiiii iiiii iiiii iiiiii
描述:GPR[31] = PC + 4; PC = PC[31,28]||instr_index||00 - jr: jr rs
编码:000000 sssss 00000 00000 00000 001000
描述:PC = GPR[rs] - nop:nop
编码:000000 00000 00000 00000 00000 000000
描述:GPR[0] = GPR[0]
2.各部件接口设计
NPC
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| PC | I | 31:0 | 前一拍指令地址 |
| offset | I | 31:0 | 用于计算B类指令跳转地址 |
| Ins_26 | I | 25:0 | 用于计算J、JAL跳转地址 |
| Ra | I | 31:0 | 用于读取JR、JALR中寄存器存储的地址 |
| B_judge | I | 1 | 用于判断B类指令是否满足跳转条件 |
| if_Branch | I | 1 | 用于判断是否为B类指令 |
| if_JR | I | 1 | 用于判断是否为J、JR指令 |
| Next_PC | O | 31:0 | 下一拍指令地址 |
| PC+4 | O | 31:0 | 上一拍指令地址+4,用于JAL、JALR地址存储 |
注意跳转指令PC要是它本身的PC,即beq指令的PC要和上一个周期的一样,不和延迟槽一样,不用+4.
IFU
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| clk | I | 1 | 时钟信号 |
| Reset | I | 1 | 同步复位信号 |
| Next_PC | I | 31:0 | 计算出的下一拍指令地址 |
| En | I | 1 | 有效信号,阻塞时为0 |
| PC | O | 31:0 | 当前指令地址 |
| Instr | O | 31:0 | 当前指令内容 |
GRF
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| PC | I | 31:0 | 当前指令地址 |
| RA1 | I | 4:0 | 读寄存器1地址 |
| RA2 | I | 4:0 | 读寄存器2地址 |
| WA | I | 4:0 | 写寄存器地址 |
| WD | I | 31:0 | 写寄存器数据 |
| clk | I | 1 | 时钟信号 |
| reset | I | 1 | 同步复位信号 |
| RegWrite | I | 1 | 写寄存器控制信号 |
| RD1 | O | 31:0 | 读寄存器1数据 |
| RD2 | O | 31:0 | 读寄存器2数据 |
ALU
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| ALU_Op | I | 3:0 | ALU操作码 |
| Src_A | I | 31:0 | ALU输入A数据 |
| Src_B | I | 31:0 | ALU输入B数据 |
| shift | I | 5:0 | 位移量(对B数据进行位移) |
| ALU_Result | O | 31:0 | ALU输出结果 |
DM
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| clk | I | 1 | 时钟信号 |
| Reset | I | 1 | 同步复位信号 |
| PC | I | 31:0 | 当前指令地址 |
| MemWrite | I | 1 | 写内存控制信号 |
| A | I | 31:0 | 写地址 |
| WD | I | 31:0 | 写数据 |
| RD | O | 31:0 | 读数据 |
EXT
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| Imm | I | 15:0 | 扩展前的立即数 |
| if_sign | I | 1 | 是否为有符号扩展 |
| offset | O | 31:0 | 扩展后的立即数 |
CMP
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| D1 | I | 31:0 | 数据1 |
| D2 | I | 31:0 | 数据2 |
| CMPOP | I | 2:0 | 比较操作码 |
| out | O | 1 | 比较结果 |
lui
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| Imm | I | 15:0 | 扩展后的立即数 |
| lui_reset | O | 31:0 | 计算结果 |
3.数据通路设计
NPC
| 操作 | PC | offset | Ins_26 | RA | if_Branch | if_jr | if_jal |
|---|---|---|---|---|---|---|---|
| all | IFU | EXT | splitter | GRF | control | control | control |
IFU
| 操作 | clk | Reset | Next_PC |
|---|---|---|---|
| all | clk | reset | NPC |
GRF
| 操作 | clk | Reset | RA1 | RA2 | WA | WD | RegWrite |
|---|---|---|---|---|---|---|---|
| add | clk | reset | splitter[s] | splitter[t] | splitter[d] | ALU | control |
| sub | clk | reset | splitter[s] | splitter[t] | splitter[d] | ALU | control |
| ori | clk | reset | splitter[s] | - | splitter[t] | ALU | control |
| lw | clk | reset | splitter[s] | - | splitter[t] | DM | control |
| sw | clk | reset | splitter[s] | splitter[t] | - | - | control |
| beq | clk | reset | splitter[s] | splitter[t] | - | - | control |
| lui | clk | reset | - | - | splitter[t] | ALU | control |
| jal | clk | reset | - | - | 31 | NPC | control |
| jr | clk | reset | splitter[s] | - | - | - | control |
ALU
| 操作 | ALU_Op | Src_A | Src_B | shift |
|---|---|---|---|---|
| add | control | GRF-RD1 | GRF-RD2 | - |
| sub | control | GRF-RD1 | GRF-RD2 | - |
| ori | control | GRF-RD1 | EXT | - |
| lw | control | GRF-RD1 | EXT | - |
| sw | control | GRF-RD1 | EXT | - |
| beq | control | GRF-RD1 | GRF-RD2 | - |
| lui | control | - | EXT | 16 |
| jal | - | - | - | - |
| jr | - | - | - | - |
DM
| 操作 | clk | Reset | MemWrite | A | WD |
|---|---|---|---|---|---|
| lw | clk | reset | control | ALU-Result | - |
| sw | clk | reset | control | ALU-Result | GRF-RD2 |
| else | - | - | - | - | - |
EXT
| 操作 | Imm | if_sign |
|---|---|---|
| all | splitter[i] | control |
4.流水线寄存器
F/D
| 名称 | 位宽 | 描述 |
|---|---|---|
| clk | 1 | 时钟信号 |
| reset | 1 | 同步复位信号 |
| en | 1 | 有效信号,阻塞时为0 |
| - | - | - |
| PC | 31:0 | 指令地址 |
| Instr | 31:0 | 指令内容 |
| PC+8 | 31:0 | Jal存的内容 |
D/E
| 名称 | 位宽 | 描述 |
|---|---|---|
| clk | 1 | 时钟信号 |
| reset | 1 | 同步复位信号 |
| clr | 1 | 清空信号,阻塞时为0 |
| - | - | - |
| PC | 31:0 | 指令地址 |
| op | 5:0 | 指令操作码 |
| Rfunct | 5:0 | R指令功能码 |
| useReg | 1 | 是否使用寄存器 |
| WA | 4:0 | 写寄存器地址 |
| WD | 31:0 | 写寄存器数据 |
| RegWrite | 1 | 写寄存器控制信号 |
| Tnew | 1 | Tnew = (Tnew > 0)? Tnew - 1 : Tnew |
| - | - | - |
| RD1 | 31:0 | 读寄存器1数据 |
| RD2 | 31:0 | 读寄存器2数据 |
| rs | 4:0 | 读寄存器1地址 |
| rt | 4:0 | 读寄存器2地址 |
| offset | 31:0 | 扩展后的立即数 |
| shift | 5:0 | 位移量(对B数据进行位移) |
E/M
| 名称 | 位宽 | 描述 |
|---|---|---|
| clk | 1 | 时钟信号 |
| reset | 1 | 同步复位信号 |
| - | - | - |
| PC | 31:0 | 指令地址 |
| op | 5:0 | 指令操作码 |
| Rfunct | 5:0 | R指令功能码 |
| useReg | 1 | 是否使用寄存器 |
| WA | 4:0 | 写寄存器地址 |
| WD | 31:0 | 写寄存器数据 |
| RegWrite | 1 | 写寄存器控制信号 |
| Tnew | 1 | Tnew = (Tnew > 0)? Tnew - 1 : Tnew |
| - | - | - |
| RD2 | 31:0 | 读寄存器2数据 |
| ALU_Result | 31:0 | ALU输出结果 |
M/W
| 名称 | 位宽 | 描述 |
|---|---|---|
| clk | 1 | 时钟信号 |
| reset | 1 | 同步复位信号 |
| - | - | - |
| PC | 31:0 | 指令地址 |
| WA | 4:0 | 写寄存器地址 |
| WD | 31:0 | 写寄存器数据 |
| RegWrite | 1 | 写寄存器控制信号 |
| Tnew | 1 | Tnew = (Tnew > 0)? Tnew - 1 : Tnew |
5.控制信号
总控制
| 数值 | MemToReg | MemWrite | ALUSrc | RegWrite | signedEXT | RegDst | if_Branch | if_JR | if_JAL | ALUToReg | Ralink | luitoReg |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 控制的位置 | GRF-WD | DM-MemWrite | ALU-SrcB | GRF-RegWrite | EXT-if_sign | GRF-WA | NPC-if_Branch | NPC-if_JR | NPC-if_JAL | GRF-WD | GRF-WA | ALU_shift |
| 0 | ALU | unable | GRF-RD2 | unable | zero_extend | splitter[t] | NOT-BEQ | NOT-JR | NOT-JAL | WD不是ALU结果 | - | lui_result是WD需要的 |
| 1 | DM | enable | EXT | enable | sign_extend | splitter[d] | BEQ | JR | JAL | WD不是ALU结果 | 将PC+4存入寄存器的编号(31)(WA的分支) | lui_result是WD需要的 |
ALU控制
0000 : +
0001 : -
0010 : |
0011 : 数据B向左位移shift位
cmp控制
000: ==
指令对应控制信号
| 指令 | ALU操作 | cmp控制 | ALUcontrol | MemToReg | MemWrite | ALUSrc | RegWrite | signedEXT | RegDst | if_Branch | if_JR | if_JAL | ALUToReg | Ralink | luiToreg |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| add | + | 0000 | 000 | 0 | 0 | 0 | 1 | - | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
| sub | - | 0001 | 000 | 0 | 0 | 0 | 1 | - | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
| ori | or | 0010 | 000 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| lw | + | 0000 | 000 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| sw | + | 0000 | 000 | - | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| beq | == | - | 000 | 0 | 0 | 0 | 0 | 1 | - | 1 | 0 | 0 | 0 | 0 | 0 |
| lui | << | 0011 | 000 | 0 | 0 | 1 | 1 | - | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| jal | - | - | 000 | 0 | 0 | - | 1 | - | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
| jr | - | - | 000 | 0 | 0 | - | 0 | - | - | 0 | 1 | 0 | 0 | 0 | 0 |
D控制
RegWrite,signedEXT,RegDst,if_Branch,if_JR,if_JAL,luiToReg,Ralink,cmp_op
E控制
ALUSrc,ALUToReg,ALU_Op
M控制
MemToReg,MemWrite
阻塞转发
Tuse 和 Tnew 的分析
Tuse表示数据到了 D 级之后还需要多少个周期要使用,每个指令的是固定不变的。
Tnew表示数据还有多长时间产生,会随着数据的流水动态的减少。(但是大于1)
Tuse表格
| 指令 | rs_Tuse | rt_Tuse |
|---|---|---|
| R(除了jr) | 1 | 1 |
| ori | 1 | - |
| lw | 1 | - |
| sw | 1 | 2 |
| beq | 0 | 0 |
| JR | 0 | - |
| 其他的都是if_use=0 |
D级时Tnew表格
| 指令 | Tnew |
|---|---|
| R(除了jr) | 2 |
| ori | 2 |
| lw | 3 |
| lui | 1 |
| jal | 0 |
| 其他的都是RegWrite=0 |
阻塞转发的判断
当Tuse≥Tnew时,可以通过转发解决;当Tuse < Tnew时,必须阻塞流水线。
阻塞操作:
冻结PC寄存器(IFU_en = ~stall = 0)
冻结D级寄存器(D_en = ~stall = 0)
清空E级寄存器(E_clr = stall = 1)
转发操作:
转发需要:
- GRF内部转发
- D需要E、M的转发
- E需要M、W的转发
- M需要W的转发
课上
保证课下正确很重要!可以写评测机,或者使用伟大的cot评测机!!
P5以及P6的题目类型都比较固定,可以提前写好一些接口,比如control可以增加判断新指令的接口,流水线寄存器也可以增加传递的接口,接口可以命名为new之类的,方便课上使用。
以下是课上题目的一些分析:
1. 计算类型
形如:
1 | WriteData <- f(GPR[rs], GPR[rt]) |
做法:
- 在控制模块中识别新指令,设置控制信号
- 新增 ALU 计算类型
- 新增各流水线级转发数据
仿照 add 指令编写
2. 条件跳转类型
形如:
1 | condition <- f(GPR[rs], Ext_Ans) |
- 控制模块中设置控制信号
- 计算 condition
- 跳转仿照 beq
- 条件写入用特判+凌驾的做法,记得改 D-E 级流水线寄存器的输入
- 清空延迟槽,新增控制信号Clr_F,Clr_F为真的条件:condition为假 && D级是新指令 && 此时不能阻塞
3. 访存类型
形如:
1 | DMdata <- DM[addr] |
- data、dest_reg 是在 W级 算出来的,M-W 流水线传 rt
- W级该指令转发 data
- dest_reg 最好在 没算出来之前都是 0,防止转发冲突
- dest_reg 的范围很重要
- 比较阻塞的时候,依次比较E,M级,条件为:是新指令 && 要写寄存器 && 寄存器处在 dest_reg 范围内 && RA!=0 && Tuse < Tnew
往年题依旧指路室友博客
思考题点击展开,不保证正确性,建议自己思考
思考题
1、我们使用提前分支判断的方法尽早产生结果来减少因不确定而带来的开销,但实际上这种方法并非总能提高效率,请从流水线冒险的角度思考其原因并给出一个指令序列的例子。
提前判断意味着需要更早从寄存器堆读取数据,如果数据还未写回(比如上一条指令的 ALU 结果在 EX 阶段才产生),就需要转发或停顿。如果分支判断依赖于上一条指令的结果(比如 LW 后跟 BEQ 使用刚加载的值),即使提前到 ID 阶段判断,仍然需要等待数据就绪,否则必须停顿。
2、因为延迟槽的存在,对于 jal 等需要将指令地址写入寄存器的指令,要写回 PC + 8,请思考为什么这样设计?
jal指令在IF阶段取指,下一条指令(延迟槽指令)在IF的同时,jal进入ID阶段。因为延迟槽指令一定会执行,所以 jal 应该返回到延迟槽之后的指令,即 PC+8,这样才能跳过延迟槽,正确返回。
3、我们要求大家所有转发数据都来源于流水寄存器而不能是功能部件(如 DM 、 ALU ),请思考为什么?
如果直接从功能部件转发,可能因为组合逻辑路径过长导致时钟周期过长,导致性能下降。
4、我们为什么要使用 GPR 内部转发?该如何实现?
目的:解决写数据冒险。当一条指令在WB阶段写寄存器,同时下一条指令在ID阶段读同一个寄存器,如果不处理,会读到旧值。
实现:在寄存器堆的写端口与读端口之间添加内部转发逻辑。比较 WB 阶段要写入的寄存器地址与 ID 阶段要读取的寄存器地址。如果地址相同且写使能有效,则将 WB 阶段要写入的数据直接作为读出的数据送给 ID 阶段,而不等待真正写入寄存器堆。这样 ID 阶段能立即拿到刚计算出的结果,避免 1 个周期的停顿。
5、我们转发时数据的需求者和供给者可能来源于哪些位置?共有哪些转发数据通路?
数据需求者:ID 阶段、EX 阶段、MEM 阶段
数据供给者:MEM 阶段、WB 阶段,还有GPR内部转发。
转发数据通路:
MEM 阶段/WB 阶段/GPR内部转发 -> ID 阶段
MEM 阶段/WB 阶段 -> EX 阶段
WB 阶段 -> MEM 阶段
6、在课上测试时,我们需要你现场实现新的指令,对于这些新的指令,你可能需要在原有的数据通路上做哪些扩展或修改?提示:你可以对指令进行分类,思考每一类指令可能修改或扩展哪些位置。
1. 计算类型
形如:
1 | WriteData <- f(GPR[rs], GPR[rt]) |
做法:
- 在控制模块中识别新指令,设置控制信号
- 新增 ALU 计算类型
- 新增各流水线级转发数据
仿照 add 指令编写
2. 条件跳转类型
形如:
1 | condition <- f(GPR[rs], Ext_Ans) |
- 控制模块中设置控制信号
- 计算 condition
- 跳转仿照 beq
- 条件写入用特判+凌驾的做法,记得改 D-E 级流水线寄存器的输入
- 清空延迟槽,新增控制信号Clr_F,Clr_F为真的条件:condition为假 && D级是新指令 && 此时不能阻塞
3. 访存类型
形如:
1 | DMdata <- DM[addr] |
- data、dest_reg 是在 W级 算出来的,M-W 流水线传 rt
- W级该指令转发 data
- dest_reg 最好在 没算出来之前都是 0,防止转发冲突
- dest_reg 的范围很重要
- 比较阻塞的时候,依次比较E,M级,条件为:是新指令 && 要写寄存器 && 寄存器处在 dest_reg 范围内 && RA!=0 && Tuse < Tnew
7、确定你的译码方式,简要描述你的译码器架构,并思考该架构的优势以及不足。
译码方式:罗列控制信号每种取值所对应指令。
译码器结构:分布式译码:每一级都部署一个控制器,负责译出当前级所需控制信号。另外判断阻塞的信号单独生成。
优势:分布式译码较为灵活,有效降低了流水级间传递的信号量。方便明白这一级中完成了哪些内容
不足:需要实例化多个控制器,增加了后续流水级的逻辑复杂度。同时,增加指令需要增加修改多个控制器。
