「BUAA-CO」 P3课下


简介

从P3开始,我们正式开始设计CPU,首先从单周期开始入手,P3使用logisim,并且不会涉及特别复杂的状态机,指令也相对容易。

设计方案综述

要求实现的指令集包括 add(不考虑溢出), subu(不考虑溢出), ori, lw, sw, beq, lui, nop,j,jal,jr,jalr

整体架构图如下:
整体结构

参考了课件中的缩略简图:
缩略图

关键模块的定义

IFU:由PC(程序计数器)和IM组成

端口说明:

信号名称 方向 功能描述
NPC[31:0] I 输入NPC
clk I 时钟信号
reset I 复位信号
instruct[31:0] O 输出当前指令
PC[31:0] O 输出当前指令地址

元件示意图

IFU元件示意图

tips:尽量将PC和IM分开写,方便调试时观测RAM指令执行的变化

NPC(次地址计算单元)

端口说明

信号名称 方向 功能描述
ra I rs寄存器中存储的数据
PC[31:0] I 当前正在执行的指令地址
zero I $rs$rt是否相等
npcOp I 控制信号
imm[25:0] I 用于拼接出NPC
npc[31:0] O 次地址
PC+4[31:0] O 当前正在执行的指令地址+4

控制信号说明

  1. npcOp
控制信号值 功能
3’b000 PC+4作为次地址
3’b001 beq
3’b010 j,jal
3’b011 jr,jalr

元件示意图

NPC元件示意图

GRF(寄存器堆)

端口说明:

信号名称 方向 功能描述
A1[4:0] I 5位地址输入信号,将其储存的数据读出到 RD1
A2[4:0] I 5位地址输入信号,将其储存的数据读出到 RD2
A3[4:0] I 5位地址输入信号,将其作为写入数据的目标寄存器
WD[31:0] I 32位数据输入信号,将该数据写入目标寄存器A3
WE I 写使能
clk I 时钟信号
reset I 复位信号
RD1[31:0] O 输出A1指定寄存器中的数据
RD2[31:0] O 输出A2指定寄存器中的数据

控制信号说明:

  1. weSel
控制信号值 功能
1’b0 不可写入
1’b1 可写入
  1. wrA3Sel[2:0]
控制信号值 功能
3’b000 rt作为写入数据的目标寄存器
3’b001 rd作为写入数据的目标寄存器
3’b010 $31作为写入数据的目标寄存器
  1. WDSel[2:0]
控制信号值 功能
3’b000 写入寄存器的数据来自ALU的运算结果
3’b001 写入寄存器的数据来自DM
3’b010 写入寄存器的数据是PC+4

元件示意图:

GRF元件示意图

ALU(算数逻辑单元)

端口说明

信号名称 方向 功能描述
A[31:0] I 运算数A
B[31:0] I 运算数B
shamt[4:0] I sll的位数
ALUOp[2:0] I 控制信号
C[31:0] O 运算输出结果
zero O A和B是否相等

控制信号说明

  1. ALUOp[2:0]
控制信号值 功能
3’b000 add
3’b001 sub
3’b010 ori
3’b011 lui
3’b100 sll
  1. BSel
控制信号值 功能
1’b0 B的值为$rt
1’b1 B的值为imm

元件示意图

ALU元件示意图

DM (数据存储器)

端口说明

信号名称 方向 功能描述
addr[31:0] I 待操作的内存地址
WD[31:0] I 待写入的内存值
WR I 写使能信号
clk I 时钟信号
reset I 复位信号
RD[31:0] O 输入地址指向的内存中存储的值

控制信号说明

  1. WR
控制信号值 功能
1’b0 不能写入
1’b1 可以写入

元件示意图

DM元件示意图

EXT(位扩展)

端口说明

信号名称 方向 功能描述
imm I 输入待扩展的立即数
extOp I 控制信号
extend O 位扩展后的结果

控制信号说明

控制信号值 功能
1’b0 0扩展
1’b1 符号扩展

元件示意图

EXT元件示意图

CTRL(控制信号模块)

端口说明

信号名称 方向 功能描述
OPcode[5:0] I 输入标识信号
FuncCode[5:0] I 输入标识信号
NPCOp[2:0] O npc计算方式
WDSel[2:0] O GRF存的数据
WeSel O GRF写使能
WRA3Sel[2:0] O GRF写入地址
ALUOp[2:0] O ALU模式
Bsel O B的数据类型
extOp O 有/无符号扩展
DMWR O DM写使能

元件示意图

CTRL元件示意图

RTL指令

1. add: GPR[rd] <- GPR[rs]+GPR[rt]
2. sub: GPR[rd] <- GPR[rs] - GPR[rt]
3. ori: GPR[rt] <- GPR[rs] OR immediate
4. lw: GPR[rt] <- memory[GPR[base]+offset]
5. sw: memory[GPR[base]+offset] <- GPR[rt]
6. beq: if (GPR[rs] == GPR[rt]) then 转移
7. lui: GPR[rt] <- immediate{16{0}}
8. sll: GPR[rd] <- GPR[rt] << s
9. j : PC <- PC31..28 || instr_index || 00
10. jal: PC <- PC31..28 || instr_index || 00
GPR[31] <- PC + 4
11. jalr: PC <- GPR[rs]
GPR[rd] <- PC + 4
12. jr: PC <- GPR[rs]

数据通路分析

指令 opcode funct npcOp WDSel weSel wrA3Sel ALUOp BSel extOp WR
功能 instruct[26:21] instruct[5:0] npc计算方式 GRF存的数据 GRF写使能 GRF写入地址 ALU模式 B的数据类型 有/无符号扩展 DM写使能
add 000000 100000 000 000 1 001 000 0 x 0
sub 000000 100010 000 000 1 001 001 0 x 0
ori 001101 x 000 000 1 000 010 1 0 0
lw 100011 x 000 001 1 000 000 1 1 0
sw 101011 x 000 x 0 x 000 1 1 1
beq 000100 x 001 x 0 x 001 0 x 0
lui 001111 x 000 000 1 000 011 1 0 0
sll 000000 000000 000 000 1 001 100 0 x 0
j 000010 x 010 x 0 x x x x 0
jal 000011 x 010 010 1 010 x x x 0
jalr 001001 x 011 010 1 001 x x x 0
jr 001000 x 011 x 0 x x x x 0

PS:

  1. 在课下设计时尽可能将控制信号设为3位2位,这样在课上考试时就不需要使用实验室逆天鼠标来完成这一部分工作了。
  2. 课下设计尽可能留足扩展空间,不要画的太密,否则考场红温时无疑是雪上加霜😨。

测试方案

测试ori指令

ori $0, $0, 0
ori $1, $1, 1
ori $2, $2, 2
ori $3, $3, 3
ori $4, $4, 4
ori $5, $5, 5
ori $6, $6, 6
ori $7, $7, 7
ori $8, $8, 8
ori $9, $9, 9
ori $10, $10, 10
ori $11, $11, 11
ori $12, $12, 12
ori $13, $13, 13
ori $14, $14, 14
ori $15, $15, 15
ori $16, $16, 16
ori $17, $17, 17
ori $18, $18, 18
ori $19, $19, 19
ori $20, $20, 20
ori $21, $21, 21
ori $22, $22, 22
ori $23, $23, 23
ori $24, $24, 24

在MIPS中运行出结果以后在logisim中的GRF查看

测试sw指令

ori $1, $1, 20
sw $1, 4($0)
sw $1, 8($0)
sw $1, 12($0)
sw $1, 16($0)
sw $1, 20($0)
sw $1, 24($0)
sw $1, 28($0)
sw $1, 32($0)
sw $1, 36($0)
sw $1, 40($0)
sw $1, 44($0)
sw $1, 48($0)
sw $1, 52($0)
sw $1, 56($0)
sw $1, 60($0)
sw $1, 64($0)
sw $1, 68($0)
sw $1, 72($0)
  • 在logisim中的DM中查看
  • 偏移量范围是 -2^15 ~ 2^15 - 1
  • 跟寄存器没关系,涉及的是DM只是用了寄存器中的值

测试lw

ori $1, $1, 1
sw $1, 0($0)
lw $2, 0($0)
lw $3, 0($0)
lw $4, 0($0)
lw $5, 0($0)
lw $6, 0($0)
lw $7, 0($0)
lw $8, 0($0)
lw $9, 0($0)
lw $10, 0($0)
lw $11, 0($0)
lw $12, 0($0)
lw $13, 0($0)
lw $14, 0($0)
lw $15, 0($0)
lw $16, 0($0)
lw $17, 0($0)
lw $18, 0($0)
lw $19, 0($0)

先用之前测出的or,sw,然后再用lw往出导,导出到不同的寄存器里面,在logisim中的GRF查看

测试lui

lui $00
lui $1,1
lui $2,2
lui $3,3

在logisim中的GRF查看,与MIPS的运行结果做对照

测试addu,subu,beq,nop

ori  $1, $1, 10
ori $2, $2, 20
addu $3, $1, $1
addu $4, $1, $2
subu $5, $2, $1
subu $6, $1, $1
lui $4, 0xffff
nop
beq $1, $0, end
lui $1, 0xffff
end:

这里因为addu和add的Opcode不同,sub同理,所以在导出之前需要将addu,subu全部转成add,sub,但是MIPS运行仍然使用addu和subu

将MIPS的DM导出,与logisim的DM导出信息做对照,如果相同则证明程序正确。

当然以上只是小规模测试,如需广泛测试需要借助python自动生成MIPS代码,进而通过git diff做比较 (在大规模测试下其实只需要比较DM即可,无需对GRF进行对拍) ,或者通过python程序进行比较。

思考题

  • 上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。

状态存储:IM GRF DM
状态转移: NPC ALU

  • 现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。

IM 只会被读取,其中的值不用修改,使用ROM合理
DM 需要读取和写入,可以使用RAM和Register。但是DM要求的内存是3072的空间,使用Register的话可能手绘有一定困难
GRF频繁读写指令,Register可以保证读写速度

  • 在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。

实现了sll。sll是将$rt寄存器中的值左移shamt位放到$rd中,因此需要在ALU模块新增shamt输入,在CTRL中实现此时wrA3Sel=1,weSel=1,BSel = 0

  • 事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?
  1. nop指令会被识别成R型指令,$rs,$rt,$rd均为0号寄存器,而0号寄存器的值恒为0,因此不需要特殊处理。
  2. 该CPU设计实现了sll,nop指令相当于 sll $0 $0 0,相当于把$0寄存器中的值左移0位并写入$0寄存器,因为$0的值始终为0,不会被修改,因此该指令执行后没有任何影响。
  • 阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。

lw 和 sw指令没有覆盖offset为负数的情况
没有测试nop和sub指令

Lyrics Sharing

他字字未提喜欢你
你句句都是我愿意
他一句寂寞时候的回应
你却激动不已
他次次回避着话题
你傻傻热情地贴近
穿过多少城市为了他
只因为他
淋湿了自己

文章作者: Cordial-Kid
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Cordial-Kid !
  目录