P3

Coooookie hahah

P3_Study

1 单周期CPU设计方案

1.1 设计概述

本文所呈现的是利用logisim实现的MIPS架构CPU,可以支持8条指令,含有IFU、GRF、ALU、DM、Controller、BranchControl、EXT、NPC模块。整体设计实现采用自下而上的设计思路,先逐一设计搭建每个子模块,再将所有子模块整合连接,从而实现完整的数据通路。
CPU顶层模块如下:
1

1.2 实现指令说明

1.2.1 R型指令

算术指令:add(暂视为不做溢出检查),sub(暂视为不做溢出检查)
若要扩展支持其他R型指令,主要改变Controller模块与ALU模块

1.2.2 I型指令

B类指令:beq
算术指令:lui,ori
访存指令:lw,sw
若要扩展支持其他I型指令,主要改变Controller模块,EXT模块,ALU模块

1.2.3 J型指令

本CPU不支持J型指令,若要支持j,jr等指令可做以下拓展:
①Controller模块增加JR与JUMP信号输出
②NPC模块增加JR与JUMP信号输入、将Imm16改为Imm26、增加GPR[ra]数据输入

1.2.4 其他

nop

1.3 数据通路模块

1.3.1 IFU(取指令单元)

该模块包含PC(程序计数器)和IM(指令存储器),PC通过32位寄存器实现,存储当前需执行指令的地址,IM通过ROM实现(32 * 32bit),存储所有需要执行的指令。根据PC的值从IM中取出对应指令传入后续数据通路。
注意
ROM中是以字为单位存储的,并且ROM容量为32条指令,只需五位数表示地址,所以取PC的62位即可。同理,若ROM容量为1024*32bit则取PC的112位。

  • 模块外观
    1
  • 模块内部电路
    1
  • 端口定义
    信号名 方向 位宽 描述
    reset I 1 异步复位信号
    NPC I 32 下一条要被执行的指令的地址
    Imm O 16 当前指令的15~0位
    func O 6 当前指令的5~0位
    Opcode O 6 当前指令的31~26位
    Instr O 32 当前执行的指令
    rs O 5 当前指令的25~21位
    rt O 5 当前指令的20~16位
    rd O 5 当前指令的15~11位
    PC O 32 当前指令的地址
  • 功能定义
    序号 功能名称 功能描述
    1 复位 reset信号为1时,PC寄存器的值置为0x00000000
    2 取指令 根据当前PC寄存器的值从ROM中读取指令
    3 取数据 根据不同位代表不同信息读取指令包含的数据

1.3.2 GRF(通用寄存器组)

该模块包含32个32位寄存器,对应MIPS架构中$0~$31通用寄存器,可以通过输入的5位地址访存寄存器,其中,$0寄存器设置写入信息为const0。

  • 模块外观
    1
  • 模块内部电路
    1
  • 端口定义
    信号名 方向 位宽 描述
    reset I 1 异步复位信号
    WE I 1 写使能信号,决定当前寄存器堆是否可以写入
    rs I 5 需要读取的第1个寄存器的编号
    rt I 5 需要读取的第2个寄存器的编号
    rd I 5 需要写入的寄存器的编号
    Data I 32 需要写入寄存器的数据
    RD1 O 32 读取到的第1个寄存器的数据
    RD2 O 32 读取到的第2个寄存器的数据
  • 功能定义
    序号 功能名称 功能描述
    1 复位 reset信号为1时,所有寄存器的值变为0
    2 读取数据 将rs,rt地址对应寄存器的数据加载到RD1,RD2
    3 写入数据 WE信号为1且时钟上升沿到来时,将Data中数据写入rd地址对应的寄存器

1.3.3 ALU(算术运算单元)

该模块可以实现加、减、与、或运算,通过ALUOp信号选择何种运算,通过logisim内置算术运算与逻辑运算原件实现运算。

  • 模块外观
    1
  • 模块内部电路
    1
  • 端口定义
    信号名 方向 位宽 描述
    ALUControl I 3 ALU功能选择信号
    SourceA I 32 参与ALU计算的第一个数据
    SourceB I 32 参与ALU计算的第二个数据
    ALUResult O 32 ALU计算结果
  • 功能定义
    |序号|功能名称|ALUOp|功能描述|
    |1|按位与|000|ALUResult = SourceA & SourceB|
    |2|按位或|001|ALUResult = SourceA | SourceB|
    |3|加|010|ALUResult = SourceA + SourceB|
    |4|减|110|ALUResult = SourceA - SourceB|
  • 可能的拓展:
  1. 移位指令sll,srl,sra:
    增加ALU模块的shift数据输入,利用logisim内置移位器实现移位;增加ALU模块中ALUOp的对应信号
  2. add、sub溢出判断:
    增加ALU模块FlowJudge信号输入,指示是否需要判断溢出,通过比较temp32与temp31是否相等判断是否溢出,增加ALU模块Overflow信号输出,输出是否溢出

1.3.4 DM(数据存储器)

该模块利用RAM元件实现对数据的访存
注意
①RAM要设置separate load and store ports
②RAM写使能端不可浮空,否则会随机写入

  • 模块外观
    1
  • 模块内部电路
    1
  • 端口定义
    信号名 方向 位宽 描述
    reset I 1 异步复位信号
    WE I 1 写使能信号,决定当前RAM是否可以写入
    ALUResult I 32 需要访存的地址,指向RAM中的某个存储单元
    WriteData I 32 需要写入的数据
    ReadData O 32 从RAM中读取到的数据
  • 功能定义
    序号 功能名称 功能描述
    1 复位 reset信号有效时,RAM中的数据将被清零
    2 读取数据 读取ALUResult地址对应存储单元的数据,将其加载到ReadData
    3 写入数据 当WE信号有效且时钟上升沿到来,将WriteData写入ALUResult对应的存储单元
  • 可能的拓展
    对于lb,lbu,lh,lhu,sb,sh等对字节或者半字进行访存操作的指令,可以设置一个另一个子模块LSControl。首先根据ALUResult将RAM中的对应数据ReadData读取/由WriteData输入数据,然后把32位数据输入LSControl处理,得到真正需要读取的数据LoadData或写入数据StoreData,然后通过mux实现真正数据的读取和写入。

1.3.5 Controller(控制器)

通过解码Opcode(3125)和func(50)以及Instr整体,输出当前指令对应的控制信号。主要组成是AND逻辑——当前是和指令;OR逻辑——当前指令对应的信号输出。

  • 模块外观
    1
    1
  • 模块内部电路
    1
    1
  • 端口定义
    信号 方向 位宽 描述 触发信号
    Opcode I 6 指令的31~26位
    func I 6 指令的5~0位,确定是何种R型指令,I型和J型指令不必要判断 add,sub
    Instr I 32 整条指令,这里主要是判断是否为nop指令
    RegDst O 1 0:寄存器堆写入寄存器为rt(2016) 1:寄存器堆写入寄存器为rd(1511) add,sub
    RegWrite O 1 0:寄存器堆不可写入 1:寄存器堆写入使能 add,sub,ori,lw,lui
    MemtoReg O 1 0:ALUResult写入寄存器堆 1:ReadData写入寄存器堆 lw
    ALUSource O 1 0:ALU的SourceB是RD2 1:ALU的SourceB是ImmExtend ori,lw,sw,lui
    MemWrite O 1 0:DM不可写入 1:DM可以写入 sw
    Branch O 1 0:是Beq指令 1:不是Beq指令 beq
    EXTCtrl O 2 00:符号拓展 01:零拓展 10:将imm16左移16位 11:sign_extend(imm16 + 00) ori,lw,sw,beq,lui
    ALUCtrl O 3 控制ALU进行何种运算的信号 add,sub,ori,lw,sw,beq,lui
  • 功能定义
    序号 功能名称 功能描述
    1 输出控制信号 根据Opcode和func输出控制整个数据通路的信号
  • 可能的拓展
    跟据不同的指令要求,输出所需要的控制信号

1.3.6 BranchControl(B类指令控制模块)

该模块主要用于处理B类指令是否跳转,B类指令可以跳转有两个条件:①是B类指令②满足跳转要求

  • 模块外观
    1
  • 模块内部电路
    1
  • 端口定义
    信号 方向 位宽 描述
    ALUResult I 1 判断是否为0、不为0、大于0等等,从而去匹配B类指令的跳转条件
    Branch I 1 当前指令是否是B类指令
    PCSource O 1 控制信号,控制NPC是(PC+4)或者B类信号跳转到的PC值
  • 功能定义
    序号 功能名称 功能描述
    1 PC跳转信号输出 决定NPC是否是B类指令跳转到的PC值
  • 可能的拓展
    ①需要增加BranchOp信号输入,判断当前是何种B类指令
    ②对于bltz,blez,bgtz,bgez等指令,增加32位操作数输入,直接与0比较判断即可。
    最终PCSource信号输出由mux和与门合力完成。

1.3.7 EXT(扩展单元)

本模块根据不同指令对立即数的扩展需要对立即数进行扩展

  • 模块外观
    1
  • 模块内部电路
    1
  • 端口定义
    信号 方向 位宽 描述
    Imm I 16 需扩展的立即数
    EXTControl I 2 控制进行何种扩展
    ImmExtend O 16 扩展结果
  • 功能定义
    序号 功能名称 功能描述
    1 立即数扩展 把立即数进行相应拓展

1.3.8 NPC(计算下一条指令的地址)

该模块用于计算下一条需要执行的指令的地址

  • 模块外观
    1
  • 模块内部电路
    1
  • 端口定义
    信号 方向 位宽 描述
    PC I 32 当前指令地址
    Imm I 16 当前指令的15~0位
    PCSource I 1 0:PC+4 1:B类指令跳转地址
    NPC O 32 下一条指令地址
  • 功能定义
    序号 功能名称 功能描述
    1 计算NPC 根据当前指令计算NPC
  • 可能的拓展
    主要是对于J类指令的拓展
    对于J需要增加指令25~0位输入,对于jal还需增加PC+4输出,对于jr指令需增加$ra的数据输入。然后利用splitter和mux实现最终的NPC输出。

2 测试方案

2.1 典型测试样例

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
29
30
31
32
33
34
35
36
# 算术指令
ori $a0, $0, 123
ori $a1, $a0, 456
ori $a2,$a1,65534
lui $a3, 123
lui $t4, 0xffff
lui $t1,42528
ori $a3, $a3, 0xffff
add $s0, $a0, $a2
add $s1, $a0, $a3
add $s2, $a3, $a3
sub $t2,$t1,$a0
sub $t3,$t2,$a2
ori $t0, $0, 0x0000
# 访存指令
sw $a0, 0($t0)
sw $a1, 4($t0)
sw $t5, 8($t0)
sw $t6, 12($t0)
sw $t7, 16($t0)
sw $t8, 20($t0)
sw $t9, 24($t0)
lw $a0, 0($t0)
lw $a1, 12($t0)
sw $a0, 28($t0)
sw $a1, 32($t0)
# 跳转指令
ori $s4, $0, 1
ori $s5, $0, 2
ori $s6, $0, 1
beq $s4, $s6, loop1
beq $s4, $s5, loop2
loop1:sw $s2, 36($t0)
lui $s7,65535
loop2:sw $s3, 40($t0)

1
1
1
1
可见除$gp $sp 等特殊寄存器外,其他寄存器值相同
此外通过和同学对拍测试其他数据(包括边界数据65533、65535等),只有在使能端不关心这个数据(即这个数据并不进入这条指令对应的数据通路)时,数据会出现不同,其他情况下输出相同。

2.2 自动测试工具

该python程序可以实现将MIPS程序text.asm 转化成rom.txt并自动加载进存有cpu电路文件P3_v2.circ的ROM元件中,并生成新的电路文件P3_v2_remake.circ
使用虚拟机里python3然后CTRL+D退出,然后cd到python文件所在文件夹,输入python3 -u <filename>.py 即可执行python文件。

1
2
3
4
5
6
7
8
9
10
import os
import re
command = "java -jar Mars4_5.jar text1.asm nc mc CompactTextAtZero a dump .text HexText rom.txt"
os.system(command)
content = open("rom.txt").read()

circ = open("P3_v2.circ", encoding="utf-8").read()
circ = re.sub(r'addr/data: 5 32([\s\S]*)</a>', "addr/data: 5 32\n" + content + "</a>", circ)
with open("P3_v2_remake.circ", "w", encoding="utf-8") as file:
file.write(circ)

3 思考题解答

  1. 现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。
    答:
    ①首先这个设计思路是合理的,ROM是是只读存储器,不能修改数据,可以用于存储指令,因此满足IM对于只读功能的需要;RAM是随机存储器,可以实现读取和写入数据,因此可以满足DM的读写功能需求;GRF是寄存器堆,用寄存器实现也是合理的。
    ②但我们应当注意到指令存储器为ROM时,存在过度简化。在大多数实际处理器中,指令存储器必须是可写的,从而使操作系统可以载入一个新的程序到存储器中。(好像黑书某个注释部分写的有,记不清楚辣)

祝P3上机一切顺利!

P3_Review

  1. 具体题目记得不是很清楚了,前两个题比较简单,要注意的是在模块增加输入输出端口的时候,模块外观也会改变,接线可能出错,注意一下模块接口。
  2. 需要注意的是,课上的MARS需要导入新的指令,setting -> load instruction,将下载的class.h文件导入。手写测试机器码真的太蠢了。

再接再励!一口气干到P8!

  • Post title:P3
  • Post author:Coooookie
  • Create time:2023-01-02 20:29:52
  • Post link:https://coooookie0913.github.io/2023/01/02/P3/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.