简介
从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 | 输出当前指令地址 |
元件示意图

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 |
控制信号说明
- npcOp
| 控制信号值 | 功能 |
|---|---|
| 3’b000 | PC+4作为次地址 |
| 3’b001 | beq |
| 3’b010 | j,jal |
| 3’b011 | jr,jalr |
元件示意图

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指定寄存器中的数据 |
控制信号说明:
- weSel
| 控制信号值 | 功能 |
|---|---|
| 1’b0 | 不可写入 |
| 1’b1 | 可写入 |
- wrA3Sel[2:0]
| 控制信号值 | 功能 |
|---|---|
| 3’b000 | rt作为写入数据的目标寄存器 |
| 3’b001 | rd作为写入数据的目标寄存器 |
| 3’b010 | $31作为写入数据的目标寄存器 |
- WDSel[2:0]
| 控制信号值 | 功能 |
|---|---|
| 3’b000 | 写入寄存器的数据来自ALU的运算结果 |
| 3’b001 | 写入寄存器的数据来自DM |
| 3’b010 | 写入寄存器的数据是PC+4 |
元件示意图:

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是否相等 |
控制信号说明
- ALUOp[2:0]
| 控制信号值 | 功能 |
|---|---|
| 3’b000 | add |
| 3’b001 | sub |
| 3’b010 | ori |
| 3’b011 | lui |
| 3’b100 | sll |
- BSel
| 控制信号值 | 功能 |
|---|---|
| 1’b0 | B的值为$rt |
| 1’b1 | B的值为imm |
元件示意图

DM (数据存储器)
端口说明
| 信号名称 | 方向 | 功能描述 |
|---|---|---|
| addr[31:0] | I | 待操作的内存地址 |
| WD[31:0] | I | 待写入的内存值 |
| WR | I | 写使能信号 |
| clk | I | 时钟信号 |
| reset | I | 复位信号 |
| RD[31:0] | O | 输入地址指向的内存中存储的值 |
控制信号说明
- WR
| 控制信号值 | 功能 |
|---|---|
| 1’b0 | 不能写入 |
| 1’b1 | 可以写入 |
元件示意图

EXT(位扩展)
端口说明
| 信号名称 | 方向 | 功能描述 |
|---|---|---|
| imm | I | 输入待扩展的立即数 |
| extOp | I | 控制信号 |
| extend | O | 位扩展后的结果 |
控制信号说明
| 控制信号值 | 功能 |
|---|---|
| 1’b0 | 0扩展 |
| 1’b1 | 符号扩展 |
元件示意图

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写使能 |
元件示意图

RTL指令
1. add: GPR[rd] <- GPR[rs]+GPR[rt] |
数据通路分析
| 指令 | 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:
- 在课下设计时尽可能将控制信号设为3位或2位,这样在课上考试时就不需要使用实验室逆天鼠标来完成这一部分工作了。
- 课下设计尽可能留足扩展空间,不要画的太密,否则考场红温时无疑是雪上加霜😨。
测试方案
测试ori指令
ori $0, $0, 0 |
在MIPS中运行出结果以后在logisim中的GRF查看
测试sw指令
ori $1, $1, 20 |
- 在logisim中的DM中查看
- 偏移量范围是 -2^15 ~ 2^15 - 1
- 跟寄存器没关系,涉及的是DM只是用了寄存器中的值
测试lw
ori $1, $1, 1 |
先用之前测出的or,sw,然后再用lw往出导,导出到不同的寄存器里面,在logisim中的GRF查看
测试lui
lui $0,0 |
在logisim中的GRF查看,与MIPS的运行结果做对照
测试addu,subu,beq,nop
ori $1, $1, 10 |
这里因为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 空指令,我们并不需要将它加入控制信号真值表,为什么?
- nop指令会被识别成R型指令,
$rs,$rt,$rd均为0号寄存器,而0号寄存器的值恒为0,因此不需要特殊处理。- 该CPU设计实现了sll,nop指令相当于
sll $0 $0 0,相当于把$0寄存器中的值左移0位并写入$0寄存器,因为$0的值始终为0,不会被修改,因此该指令执行后没有任何影响。
- 阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。
lw 和 sw指令没有覆盖offset为负数的情况
没有测试nop和sub指令
Lyrics Sharing
他字字未提喜欢你 |