Logisim 单周期CPU设计文档

CPU设计方案综述

(一)总体设计概述

使用Logisim设计开发一个初步的单周期CPU,总体概述如下:

  1. 此CPU为32位CPU
  2. 此CPU为单周期
  3. 此CPU支持的指令集为:{add, sub, addu, subu, ori, lw, sw, beq, lui, nop, j}
  4. nop的机器码为0x00000000
  5. addu,subu不支持溢出

(二)关键模块定义

1.IFU

(1) 端口说明

表 1-IFU端口说明

序号 信号名称 方向 描述
1 CLK I 时钟信号
2 RESET I 异步复位信号,将PC值置为0x00000000 0:无效 1:复位
3 PCN[31:0] I PC的下一个值
4 Instr[31:0] O 输出IM中将要执行的指令
5 PC[31:0] O 输出当前PC值

(2) 功能定义

表 2-IFU功能定义

序号 功能 描述
1 复位 当RESET有效时,将PC值置为0x00000000
2 更新PC值 PC\<=NPC
3 输出指令 根据PC的值,取出IM中的指令

2.GRF

(1) 端口说明

表 3-GRF端口说明

序号 信号名称 方向 描述
1 CLK I 时钟信号
2 WE I 写使能信号 0:不能向GRF写入数据 1:可向GRF写入数据
3 RESET I 异步复位信号 1:将所有寄存器清零 0:无效
4 A1[4:0] I 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD1
5 A2[4:0] I 5位地址输入信号,指定32个寄存器中的一个,将其中存储的数据读出到RD2
6 A3[4:0] I 5位地址输入信号,指定32个寄存器中的一个,将WD的数据读入到该寄存器
7 WD[31:0] I 32位写入到A3所指向寄存器的数据
8 RD1[31:0] O 输出A1指定的寄存器的32位数据
9 RD1[31:0] O 输出A2指定的寄存器的32位数据

(2)功能定义

表 4-GRF功能定义

序号 功能 描述
1 异步复位 当RESET的值有效时,将所有寄存器异步清零
2 写入数据 当WE为1且时钟上升沿来临时,将WD写入到A3对应的寄存器
3 读出数据 将A1和A2地址对应的寄存器的值分别通过RD1和RD2读出

3.DM

(1)端口说明

表 5-DM端口说明

序号 信号名称 方向 描述
1 CLK I 时钟信号
2 WE I 写使能信号 0:不能向DM写入数据 1:可向DM写入数据
3 RESET I 异步复位信号 0:无效 1:将DM清零
4 A[4:0] I 5位读取或写入地址信号
5 WD[31:0] I 32位写入数据
6 RD[31:0] O 32位读出数据

(2)功能定义

表 6-DM功能定义

序号 功能 描述
1 异步复位 当RESET的值有效时,将DM清零
2 写入数据 当WE为1且时钟上升沿来临时,将WD写入A对应的地址中
3 读出数据 RD时刻读出A对应地址的值

4.ALU

(1)端口说明

表 7-ALU端口说明

序号 信号名称 方向 描述
1 SrcA[31:0] I 操作数1
2 SrcB[31:0] I 操作数2
3 ALUControl[2:0] I 决定ALU操作: 000:无符号加 001:无符号减 010:与 011:或 100:将SrcB左移10位
4 Bigger O SrcA与ScrB是否相等 0:不相等 1:相等
5 Res[31:0] O 输出结果

(2)功能定义

表 8-ALU功能定义

序号 功能 描述
1 无符号加 Res=SrcA+SrcB
2 无符号减 Res=SrcA-SrcB
3 Res=SrcA&SrcB
4 Res=SrcA\ SrcB
5 将SrcB左移10位 Res=SrcB\<\<10

5.NPC

(1)端口说明

表 9-NPC端口说明

序号 信号名称 方向 描述
1 PC[31:0] I 当前PC值
2 Jump I Jump选择信号 0:无效 1:指令是J
3 SignImm[31:0] I 扩展后的32位信号
4 EPC[27:0] I 扩展后的28位信号
5l NPCControl I NPCControl选择信号 0:无效 1:指令是beq
6 PCN[31:0] O 下一PC值

(2)功能定义

表 10-NPC功能定义

序号 功能 描述
1 选择是否为J指令 0:无效 1:是J指令
2 选择是否为Beq指令 0:无效 1:为Beq指令
3 输出下一PC值 PCN为下一PC值

6.Control

(1) 端口说明

表 11-Control端口说明

序号 信号名称 方向 描述
1 Op[5:0] I Instr[31:26] 6位控制信号
2 FC[5:0] I Instr[0:5] 6位控制信号
3 ALUop[2:0] O ALU的控制信号
4 Jump O 是否为J指令 0:不是 1:是
5 RegWrite O GRF的写入信号 0:禁止写入 1:允许写入
6 MemWrite O DM的写入信号 0:禁止写入 1:允许写入
7 MemToReg O 选择写入REG的数据 0:ALU 1:DM
8 RegDest O 选择A3的地址 0:Instr[20:16] 1:Instr[15:11]
9 RegSrc O 选择SrcB数据来源 0:GRF 1:立即数
10 Branch O 是否为Beq指令 0:不是 1:是

(2)真值表

表 12-Control真值表

端口 addu subu ori lw sw lui beq J
Op 000000 000000 001101 100011 101011 001111 000100 000010
FC 100001 100011
ALUop 000 001 011 000 000 100 000 000
Jump 0 0 0 0 0 0 0 1
RegWrite 1 1 1 1 0 1 0 0
MemWrite 0 0 0 0 1 0 0 0
MemToReg 0 0 0 1 0 0 0 0
RegDest 1 1 0 0 0 0 0 0
RegSrc 0 0 1 1 1 1 0 0
Branch 0 0 0 0 0 0 1 0

表 12-Control真值表(续)

端口 add sub
Op 000000 000000
FC 100000 100010
ALUop 000 001
Jump 0 0
RegWrite 1 1
MemWrite 0 0
MemToReg 0 0
RegDest 1 1
RegSrc 0 0
Branch 0 0

测试方案

(1)测试代码

ori $t1 , $t2 , 100

addu(add) $t2 , $t1 , $t1

subu(sub) $t3 , $t2 , $t1

sw $t3 , 0($0)

lw $t4 , 0($0)

nop

Label:

beq $t3 , $t2 , Label_End

addu $t3 , $t3 , $t1

j Label

Label_End:

(2)MARS中运行结果


(3)该CPU运行结果


思考题

(一)上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。

答:状态存储:DM,GRF

状态转移:IFU,NPC,EXT,ALU,Control

(二)现在我们的模块中IM使用ROM, DM使用RAM, GRF使用Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。

答:合理

IM是指令单元,指令在程序执行过程中不会再更改,也不应该被更改,也就是应只读,ROM就是只具有读取功能的存储器,满足要求

DM是存储单元,在程序的执行过程中需要不断地向DM中存入,读出数据,所以具有读入与读出的RAM满足要求

之所以DM不选择寄存器的原因是寄存器相比RAM是昂贵的,且DM需要存储大量数据,采用寄存器会导致成本成倍增长,得不偿失。

GRF为寄存器堆,自然选择32个寄存器去实现32个寄存器,符合要求与逻辑

没有改进意见

(三)在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。

答:是,设计了NPC模块,介绍:NPC模块是用于计算PC的下一值的集成式计算中心,它包容了Beq j等跳转指令的计算,使得对于PC下一值的处理整体而思路清晰。设计思路:首先NPC作为计算PC下一值的模块最基本的功能就是PC\<=PC+4,这个是首要实现的功能,接着就是想办法实现BEQ,J等跳转指令的功能,结合BEQ和J等指令的机器码,将其有用的部分输入进NPC当中进行计算就可得到当前指令若为BEQ和J,它们的值分别为多少,但这里就涉及到了选择的问题,NPC如何知道提供给PC的下一值是PC+4,还是BEQ的跳转,还是J的跳转?因而就需引入多路选择器,并辅以控制选择信号(Conrtol部分生成)来选择PC的下一值,由于有3个选择,我这里采用了2个多路选择器,先二选一,再加入另一个再二选一,具体如图:

(四)事实上,实现nop空指令,我们并不需要将它加入控制信号真值表,为什么?请给出你的理由

答:nop为空指令,不会进行任何操作,仅仅占用一个指令而使得PC\<=PC+4,所以不管加不加都不会影响它的效果

(五)上文提到,MARS 不能导出 PC 与 DM 起始地址均为 0 的机器码。实际上,可以避免手工修改的麻烦。请查阅相关资料进行了解,并阐释为了解决这个问题,你最终采用的方法。

答:其实经过研究发现,在我目前所添加的指令中,PC所影响的指令就是J指令,因为J指令是直接跳转到地址,BEQ算的是偏移量,但为了修改方便,我还是针对PC进行了修改,而不是对J指令那一路进行修改,起始时首先PC应该复位为0x3000而且保证该周期正常取指,所以选择了多路选择器,在第一周期屏蔽PC的影响,直接取0位置处的地址(利用PC起始为0与0相等),在第二周期PC被成功赋值后再由PC决定取指,之后就简单了,每次PC的值作用于ROM时都先减去0x3000即可。实现如图:

(六)阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。

答:测试样例反汇编为:

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
28
lui $t0, 0x1
ori $t0, $t0, 0x2f6a
ori $a0, $0, 0x4
ori $a1, $0, 0x1
ori $a2, $0, 0x4
ori $a3, $0, 0x20
sw $a3, 0x0($0)
lw $t2, 0x0($0)
sw $t0, 0x0($a0)
subu $t1, $t0, $t2
addu $a0, $a0, $a2
sw $a1, 0x0($a0)
addu $a0, $a0, $a2
addu $a1, $a1, $a1
sw $a1, 0x0($a0)
addu $a0, $a0, $a2
addu $a1, $a1, $a1
sw $a1, 0x0($a0)
addu $a0, $a0, $a2
addu $a1, $a1, $a1
sw $a1, 0x0($a0)
addu $a0, $a0, $a2
addu $a1, $a1, $a1
sw $a1, 0x0($a0)
addu $a0, $a0, $a2
addu $a1, $a1, $a1
sw $a1, 0x0($a0)
beq $a3, $a1, label_1

首先肯定的是所有的要求实现的指令至少都出现了一次进行了测试(虽然要实现的是add和sub这里是addu和subu,但是由设计要求可知在这里add和addu等价,subu和sub等价),且除了Beq以外都测试了多次,提升了检测出Bug的概率,强度还是有的,但放到Logisim跑了一遍和观察汇编指令发现,全程并无负数出现,也就是只测试了正数,且正数不大,数据覆盖范围比较窄,同时发现检测的寄存器是固定的几个,寄存器检测范围比较窄,对于beq则没有检测原地跳,向后跳,只是检测了向前跳。跳转范围检测比较窄。