体系结构
指令集: add, sub, ori, lw, sw, beq, lui, nop
端口:
| 信号 | 方向 | 描述 |
|---|---|---|
CLK | I | 时钟信号 |
RST | I | 异步复位信号 |
基本模块
IFU
PC & NPC
此处尚未要求 J 类型指令和 jr 指令, 留出扩展空间
- Moore 状态机:
- 下一状态:NPC
- PC + 4
- PC + 4 + Offset
- Jump
- 输出:PC
- 下一状态:NPC
PC
-
端口:
信号 方向 描述 CLKI 时钟信号 RSTI 异步复位信号 NPC[31:0]I 下一指令地址 PC[31:0]O 取指令地址
NPC
-
端口:
信号 方向 描述 PC[31:0]I 当前指令地址 IN[31:0]I 跳转地址 ( offset[15:0],jump_address[25:0],ra[31:0])NPCSel[2:0]I 下一 PC 选择信号 ZeroI rs 和 rt 相等标志 (1=相等, 0=不等) PCPlus4[31:0]O 当前指令地址+4 NPC[31:0]O 下一指令地址 -
NPCSel编码:NPCSel 含义 00顺序执行 01分支跳转 10J 型跳转 11JR 寄存器跳转
IM
-
元件: ROM
-
地址范围: 0 x 00003000 ~ 0 x 00006 FFF
-
实际地址宽度:
0x00006FFF - 0x00003000 + 1 = 0x3FFF + 1 = 0x40000x4000 -> 16384Byte16384 / 4 = 4096 = 2 ** 12
-
按照指令寻址:
ROM_addr = (PC - 0x00003000) >> 2 -
端口:
信号 方向 描述 A[31:0]I 取指令地址 RD[31:0]O 取出指令
RF
已经在 P 0 课下完成, 直接复用模块
| 信号名 | 方向 | 描述 |
|---|---|---|
CLK | I | 时钟信号 |
RST | I | 异步复位信号 |
WE | I | 写使能信号 |
A1[4:0] | I | 地址输入信号, 读出到 RD 1 |
A2[4:0] | I | 地址输入信号, 读出到 RD 2 |
A3[4:0] | I | 地址输入信号,写入的目标寄存器 |
WD[31:0] | I | 32 位数据输入信号 |
RD1[31:0] | O | 输出 A 1 指定的寄存器数据 |
RD2[31:0] | O | 输出 A 2 指定的寄存器数据 |
ALU
目前运算共三种, 但留出三位以供扩展
ALUControl编码:
| 运算 | 编码 |
|---|---|
or | 001 |
add | 010 |
sub | 110 |
端口:
| 信号 | 方向 | 描述 |
|---|---|---|
SrcA | I | 操作数 1 |
SrcB | I | 操作数 2 |
ALUControl[2:0] | I | 运算种类控制信号 |
Zero | O | 使用 ALU 进行比较时, 做减法运算, 判断结果是否为 0 |
Result[31:0] | O | 运算结果 |
DM
-
元件: RAM, 读写双端口分离
-
地址范围: 0 x 00000000 ~ 0 x 00002 FFF
-
端口:
信号 方向 描述 CLKI 时钟信号 RSTI 异步复位信号 WEI 写使能信号 A[31:0]I 读写地址 WD[31:0]I 写入数据 RD[31:0]O 读出数据
EXT
| 信号 | 方向 | 描述 |
|---|---|---|
imm[15:0] | I | 指令中的立即数 |
Op | I | 扩展控制信号 (0=无符号扩展,1=符号扩展) |
Out[31:0] | O | 扩展后的结果 |
Controller
Main Decoder
端口:
| 信号 | 方向 | 描述 |
|---|---|---|
Opcode[5:0] | I | 指令的[31:26], op 部分 |
MemtoReg | O | 寄存器回写的数据来源 (0=ALU 结果, 1=内存数据) |
MemWrite | O | DM 写使能信号 |
NPCSel[2:0] | O | 下一 PC 选择信号 |
ALUOp[1:0] | O | 传入 ALU 译码器计算ALUControl(00=加法, 01=减法, 10=根据 funct 字段) |
ALUSrc | O | ALU 第二操作数选择(0=寄存器, 1=立即数) |
RegDst | O | 寄存器写入目标(0=rt, 1=rd) |
RegWrite | O | 寄存器写使能信号 |
ALU Decoder
端口:
| 信号 | 方向 | 描述 |
|---|---|---|
Funct[5:0] | I | R 指令的[5:0], funct 部分 |
ALUOp[1:0] | I | 与Funct配合计算ALUControl |
ALUControl[2:0] | O | 选择 ALU 的具体运算功能 |
真值表:
ALUOp | Funct | ALUControl |
|---|---|---|
| 00 | X | 010 |
| X 1 | X | 110 |
| 1 X | 100000 (add) | 010 |
| 1 X | 100010 (sub) | 110 |
| 1 X | 100101 (or) | 001 |
指令
指令集: add, sub, ori, lw, sw, beq, lui, nop
- 数据通路:
| 部件 | IM | RF | EXT | ALU | DM | ||||
|---|---|---|---|---|---|---|---|---|---|
| 输入 | A | A 1 | A 2 | A 3 | WD | A | B | A | |
add | PC. PC | IM. RD[25:21] | IM. RD[20:16] | IM. RD[15:11] | ALU. Result | RF. RD 1 | RF. RD 2 | ||
sub | PC. PC | IM. RD[25:21] | IM. RD[20:16] | IM. RD[15:11] | ALU. Result | RF. RD 1 | RF. RD 2 | ||
ori | PC. PC | IM. RD[25:21] | IM. RD[20:16] | IM. RD[15:0] | RF. RD 1 | EXT. Out | |||
lw | PC. PC | IM. RD[25:21] | IM. RD[20:16] | DM. RD | IM. RD[15:0] | RF. RD 1 | EXT. Out | ALU. Result | |
sw | PC. PC | IM. RD[25:21] | IM. RD[20:16] | IM. RD[15:0] | RF. RD 1 | EXT. Out | ALU. Result | ||
beq | PC. PC | IM. RD[25:21] | IM. RD[20:16] | RF. RD 1 | RF. RD 2 | ||||
jal | PC. PC | 0 x 1 F | PC. PCPlus 4 | ||||||
jr | PC. PC | IM. RD[25:21] | |||||||
| Total | PC. PC | IM. RD[25:21] | IM. RD[20:16] | IM. RD[15:11] | ALU. Result DM. RD PC. PCPlus 4 | IM. RD[15:0] | RF. RD 1 | RF. RD 2 EXT. Out | ALU. Result |
P 3 思考题
- 上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。
上游的 Moore 状态机部分, PC 寄存器起到状态存储功能, NPC 内部的逻辑起到的是状态转移功能; 下游的 Mealy 状态机部分, GF 起到的是状态存储功能, 其余的 IM, ALU, EXT, DM 的各个输出做各类组合逻辑运算, 计算内容再次存储到寄存器内, 发挥了状态转移功能.
- 现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。
我认为比较合理.
- IM 中的指令在运行时不可变, 因此用 ROM 存放.
- DM 中的数据在运行时可读写, 因此用 RAM 存放.
- GRF for Global Register File, 顾名思义, 负责在运行过程中存储状态, 参与计算. 并且在运行过程中要反复读写, 而寄存器读写速度快于存储器. 因此采用 Register.
但是有些地方存在着简化, 即 IM 使用 ROM, 实际的处理器其应该可读写, 否则无法使操作系统载入新的程序到 IM 中, 使用 RAM 可能更适合实际使用.
- 在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。
- 我在 IFU 设计中, 分离了 PC 和 NPC 的设计, 使得职责单一化, PC 只负责输出当前指令的地址给 IM, 用以取出指令, 而复杂的下一条指令地址计算被封装进了 NPC.
- 我在 Controller 设计中, 我将控制单元分为两部分组合逻辑
首先是 Main Decoder, 负责根据指令的 Opcode 字段计算大部分控制信号, 包括读写使能, 寄存器选择等, 并且输出一个两位的 ALUOp 控制信号给 ALU Decoder 部分.
ALU Decoder 结合 Funct 字段, 计算出 ALUControl 信号, 控制 ALU 计算行为, 使得设计更加清晰且结构化.
因为一些计算指令如
add和addi都会用到 ALU, 前者有 Funct 字段而后者无; 一些非计算指令也会用到 ALU, 例如beq,lw,sw指令. 因此把计算行为的选择分离出来是有必要的, 这样可以综合 Opcode 和 Funct 考虑所用的运算, 并且两个模块都不会同时接入两个输入, 更简洁. 并且运算种类较多, 涉及到的计算的选择, 电路更复杂, 分离出来使得两个模块复杂度都降低了.
- 事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?
nop空指令的机器码为 0 x 00000000, opcode 字段为 000000, 因此会被解读是 R 型指令, 即为sll $zero, $zero, 0指令.
我们目前没有实现sll指令, 但是控制器根据逻辑生成的控制信号, 使得其仍旧不会真正进行有效的读写, 而是空转一周期, 因此无需纳入真值表单独考虑.
- 阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。