思考题
1 通过FSM理解CPU
单周期CPU所用到的模块中,发挥状态存储功能的包括IFU(PC寄存器),GRF(通过寄存器组存储状态),DM(使用RAM存储状态);发挥状态转移功能的包括IFU(实现PC地址不断自增),Controller(实现将指令转移为控制信号),ALU(计算得到目标结果)。
2 存储元件合理性
- IM使用ROM:IM一次只需要读取一条指令,且IM中存储的指令不可被修改,符合ROM大容量存储且只读的特点,同时现实中的计算机需要保证在关闭电源后也能保存指令,因此不能使用断电会丢失数据的RAM。
- DM使用RAM:DM一次只需要读取一个地址对应的数据且需要支持可读写,符合RAM大容量存储且可反复读写的特点,同时现实中的寄存器单位存储大小的价格远高于RAM,对于没有那么刚需读写速度的DM而言使用RAM成本最为合适。
- GRF使用寄存器:基本上CPU执行每条指令都涉及到对于GRF的读写,且常常需要一次读取两个不同寄存器的值,Logisim中的RAM和ROM都不支持同时读取两个地址值加写入数据(ROM甚至完全不支持写入),因此需要使用分开来的寄存器组实现同时读取数据和写入数据。且现实中的寄存器读写速度远高于RAM和ROM,符合CPU需要频繁读取和写入GRF的特点。
- 为何不使用Logisim提供的其他触发器:其他触发器是组成寄存器的基础电路,使用其他触发器再行包装的话与直接使用寄存器元件效果并无不同,却更容易在包装过程中造成问题,为了减少开发难度选择不使用这些元件。
3 其他模块
无其他模块
4 nop指令
对于nop指令,由于Controller采用的电路连接方式,op和func全0时AND_logic输出也是全0,对应OR_logic输出也是全0,所以不会执行指令跳转,不会执行任何GRF或DM写入,因此此时不会执行任何改变当前状态的操作,等价于nop空指令需要达成的效果。
5 MARS导出指令
在课程MIPS要求的设置下,Data从0x00000000开始,Text即代码部分从0x00003000开始,则可以直接通过单独区分J型指令并将其对应指令地址与0x00003000相减即可,增加一个减法器即可解决问题,不需要手工修改最终的机器码。
但在本次P3题目中,由于Compact设置为Data从0x00000000开始,且唯一的跳转指令beq并不涉及指令地址的绝对位置,只涉及相对位置,因此并不需要进行相关修改。
6 Pre的MIPS测试样例强度
各个指令的覆盖率分析
- ori 立即数范围可以更扩大,测试边界情况(如0xffff)时的情况
- lui 应增加被改变寄存器本来已存有值的情况,比如lui误实现为“将立即数置于高位但保留原数低16位”的情况就不能检测到
- add 没有检测到寄存器自增自减的情况
- sw 没有测试同寄存器同时作为地址存储和被写入寄存器的情况
- lw 同上,且测试范围不够,可以考虑测试更多边界条件。
- beq 没有考察立即数是负数的情况(向前跳跃)
测试方案
- 采用github上的hex2mips工具将所给的P3_Testcode.txt转回汇编语言后,逐行运行对比观察模块行为,处理不同的位置并定位bug。
- 采用自己书写的测试MIPS汇编代码,通过自动化测试工具(使用python在已有MIPS模拟器的基础上进行更改、修复与扩展后输出对应信息到logisim自动测试模块内进行评测并定位问题,进而处理bug,详见自动化测试文件提交。
指令集综述
简要说明
包含指令集如下:
1 | add sub ori lw sw beq lui nop |
R I J 型指令说明
R型
包括
1 | add sub and or nor sll srl jr |
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16 (5) | 15~11 (5) | 10~6 (5) | 5~0 (6) |
---|---|---|---|---|---|---|
类型 | 操作码 | 寄存器地址 | 寄存器地址 | 寄存器地址 | 五位立即数 | 操作码 |
名称 | OP | rs | rt | rd | shamt (sa) | func |
解释 | 恒为0x0 | 第一参 | 第二参 | 目的参 | 移位量 | 指示操作类型 |
I型
包括
1 | addi andi ori xori lw sw beq bne lui |
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16(5) | 15~0 (16) |
---|---|---|---|---|
类型 | 操作码 | 寄存器地址 | 寄存器地址 | 立即数 |
名称 | OP | rs | rt | imm |
解释 | 指示操作类型 | 第一参 | 目的参 | 立即数 |
J型
包括
1 | j jal |
位置 (位数) | 31~26 (6) | 25~0 (26) |
---|---|---|
类型 | 操作码 | 指令地址 |
名称 | OP | address |
解释 | 指示操作类型 | 目标跳转位置 |
(无)符号加 add
按照要求,依照add的机器码处理,但加法依照无符号数加法完成。取两个寄存器中的数,无符号相加(不考虑溢出)后存入目标寄存器。为R型指令。
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16 (5) | 15~11 (5) | 10~6 (5) | 5~0 (6) |
---|---|---|---|---|---|---|
名称 | OP | rs | rt | rd | 0 | func |
解释 | 000000 | 第一加数地址 | 第二加数地址 | 结果存储地址 | 00000 | 100000 |
(无)符号减 sub
按照要求,依照sub的机器码处理,但减法依照无符号数加法完成。无符号相减(不考虑溢出)后存入目标寄存器。为R型指令。
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16 (5) | 15~11 (5) | 10~6 (5) | 5~0 (6) |
---|---|---|---|---|---|---|
名称 | OP | rs | rt | rd | 0 | func |
解释 | 000000 | 被减数地址 | 减数地址 | 结果存储地址 | 00000 | 100010 |
或立即数 ori
按照要求,即为将某寄存器中数或一个16位立即数,将得到的数存到目标寄存器中。为I型指令。
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16(5) | 15~0 (16) |
---|---|---|---|---|
名称 | OP | rs | rt | imm |
解释 | 001101 | 被或数地址 | 结果存储地址 | 立即数 |
加载字 lw
取内存中的一字长的内容存入目标寄存器。为I型指令。
1 | addr <- GPR[base] + sign_ext(offset) |
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16(5) | 15~0 (16) |
---|---|---|---|---|
名称 | OP | base | rt | offset |
解释 | 100011 | 内存地址所在寄存器 | 结果存储地址 | 内存地址偏移量(立即数) |
存储字 sw
将目标寄存器中的内容存入内存中一字长的位置。为I型指令。
1 | addr <- GPR[base] + sign_ext(offset) |
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16(5) | 15~0 (16) |
---|---|---|---|---|
名称 | OP | base | rt | offset |
解释 | 101011 | 内存地址所在寄存器 | 目标寄存器地址 | 内存地址偏移量(立即数) |
相等时转移 beq
当两个目标寄存器中存储的值相等时转移到目标指令处。为I型指令。
1 | if (GPR[rs] == GPR[rt]) |
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16(5) | 15~0 (16) |
---|---|---|---|---|
名称 | OP | rs | rt | offset |
解释 | 000100 | 第一目标寄存器 | 第二目标寄存器 | 转移目标指令地址 |
立即数加载至高位 lui
将立即数赋值到目标寄存器的高16位。为I型指令。
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16(5) | 15~0 (16) |
---|---|---|---|---|
名称 | OP | 0 | rt | imm |
解释 | 001111 | 00000 | 目标寄存器 | 立即数 |
空指令 nop
机器码为0x00000000,不进行任何有效行为。
IFU 取指令单元
功能要求
- 内部包括 PC(程序计数器)、IM(指令存储器)及相关逻辑。
- PC 用寄存器实现,应具有异步复位功能,复位值为起始地址。
- 起始地址:0x00000000。
- IM 用 ROM 实现,容量为 32bit × 32字。
- IM 实际地址宽度仅为 5 位,需要使用恰当的方法将 PC 中储存的地址同 IM 联系起来。
输入端口
- clk 时钟信号
- Reset 异步复位信号
- ALUZero 代表Branch指令是否满足要求
- PCSrc [1:0] 指示当前的指令类型,00代表指令不影响跳转,01代表Branch指令,10代表J型指令(跳转到标记),11代表jr类指令(跳转到寄存器地址)
- PCBranch [7:0] Branch后的新指令地址
- PCJAL [31:0] 连接RD1,对应jal取出的寄存器内存储的值。
输出端口
- Inst [31:0] 取得的指令
- PCNxt [7:0] 下一条要执行的指令地址
行为逻辑
- 任何时刻,输出当前PC寄存器对应ROM地址中的指令
- 每个clk上沿将当前PC寄存器存储的地址+4并输出到PCNxt,PCSrc为00时将该值存储到PC寄存器中
- 每个clk上沿PCSrc为01时,判断ALUZero是否为真,为真则将PCBranch存入PC寄存器中,为假将PC+4存入PC寄存器中。
- 每个clk上沿PCSrc为10或11时,分别对应J型和jr类指令,按照相应方式处理。
ALU 算术逻辑单元
功能要求
- 提供 32 位加、减、或运算及大小比较功能。
- 加减法按无符号处理(不考虑溢出)。
输入端口
- SrcA [31:0] 第一个参数输入
- SrcB [31:0] 第二个参数输入
- ALUCtrl [2:0] ALU操作类型控制码
输出端口
- Zero 输出结果是否为0
- ALUResult [31:0] 计算结果
控制信号说明
- 或运算 000
- 与运算 001
- 加法运算(无符号无溢出) 010
- 减法运算(无符号无溢出) 011
- 立即数加载至高位 100
- 或立即数(将SrcB取低16位进行0扩展后和SrcA取或) 101
GRF 通用寄存器组
功能要求
- 用具有写使能的寄存器实现,寄存器总数为 32 个,应具有异步复位功能。
- 0 号寄存器的值始终保持为 0。其他寄存器初始值(复位后)均为 0,无需专门设置。
输入端口
- clk 时钟信号
- Reset 异步复位信号
- WE 写使能信号
- A1 [4:0] 第一读取地址
- A2 [4:0] 第二读取地址
- A3 [4:0] 写入地址
- WD [31:0] 写入数据输入
输出端口
- RD1 [31:0] 第一读取输出 输出A1地址的寄存器中的数据
- RD2 [31:0] 第二读取输出 输出A2地址的寄存器中的数据
功能说明
- 任何时刻,RD1和RD2都输出A1和A2对应地址的寄存器中的数据
- Reset异步复位,将所有寄存器复位为0
- 0号寄存器永远保持为0
- 当WE为真时,将WD的数据在clk上沿写入A3对应地址的寄存器
DM 数据存储器(内存)
功能要求
- 使用 RAM 实现,容量为 32bit × 32字,应具有异步复位功能,复位值为 0x00000000。
- 起始地址:0x00000000。
- RAM 应使用双端口模式,即设置 RAM 的 Data Interface 属性为 Separate load and store ports。
输入端口
- clk 时钟信号
- Reset 异步复位信号
- WE 写使能信号
- Addr [4:0] 写入/读取地址
- WD [31:0] 写入数据
输出端口
- RD [31:0] 读取结果数据
功能说明
- 任何时刻,在RD输出AddrR对应内存地址的内存数据
- 当WE为真时,将WD的数据在clk上沿写入AddrW对应内存地址
Controller 控制器
功能要求
将输入的opcode和funct转化为CPU各部分对应信号输出
实现方式
将Controller分为将opcode和funct转化为各个指令对应的独热输出的AND_logic模块和将各个指令对应到不同输出信号的OR_logic模块,两模块不额外进行封装,直接在顶层模块中进行连接,这样可以通过两模块中间线的高亮直观判断当前在执行何种指令。
输入端口
- OP [5:0] 标识命令种类的指令,按MIPS汇编标准执行
- func [5:0] 标识R型指令具体种类的指令,按MIPS汇编标准执行
输出端口
- Mem2Reg [1:0] 决定GRF写入数据来源,00代表ALU输出,01代表DM输出
- MemWrite 决定DM是否允许写入
- Branch [1:0] 指示当前的指令类型,00代表指令不影响跳转,01代表Branch指令,10代表J型指令(跳转到标记),11代表jr类指令(跳转到寄存器地址)
- ALUCtrl [2:0] 决定当前ALU执行何种运算,具体见ALU说明
- ALUSrc 决定ALU的第二参来自GRF的RD2(对应值0)还是立即数(对应值1)
- RegDest [1:0] 决定寄存器写入地址来源是Inst[15:11](对应值01)还是Inst[20:16](对应值00)
- RegWrite 决定GRF是否允许写入
- ExtSign 决定扩展方式,0代表0扩展,1代表符号扩展。
各指令对应Controller输出
func | 10 0000 | 10 0010 | - | ||||
---|---|---|---|---|---|---|---|
op | 00 0000 | 00 0000 | 00 1101 | 10 0011 | 10 1011 | 00 0100 | 00 1111 |
add | sub | ori | lw | sw | beq | lui | |
Mem2Reg | 00 | 00 | 00 | 01 | - | - | 0 |
MemWrite | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
Branch | 00 | 00 | 00 | 00 | 00 | 01 | 00 |
ALUCtrl | 2 | 3 | 5 | 2 | 2 | 3 | 4 |
(CalcMode) | add | sub | ori | add | add | sub | lui |
ALUSrc | 0 | 0 | 1 | 1 | 1 | 0 | 1 |
RegDest | 01 | 01 | 00 | 00 | - | - | 00 |
RegWrite | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
ExtSign | 0 | 0 | 0 | 1 | 1 | 1 | 0 |