CO | P3_单周期CPU设计
思考题
-
上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。
状态存储:GRF,IFU
状态转移:Controller,ALU
-
现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。
合理
ROM是只读的存储器,在IM模块,我们需要将存储的程序代码读取出来,而不需要修改程序代码的值。使用ROM不容易造成数据的丢失,系统更稳定 更安全。
RAM是随机存储器,DM需要寄存器可读、可写,且具有大量内存,RAM正具有这些特点
Register寄存器,是高速存储设备,访问数据的速度最快,性能好,在GRF模块使用Register正合适
-
在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。
Splitter,SrcBSrc;(详细设计见下方草稿)
Spliiter用于将Instr(32位指令)分解,输出op,funct,offset等各个部分的指令
SrcBSrc用于选择需要进行ALU操作的数,是从RD2读取的数据 还是扩展后的立即数
-
事实上,实现
nop空指令,我们并不需要将它加入控制信号真值表,为什么?nop指令是0x00000000,在程序中只会进行pc=pc+4的操作,对电路中的其他部件都没有影响,所以不需要修改其他东西的值 -
阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。
ori模块没有测试其立即数的无符号位扩展功能,建议在第三行加上ori $a4,$a0,0xffff得到的结果应该是0xffffbeq模块没有实现向前跳转的功能- 数量太少,覆盖的地址不够
该节中的测试样例:
ori $a0, $0, 123
ori $a1, $a0, 456
lui $a2, 123 # 符号位为 0
lui $a3, 0xffff # 符号位为 1
ori $a3, $a3, 0xffff # $a3 = -1
add $s0, $a0, $a2 # 正正
add $s1, $a0, $a3 # 正负
add $s2, $a3, $a3 # 负负
ori $t0, $0, 0x0000
sw $a0, 0($t0)
sw $a1, 4($t0)
sw $a2, 8($t0)
sw $a3, 12($t0)
sw $s0, 16($t0)
sw $s1, 20($t0)
sw $s2, 24($t0)
lw $a0, 0($t0)
lw $a1, 12($t0)
sw $a0, 28($t0)
sw $a1, 32($t0)
ori $a0, $0, 1
ori $a1, $0, 2
ori $a2, $0, 1
beq $a0, $a1, loop1 # 不相等
beq $a0, $a2, loop2 # 相等
loop1:sw $a0, 36($t0)
loop2:sw $a1, 40($t0)
设计要求
-
处理器为 32 位单周期处理器,应支持的指令集为:
add, sub, ori, lw, sw, beq, lui, nop
,其中:
nop为空指令,机器码0x00000000,不进行任何有效行为(修改寄存器等)。add, sub按无符号加减法处理(不考虑溢出)。
-
需要采用模块化和层次化设计。顶层有效的驱动信号要求包括且仅包括异步复位信号 reset(clk 请使用内置时钟模块)。
设计草稿(模块规格)
模块规格设计包含了 CPU 各个模块的端口说明与功能定义。一个好的模块规格可以让其他人快速理解该模块的功能并加以实现。这就相当于一份 CPU 重要部件的“说明书”,需着重设计。
IFU(取指令单元)
PC(程序计数器)
- PC 用寄存器实现,应具有异步复位功能,复位值为起始地址。
- 起始地址:0x00003000。
- 地址范围:0x00003000 ~ 0x00006FFF。
IM(指令存储器)
- IM 用 ROM 实现,容量为 4096 × 32bit。
- ROM 内部的起始地址是从 0 开始的,即 ROM 的 0 位置存储的是 PC 为 0x00003000 的指令,每条指令是一个 32bit 常数。
- 经过以上分析,不难发现 ROM 实际地址宽度仅需 12 位,请使用恰当的方法将 PC 中储存的地址同 IM 联系起来。
电路外观:
电路内部:

端口说明:
| 信号名 | 方向 | 描述 |
|---|---|---|
| clk | I | 时钟信号 |
| reset | I | 异步复位信号,将32个寄存器中的值全部清零 1:复位 0:无效 |
| Zero | I | ALU输出端是否为零,用以判断是否符合跳转条件 0:不跳转 1:跳转 |
| Branch | I | 是否是跳转指令 0:不是跳转指令 1:是跳转指令 |
| SignImm[31:0] | I | 16位立即数 位扩展成32位后的结果,用于beq跳转,表示要跳转到本条指令后的第SignImm条指令 |
| Instr[31:0] | O | 32位指令 |
| pc[31:0] | O | 输出pc寄存器存储的地址 |
功能定义:
| 序号 | 功能名称 | 描述 |
|---|---|---|
| 1 | 异步复位 | reset信号有效时,将寄存器存储的数值置为0x00003000 |
| 2 | 跳转判断 | Zero和Branch同时为1时,PCSrc的值为1,表明是跳转指令且符合跳转条件,跳转到指定语句位置;其他情况下,PCSrc的值为0,进入下一条语句,不进行跳转。 |
| 3 | 取指令 | 将pc存储的地址,减0x00003000(因为ROM 内部的起始地址是从 0 开始的),再取2-13位(0x00003000~0x00006fff)的内容,作为ROM的输入端 |
| 4 | PC地址计算 | 常规情况下pc=pc+4; 当需要beq跳转(Branch&Zero==1)时pc=pc+4+ sign_extend(offset) |
GRF(通用寄存器组,也称为寄存器文件、寄存器堆)
- 用具有写使能的寄存器实现,寄存器总数为 32 个,应具有异步复位功能。
- 0 号寄存器的值始终保持为 0。其他寄存器初始值(复位后)均为 0,无需专门设置。
电路外观:
电路内部:

端口说明:
| 信号名 | 方向 | 描述 |
|---|---|---|
| clk | I | 时钟信号 |
| reset | I | 异步复位信号,将32个寄存器中的值全部清零 1:复位 0:无效 |
| WE | I | 写入使能信号 1:可以向GRF中写入数据 0:不能向GRF中写入数据 |
| A1[4:0] | I | 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读到RD1 |
| A2[4:0] | I | 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读到RD2 |
| A3[4:0] | I | 5位地址输入信号,指定32个寄存器中的一个作为写入目标寄存器 |
| WD3[31:0] | I | 32位数据输入信号 |
| RD1 | O | 输出A1指定的寄存器中的32位数据 |
| RD2 | O | 输出A2指定的寄存器中的32位数据 |
功能定义:
| 序号 | 功能名称 | 描述 |
|---|---|---|
| 1 | 复位 | reset信号有效时,所有寄存器存储的数值清零,其行为与logisim自带部件register的reset接口完全相同 |
| 2 | 读数据 | 读出A1,A2地址对应寄存器中所存储的数据到RD1,RD2 |
| 3 | 写数据 | 当WE 有效且时钟上升沿来临时,将 WD 写入A3 所对应的寄存器中 |
ALU(算术逻辑单元)
- 提供 32 位加、减、或运算及大小比较功能。
- 加减法按无符号处理(不考虑溢出)。
端口说明:
| 信号名 | 方向 | 描述 |
|---|---|---|
| SrcA[31:0] | I | 32位输入,用于运算的数A |
| SrcB[31:0] | I | 32位输入,用于运算的数B |
| ALUCtr[3:0] | I | 4位输入,控制ALU中要进行什么运算 0000:加法 0001:减法 0010:或 0011:左移16位(lui) 0100:与 0101:左移shamt位(sll) |
| ALUResult[31:0] | O | 32位输出,进行ALU处理之后得到的结果 |
| Zero | O | 输出Ans是否为0 0:Ans不为0 1:Ans为0 |
功能定义:
| 序号 | 功能名称 | 描述 |
|---|---|---|
| 1 | 加法 | 当ALUCtr等于0000时,将SrcA的值和SrcB的值相加,将结果输出到ALUResult |
| 2 | 减法 | 当ALUCtr等于0001时,用SrcA的值减去SrcB的值,将结果输出到ALUResult |
| 3 | 或 | 当ALUCtr等于0010时,将SrcA的值和SrcB的值或运算,将结果输出到ALUResult |
| 4 | 左移16位(lui) | 当ALUCtr等于0011时,将SrcB的值左移16位,将结果输出到ALUResult |
| 5 | 与 | 当ALUCtr等于0100时,将SrcA的值和SrcB的值做与运算,将结果输出到ALUResult |
| 6 | 左移shamt位(sll) | 当ALUCtr等于0011时,将SrcB的值左移shamt位,将结果输出到ALUResult |
DM(数据存储器)
- 使用 RAM 实现,容量为 3072 × 32bit,应具有异步复位功能,复位值为 0x00000000。
- 起始地址:0x00000000。
- 地址范围:0x00000000 ~ 0x00002FFF。
- RAM 应使用双端口模式,即设置 RAM 的 Data Interface 属性为 Separate load and store ports。
端口说明:
| 信号名 | 方向 | 描述 |
|---|---|---|
| clk | I | 时钟信号 |
| reset | I | 异步复位信号,将RAM 中的值全部清零 1:复位 0:无效 |
| ALUResult[31:0] | I | 32位输入,ALU模块计算后的输出值 可能是RAM写入地址(取第2-13位),也可能是A3要写入的值(ReadData) |
| WriteData[31:0] | I | 32位输入,从寄存器中取出来的值,并准备存到RAM中 |
| MemToReg | I | 控制WD3输入的是从DM(数据存储器)读取的值还是ALU运算结果 0:ALUResult 1:ReadData x:不需要向写寄存器写入 |
| MemWrite | I | 决定DM是写入数据还是读取数据 1:从DM中读取数据 0:向DM中写入数据 |
| ReadData[31:0] | O | 32位输出,表示要写入A3的值 可能是ALUResult,也可能是从RAM中读取出来的值 |
功能定义:
| 序号 | 功能名称 | 描述 |
|---|---|---|
| 1 | 异步复位 | 异步复位,将RAM 中的值全部清零 |
| 2 | 存储数据 | 将WriteData写入 RAM中ALUResult的2-6位地址 |
| 3 | 读取数据 | 将RAM中 ALUResult的2-6位地址存储的数据,读取出来,作为ReadData输出 |
EXT(扩展单元)
- 可以使用 Logisim 内置的 Bit Extender。
端口说明:
| 信号名 | 方向 | 描述 |
|---|---|---|
| offset[15:0] | I | 输入16位的立即数 |
| ExtOp | I | 将16位的立即数扩展成32位时,选用有符号扩展还是无符号扩展 0:无符号扩展 1:有符号扩展 x:无效 |
| SignImm | O | 输出扩展后的32位数据 |
功能定义:
| 序号 | 功能名称 | 描述 |
|---|---|---|
| 1 | 位扩展 | 将16位的立即数扩展成32位,分符号扩展和无符号扩展两种情况 |
Splitter(分线器)
- 将每条指令的 32 位二进制码分成分别表示寄存器号、funct 码等的二进制码段
端口说明:
| 信号名 | 方向 | 描述 |
|---|---|---|
| Instr[31:0] | I | 输入 从IM中读取的32位指令 |
| RegDst | I | 控制给A3传递的地址是Rt还是Rd 0:传Rt的地址(//I类指令) 1:传Rd的地址(//R类指令) x:不需要向写寄存器写入 |
| A1[4:0] | O | 5位地址输出信号,对应GRF中的A1 |
| A2[4:0] | O | 5位地址输出信号,对应GRF中的A2 |
| A3[4:0] | O | 5位地址输出信号,对应GRF中的A3 |
| offset[15:0] | O | 16位输出,输出立即数 |
| op[5:0] | O | 6位操作数,对应op |
| funct[5:0] | O | 6位输出,对应funct |
| Shamt[4:0] | O | 5位输出,对应ALU模块的Shamt |
功能定义:
| 序号 | 功能名称 | 描述 |
|---|---|---|
| 1 | 分线 | 输入Instr,输出不同的值作为输出 |
Controller(控制器)
- 使用与或门阵列构造控制信号的具体方法见后文叙述。
- 也可以通过其它方式构造控制信号,同学们可以自行探索。
课下测试要求的功能:
| funct(5-0) | 100000 | 100010 | |||||
|---|---|---|---|---|---|---|---|
| op(31-26) | 000000 | 000000 | 001101 | 100011 | 101011 | 000100 | 001111 |
| 和逻辑↑/或逻辑↓ | add | sub | ori | lw | sw | beq | lui |
| RegDst | 1 | 1 | 0 | 0 | x | x | 0 |
| ALUSrc | 0 | 0 | 1 | 1 | 1 | 0 | 1 |
| MemToReg | 0 | 0 | 0 | 1 | x | x | 0 |
| RegWrite | 1 | 1 | 1 | 1 | 0 | 0 | 1 |
| MemWrite | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| Branch | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| ExtOp | x | x | 0 | 1 | 1 | 1 | 0 |
| ALUCtr | add(0000) | sub(0001) | or(0010) | add(0000) | add(0000) | sub(0001) | shift(0011) |
我自己补充的一些功能:
| funct(5-0) | 100100 | 100101 | 000000 | x | 001000 |
|---|---|---|---|---|---|
| op(31-26) | 000000 | 000000 | 000000 | 000011 | 000000 |
| 和逻辑↑/或逻辑↓ | and | or | sll | jal | jr |
| RegDst | 1 | 1 | 1 | 0 | 0 |
| ALUSrc | 0 | 0 | 0 | 0 | 0 |
| MemToReg | 0 | 0 | 0 | 0 | 0 |
| RegWrite | 1 | 1 | 1 | 1 | 0 |
| MemWrite | 0 | 0 | 0 | 0 | 0 |
| Branch | 0 | 0 | 0 | 0 | 0 |
| ExtOp | x(0) | 0 | 0 | 0 | 0 |
| JRSrc | 0 | 0 | 0 | 0 | 1 |
| JalSrc | 0 | 0 | 0 | 1 | 0 |
| ALUCtr | and(0100) | or(0010) | sll(0101) | 0 | 0 |
sll、jal、j、jr ,以及存取指令 sb、sh、lb、lh
| funct(5-0) | x | x | 110001 | |||
|---|---|---|---|---|---|---|
| op(31-26) | 000011 | 100000 | 101000 | 000000 | ||
| 和逻辑↑/或逻辑↓ | jal | j | lb | sb | bezal | |
| RegDst | 0 | 0 | ||||
| ALUSrc | 1 | 1 | ||||
| MemToReg | 1 | 0 | ||||
| RegWrite | 1 | 0 | ||||
| MemWrite | 0 | 1 | ||||
| Branch | 0 | 0 | ||||
| ExtOp | 1 | 1 | ||||
| JRSrc | 0 | 0 | 0 | 0 | 0 | 0 |
| Byte | 0 | 0 | 1 | 1 | 0 | 0 |
| ALUCtr | 0 | 0 |
课上增加的功能
| funct(5-0) | 100100 | |||||
|---|---|---|---|---|---|---|
| op(31-26) | 000000 | |||||
| 和逻辑↑/或逻辑↓ | and | |||||
| RegDst | ||||||
| ALUSrc | ||||||
| MemToReg | ||||||
| RegWrite | ||||||
| MemWrite | ||||||
| Branch | ||||||
| ExtOp | ||||||
| JRSrc | 0 | 0 | 0 | 0 | 0 | 0 |
| ALUCtr |
端口说明:
| 信号名 | 方向 | 描述 |
|---|---|---|
| funct[5:0] | I | (5-0) |
| op[5:0] | I | (31-26) |
| RegDst | O | 控制给A3传递的地址是Rt还是Rd 0:传Rt的地址(//I类指令) 1:传Rd的地址(//R类指令) x:不需要向写寄存器写入 |
| ALUSrc | O | 控制ALU的SrcB端口输入,是RD2的值还是立即数 0:RD2的值 1:立即数 |
| MemToReg | O | WD3输入的是从DM(数据存储器)读取的值还是ALU运算结果 0:ALUResult 1:ReadData x:不需要向写寄存器写入 |
| RegWrite | O | GRF的使能信号 1:需要向GRF中写入数据 0:不需要向GRF中写入数据 |
| MemWrite | O | 决定DM是写入数据还是读取数据 1:从DM中读取数据 0:向DM中写入数据,或不需要读取(即除了lw等指令,都是0) |
| Branch | O | 是否是跳转语句 0:不是跳转语句 1:是跳转语句 |
| ExtOp | O | 将16位的立即数扩展成32位时,选用有符号扩展还是无符号扩展 0:无符号扩展 1:有符号扩展 |
| ALU[3:0] | O | ALU需要进行的计算操作类型 0000:加法 0001:减法 0010:或 0011:左移16位 0100:与 |
| JRSrc | O | 是不是jr指令 |
| JSrc | O | 是不是j或jal指令 |
| JalSrc | O | 是不是jal指令(是否需要给ra存值) |
| Is_Byte | O | 是不是lb/sb |
功能定义:
| 序号 | 功能名称 | 描述 |
|---|---|---|
| 1 | 判断操作类型 | 根据op和funct确定指令操作类型 |
| 2 | 产生控制型号 | 根据操作类型,生成控制信号 |
关于MIPS指令编码表
(一般情况下,有例外。如sllv,rs,rt反着的,在ALU中把RD2(A2)左移RD1(A1)位即可)
| 31……26 | 25……21 | 20……16 | 15……11 | 10……6 | 5……0 |
|---|---|---|---|---|---|
| op | rs(A1) | rt(A2) | rd(A3) | shamt | funct |
| base(0) | offset | offset | offset |
要加上 v2.0 raw
V0:课下要求版本,加sll
V1:加了j,jal,jr
V2:加了j,jal,jr,lb,sb
P3第二次上机
第一题:
orz
| 31……26 | 25……21 | 20……16 | 15……0 |
|---|---|---|---|
| op 101010 |
rs | rt | immediate |
$$
\begin{flalign}
&if \quad GPR[rs]_0 = 0\quad then \
&\qquad GPR[rt]← GPR[rs]\ OR\ sign_extend(immediate)\
&else\
&\qquad GPR[rt]← GPR[rs]\
&Endif
\end{flalign}
$$
第二题:
bno
Branch if Not
| 31……26 | 25……21 | 20……16 | 15……0 |
|---|---|---|---|
| op 101111 |
rs | rt | immediate |
$$
\begin{flalign}
&a← GPR[rs]{30}+GPR[rs]{29}+…+GPR[rs]0\
&b← GPR[rt]{30}+GPR[rt]_{29}+…+GPR[rt]_0\
&temp←a_4||a+b_4||b\
&if\ (temp_4=temp_5) \
&\qquad PC←PC+4+sign_extend(immediate)\
&else\
&\qquad PC←PC+4\
&Endif
\end{flalign}
$$
第三题:
lob
Load Or Byte
$ Addr ← GPR[base] + sign_ext(offset)\ memword← memory[Addr] byte ← Addr_{1…0}\ GPR[rt] ← zero_ext(memword_{7+8byte…8byte})$



