单周期CPU设计方案
概述
本次课下主要依靠P3的logisim进行翻译,不过我也对P3设计的一系列不合理之处进行了调整。我首先根据logisim电路图搭建各个模块,随后在mips.v文件中把各个模块合理地连接起来,最后添加controller模块产生控制信号。
指令说明
本文实现的CPU在P3基础上添加了2条指令,即jal
和jr
。
R型指令
add
sub
jr
实际上实现的指令相当于
addu
和subu
,因为题目明确指出不考虑溢出
I型指令
ori
lw
sw
beq
lui
J型指令
jal
空指令
nop
功能模块设计
IFU
将PC和IM合二为一。IM使用$readmehn
语句读取code.txt
文件里的指令。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
clk | input | 时钟信号 | 1 |
reset | input | 复位信号 | 1 |
next_pc | input | 下一个指令地址 | 32 |
PC | output | 当前指令地址寄存器 | 32 |
instr | output | 当前指令 | 32 |
NPC
用组合逻辑计算出下一个指令的地址,通常是变为PC+4
。根据控制信号不同会执行beq/jal/jr
三种跳转。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
PC | input | 当前指令地址 | 32 |
offset | input | 分支偏移量 | 16 |
instr_26 | input | 指令中26位目标地址 | 26 |
ra_data | input | 从GRF返回的地址数据 | 32 |
zero | input | 分支条件标志 | 1 |
NPCOp | input | 下一地址选择操作码 | 3 |
next_pc | output | 计算后的下一指令地址 | 32 |
pc_add_4 | output | 当前地址加4的值 | 32 |
GRF
寄存器堆,同样是翻译即可。注意要display
否则无法评测。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
clk | input | 时钟信号 | 1 |
reset | input | 复位信号 | 1 |
RegWrite | input | 寄存器写入使能信号 | 1 |
RA1 | input | 读寄存器1地址 | 5 |
RA2 | input | 读寄存器2地址 | 5 |
WA | input | 写寄存器地址 | 5 |
WD | input | 写入数据 | 32 |
RD1 | output | 读寄存器1数据 | 32 |
RD2 | output | 读寄存器2数据 | 32 |
PC | input | 当前程序计数器地址 | 32 |
DM
数据存储器,朴实无华的读写功能。同样需要display
。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
PC | input | 当前程序计数器地址 | 32 |
clk | input | 时钟信号 | 1 |
reset | input | 复位信号 | 1 |
MemWrite | input | 内存写入使能信号 | 1 |
WA | input | 写地址 | 32 |
WD | input | 写入数据 | 32 |
RD | output | 读取数据 | 32 |
ALU
本文需要的指令只需要四个运算:加、减、或。相等的判断用减法实现。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
ALUOp | input | ALU操作选择信号 | 3 |
A | input | ALU第一个操作数 | 32 |
B | input | ALU第二个操作数 | 32 |
zero | output | 结果是否为零标志 | 1 |
ALU_res | output | ALU运算结果 | 32 |
MUX
这个模块的设计和每个人自身的电路设计高度相关,我认为先建立mips.v
顶层文件再构造更好。具体内容在下一部分讲解。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
rt | input | 源寄存器1地址 | 5 |
rd | input | 源寄存器2地址 | 5 |
WAOp | input | 写地址选择操作码 | 2 |
MemRD | input | 从内存读取的数据 | 32 |
offset | input | 偏移量 | 16 |
ALU_res | input | ALU运算结果 | 32 |
pc_add_4 | input | 当前PC加4的值 | 32 |
WDOp | input | 写数据选择操作码 | 2 |
RD2 | input | 第二个读寄存器的数据 | 32 |
Ext_imm16 | input | 扩展的16位立即数 | 32 |
BOp | input | B选择信号 | 1 |
B | output | 选择的第二个操作数 | 32 |
WD | output | 写入的数据 | 32 |
A3 | output | 写入的目标寄存器地址 | 5 |
组建数据通路
verilog组建数据通路的难度远大于logisim,因为不够直观,很容易漏接某些线。我认为比较不容易出错的设计方式如下:
- 将除MUX外的所有模块放入
mips.v
文件中并接上所有的I/O
端口。为防出错,所有的接口都用同名wire
连接,可以先连好再定义这些wire
型变量。 - 观察不同模块的接口,根据自己整理的表格/P3的电路图确定哪些端口之间是一对一连接的。使用assign语句把连接到对应端口的
wire
变量链接在一起。 - 对于需要用到多路选择器的端口,我们建立一个统一的
MUX
模块,这个模块负责处理所有需要进行信号选择的端口。我们将所有可能的输入信号和一会用Controller.v
产生的选择信号全部作为MUX
的输入,所有输出信号作为输出,内部使用条件判断语句为各个输出信号赋上对应的值。随后将其添加到mips.v
中。
控制信号
建立一个新的Controller.v
模块,因为CPU操作只跟指令有关,所以输入只需要opcode
和funct
。对于其内部结构,我喜欢先为每一条指令单独定义一个wire
型变量,再以这些变量为条件去编写控制信号的生成逻辑。
信号名 | 方向 | 描述 | 位宽 |
---|---|---|---|
opcode | input | 指令操作码 | 6 |
funct | input | 功能码,用于区分操作类型 | 6 |
NPCOp | output | 下一地址选择操作码 | 3 |
RegWrite | output | 寄存器写使能信号 | 1 |
ALUOp | output | ALU操作码 | 3 |
MemWrite | output | 内存写使能信号 | 1 |
ExtOp | output | 立即数扩展操作选择信号 | 1 |
WAOp | output | 写地址选择操作码 | 2 |
WDOp | output | 写数据选择操作码 | 2 |
BOp | output | 第二操作数选择信号 | 1 |
思考题
- 阅读下面给出的 DM 的输入示例中(示例 DM 容量为 4KB,即 32bit × 1024字),根据你的理解回答,这个
addr
信号又是从哪里来的?地址信号addr
位数为什么是 [11:2] 而不是 [9:0] ?答:
addr
是ALU通过将寄存器值与偏移量offset
相加得到的地址信号,之所以取[11:2]是因为DM的存储是以32bit为单位,而地址信号是以8bit为单位,故略去后两位使地址信号与DM内的存储地址一致。 - 思考上述两种控制器设计的译码方式,给出代码示例,并尝试对比各方式的优劣
答:指令对应的控制信号如何取值:
控制信号每种取值时对应的指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
case (opcode) 6'b000000: begin // R型指令,funct决定操作 case (funct) 6'b100000: begin // add RegWrite = 1; ALUOp = 3'b000; WAOp = 2'b01; NPCOp = 3'b0; MemWrite = 0; ExtOp = 0; WDOp = 2'b0; BOp = 0; end 6'b100010: begin // sub RegWrite = 1; ALUOp = 3'b001; WAOp = 2'b01; NPCOp = 3'b0; MemWrite = 0; ExtOp = 0; WDOp = 2'b0; BOp = 0; end //略 endcase //略 endcase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
assign NPCOp = beq ? 3'b001: jal ? 3'b010: jr ? 3'b011: 3'b0; assign RegWrite = add || sub || ori || lw || lui || jal; assign ALUOp = add || lw || sw ? 3'b000: sub || beq ? 3'b001: ori ? 3'b010: 3'b111; assign MemWrite = sw; assign ExtOp = ori; assign WAOp = add || sub || jr ? 2'b01: jal ? 2'b10: 2'b0; assign WDOp = lw ? 2'b01: lui? 2'b10: jal? 2'b11: 2'b0; assign BOp = ori || lw || sw;
- 在相应的部件中,复位信号的设计都是同步复位,这与 P3 中的设计要求不同。请对比同步复位与异步复位这两种方式的
reset
信号与clk
信号优先级的关系。 答:同步复位中clk
信号优先级更高,只有clk
上升沿到来时reset
信号才有影响,而异步复位二者地位相当,二者都可以随时生效。 - C 语言是一种弱类型程序设计语言。C 语言中不对计算结果溢出进行处理,这意味着 C 语言要求程序员必须很清楚计算结果是否会导致溢出。因此,如果仅仅支持 C 语言,MIPS 指令的所有计算指令均可以忽略溢出。 请说明为什么在忽略溢出的前提下,
addi
与addiu
是等价的,add
与addu
是等价的。 答:以add
与addu
为例,从RTL描述中可以看出,addu
就是在add
的基础上增加了如果运算结果最高位和次高位不相等就抛出报错,所以如果忽略这个溢出,二者就是等价的。addi
和addiu
同理。