MIPS单周期处理器设计
本文内容为原创,内容仅供参考,请勿照搬
1. 单周期数据通路设计
参考图

最终设计

顶层输出端口

1.1 组合部件
1.1.1 ALU (算术逻辑运算单元)
- in:
- SrcA, SrcB
- ALUCtrl [3:0]
- Shift 移位数 5
- FlowJudge 是否进行溢出判断
- out:
- Equal(zero)
- Result
- Overflow
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
SrcA |
I | 32 | 32 位操作数 A |
SrcB |
I | 32 | 32 位操作数 B |
ALUCtrl |
I | 4 | 4 位运算控制信号 |
Shift |
I | 5 | 5 位移位量 (shamt) |
Result |
O | 32 | 计算结果 |
Zero |
O | 1 | 1 位零标志位 Equal(zero) |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | 运算选择 | 根据 4 位 ALUCtrl 信号,选择要执行的运算 (见下表)。 |
| 2 | 算术逻辑运算 | 对 SrcA, SrcB 和 Shift 执行选定的 32 位运算,并将结果输出到 Result 端口。 |
| 3 | 零标志位 | 比较 Result 端口的 32 位输出是否等于 0x00000000。如果是,Zero 端口输出 1,否则输出 0。 |
ALUCtrl (功能 1) 的详细逻辑:
0000:Result = SrcA + SrcB(加法)0001:Result = SrcA - SrcB(减法)0010:Result = SrcA & SrcB(按位与)0011:Result = SrcA | SrcB(按位或)0100:Result = (SrcA < SrcB) ? 1 : 0(有符号slt)0101:Result = SrcB << Shift(逻辑左移)

1.1.2 GRF(通用寄存器组,也称为寄存器文件、寄存器堆)
- 读
in: RA1 RA2
out: RD1=RF[RA1] , RD2=RF[RA2] - 写
in: WA, WD, clkRegWrite out 1 RF[WA] -> WD 0 无
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
ReadAddr1 |
I | 5 | 读端口 1 地址 RA1 |
ReadAddr2 |
I | 5 | 读端口 2 地址 RA2 |
WriteAddr |
I | 5 | 写端口地址 WA |
WriteData |
I | 32 | 32 位待写入数据 WD |
RegWrite |
I | 1 | 写使能信号 |
clk |
I | 1 | 时钟信号 |
reset |
I | 1 | 异步复位信号 |
ReadData1 |
O | 32 | RF[RA1] |
ReadData2 |
O | 32 | RF[RA2] |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | 并发异步读 (Concurrent Read) |
异步地(即立即)从 ReadAddr1 和 ReadAddr2 指定的地址读取 32 位数据,并分别从 ReadData1 和 ReadData2 端口输出。 (即 RD1=RF[RA1], RD2=RF[RA2]) |
| 2 | 同步写 (Synchronous Write) |
当 RegWrite = 1 时,在 clk 信号的上升沿,将 WriteData 端口的 32 位数据写入到 WriteAddr 指定的寄存器。 (即 RF[WA] = WD) |
| 3 | $zero 寄存器逻辑 |
对应的 ReadData 端口必须输出 0x00000000。Reg0 永远不能被修改。 |
| 4 | 异步复位 |


1.1.3 DM (数据存储器)
- MemRead RE
- 读
in: A
out: RD = DM[A]
- 读
- MemWrite WE
- 写
- clk
in: A, WD
out: DM[A] = WD
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
Address |
I | 32 | 32 位字节地址 (来自 ALUResult) |
WriteData |
I | 32 | 32 位待写入数据 (WA) |
MemRead |
I | 1 | 读使能信号 (RE) |
MemWrite |
I | 1 | 写使能信号 ( WE) |
clk |
I | 1 | 时钟信号 (用于同步写入) |
reset |
I | 1 | 异步复位信号 (用于清空 RAM) |
ReadData |
O | 32 | 32 位读出数据 (送往 MemtoReg MUX) |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | 地址映射 (Address Mapping) |
将输入的 32 位字节地址 Address,通过提取 [13:2] 位,转换为内部 RAM 所需的 12 位字地址。 |
| 2 | 存储器读 (Memory Read) |
当 MemRead = 1 时,异步地从转换后的字地址中读取 32 位数据,并将其从 ReadData 端口输出。 (即 RD = DM[A]) |
| 3 | 存储器写 (Memory Write) |
当 MemWrite = 1 时,在 clk 信号的上升沿,将 WriteData 端口的32位数据写入到转换后的字地址。 (即 DM[A] = WD) |
| 4 | 异步复位 | 当 reset = 1 时,异步地将内部 RAM 的所有单元清零。 |

1.1.4 nPC – 有限状态机:
- PC (程序计数器) —状态转移—> nPC
PC 寄存器 – 状态存储模块
- en, reset, clk
NPC 模块 – 状态转移电路
Pc += 4
in:
- pc
- IsB –> Offset(32) << 2 + pc
- JUMP –> In_26(26) << 2 + pc[31:28]
- JR –> Ra
out:
- Next_PC
- PC+4
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
PC_in |
I | 32 | PC 的值 (来自 IFU 模块) |
Offset_Ext |
I | 32 | 32 位符号扩展的立即数 (来自 EXT 模块) |
imm26 |
I | 26 | 26 位跳转立即数 (来自 Splitter_Unit 模块) |
Ra |
I | 32 | rs 寄存器的值 (来自 GRF 的 ReadData1) |
IsB |
I | 1 | 分支使能信号。(Main_Control[Branch] AND ALU[Zero]) |
JUMP |
I | 1 | j 跳转使能信号 (来自 Main_Control_Unit) |
JR |
I | 1 | jr 跳转使能信号 (来自 ALU_Control_Unit) |
Next_PC |
O | 32 | 计算得出的下一条指令地址 (送往 IFU 的 Next_PC 端口) |
PC_plus_4 |
O | 32 | PC + 4 的值 (来自 IFU 模块) |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | 分支地址计算 (Branch Target Calculation) |
计算 beq 指令的目标地址:Branch_Target = PC_plus_4 + (Offset_Ext << 2)。 |
| 2 | 跳转地址计算 (Jump Target Calculation) |
计算 j 指令的目标地址:Jump_Target = { PC_plus_4[31:28] , (imm26 << 2) }。 |
| 3 | PC 优先级选择 (Next PC Selection) |
使用一个 4-to-1 MUX (多路选择器),根据 JR, JUMP, IsB 信号的优先级,在四个可能的下一地址中选择一个作为 Next_PC 输出。 |
PC 优先级选择 (功能 3) 的详细逻辑:
JR= 1:Next_PC = Ra(最高优先级)JR= 0,JUMP= 1:Next_PC = Jump_TargetJR= 0,JUMP= 0,IsB= 1:Next_PC = Branch_TargetJR= 0,JUMP= 0,IsB= 0:Next_PC = PC_plus_4(默认顺序执行)

1.1.5 IFU(取指令单元)
IM (指令存储器)
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
A |
I | 32 | 32 位字节地址 (来自 IFU 模块的 PC) |
RD |
O | 32 | 从存储器中读取的 32 位指令码 |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | 地址映射 (Address Mapping) |
将输入的 32 位字节地址 A,通过减去起始地址偏移量 0x3000 并右移两位(即提取 [13:2] 位),转换为 12 位的字地址。 |
| 2 | 指令读取 (Instruction Read) |
使用转换后的 12 位字地址,从内部 ROM (只读存储器) 中异步读取一条 32 位的指令,并将其从 RD 端口输出。 |

IFU (取指令单元)
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
Next_PC |
I | 32 | 下一个时钟周期将要载入的 PC 值 (来自 NPC 模块) |
clk |
I | 1 | 系统时钟信号 |
reset |
I | 1 | 系统异步复位信号 |
stop |
I | 1 | 时钟使能 (Clock Enable) 信号 (低电平有效,用于暂停 PC) |
PC |
O | 32 | 当前 PC 寄存器中存储的地址值 (送往 IM 和 NPC) |
Instr |
O | 32 | PC 对应的当前指令码 (来自 IM 模块) |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | PC 更新 (PC Update) |
在 clk 上升沿,且 stop 信号为 0 (无效) 时,将 Next_PC 的值载入 PC 寄存器。 |
| 2 | PC 复位 (PC Reset) |
当 reset 信号为 1 时,异步地将 PC 寄存器的值强制设为起始地址 0x00003000。 |
| 3 | 指令获取 (Instruction Fetch) |
将 PC 寄存器的当前值输出到 PC 端口,并将其送至内部的 IM 模块,以获取对应的 32 位指令,并从 Instr 端口输出。 |
| 4 | PC+4 计算 (PC+4 Calculation) |
并行地计算 PC 寄存器当前值加 4 的结果,并从 PC_plus_4 端口输出。 |

1.1.6 EXT (拓展单元)
将16位立即数符号拓展为32位。这里为了提高可拓展性,添加了 OPExt 接口
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
| Imm_16 | I | 16 | 16位立即数输入信号 |
| OPExt | I | 1 | 符号拓展信号 0:无符号拓展(0拓展) 1:符号拓展 |
| Imm_32 | O | 32 | 32位立即数输出信号 |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | 符号拓展 | 将16位立即数进行符号拓展 |

1.1.7 SPLI(指令分离器模块)
- 端口定义
| 信号名 | 方向 | 位宽 | 描述 |
|---|---|---|---|
Instr |
I | 32 | 来自 IFU (指令存储器) 的 32 位完整指令码 |
Opcode |
O | 6 | 操作码 ([31:26]),送往 Main Control Unit |
rs |
O | 5 | 源寄存器 1 ([25:21]),送往 GRF[RA1] 和 NPC[Ra] |
rt |
O | 5 | 源寄存器 2 / 目标 ([20:16]),送往 GRF[RA2] 和 RegDst MUX |
rd |
O | 5 | 目标寄存器 ([15:11]),送往 RegDst MUX |
shamt |
O | 5 | 移位量 ([10:6]),送往 ALU[Shamt] |
funct |
O | 6 | 功能码 ([5:0]),送往 ALU Control Unit |
imm16 |
O | 16 | 16 位立即数 ([15:0]),送往 EXT 和 LUI Shifter |
imm26 |
O | 26 | 26 位跳转地址 ([25:0]),送往 NPC |
- 功能定义
| 序号 | 功能名称 | 功能描述 |
|---|---|---|
| 1 | 指令字段分离 (Instruction Field Separation) |
将 32 位的 Instruction 输入,根据 MIPS 指令格式,并行地分离为 Op, rs, rt, rd, shamt, funct, imm16 和 imm26 共 8 个字段,并从对应端口输出。 |

指令格式
R型指令格式

add,sub,and,or rd, rs, rt
| 操作 | Op | Rs | Rt | Rd | Shamt | Func |
|---|---|---|---|---|---|---|
| 位宽 | 6 | 5 | 5 | 5 | 5 | 6 |
LW&SW指令格式

lw,sw rt, rs, imm16
| 操作 | Op | Rs | Rt | imm |
|---|---|---|---|---|
| 位宽 | 6 | 5 | 5 | 16 |
分支指令格式

beq rs, rt, imm16
| 操作 | Op | Rs | Rt | imm |
|---|---|---|---|---|
| 位宽 | 6 | 5 | 5 | 16 |
跳转指令格式

j add26
| 操作 | Op | JAdd |
|---|---|---|
| 位宽 | 6 | 26 |
2. 单周期控制器设计

2.1 单周期通路所需控制信号
2.1.1 ALU控制(ALUCtrl):4位
| 输入 | ALUCtrl | 运算 |
|---|---|---|
| A, B | 0000 | A & B |
| A, B | 0001 | A | B |
| A, B | 0010 | A + B |
| A, B | 0110 | A - B |
2.1.2 8个控制信号:
| 控制信号 | 0 | 1 |
|---|---|---|
| RegDst | Reg堆写入端地址:Rt | Reg堆写入端地址:Rd |
| RegWrite | 无 | Reg写入:Reg[WA] = WD |
| ALUSrc | ALU-B:RD2 | ALU-B:imm Signext |
| PCSrc | PC = (PC+4) | PC = NAdd (beq目的地址) |
| PCJump | PC = MUX的输出 | PC = J指令目的地址 |
| MemRead | 无 | DM读(输出) |
| MemWrite | 无 | DM写(输入) |
| MemtoReg | Reg写入 <– ALU | Reg写入 <– DM |
| Branch | 无 | 为Beq指令 |
2.2 控制器设计

2.2.1 Main Control Unit
2.1 设计思路
MemtoReg(2 位):00:ALUResult(用于 R-type,ori)01:DM[ReadData](用于lw)10:LUI_Value(用于lui)
ExtOp(1 位): 用于控制扩展单元。0: 零扩展 (forori)1: 符号扩展 (forlw,sw,beq)
Jump(1 位): (图中的PCJump) 用于j指令。JR信号: 已移至ALU Control Unit。
2.2 端口定义
- 输入 (Inputs):
Opcode[5:0]: 来自指令码的[31:26]位 (操作码)。
- 输出 (Outputs):
RegDst[0]: (1: R-type 写rd, 0: I-type 写rt)ALUSrc[0]: (1: 立即数, 0:GRF[ReadData2])MemtoReg[1:0]: (2 位) (写回 GRF 的数据源选择)RegWrite[0]: (1: 允许写入 GRF)MemRead[0]: (1: 允许读取 DM)MemWrite[0]: (1: 允许写入 DM)Branch[0]: (1:beq指令)Jump[0]: (1:j指令)ExtOp[0]: (1: 符号扩展, 0: 零扩展)ALUOp[2:0]: (3 位) (送往ALU Control Unit)
2.3 真值表
| 指令 | Opcode |
RegDst |
ALUSrc |
MemtoReg |
RegWrite |
MemRead |
MemWrite |
Branch |
Jump |
ExtOp |
ALUOp[2:0] |
|---|---|---|---|---|---|---|---|---|---|---|---|
R-type |
000000 |
1 | 0 | 00 |
1 | 0 | 0 | 0 | 0 | X | 100 |
lw |
100011 |
0 | 1 | 01 |
1 | 1 | 0 | 0 | 0 | 1 | 000 |
sw |
101011 |
X | 1 | XX |
0 | 0 | 1 | 0 | 0 | 1 | 000 |
beq |
000100 |
X | 0 | XX |
0 | 0 | 0 | 1 | 0 | 1 | 001 |
ori |
001101 |
0 | 1 | 00 |
1 | 0 | 0 | 0 | 0 | 0 | 010 |
lui |
001111 |
0 | X | 10 |
1 | 0 | 0 | 0 | 0 | X | XXX |
addi |
001000 |
0 | 1 | 00 |
1 | 0 | 0 | 0 | 0 | 1 | 000 |
j |
000010 |
X | X | XX |
0 | 0 | 0 | 0 | 1 | X | XXX |
3 位 ALUOp 编码:
000:lw/sw(主控制器要求 ADD)001:beq(主控制器要求 SUB)010:ori(主控制器要求 OR)011: (预留, e.g., forandi)100: R-type (主控制器说:“我不知道,请查看funct码”)101: (预留, e.g., forxori)110: (预留, e.g., foraddi)111: (预留)
关于 nop (0x00000000) 的说明:nop 指令的 Opcode 是 000000,Funct 是 000000。
Main Control会将其视为 R-type。ALU Control会将其视为sll。- 它最终执行
sll $zero, $zero, 0。 - 控制器会尝试将结果
0写入$zero寄存器。
顶层逻辑补充
- 最终的
RegWrite信号:jr指令不应该写寄存器。因此,连接到 GRF 的最终RegWrite_Enable信号应该是:RegWrite_Enable=Main_Control[RegWrite]AND ( NOTALU_Control[JR])


2.2.2 ALU Control Unit
1.2 端口定义
- 输入 (Inputs):
ALUOp[2:0]: 来自 Main Control Unit (主控制器) 的 3 位操作码。Func[5:0]: 来自指令码的[5:0]位 (功能码)。
- 输出 (Outputs):
ALUCtrl[3:0]: 送往 ALU 的 4 位最终运算码 (我们之前已约定0000=ADD,0001=SUB,0010=AND,0011=OR,0100=SLT,0101=SLL)。JR[0]: (新增输出) 用于jr指令。当ALUOp=100且Func=001000时,此信号为 1。
1.3 真值表
ALUOp[2:0] (输入) |
Func[5:0] (输入) |
备注 (指令) | ALUCtrl[3:0] (输出) |
JR[0] (输出) |
|---|---|---|---|---|
000 |
X (任意) | lw / sw |
0000 (ADD) |
0 |
001 |
X (任意) | beq |
0001 (SUB) |
0 |
010 |
X (任意) | ori |
0011 (OR) |
0 |
011 |
X (任意) | (预留) | X (e.g., 0000) |
0 |
100 |
100000 |
add |
0000 (ADD) |
0 |
100 |
100010 |
sub |
0001 (SUB) |
0 |
100 |
100100 |
and |
0010 (AND) |
0 |
100 |
101010 |
slt |
0100 (SLT) |
0 |
100 |
000000 |
sll (或 nop) |
0101 (SLL) |
0 |
100 |
001000 |
jr |
X (e.g., 0000) |
1 |
| (other) | (other) | (Undefined) | X (e.g., 0000) |
0 |

课下总结:
P3做出来两道题,也算是通过了,第一周的时候没估算好要完成的时间,结果周日前没做完,直接拖了一周进度。
于是周一喜提上机放假,最终又花了一天半多才完成最终版。
但是最终版甚至连弱测都没有通过。省略漫长的debug阶段,最终还是通过求助助教,发现bug的原因竟是 多了一个ROM !!
把这个ROM改成逻辑元件后,就通过了。
不能有多余ROM的原因,是评测机在评测时,这会通过正则匹配到ROM,然后读取其中的数据,所以我增加一个ROM后就会导致评测出错。
思考题
- 现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。
A:合理。ROM是只读存储器,因此可以用来储存指令;RAM既可以读也可以写,因此满足DM对读写的要求;GRF是寄存器堆,需要较高的读写速度,因此适合用寄存器实现。 - 事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?请给出你的理由。
A:nop指令码为0x00000000,相当于sll $0, $0, 0, 相当于把$0寄存器中的值左移0位并写入$0寄存器,因为$0的值始终为0,不会被修改,因此该指令执行后没有任何影响。即使cpu没有设置sll指令,nop指令也不会对电路中任何元件进行操作,对电路没有任何影响。