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

P6CPU设计文档及思考题
设计草稿
1.指令详解
(1)calc_R:
- 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 - AND:and RD, RS, RT
编码:000000 sssss ttttt ddddd 00000 100100
描述:GPR[rd] = GPR[rs] & GPR[rt] - OR:or RD, RS, RT
编码:000000 sssss ttttt ddddd 00000 100101
描述:GPR[rd] = GPR[rs] | GPR[rt] - SLT:slt RD, RS, RT
编码:000000 sssss ttttt ddddd 00000 101010
描述:if GPR[rs] < GPR[rt], GPR[rd] = 1; else GPR[rd] = 0;是有符号的比较 - SLTU:sltu RD, RS, RT
编码:000000 sssss ttttt ddddd 00000 101011
描述:if GPR[rs] < GPR[rt], GPR[rd] = 1; else GPR[rd] = 0; 但是是无符号的比较
(2)calc_I:
- ADDI:addi RT, RS, IMM
编码:001000 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = GPR[rs] + sign_ext(IMM) - ANDI:andi RT, RS, IMM
编码:001100 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = GPR[rs] & zero_extend(IMM) - ORI:ori RT, RS, IMM
编码:001101 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = GPR[rs] | zero_extend(IMM)
(3)load:
- LB:lb RT, OFFSET(RS)
编码:100000 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = sign_ext(MEM[GPR[rs] + sign_ext(OFFSET)][7:0])
操作:
1 | Addr = GPR[base] + sign_ext(offset) |
- LH:lh RT, OFFSET(RS)
编码:100001 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = sign_ext(MEM[GPR[rs] + sign_ext(OFFSET)][15:0])
操作:
1 | Addr = GPR[base] + sign_ext(offset) |
- LW:lw RT, OFFSET(RS)
编码:100011 sssss ttttt iiiii iiiii iiiiii
描述:GPR[rt] = MEM[GPR[rs] + sign_ext(OFFSET)]
(4)store:
- SB:sb RT, OFFSET(RS)
编码:101000 sssss ttttt iiiii iiiii iiiiii
描述:MEM[GPR[rs] + sign_ext(OFFSET)] = GPR[rt][7:0]
操作:
1 | Addr = GPR[base] + sign_extend(offset) |
- SH:sh RT, OFFSET(RS)
编码:101001 sssss ttttt iiiii iiiii iiiiii
描述:MEM[GPR[rs] + sign_ext(OFFSET)] = GPR[rt][15:0]
操作:
1 | Addr = GPR[base] + sign_extend(offset) |
- SW:sw RT, OFFSET(RS)
编码:101011 sssss ttttt iiiii iiiii iiiiii
描述:MEM[GPR[rs] + sign_ext(OFFSET)] = GPR[rt]
(5)md:
- mult:mult rs, rt
编码:000000 sssss ttttt 00000 00000 011000
描述:HI, LO = GPR[rs] * GPR [rt](有符号) - multu:multu rs, rt
编码:000000 sssss ttttt 00000 00000 011001
描述:HI, LO = GPR[rs] * GPR[rt] - div:div rs, rt
编码:000000 sssss ttttt 00000 00000 011010
描述:LO = GPR[rs]/ GPR[rt]; HI = GPR[rs] % GPR[rt](有符号) - divu:divu rs, rt
编码:000000 sssss ttttt 00000 00000 011011
描述:LO = GPR[rs] / GPR[rt]; HI = GPR[rs] % GPR[rt]
(6)mf:
- mflo:mflo rd
编码:000000 00000 00000 ddddd 00000 010010
描述:GPR[rd] = LO - mfhi:mfhi rd
编码:000000 00000 00000 ddddd 00000 010000
描述:GPR[rd] = HI
(7)mt:
- mtlo:mtlo rs
编码:000000 sssss 00000 00000 00000 010011
描述:LO = GPR[rs] - mthi:mthi rs
编码:000000 sssss 00000 00000 00000 010001
描述:HI = GPR[rs]
(8)B:
- 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; - BNE:bne RS, RT, LABEL
编码:000101 sssss ttttt iiiii iiiii iiiiii
描述:if GPR[rs] != GPR[rt], PC = PC + 4 + sign_ext(LABEL); else PC = PC + 4;
(9)Other:
- 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 | 当前指令地址 |
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输出结果 |
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 | 比较结果 |
MDU
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| clk | I | 1 | 时钟信号 |
| reset | I | 1 | 同步复位信号 |
| start | I | 1 | 乘除法开始运算信号 |
| MDU_Op | I | 3:0 | MDU操作码 |
| A | I | 31:0 | 操作数A |
| B | I | 31:0 | 操作数B |
| HI | O | 31:0 | 乘除法结果 |
| LO | O | 31:0 | 乘除法结果 |
| out | O | 31:0 | 运算结果 |
| busy | O | 1 | 乘除法运算忙信号 |
BE
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| A | I | 1:0 | DM写入地址的低2位 |
| LSOp | I | 2:0 | 访存功能信号 |
| WD_in | I | 31:0 | 未经处理的DM写入数据 |
| byteen | O | 3:0 | DM字节使能信号 |
| WD_out | O | 31:0 | 经过处理的DM写入数据 |
DE
| 接口名称 | 输入或输出 | 位宽 | 描述 |
|---|---|---|---|
| A | I | 2 | |
| RD_in | I | 32 | 未经处理的DM读出数据 |
| LSOp | I | 2 | 访存功能信号 |
| RD_out | O | 32 | 处理后的DM读出数据 |
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 | - | - | - | - |
EXT
| 操作 | Imm | if_sign |
|---|---|---|
| all | splitter[i] | control |
4.流水线寄存器
| 名称 | 功能 | F_D | D_E | E_M | M_W |
|---|---|---|---|---|---|
| IR | 指令 | √ | √ | √ | √ |
| PC | 指令地址 | √ | √ | √ | √ |
| WD | 写寄存器数据 | - | √ | √ | √ |
| WA | 写寄存器地址 | - | √ | √ | √ |
| RegWrite | 写寄存器控制信号 | - | √ | √ | √ |
| Tnew | 数据产生时间 | - | √ | √ | √ |
| 其他 | PC+8 | RD1,RD2,offset | RD2,ALU_Result | - |
5.控制信号
总控制
| 数值 | MemToReg | MemWrite | ALUSrc | RegWrite | signedEXT | RegDst | if_Branch | if_JR | if_JAL | ALUToReg | Ralink | lui |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 控制的位置 | 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结果 | - | 位移是shifter |
| 1 | DM | enable | EXT | enable | sign_extend | splitter[d] | BEQ | JR | JAL | WD不是ALU结果 | 将PC+4存入寄存器的编号(31)(WA的分支) | lui是位移16位 |
ALU控制
0000 : +
0001 : -
0010 : &
0011 : |
0100 : slt
0101 : sltu
1111 : 数据B向左位移shift位
cmp控制
000: ==
001: !=
111:else
LSOp控制
sw 3’b000
sh 3’b001
sb 3’b010
lw 3’b011
lh 3’b100
lb 3’b101
space 3’b111
MDUOp控制
space 4’b0000
mult 4’b0001
multu 4’b0010
div 4’b0011
divu 4’b0100
mfhi 4’b0101
mflo 4’b0110
mthi 4’b0111
mtlo 4’b1000
6.阻塞转发
Tuse 和 Tnew 的分析
Tuse表示数据到了 D 级之后还需要多少个周期要使用,每个指令的是固定不变的。
Tnew表示数据还有多长时间产生,会随着数据的流水动态的减少。(但是大于1)
Tuse表格
| 指令 | rs_Tuse | rt_Tuse |
|---|---|---|
| calc_R | 1 | 1 |
| calc_I | 1 | - |
| md | 1 | 1 |
| mt | 1 | - |
| load | 1 | - |
| store | 1 | 2 |
| B | 0 | 0 |
| JR | 0 | - |
| 其他的都是if_use=0 |
D级时Tnew表格
| 指令 | Tnew |
|---|---|
| calc_R | 2 |
| calc_I | 2 |
| mf | 2 |
| load | 3 |
| lui | 2 |
| 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的转发
课上
和P5差不多啦!但是不要掉以轻心,P5过了P6挂的人也有不少)
依旧保证课下正确。欢迎使用伟大的cot评测机!!
如果P5写好了扩展性好的接口可以继续使用。如果没有写好,经过P5应该也知道要增加什么了)
依旧是一样的课上题目分析:
1. 计算类型
形如:
1 | WriteData <- f(GPR[rs], GPR[rt]) |
做法:
- 在控制模块中识别新指令,设置控制信号
- 新增 ALU 计算类型
- 新增各流水线级转发数据
仿照 add 指令编写
与P5不一样的是,会有乘除槽指令的增加(当然也可能不是乘除槽
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?为何需要有独立的 HI、LO 寄存器?
分离设计的原因:乘除法运算需要多个周期(乘法5周期,除法10周期),而ALU的算术逻辑指令是单周期完成。若整合进ALU,会严重拖累流水线效率。并且,乘除法器硬件结构复杂,面积大,独立模块便于优化和复用。
HI、LO寄存器的作用:乘除法的结果往往是64位(32位×32位),HI存高32位,LO存低32位。专用寄存器避免占用通用寄存器堆,同时通过mfhi/mflo指令提供访问接口。
2、真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。
一. 核心思想:用更简单的操作来实现复杂操作
乘法和除法本质上可以用更基本的加法、移位和比较来实现。CPU的算术逻辑单元(ALU)最初就是被这样设计的。
二. 乘法是如何实现的?
我们先从最直观的算法说起。
方法一:模拟手算乘法(移位相加)
我们以两个4位二进制数 1011 (11) 和 1101 (13) 为例:
1 | 1 0 1 1 (被乘数) |
CPU如何用硬件实现这个过程?
- 初始化:准备三个寄存器,一个存放被乘数,一个存放乘数,一个初始为0用于存放结果(积)。
- 循环检查:
· 检查乘数的最低位。
· 如果为1,则将被乘数加到结果寄存器上。
· 如果为0,则不加。 - 移位:
· 将被乘数寄存器向左移位一位(相当于值翻倍)。
· 将乘数寄存器向右移位一位(这样下一次循环就能检查下一位)。 - 判断终止:重复步骤2和3,直到乘数寄存器为0。
这个方法的缺点是速度慢,需要N个时钟周期(N是数据的位数)。
方法二:更快的现代方法
为了提速,现代CPU采用了更先进的技术:
· 布斯算法:一种优化算法,可以处理带符号数,并且能跳过连续的0或1,减少加法次数。
· 并行乘法器/硬件乘法器:这是现代CPU的主流方式。它不像上面那样一步一步串行计算,而是使用一个巨大的、专门设计的组合逻辑电路(比如“华莱士树”),通过多级加法器并行地计算所有部分积的和。
· 优点:速度极快!可以在1个或几个时钟周期内完成一次乘法运算。
· 缺点:电路非常复杂,晶体管数量多,功耗和芯片面积都更大。
所以,当你写 a = b * c 时,CPU几乎是在瞬间通过这个专用的硬件乘法器计算出结果的。
三. 除法是如何实现的?
除法是乘法的逆运算,但它更复杂,速度也更慢。
方法一:模拟手算除法(恢复余数法)
我们以 1111 (15) 除以 0100 (4) 为例:
1 | 0011 (商 = 3) |
CPU如何用硬件实现这个过程?
- 初始化:准备寄存器存放被除数(和余数)、除数、商。
- 对齐:将除数和被除数的最高有效位对齐。
- 尝试减法:从当前被除数(或余数)中减去除数。
- 判断结果:
· 如果结果大于等于0,说明够减,则将商的对应位置1,并将减法结果作为新的余数。
· 如果结果小于0,说明不够减,则将商的对应位置0,并恢复原来的被除数/余数(这就是“恢复余数法”名称的由来)。 - 移位:将除数右移一位,准备下一次计算。
- 循环:重复步骤3-5,直到处理完所有位。
这个方法同样很慢,最坏情况下需要2N个时钟周期(N是数据的位数)。
方法二:更快的现代方法
· 不恢复余数法/SRT除法:这是“恢复余数法”的优化。它在发现不够减时,不去恢复余数,而是记录一个负的余数,并在下一步操作中通过加回来进行修正。这减少了很多不必要的步骤,是现代CPU常用的算法之一。
· 牛顿-拉弗森迭代法:这是一种利用乘法来逼近除法结果的方法。它通过一系列迭代,快速收敛到精确的商。公式大致是: x_{n+1} = x_n * (2 - D * x_n) ,其中D是除数。这种方法需要快速的乘法器支持,在计算高精度除法时特别有效。
· 查表法:对于某些初始近似值,CPU可能会使用一个小的、快速的查找表来获取,以加速迭代过程的启动。
重要提示:除法在所有基本运算中是最慢的。即使是最优化的硬件实现,一次整数除法也可能需要几十个时钟周期,而一次整数乘法或加法可能只需要1个或几个周期。这就是为什么在优化代码时,要尽量避免不必要的除法操作。
3、请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?
乘除法到达E级时,产生控制信号Start=1
当start=1时,Busy=1持续到运算完成。
在D级检测到Busy=1或者start=1时,同时D级是乘除法指令暂停流水线(插入气泡)。乘除法完成后Busy=0,解除阻塞,流水线继续。
4、请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)
清晰性:m_data_byteen[3:0]明确指定写入哪些字节,避免手动拼接数据。
统一性:所有存储指令(sw/sh/sb)共用同一写端口,通过byteen区分行为,简化DM接口。
5、请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?
读操作:从DM读出的总是整个字(32位),再由数据扩展模块提取并扩展特定字节。
写操作:向DM写入的是整个字,但通过byteen控制哪些字节实际被写入。
当程序频繁访问非字对齐数据时,按字节访问可避免读写整个字,减少内存带宽浪费。在稀疏数据结构(如字符数组、标志位数组)中,字节访问更高效。
6、为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?
抽象手段:
1.将指令按功能分类(如算术、访存、跳转、乘除),统一生成控制信号。
2.用 define,替代判断选择信号的常量
规范手段:
1.模块化设计,各功能模块职责单一,接口清晰。
2.流水线寄存器规范化,统一传递必要信号。
特点和帮助:
逻辑更加简单,容易看懂,容易debug,方便修改。
7、在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?
遇到的冲突:
1.控制冲突:分支指令和跳转指令导致的控制流改变。
2.数据冲突:算术指令之间的读后写(RAW)冲突。
(1)在E级产生结果的指令和在D级需要数据的指令之间的冲突,如add后beq
(2)在M级产生结果的指令和在D级需要数据的指令之间的冲突,如lw后beq
(3)在E级产生结果的指令和在E级需要数据的指令之间的冲突,如add后add
(4)在M级产生结果的指令和在E级需要数据的指令之间的冲突,如lw后add
(5)在M级产生结果的指令和在M级需要数据的指令之间的冲突,如lw后sw
(6)乘除发指令之间的冲突
4.解决:通过延迟槽,阻塞,转发的方式解决
测试样例:部分测试如下表示,但是由于测试比较长,没有完全粘贴过来
1 | main: |
8、如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。
手动构造样例策略:
1.先确保每一个寄存器都可以正常使用,用lui、ori、addi、andi对每一个寄存器先赋不同值,寄存器的值保证,有正有负有0有32’HFFFF_FFFF等
2.运用寄存器中的值,对每一条指令是否正常进行进行检测
3.构造冒险,保证每一类冒险的情况都覆盖
通过以上方式构造,同时确保每一点都覆盖所有情况,则可以基本保证覆盖了所有需要测试的情况。
