计组P3课下作业总结
数据通路基础部件建模
PC(程序计数器)
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 处理时钟 |
Reset | I | 复位信号(异步复位) |
DI[31:0] | I | 32位输入下一条指令地址 |
DO[31:0] | O | 32位输出本条指令地址 |
注意,本题规定起始指令的地址为0x00000000,即开始时32位寄存器初值为0。
实际上就是一个普普通通的32位寄存器的输入输出。
NPC(指令计算器)
(由于本题没有要求jal和jr,因此接口有所减少)
分为顺序执行指令:add/sub/ori/lw/sw/lui/nop,和可能跳转的指令beq,接口如下:
端口 | 方向 | 功能 |
---|---|---|
PC[31:0] | I | 输入当前指令的地址 |
NPCOp | I | NPC计算方法控制(+4或跳转) |
Zero | I | 判断beq指令中rs和rt是否相等的比较结果 |
Imm16[15:0] | I | 16位立即数(beq跳转指令地址) |
NPC[31:0] | O | 32位输出(下一条指令的地址) |
首先需要对跳转的位置作出处理,MIPS中的beq规定为:
if (GPR[rs] == GPR[rt]) PC <- PC + 4 + sign_extend(offset||0^2)
所以处理方法是先给imm16用分线器低位补两个0,然后符号扩展至32位和PC+4再做加法。
通过NPCOp和Zero两个输入共同确定是否是跳转指令,要跳转到Imm16中的地址处理后的位置。同时利用多路选择器选择NPC=PC+4还是Imm16处理后位置。
IM(指令存储器)
本题的指令存储利用Rom实现,输入当前指令的地址就输出当前指令内容。接口如下:
端口 | 方向 | 功能 |
---|---|---|
PC[31:0] | I | 输入当前指令的地址 |
Instr[31:0] | O | 输出当前指令内容 |
要注意,本题指令存储器容量为32bits * 32字,则Rom的实际地址只有5位,当Rom的地址增加一位时,相当于读取下一条指令,即PC+4(指令地址加32bit),所以要取指令地址的[6:2]位作为Rom的地址。
IFU(取指令单元)
取指令单元就是程序计数器,指令计算器和指令存储器的合成,从程序计数器中读出当前指令地址,输入指令存储器获取当前指令,同时输入指令计算器计算下一个指令的地址,并把下个指令地址输出,在下个上升沿存入程序计数器。
其接口就是前面三个接口的整合:
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 处理时钟 |
Reset | I | 复位信号(异步复位) |
Imm16[15:0] | I | 16位立即数(beq跳转指令地址) |
NPCOp | I | NPC计算方法控制(+4或跳转) |
Zero | I | 判断beq指令中rs和rt是否相等的比较结果 |
GRF(通用寄存器组)
32个32位寄存器组成,并且0号寄存器不能被写入(接地)接口如下:
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 处理时钟 |
Reset | I | 复位信号(异步复位) |
A1[4:0] | I | 读取的第一个寄存器编号 |
A2[4:0] | I | 读取的第二个寄存器编号 |
A3[4:0] | I | 写入的寄存器编号 |
WD[31:0] | I | 写入值 |
Wr | I | 写使能,判断是否能够写入 |
RD1[31:0] | O | 读取的第一个寄存器值 |
RD2[31:0] | O | 读取的第二个寄存器值 |
(对之前的布线做了一些美化)
ALU(算术逻辑单元)
需要满足所有指令的运算功能,包括add/sub/ori/lw/sw/lui/nop/beq。具体运算如下:
指令 | 描述 |
---|---|
addu | R[rd]<-R[rs]+R[rt]; PC<-PC+4 |
subu | R[rd]<-R[rs]–R[rt]; PC<-PC+4 |
ori | R[rt]<-R[rs]|zero_ext(imm16); PC<-PC+4 |
lw | R[rt]<-MEM[R[rs]+sign_ext(imm16)]; PC<-PC+4 |
sw | MEM[R[rs]+sign_ext(imm16)]<-R[rt]; PC<-PC+4 |
lui | GPR[rt]<-immediate||0^16 |
beq | if ( R[rs] == R[rt] ) then PC<-PC+4 + (sign_ext(imm16) || 00) else PC<-PC+4 |
因此我们需要靠ALU完成的运算包括:+,-,|,==。而且对于lui指令,由于其已经在ext中完成了运算,这里希望能够直接输出B中的数据。接口如下:
端口 | 方向 | 功能 |
---|---|---|
A[31:0] | I | 第一个读入数RS |
B[31:0] | I | 第二个读入数RT或立即数 |
M1Sel | I | 判断需要进行加法还是减法运算(控制信号) |
M2Sel | I | 判断需要进行加减法运算还是与运算(控制信号) |
C[31:0] | O | ALU运算结果输出 |
Zero | O | 相等判断结果(==) |
加减法不需要只使用加法器完成,因为要考虑补码比较麻烦,直接分别使用加减法计算器然后进行多路选择选择结果即可。
我们的控制信号设定为:m1sel低位,m2sel高位,00为加,01为减,10为or,11为输出B。
相等判断直接判断减法器输出的结果是否为0即可。
DM(数据存储器)
相当于mips开头.data中分配的数据空间,利用Ram完成,接口如下:
端口 | 方向 | 功能 |
---|---|---|
CLK | I | 处理时钟 |
Reset | I | 复位信号(异步复位) |
A[31:0] | I | 地址输入 |
WD[31:0] | I | 需要被写入地址A的数据 |
Wr | I | 控制是读功能还是写功能的控制信号,0读1写 |
RD[31:0] | O | 将A地址中的数据从RD读出 |
同时有两个功能:读功能是组合逻辑,只要控制信号Wr为0则将A单元数据输出至RD。写功能是时序逻辑,在时钟上升沿时如果控制信号Wr为1,则将WD的内容写入DM(注意wr的接口是str)。
和指令存储器一样,同样取输入数据A的[6:2]位作为输入ram的真正地址。
(后面添加了一个memaddr接口用来输出真正的地址,用来展示)
EXT(扩展单元)
由于不同的指令可能用到不同的扩展方式,因此扩展单元也需要进行控制,尤其是针对lui指令,我们希望在ext中就直接让它载入高16位,就不用在ALU中进行运算了,所以需要两个控制信号,接口如下:
端口 | 方向 | 功能 |
---|---|---|
Input[15:0] | I | 需要被扩展的16位输入 |
EXTOp1 | I | 扩展单元控制信号 |
EXTOp2 | I | 扩展单元控制信号 |
Ext[31:0] | O | 输出32位的扩展结果 |
控制信号的判定是,以extop1为低位,extop2为高位,00为0扩展,01为符号扩展,10为Lui操作(加载到高16位)
在logisim中利用bit extender的两种不同模式并进行选择即可。
控制信号建模
控制器“和”逻辑单元
此单元是用来将代码的opcode和funct部分拆分读入,然后将其翻译成对应的指令。因此其对应的接口如下:
端口 | 方向 | 功能 |
---|---|---|
opcode[5:0] | I | 机器码中六位opcode输入 |
funct[5:0] | I | 机器码中六位funct输入 |
add | O | 代表指令为add(opcode:000000,funct:100000) |
sub | O | 代表指令为sub(opcode:000000,funct:100010) |
ori | O | 代表指令为ori(opcode:001101) |
lw | O | 代表指令为lw(opcode:100011) |
sw | O | 代表指令为sw(opcode:101011) |
lui | O | 代表指令为lui(opcode:001111) |
beq | O | 代表指令为beq(opcode:000100) |
nop | O | 代表指令为nop(opcode:000000,funct:000000) |
查看Mips对应指令机器码对其进行判断并输出即可,简单的每一位见0取反然后用与门联合输出。
控制器“或”逻辑单元
此单元是针对已经被翻译出的指令判断某个控制信号是否应该打开。对应的接口如下:
端口 | 方向 | 功能 |
---|---|---|
add | I | 代表指令为add |
sub | I | 代表指令为sub |
ori | I | 代表指令为ori |
lw | I | 代表指令为lw |
sw | I | 代表指令为sw |
lui | I | 代表指令为lui |
beq | I | 代表指令为beq |
nop | I | 代表指令为nop |
Br | O | NPC:0 → +4; 1 → branch |
WRSel | O | A3:0 → “rt”; 1 → “rd” |
WDSel | O | RF-WD:0 → ALU; 1 → Mem |
RFWr | O | RF:1 → write register |
EXTOp1 | O | 见ext |
EXTOp2 | O | 见ext |
BSel | O | ALU-B:0 → RF-RD2; 1 → imm16 |
M1Sel | O | 见alu |
M2Sel | O | 见alu |
DMWr | O | DM-Wr: 1 → write memory |
指令 | NPCOp | WRSel | WDSel | RFWr | EXTOp | BSel | MSel | DMWr |
---|---|---|---|---|---|---|---|---|
nop | 00 | 00 | 00 | 0 | 00 | 0 | 00 | 0 |
add | 00 | 01 | 00 | 1 | 00 | 0 | 00 | 0 |
sub | 00 | 01 | 00 | 1 | 00 | 0 | 01 | 0 |
ori | 00 | 00 | 00 | 1 | 00 | 1 | 10 | 0 |
lw | 00 | 00 | 01 | 1 | 01 | 1 | 00 | 0 |
sw | 00 | 00 | 00 | 0 | 01 | 1 | 00 | 1 |
lui | 00 | 00 | 00 | 1 | 10 | 1 | 11 | 0 |
beq | 01 | 00 | 00 | 0 | 00 | 0 | 00 | 0 |
jal | 10 | 10 | 10 | 1 | 00 | 0 | 00 | 0 |
jr | 11 | 00 | 00 | 0 | 00 | 0 | 00 | 0 |
同样的,对于每一个指令针对需要完成的任务判断对应的控制信号是否需要为1即可。此时需要用或门进行联合输出。
总体数据通路连接
注意要多使用tunnel,让电路更加美观。上面的控制信号控制的位置都通过tunnel展现,右侧的输出为题目要求。
注意看一下左侧从inf中输出的机器码的分割方式,不但要按opcode\rs\rt\rd\shamt\funct进行分割,还要把rd\shamt\funct合并成立即数imm送入ext单元。
测试数据翻译
直接利用的给出的testcode进行测试,不过要先翻译成mips语言再进行观察。翻译结果如下:
思考题
- 上面我们介绍了通过 FSM 理解单周期 CPU 的基本方法。请大家指出单周期 CPU 所用到的模块中,哪些发挥状态存储功能,哪些发挥状态转移功能。
IM,GRF,DM发挥状态存储功能,NPC,ALU,EXT发挥状态转移功能。
- 现在我们的模块中 IM 使用 ROM, DM 使用 RAM, GRF 使用 Register,这种做法合理吗? 请给出分析,若有改进意见也请一并给出。
我认为比较合理,在本题的框架下,指令的机器码已经提前全部给出,不需要改变,因此直接利用ROM进行读取即可(而且ROM也方便导入),而DM数据存储可能读取也可能存入,因此利用读写分离的RAM正好,而GRF就是寄存器,直接利用register就可以满足寄存器的功能。
- 在上述提示的模块之外,你是否在实际实现时设计了其他的模块?如果是的话,请给出介绍和设计的思路。
暂时还没有,不过可以根据MIPS指令的描述进行构建。
- 事实上,实现 nop 空指令,我们并不需要将它加入控制信号真值表,为什么?
因为它不需要控制任何写入和读取,也不需要控制机器码如何分段,只需要让它跑完全程即可。
- 上文提到,MARS 不能导出 PC 与 DM 起始地址均为 0 的机器码。实际上,可以避免手工修改的麻烦。请查阅相关资料进行了解,并阐释为了解决这个问题,你最终采用的方法。
虽然我没有试过,不过也许改变mars的存储方式即可,例如在写的时候不让data在0地址,导出时再将其设定在0地址。
- 阅读 Pre 的 “MIPS 指令集及汇编语言” 一节中给出的测试样例,评价其强度(可从各个指令的覆盖情况,单一指令各种行为的覆盖情况等方面分析),并指出具体的不足之处。
强度不是很高,例如sw,lw指令的测试只使用了连续的偏移量,没有尝试操作一个很大的偏移量的读取情况。还有Ori都设定的和0进行或操作,强度也可能不够。