北航计组-P3课下

CPU设计部分

概述

本次笔者用logisim搭建了一个单周期CPU,能够处理9条指令,分别为add sub ori lw sw beq lui nop,采用模块化和层次化设计,让结构更加清晰明了。所设计的模块如下:

  • IFU 取指令单元,通过PC的值来确定当前要执行的指令
  • GRF 通用寄存器组,通过该模块实现对32个通用寄存器的读和写
  • ALU 算术逻辑运算单元
  • DM 数据存储器,通过RAM实现
  • NPC 计算Next_PC
  • Controller 控制器,解析functop 从而将相应的控制信号置0、1
  • 最终效果如图:

数据通路部件

IFU:指令单元

该模块包含PCIM两部分,分别为程序计数器和指令存储器。

  • PC用寄存器直接实现,应具有异步复位功能,复位值为起始地址,地址范围为0x00003000 ~ 0x00006FFF
  • IM用ROM实现,容量为 4096 * 32 bit. ROM的0位置对应 PC 为 0x00003000的指令
  • 不难发现 ROM 实际地址宽度仅需 12 位,这是因为每一条程序占32位,即4byte,每次地址改变是以4byte为最小单位进行的
信号名方向位宽说明
clkI1时钟信号
resetI1异步复位信号
Next_PCI32下一条指令的地址
InstrO32正在执行的指令的编码
PCO32当前执行指令的地址

GRF:通用寄存器堆

该模块P1课下已经实现了,直接用即可,copy copy !(DMX记得开三态噢!😅)

信号名方向位宽说明
RA1I51_读取的寄存器编号
RA2I52_读取的寄存器编号
WEI1寄存器写入使能端
WAI5写入的寄存器编号
WDI32写入寄存器的数据
resetI1异步复位
clkI1时钟信号
RD1O321_读取的寄存器数据
RD2O322_读取的寄存器数据

ALU:算术逻辑运算单元

端口定义

信号名方向位宽说明
inAI32操作数1
inBI32操作数2
AluCtrolI4运算类型
resultO32运算结果
zeroO1结果是否为0

运算编码

编码功能
0000A + B
0001A - B
0010A & B
0011A or B
0100加载立即数至高位

DM:数据存储器

  • 使用RAM实现,容量为 3072 * 32 bit
  • 使用异步复位功能,复位值为0
  • RAM应使用双端口模式
信号名方向位宽说明
clkI1时钟信号
resetI1异步复位信号
addressI32内存地址
WDI32写入的数据
WEI1写入使能
RDO32读出的数据

EXT:扩展单元

直接使用logisim自带的extender即可,但是注意既有符号扩展又有0扩展(such as ori)

信号名方向位宽说明
inI16待扩展的信号
ExtOpI1扩展方式选择信号(为0则0扩展)
OutO32扩展的结果

NPC:指令地址单元

Next_PC一共有 3 种情况,分别为 PC + 4、PC + 4 + offest<< 2 (beq 指令) 、Imm26 -> Imm32(j 指令),端口定义如下

信号名方向位宽说明
PCI32当前指令地址
offestI16beq指令的相对偏移量
ImmI16j指令的26位跳转地址
branchI1是否分支(beq)
if_jI1是否j跳转
Next_PCO32下一条指令的地址

控制器设计

与逻辑模块

先根据指令的opcodefunct 来确定具体的指令类型,见下表R指令类型

R型指令OpcodeRsRtRdShamtFunct
add rd rs rt000000rsrtrdXXXXX100000
sub rd rs rt000000rsrtrdXXXXX100010

再来看看I型指令:

I型指令OpcodeRsRt16 bits offest
ori rt rs Imm001101rsrtImm16
lui rt Imm001111rsrtImm16
lw rt Imm(rs)100011rsrtImm16
sw rt Imm(rs)101011rsrtImm16
beq rs rt Imm000100rsrtImm16

J指令:

J型指令Opcodeaddress
J target000010target_26bit
Jr $rs000000(funct: 001000)$rs
Jal target000011target_26bit & $ra = PC+4

当然,也可以加入其他的指令来扩展功能,有了上表,我们就可以来设计与逻辑模块了……

或逻辑模块

在这部分中,我们要根据与逻辑输出的具体指令来指定各个控制信号的高低电平,以此来实现对整个电路的控制。与此同时,这部分要求我们将各条指令的执行过程谙熟于心。

总控信号说明

信号说明
Reg_Write寄存器文件写入使能端
DM_Write内存区域写入使能端
Reg_Dst决定被写入寄存器在指令中的位置,0为I型,1为R型
MemtoReg决定写入寄存器的是Alu结果还是DM取出的数据(if 1 :DM)
Alu_Op决定Alu运算方式,见Alu表
Alu_Src决定Alu 第二个运算数为 寄存器还是立即数,0为寄存器
Ext_Ctrl决定扩展方式,0表示0扩展,1为符号扩展
Branch是否分支,1 as 分支
If_Jump是否跳转,1 as 为跳转

指令对应的控制信号

指令Reg_WriteDM_WriteReg_DstMemtoRegAlu_OpAlu_SrcExt_CtrlBranchIf_Jump
add101000000000
sub101000010000
ori100000111000
lui100001001000
lw100100001100
sw010000001100
beq000000010110
J00000000x001
Jr000001010001
Jal100000001001

思考题

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

    • 状态存储:IFU、DM、GRF
    • 状态转移:NPC、Controller
  2. 现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。

    • 合理,因为IM中的指令是只需要被读取解析的,使用ROM只有读取功能;而DM是数据存储器,需要读出和写入,因此需要使用RAM,同时不能用寄存器,因为我们需要的内存量非常的大,有限的Register无法满足;而GRF用寄存器堆实现则可以实现高速的读写,同时与ALU的输入相连,送操作数或者存储中间变量,使用寄存器快速读写较合理。
  3. 在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。

    • 我只增加了一个NPC模块,因为自行增加了J指令,PC的计算方式有 PC + 4, PC + 4 + offset<<2 , label,甚至如果加入jr指令的话,还会直接赋值为寄存器的值,考虑到后续扩展问题,引入NPC模块
  4. 事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?

    • 因为nop指令相当于sll $0, $0, 0,对应的操作码为0x0000_0000,因为0移动0位还是0,,并且0号寄存器不能写入,所以无需加入控制信号表。同时该指令有时被用于空循环,有时被编译器用于与体系结构相关的编译优化。
  5. 阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。

    • 数据强度不够,没有涉及int边界附近的较大的数据的测试,没有覆盖边界情况
    • 只测试了少数寄存器,并未对所有寄存器的读写进行测试,虽然有些寄存器实际上并不需要读写,如1号寄存器
    • 跳转范围不够,没有测试向前跳转、本行跳转,也没有测试嵌套跳转、循环跳转等特殊情况

测试

必看

每年都有奆佬发布评测机,大家多多关注
这里提供几个工具,对于懒得折腾以及没有科学上网工具的同学,直接点击下面的链接下载即可,简单的配置方法我会讲

我是链接,快点我

工具

说明

一个是魔改MARS,能直接输出寄存器和内存的写入情况,与$display一致,来自Toby-Shi学长,链接在这里

另一个是logisim的debug工具,一个是探针可以直接根据指令码显示当前的指令语言,另一个寄存器堆可以直接查看各个寄存器的值,也可以直接修改。

配置方法

魔改MARS

注意在Settings下勾选Ignore Arithmetic Overflow以及Output Log level 1,并在Memory Configuration下选择large text,这样可以一次运行4000多行mips代码(如果用不到这么大的测试数据也可以选第二个)

logisim debug辅助工具

以下内容来源于讨论区助教帖子

工具整合 && 主题帖作者:徐俊豪助教

下载工具包,然后在 Logisim 中点击 Project - Load Library - JAR Library,选择刚刚下载的 jar 包,如图

然后在左侧可以看到这两个元件,其中寄存器堆 RegisterFile 效果如下,可以直接看到寄存器的值,也可以直接对寄存器的值进行编辑,同学们在调试的时候可以用它替换自己的寄存器堆来方便地观察寄存器状态

端口定义如下:

  • RA1: 5 位读寄存器编号 1
  • RA2: 5 位读寄存器编号 2
  • WA: 5 位写寄存器编号
  • WD: 32 位写入值
  • WE: 1 位写使能
  • RD1: 32 位读出值 1
  • RD2: 32 位读出值 2
  • clk(图中未标出,三角形那个):1 位时钟信号

Mips Probe 效果如下,可以直接将 32 位 MIPS 机器码翻译为汇编指令,支持本课程课下用到的全部指令及一些其它指令,同学们可以在调试时使用来查看机器码对应的汇编指令

注意事项

  • 该工具的导入会导致测评错误!!!因此强烈建议大家将电路复制一份来导入上述库进行调试
  • 课上不会提供该工具

北航计组-P3课下
http://pzhwuhu.github.io/2024/11/04/P3课下/
本文作者
pzhwuhu
发布于
2024年11月4日
更新于
2025年4月18日
许可协议