Logisim单周期cpu设计文档与思考题
设计文档
支持指令集
指令 | 格式 | 描述(RTL) | 机器码 | OPCODE/FUNCT |
---|---|---|---|---|
add | add rd rs rt | GPR[rd] <- GPR[rs]+GPR[rt] | R型 | 0/100000 |
sub | sub rd rs rt | GPR[rd] <- GPR[rs]-GPR[rt] | R型 | 0/100010 |
ori | ori rt rs imme | GPR[rt] <- GPR[rs] or imme | I型 | 001101 |
lw | lw rt offset(base) | GPR[rt] <- memory[GPR[base]+offset] | I型 | 100011 |
sw | sw rt offset(base) | memory[GPR[base]+offset] <- GPR[rt] | I型 | 101011 |
beq | beq rs rt offset | if(GPR[rs]==GPR[rt]) PC <- PC+4+sign_extend(offset||00) | I型 | 000100 |
lui | lui rt imme | GPR[rt] <- imme||0^16 | I型 | 001111 |
jal | jal target | PC <- PC31..28||target||00 并且 GPR[31] <- PC + 4 | J型 | 000011 |
jr | jr rs | PC <- GPR[rs] | R型 | 0/001000 |
sll | sll rd rt s | GPR[rd] <- GPR[rt] << s | R型 | 0/000000 |
jalr | jalr rd rs | PC <- GPR[rs] , GPR[rd] <- PC+4 | R型 | 0/001001 |
lb | lb rt offset(rs) | GPR[rt] <- memory[GPR[rs]+offset] | I型 | 100000 |
sb | sb rt offset(rs) | memory[GPR[rs]+offset] <- GPR[rt] | I型 | 101000 |
slt | slt rd rs rt | GPR[rd] <- (GPR[rs] < GPR[rt]) | R型 | 0/101010 |
nop | null | 不进行任何操作,空置一周期 | null | 0x00000000 |
注意:
- 其中sll 指令为R型指令,这是因为我们最多移位31位,没必要用16位的立即数保存。故s为5位,是一种特殊的R型(只有两个寄存器参与操作)
- nop虽然不需要进行任何操作,但是仍然需要进行PC <- PC + 4,所以在设计NPCop时不要忘了2'b00也应该能被nop激活。
mips指令集机器码补充
R型指令
opcode rs rt rd shamt funct 位长 6 5 5 5 5 6 占位 31-26 25-21 20-16 15-11 10-6 5-0 I型指令
opcode rs rt immediate 位长 6 5 5 16 占位 31-26 25-21 20-16 15-0 J型指令
opcode instr_index 位长 6 26 占位 31-26 25-0
数据通路形式建模
模块分析
- IFU(Instruction Fetch Unit 取指令单元)
模块描述
内部包括 PC(程序计数器)、IM(指令存储器)、NPC(进行指令的转移地址计算)。
PC 用寄存器实现,应具有异步复位功能,复位值为起始地址。
起始地址:0x00003000。
地址范围:0x00003000 ~ 0x00006FFF。
IM 用 ROM 实现,容量为 4096 × 32bit。
ROM 内部的起始地址是从 0 开始的,即 ROM 的 0 位置存储的是 PC 为 0x00003000 的指令,每条指令是一个 32bit 常数。
经过以上分析,不难发现 ROM 实际地址宽度仅需 12 位,请使用恰当的方法将 PC 中储存的地址同 IM 联系起来。
模块功能及引脚定义PC:当Reset=1时,异步复位至地址0x0000_3000
信号 方向 位 描述 CLK I 1 单周期的时钟信号 Reset I 1 异步复位 DI I 1 NPC传来的新的指令地址 DO O 1 当前指令地址
- NPC:计算下一条指令的地址
信号 方向 位 描述 PC I 32 当前指令地址 imm16 I 16 beq时跳转到的地址 ZERO I 1 ALU减法结果是否为 0 NPCop I 2 决定NPC应该进行何种计算 NPC O 32 下一条指令的地址
- IM:读取指令机器码并分解
信号 方向 位 描述 PC I 32 当前指令地址 OPCODE O 6 当前指令的OPCODE rs O 5 rs编号 rt O 5 rt编号 rd O 5 rd编号 imm16 O 16 立即数 FUNCT O 6 当前指令的FUNCT
- GRF(寄存器堆)
模块描述
用具有写使能的寄存器实现,寄存器总数为 32 个,应具有异步复位功能。
0 号寄存器的值始终保持为 0。其他寄存器初始值(复位后)均为 0,无需专门设置。
0号寄存器采用接地设计,故实际上只有31个寄存器。
模块功能及引脚定义GRF:寄存器堆用于存储寄存器中的数据
信号 方向 位 描述 CLK I 1 时钟信号 Reset I 1 异步复位信号 GRFWe I 1 写使能信号 A1 I 5 rs寄存器编号 A2 I 5 rt寄存器编号 A3 I 5 rd寄存器编号 WD I 32 写入rd的数据 RD1 O 32 rs寄存器读出值 RD2 O 32 rt寄存器读出值
- ALU(算术逻辑单元)
模块描述
- 提供 32 位加、减、或运算及大小比较功能。
- 加减法按无符号处理(不考虑溢出)。
模块功能及引脚定义
信号 方向 位 描述 A I 32 GPR[rs] B I 32 GPR[rt] 或 扩展后的立即数 ALUop I 2 执行何种运算 RD O 32 运算结果 ZERO O 1 执行减法时结果是否为 0
- 注:规定ALUop的2'b00,2'b01,2'b10分别表示add、sub、ori操作
- DM(数据存储器)
模块描述
- 使用 RAM 实现,容量为 3072 × 32bit,应具有异步复位功能,复位值为 0x00000000。
- 起始地址:0x00000000。
- 地址范围:0x00000000 ~ 0x00002FFF。
- RAM 应使用双端口模式,即设置 RAM 的 Data Interface 属性为 Separate load and store ports。
模块功能及引脚定义
信号 方向 位 描述 A I 32 数据存储器地址 DI I 32 写入的数据 We I 1 写使能 CLK I 1 时钟信号,异步复位 Reset I 1 复位信号 DO O 32 输出的数据
- EXT(扩展单元)
模块描述
- 使用 Logisim 内置的 Bit Extender。
- 作用为将immediate16拓展为32bit。
- 具有拓展选择信号EXTop。
控制器(Controller)建模
- 模块描述
- 使用与或门阵列构造控制信号。
- 分析可知,决定控制信号的就是当前在执行哪种指令,我们便需要建立每种信号的布尔表达式
- 与阵列中,我们将每种指令作为输出,布尔表达式建立方法如:beq的opcode为000100,则beq=!op[5]!op[4] !op[3]op[2]!op[1]*!op[0]
- 或阵列中,只需要把使得控制信号为真的指令或起来即可。
- 模块功能及引脚定义
信号 方向 位 描述 多位控制信号的具体描述 OPCODE I 6 指令的opcode FUNCT I 6 指令的funct NPCop O 2 决定NPC应该进行何种计算 2'b00 : +4 2'b01 : beq 2'b10 : jal 2'b11 : jr WRSel O 2 GRF写入地址来源 2'b00 : rt 2'b01 : rd 2'b10 : ra(jal) WDSel O 2 GRF写入数据来源 2'b00 : ALU的输出 2'b01 : DM的输出 2'b10 : 立即数 2'b11 : PC+4(jal) EXTop O 2 如何扩展立即数 2'b00 : 无符号扩展 2'b01 : 有符号扩展 2'b10 : 在低位扩展16个0 GRFWe O 1 GRF写使能 ALUop O 2 进行何种运算 2'b00 : 加法 2'b01 : 减法 2'b10 : 或运算 2'b11 : 左移运算 BSel O 1 运算数是否为立即数 DMWe O 1 DM写使能
思考题
-
上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。
答:单周期cpu涉及到的模块有:NPC、PC、IM、GRF、ALU、DM,可以将其划分为两个FSM:分别为NPC、PC、IM构成的Moore型FSM,称之为IFU 和 GRF、ALU、DM构成的下游mealy型FSM。在IFU中,PC发挥状态存储功能,NPC发挥状态转移功能;在下游FSM中,GRF发挥状态存储功能,整个IFU发挥状态转移功能。
-
现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。
答:我认为合理。ROM是只读存储器,只能读取而无法写入,在程序运行的过程中,我们已经将mips翻译为机器码导入IM的ROM中,无须修改,故合理。DM是cpu的主存,会与ALU和GRF间交互数据,所以DM需要同时具有读与写的功能,使用RAM合理。GRF本来就是register file,临时的存储变量,符合寄存器的特性,合理。
-
在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。
答:无。
-
事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?
答:由于我们的controller是通过与或阵列实现的,nop的机器码为0x0000_0000,在不考虑nop时,controller的与阵列的所有已经设计好的指令输出都为低电平,即cpu不会有任何高电平写使能、NPC也按照正常方式计算一下条指令地址,此时电路表现为空置一周期,故不需要考虑nop指令的设计。
-
阅读 Pre 的 "MIPS 指令集及汇编语言" 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。
答:我认为强度不够。在指令集覆盖方面缺少对sub指令的测试,同时在具体单种指令的测试上,ori的数据缺少边界情况的测试如立即数为0x0或0xffff;add指令缺少运算数包含0x0的情况;sw指令测试存在问题:lw的偏移量是负数的情况没有测试;lw与sw是同样的问题;beq指令的问题是缺少寄存器是负数的判断和跳转。mips附上成功自测出GRFbug的改进数据。 # add sub ori lw sw beq lui nop ori $a0 $0 123#a0 = 123 ori $a1 $a0 456#a1 = 507 ori $a1 $a1 0#a1 = 507 ori $a2 0xffff#a2 = 0xffff lui $a3 123#a3 = 0x007B_0000 lui $t0 0xffff ori $t0 $t0 0xffff#t0 = 0xffffffff (-1) # wrong from here s0 is -1(wrong) add $s0 $a0 $a2#s0 = 65658 add $s1 $a0 $t0#s1 = 122 add $s2 $t0 $t0#s2 = -2 add $s3 $0 $t0#s3 = -1 sub $s4 $a0 $a1#s4 = -384 sub $s5 $a1 $a0#s5 = 384 sub $s6 $t0 $t0#s6 = 0 sub $s7 $a0 $t0#s7 = 124 ori $t1 0x0000#t1 = 0 sw $a0 0($t1)#123 sw $a2 4($t1)#0xffff sw $0 8($t1)#0 sw $t0 12($t1)#-1 ori $t2 20#t2 = 20 sw $a1 -4($t2)#507 ori $t3 $0 4 lw $t4 4($t3)#t4 = 0xffff lw $t5 8($t3)#t5 = 0 lw $t6 12($t3)#t6 = -1 lw $t7 -4($t3)#t7 = 507 beq $a0 $a1 loop1#unequal beq $s3 $t0 loop2#equal loop1:lw $t8 0($t1) loop2:lw $t8 4($t1)#t8 = 0xffff