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

Post Cover

写在前面:
本人 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)

转发操作:

转发需要:

  1. GRF内部转发
  2. D需要E、M的转发
  3. E需要M、W的转发
  4. M需要W的转发

课上

保证课下正确很重要!可以写评测机,或者使用伟大的cot评测机!!

P5以及P6的题目类型都比较固定,可以提前写好一些接口,比如control可以增加判断新指令的接口,流水线寄存器也可以增加传递的接口,接口可以命名为new之类的,方便课上使用。

以下是课上题目的一些分析:

1. 计算类型

形如:

1
2
WriteData <- f(GPR[rs], GPR[rt])
GPR[rd] <- WriteData

做法:

  1. 在控制模块中识别新指令,设置控制信号
  2. 新增 ALU 计算类型
  3. 新增各流水线级转发数据

仿照 add 指令编写

2. 条件跳转类型

形如:

1
2
3
4
5
6
7
condition <- f(GPR[rs], Ext_Ans)
if condition:
jump
(GPR[rt] <- data)
else:
(NullifyDelayedBranch)

  1. 控制模块中设置控制信号
  2. 计算 condition
  3. 跳转仿照 beq
  4. 条件写入用特判+凌驾的做法,记得改 D-E 级流水线寄存器的输入
  5. 清空延迟槽,新增控制信号Clr_F,Clr_F为真的条件:condition为假 && D级是新指令 && 此时不能阻塞
3. 访存类型

形如:

1
2
3
4
DMdata <- DM[addr]
data <- f(DMdata, GPR[rt])
dest_reg <- f(data)
GPR[dest_reg] <- data
  1. data、dest_reg 是在 W级 算出来的,M-W 流水线传 rt
  2. W级该指令转发 data
  3. dest_reg 最好在 没算出来之前都是 0,防止转发冲突
  4. dest_reg 的范围很重要
  5. 比较阻塞的时候,依次比较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
2
WriteData <- f(GPR[rs], GPR[rt])
GPR[rd] <- WriteData

做法:

  1. 在控制模块中识别新指令,设置控制信号
  2. 新增 ALU 计算类型
  3. 新增各流水线级转发数据

仿照 add 指令编写

2. 条件跳转类型

形如:

1
2
3
4
5
6
7
condition <- f(GPR[rs], Ext_Ans)
if condition:
jump
(GPR[rt] <- data)
else:
(NullifyDelayedBranch)

  1. 控制模块中设置控制信号
  2. 计算 condition
  3. 跳转仿照 beq
  4. 条件写入用特判+凌驾的做法,记得改 D-E 级流水线寄存器的输入
  5. 清空延迟槽,新增控制信号Clr_F,Clr_F为真的条件:condition为假 && D级是新指令 && 此时不能阻塞
3. 访存类型

形如:

1
2
3
4
DMdata <- DM[addr]
data <- f(DMdata, GPR[rt])
dest_reg <- f(data)
GPR[dest_reg] <- data
  1. data、dest_reg 是在 W级 算出来的,M-W 流水线传 rt
  2. W级该指令转发 data
  3. dest_reg 最好在 没算出来之前都是 0,防止转发冲突
  4. dest_reg 的范围很重要
  5. 比较阻塞的时候,依次比较E,M级,条件为:是新指令 && 要写寄存器 && 寄存器处在 dest_reg 范围内 && RA!=0 && Tuse < Tnew

7、确定你的译码方式,简要描述你的译码器架构,并思考该架构的优势以及不足。

译码方式:罗列控制信号每种取值所对应指令。
译码器结构:分布式译码:每一级都部署一个控制器,负责译出当前级所需控制信号。另外判断阻塞的信号单独生成。
优势:分布式译码较为灵活,有效降低了流水级间传递的信号量。方便明白这一级中完成了哪些内容
不足:需要实例化多个控制器,增加了后续流水级的逻辑复杂度。同时,增加指令需要增加修改多个控制器。

本作品由 一一 于 2026-02-13 12:00:00 发布
作品地址:北航co计算机组成P5设计文档
除特别声明外,本站作品均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自 anAilurus' Blog
Logo
上一篇北航co计算机组成P6设计文档下一篇北航co计算机组成P4设计文档