思考题
1 DM输入示例
addr信号从ALU的输出结果取[11:2]得到,使用到DM的指令有lw和sw,都需要将对应寄存器值和立即数相加,因此这里DM的地址应为对应的ALU输出的相加结果。
位数是[11:2]而不是[9:0]因为DM是按字对齐,(目前)仅支持按字读取和写入,且DM内部存储数据是以类似这样的格式存储:
1 | reg [31:0] im_reg [0:1023] |
是按照32位数的数组进行存储的,因此如果输入时直接取[11:2]位(代表截去最低两位)可以直接取im_reg[addr]得到所需数据,不采用[9:0]的描述也是为了不混淆,明确展示这里取的是截取了末两位后取10位的ALU结果而不是直接取低10位的结果。
2 两种不同控制器译码方式
在Verilog编程中,记录指令对应的控制信号如何取值对应使用always @(*)模块,对于每个指令为控制信号赋值,以lui和jal指令为例,给出如下组合逻辑代码示例:(将输出各值定义为寄存器,此处为简便省略了default分支)
1 | always @(*) begin |
而对于记录控制信号每种取值所对应的指令,对应使用assign模块直接判断指令是否是某个值,以jal和lw为例,给出如下组合逻辑代码:(省略了部分output数据)
1 | wire isJal, isLw; |
可以看到,第一种形式按照输入的指令类型进行区分,第二种形式根据输出的端口类型进行区分。第二种不需要额外增加reg变量且代码相对更加简便,对于许多种指令只需要每个控制码输出(的每个位)写一行,对所有该值取1的指令或一遍即可。但是第二种写法缺乏可读性,不能一目了然地得到每个指令对应的控制码,增加新指令时也容易写错,而第一种按照不同的指令区分,可读性较高。因此这里选择了第一种写法。
3 对比同步异步复位
同步复位中以clk上升沿为准,如果clk上升沿时有reset再复位,因此clk信号比reset信号优先。
异步复位中只要reset为真就进行复位,不考虑clk的值,因此reset信号比clk信号优先。
4 解释addi addiu以及add addu的等价性
在MIPS32® Architecture For Programmers Volume II: The MIPS32® Instruction Set中描述指令行为的部分,Add指令的行为如下:
1 | temp <- {GPR[rs][31], GPR[rs][31:0]} + {GPR[rt][31], GPR[rt][31:0]} |
而Addu指令行为如下:
1 | temp <- GPR[rs] + GPR[rt] |
(Addi和Addiu指令大同小异,只是将对应的第二参寄存器值替换为符号扩展到33位(addi)或32位(addiu)的立即数而已,这里不额外附上对应伪代码)
可以看到,“有符号”加法Add在计算中将两个加数符号扩展到33位数,加和之后判断结果数第33位和第32位是否相等,不等的话报错“整型溢出”。逻辑很好理解——如果扩展到33位后结果数的符号位(第33位)和最高位(第32位)不等,代表使用32位数无法正确表达结果的正负性,此时就发生了结果溢出。如果忽略溢出,也就是在32位数可表达范围内规定最大值+1得到最小值,最小值-1得到最大值,则显然不需要上述处理。同理对于addi和addiu也是如此。
测试方法
采用了讨论区中石睿知同学分享的自动生成输出的魔改MARS,并且使用了如下的自动生成测试汇编代码的python程序生成汇编代码,通过将运行结果比对来进行调试。
1 | from random import randint as rd |
指令集综述
简要说明
包含指令集如下:
1 | add sub ori lw sw beq lui jal jr 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 | 目标寄存器 | 立即数 |
跳转并链接 jal
将当前的PC+4存储在GPR[31]($ra)中,按照给定的地址进行跳转。
1 | PC <- {PC[31:28], instr_index, {2{0}}} |
位置 (位数) | 31~26 (6) | 25~0 (26) |
---|---|---|
名称 | OP | instr_index |
解释 | 000011 | 目标跳转位置 |
跳转至寄存器 jr
跳转到目标寄存器存储的地址指定的指令处
1 | PC <- GPR[rs] |
位置 (位数) | 31~26 (6) | 25~21 (5) | 20~16 (5) | 15~11 (5) | 10~6 (5) | 5~0 (6) |
---|---|---|---|---|---|---|
名称 | OP | rs | 0 | 0 | 0 | func |
解释 | 000000 | 目标寄存器地址 | 00000 | 00000 | 00000 | 001000 |
空指令 nop
机器码为0x00000000,不进行任何有效行为。
IFU 取指令单元
功能要求
- 内部包括 PC(程序计数器)、IM(指令存储器)及相关逻辑。
- PC 同步复位,复位值为起始地址。
- 起始地址:0x00003000。
- IM 容量为 32bit × 4096字(按字取的地址宽度12位)。
输入端口
- clk 时钟信号
- Reset 同步复位信号
- PCSrc [1:0] 决定下条PC指令使用何处输入
- ALUZero 对应ALU的Zero输出,代表是否允许branch,1代表允许
- PCImm [15:0] 符号扩展后的立即数(对应beq给定的立即数)
- PCInstrIndex [25:0] J型指令的立即数值(对应jal给定的立即数)
- PCJumpReg [31:0] 来自寄存器的跳转地址(对应jr指令对应地址读取到的值)
输出端口
- Inst [31:0] 取得的指令
- PCNxt [31:0] 对应PC+4,用于jal
行为逻辑
- 支持同步复位
- 任何时刻,输出当前PC寄存器对应地址中的指令
- 每个clk上沿更新PC的值:
1 | if (PCSrc == 2'b00 || (PCSrc == 2'b01 && ALUZero == 0)) begin // 无b/j指令或b指令无效,直接PC+=4 |
ALU 算术逻辑单元
功能要求
- 提供 32 位加、减、或运算及大小比较等功能。
- 加减法按无符号处理(不考虑溢出)。
输入端口
- SrcA [31:0] 第一个参数输入
- SrcB [31:0] 第二个参数输入
- ALUCtrl [3:0] ALU操作类型控制码
输出端口
- Zero 输出结果是否为0
- ALUResult [31:0] 计算结果
控制信号说明
- 或运算 0000
- 与运算 0001
- 加法运算(无符号无溢出) 0010
- 减法运算(无符号无溢出) 0011
- 立即数加载至高位 0100
- 或立即数(将SrcB取低16位进行0扩展后和SrcA取或) 0101
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 × 3072字,应具有同步复位功能,复位值为 0x00000000。
- 起始地址:0x00000000。
输入端口
- clk 时钟信号
- Reset 同步复位信号
- WE 写使能信号
- Addr [31:0] 写入/读取地址
- WD [31:0] 写入数据
输出端口
- RD [31:0] 读取结果数据
功能说明
- 任何时刻,在RD输出Addr对应内存地址的内存数据
- 当WE为真时,将WD的数据在clk上沿写入Addr对应内存地址
EXT 扩展器
功能要求
- 将输入的立即数进行扩展
输入端口
- Imm [15:0] Inst指令中的立即数部分
- ExtType 扩展方式
输出端口
- ExtOut [31:0] 扩展后的结果
行为描述
ExtType | 扩展行为描述 |
---|---|
0 | 进行零扩展,即返回 , Imm} |
1 | 进行符号扩展,即返回 , Imm} |
Controller 控制器
功能要求
将输入的opcode和funct转化为CPU各部分对应信号输出
实现方式
分类判断后指定各项输出的值。
输入端口
- OPCode [5:0] 标识命令种类的指令,按MIPS汇编标准执行
- func [5:0] 标识R型指令具体种类的指令,按MIPS汇编标准执行
输出端口
- ExtType 决定扩展器扩展类型,0零扩展,1符号扩展
- Mem2Reg [1:0] 决定GRF写入数据来源,00代表ALU输出,01代表DM输出,10代表PCNxt
- MemWrite 决定DM是否允许写入
- PCSrc [1:0] 决定下条PC指令使用何处输入,00代表PC+=4,01代表branch,10代表jal,11代表jr
- ALUCtrl [3:0] 决定当前ALU执行何种运算,具体见ALU说明
- ALUSrc 决定ALU的第二参来自GRF的RD2(对应值0)还是立即数(对应值1)
- RegDest [1:0]决定GRF写入地址来源是Inst[20:16](rt, 对应值00,I型)还是Inst[15:11](rd, 对应值01,R型),还是31即$ra(对应值10,jal)
- RegWrite 决定GRF是否允许写入
各指令对应Controller输出
op | 00 0000 | 00 0000 | 00 1101 | 10 0011 | 10 1011 | 00 0100 | 00 1111 | 00 0011 | 00 0000 |
---|---|---|---|---|---|---|---|---|---|
func | 10 0000 | 10 0010 | - | - | - | - | - | - | 00 1000 |
add | sub | ori | lw | sw | beq | lui | jal | jr | |
Mem2Reg | 00 | 00 | 00 | 01 | - | - | 00 | 10 | - |
MemWrite | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
PCSrc | 00 | 00 | 00 | 00 | 00 | 01 | 00 | 10 | 11 |
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 | 10 | - |
RegWrite | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
ExtSign | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 |