思考题

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

    状态存储:GRF,IFU

    状态转移:Controller,ALU

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

    合理

    ROM是只读的存储器,在IM模块,我们需要将存储的程序代码读取出来,而不需要修改程序代码的值。使用ROM不容易造成数据的丢失,系统更稳定 更安全。

    RAM是随机存储器,DM需要寄存器可读、可写,且具有大量内存,RAM正具有这些特点

    Register寄存器,是高速存储设备,访问数据的速度最快,性能好,在GRF模块使用Register正合适

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

    Splitter,SrcBSrc;(详细设计见下方草稿)

    Spliiter用于将Instr(32位指令)分解,输出op,funct,offset等各个部分的指令

    SrcBSrc用于选择需要进行ALU操作的数,是从RD2读取的数据 还是扩展后的立即数

  4. 事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?

    nop指令是0x00000000,在程序中只会进行pc=pc+4的操作,对电路中的其他部件都没有影响,所以不需要修改其他东西的值

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

    1. ori模块没有测试其立即数的无符号位扩展功能,建议在第三行加上ori $a4,$a0,0xffff 得到的结果应该是0xffff
    2. beq模块没有实现向前跳转的功能
    3. 数量太少,覆盖的地址不够

    该节中的测试样例:

    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 联系起来。

电路外观:

image-20231028202458214

电路内部:

image-20231031111506999

端口说明:

信号名 方向 描述
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,无需专门设置。

电路外观:

image-20231028233035802

电路内部:

GRF

端口说明:

信号名 方向 描述
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

slljaljjr ,以及存取指令 sbshlblh

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 是不是jjal指令
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})$