P3 设计文档
P3 设计文档
CPU 流程,草稿
- 取指令(PC,IM)
- 指令译码(CU)
- 指令执行(GRF,ALU)
- 储存器访问(DM)
- 结果写回(GRF)
- 循环 1-5
支持指令
add, sub, ori, lw, sw, beq, lui, j, nop
(其中 add,sub 为无符号加减法)
使用模块
一、IFU(Instruction Fetch Unit)
包括 PC 和 IM
IM 用 ROM 实现,容量为 32bit × 32 字。(5 位地址)
PC 始终为 4 的倍数(字节寻址,32bit=4 字节),所以取 32 位地址中 2-6 位对应 ROM 中指令地址
端口名 | 输入 / 输出 | 位宽 | 功能 |
---|---|---|---|
clk | in | 1 | 时钟信号 |
reset | in | 1 | 异步复位 |
PCSrc | in | 1 | 下一指令地址选择信号 |
Jump | in | 1 | 跳转控制信号 |
shiftResult | in | 32 | 偏移后的指令地址 |
jumpAdd | in | 32 | 直接跳转的指令地址 |
D | out | 32 | 输出读取指令 |
三、GRF(General Register File)
- 存储 $0~$31 这 32 个寄存器数据
- 具有异步复位,读、写功能
- 最多一次同时取出两个寄存器的值运算,存入一个寄存器的值
- $0 寄存器输入为常数 0
端口名 | 输入 / 输出 | 位宽 | 功能 |
---|---|---|---|
clk | in | 1 | 时钟信号 |
reset | in | 1 | 异步复位 |
WE | in | 1 | 写入使能信号 |
A1 | in | 5 | 输出数据到 RD1 的寄存器的地址 |
A2 | in | 5 | 输出数据到 RD2 的寄存器的地址 |
WA | in | 5 | 输入到寄存器的地址 |
WD | in | 32 | 写入的数据 |
RD1 | out | 32 | RD1 输出的数据 |
RD2 | out | 32 | RD2 输出的数据 |
四、ALU(Arithmetic & logical Unit)
提供 32 位加、减、或运算及大小比较功能,
加减法按无符号处理
端口名 | 输入 / 输出 | 位宽 | 功能 |
---|---|---|---|
op | in | 3 | ALU 功能控制信号 000:加,001:减,010:或,比较:任意 |
A | in | 32 | 输入 1 |
B | in | 32 | 输入 2 |
Y | out | 32 | 输出 |
Greater | out | 1 | A>B |
Equal | out | 1 | A=B |
Less | out | 1 | A<B |
五、DM(Data Memory)
- 使用 RAM 实现,有 异步复位 功能,复位值为 0x00000000。容量为 32bit × 32 字(5 位地址),使用双端口模式
- 地址始终为 4 的倍数(lw/rw 每次操作一个字 =4 字节),所以取 32 位地址中 2-6 位对应 RAM 地址,对于 lb/rb 等指令还需更改
端口名 | 输入 / 输出 | 位宽 | 功能 |
---|---|---|---|
clk | in | 1 | 时钟信号 |
reset | in | 1 | 异步复位 |
WE | in | 1 | 写入使能信号 |
A | in | 5 | 将要读 / 写的寄存器的地址 |
WD | in | 32 | 写入的数据 |
RD | out | 32 | 读出的数据 |
六、EXT(Bit Extender)
- 将 16 位偏移量 / 立即数拓展至 32 位
端口名 | 输入 / 输出 | 位宽 | 功能 |
---|---|---|---|
EXT Select | in | 1 | 选择符号拓展 / 无符号拓展(0/1) |
in | in | 16 | 16 位输入 |
out | out | 32 | 拓展后 32 位输出 |
七、CU(Control Unit)
- 生成所有控制信号的组合逻辑电路
- 根据每条指令的数据通路列出如下控制信号表格
- R 指令控制信号为 (R==0)
- 分别根据 Opcode 和 Funct 每一位和与门控制非 R 和 R 型指令的选择
- 再将控制信号用或门收集所有需要触发的指令(多连接一个常数 0 防止没有被选择的时候输出 X)
Instuction | Opcode(in) | Funct (in) | RegWrite | RegDst | ALUsrc | Branch | MemWrite | MemToReg | EXTselect | Jump | ALUControl |
---|---|---|---|---|---|---|---|---|---|---|---|
add | 000000 | 100000 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 000 |
sub | 000000 | 100010 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 001 |
ori | 001101 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 010 | |
lw | 100011 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 000 | |
sw | 101011 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 000 | |
beq | 000100 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 000 | |
lui | 001111 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 011 | |
j | 000010 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 00 |
测试
汇编代码
1 | ori $a0,$0,1999 #ori 测试程序要实现: $0 寄存器中的内容与立即数 0x000007cf 进行或运算,储存在 $a0 寄存器中 |
机器码
1 | v2.0 raw |
mips 运行结果
CPU 运行结果
思考题
- 上面我们介绍了通过 FSM 理解单周期 CPU
的基本方法。请大家指出单周期 CPU
所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。
- 状态储存:PC, GRF, IM, DM(储存指令以及数据的状态)
- 状态转移:ALU, EXT, CU(组合逻辑)
- 现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用
Register,这种做法合理吗?
请给出分析,若有改进意见也请一并给出。
- 合理,IM 用 ROM 存储指令,掉电后不会丢失,并且需要人为进行修改,保证指令不会在运行中被更改;数据储存用 RAM,读出和写入且访问速度快于 ROM,方便每个周期读出或者写入;同时内存可能很大,不可用 Register 实现;GRF 一共 32 个数据,每个周期内需要频繁同时读出和写入,用 Register 效率最高
- 在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。
- 暂无
- 事实上,实现
nop
空指令,我们并不需要将它加入控制信号真值表,为什么?- CU 中无指令被选择时,所有控制信号输出 0,不对 GRF 写入,不对 DM 写入,rs 与 rt 均全为 0,在 GRF 选择的均为 0 号寄存器,输出为 0,ALU 运算后仍为 0,即操作为将 0 写回 $0 寄存器,所以不需要加入控制信号真值表
- 上文提到,MARS 不能导出 PC 与 DM 起始地址均为 0
的机器码。实际上,可以避免手工修改的麻烦。请查阅相关资料进行了解,并阐释为了解决这个问题,你最终采用的方法。
- 将地址减去 0x30000000,映射到 0x00000000 为起始地址
- 阅读 Pre 的 “MIPS
指令集及汇编语言”
一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。
- beq 指令强度不足,只包括向后跳转,没有测试向前跳转的负立即数
- ori 指令还可测试对 $0 寄存器赋值,检测是否会修改
- 对 DM 和 GRF,存取数据地址最好包含整个要求的地址范围和 32 个寄存器,保证范围设置正确,连接正确