BUAA_CO_P3
P3 Logisim实现单周期处理器
目录 :
[toc]
整体结构
- 所要设计的版块:
IFU,Splitter,Controller,GRF,ALU,DM,EXT - 设计要求:支持的指令集:
addsuborilwswbeqluinop其中add和sub按照无符号处理 - 顶层驱动信号:异步复位reset
指令分析:
- R型:
addsubnop# Opcode + Rs + Rt + Rd +shamt+Func - I型:
orilwswluibeq# Opcode + Rs + Rt + immediate
1 | add $t0, $t1, $t2 # 机器码:000000_01001_01010_01000_00000_100000 |
数据通路
不涉及j指令:

j指令:

指令类型分析


思路整理
在搭建完Logisim实现上述8条指令的单周期处理器后,我们尝试来回溯一下整个工作的思路。
首先,也是最重要的一步,是明确我们要实现的是哪些指令,以及这些指令到底要做一件什么事:
1 | add $t0, $t1, $t2 # Opcode:000000 Func:100000 rd = rs + rt |
接下来我们需要一张由instruction确定的控制信号表,这张表决定了各个部件在什么时候应该做什么操作:
- 我们观察到ALU涉及的计算有加法,减法,或运算,因而我们需要2位
ALUOp来指示ALU做哪种具体的运算 - 我们观察到ALU的两个输入中的第一个一定是
rs,而第二个可能是rt或者imm_ext,因而我们需要1位ALUSrc来指示第二位操作数来自哪里 - 我们观察到并非所有的ALU计算结果都要回写到
GRF,因而我们需要1位RegWrite来指示是否需要写入GRF - 我们观察到回写的目标寄存器可以是
rt或者rd,因而我们需要1位RegDst来指示是否回写入rd - 我们观察到并非所有指令都要访存,因而我们需要1位
MemRead来指示是否访存指令 - 我们观察到并非所有时候都能写入
DM,因而我们设计1位MemWrite来指示能否写入DM - 我们观察到并非所有时候
DM的输出都要回写入GRF,因而我们设计1位MemtoReg来指示是否将DM输出写入GRF - 我们还需要1位
Branch来指示是否分支指令,因为分支指令会影响NPC的值
1 | // Controller 模块规格 |
接下来就是具体的将我们要实现的指令对应到这张控制信号表中:
| RegDst | Branch | MemRead | MemtoReg | ALUOp_1 | ALUOp_0 | MemWrite | ALUSrc | RegWrite | |
|---|---|---|---|---|---|---|---|---|---|
| add | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| sub | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |
| nop | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| ori | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
| lw | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
| sw | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 |
| lui | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| beq | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
因此,当我们站在顶层回看,我们发现利用Logisim构造单周期处理器事实上只需要做三件事:
构造
IFU,GRF,ALU,DM这四个功能模块,具体的模块规格如下,这是简单的。考虑我们所涉及的所有指令来设计控制信号,并利用控制信号将功能模块组织起来,这可能要求我们灵活地添加一些多路选择器等,这是抽象而复杂的。
(考虑需要哪些控制信号 + 每个信号对应功能模块什么样的表现 + 怎么把信号与模块组织起来才能不冲突)
根据指令生成控制信号,这只需要我们稍微分析一下每条指令的功能,因而也是简单的。
1 | // IFU 模块规格 |
1 | // GRF 模块规格 |
1 | //ALU 模块规格 |
1 | // DM模块规格 |
优化
在我们第一版的设计中,Branch和Zero信号直接接入了EXT这样做不符合我们“高内聚低耦合”的设计理念,我们不希望EXT还要承担判断拓展类型的功能,而是有一个EXTOp专门告诉EXT该做怎样的拓展。
在我们目前实现的8指令中:
lw,sw,beq需要将16位imm符号拓展为32位ori需要将16位imm零拓展为32位lui需要将16位imm右补16位0add,sub,nop不需要拓展
1 | // EXTOp模块规格 |
这样,EXT就只需要按照EXTOp告诉它的方式将16位的输入imm转化为32位的输出imm_ext
1 | // EXT模块规格 |
第二个优化是关于WriteBack
WriteBack是将要写回寄存器的值,在8指令中有以下可能:
add,sub,ori写回的数据来自ALU_anssw写回的数据来自DM的输出WDlui写回的数据来自imm_extnop,sw,beq不需要写回
同样的思想,我们设计一个WBSource来说明WD的取值来自哪里
1 | // WBSource模块规格 |
思考题
Q:上面我们介绍了通过
FSM理解单周期CPU的基本方法。请大家指出单周期CPU所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能?A:将
IFU看作一个Moore_FSM,状态储存是:PC,状态转移是:NPC 将
GRF+ALU+DM看作一个Mealy_FSM,状态储存是:GRF,状态转移时ALU+DM
Q:现在我们的模块中
IM使用ROM,DM使用RAM,GRF使用Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。A:合理。
IM使用ROM是因为IM中要存储大量仅读的数据。DM使用RAM是因为DM涉及大量数据的读写。GRF涉及读写但数据量不大,使用Register访问和更改更方便。
- Q:在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。
A:实现了Splitter模块,将32位instruction按照R型指令的方式拆分。目的是便于确定具体指令。
Q:事实上,实现
nop空指令,我们并不需要将它加入控制信号真值表,为什么?A:事实上,
nop等价于sll $0, $0, $0,我们只需要在检测到Func字段为000000时把它也当作加法处理即可。
Q:阅读
Pre的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。A:
应当测试
sub指令,因为sub指令涉及了特有的Opcode的处理在测试
add指令时未考虑操作0号寄存器lw和sw指令未涉及负跳转beq跳转指令的未涉及负跳转未涉及
nop指令的测试











