P6-CPU-设计文档
指令集
分类:
alu_type:add,sub,and,or,slt,sltu
alu_imm:addi,andi,ori
mdu:mult,multu,div,divu
mf:mfhi,mflo
mt:mthi,mtlo
LdWd:lw,lb,lh
StWd:sw,sb,sh
branch:beq,bne
special:lui
jump:j,jal,jr
新增模块
MDU
| 端口名称 | 方向 | 位宽 |
|---|---|---|
| clk | I | 1 |
| reset | I | 1 |
| start | I | 1 |
| A | I | 32 |
| B | I | 32 |
| MDUOp | I | 4 |
| HI | O | 32 |
| LO | O | 32 |
| out | O | 32 |
| busy | O | 1 |
lwEXT
| 端口名称 | 方向 | 位宽 |
|---|---|---|
| Din | I | 32 |
| Addr | I | 2 |
| lwOp | I | 4 |
| Dout | O | 32 |
控制信号
ALUOp
1 | assign ALUOp = (add||addi||StWd||LdWd) ? 4'b0000 : |
MDUOp
1 | assign MDUOp = (mult) ? 4'b0001 : |
lwOp
1 | assign lwOp = (lw) ? 3'b000 : |
Byteen
1 | assign MemType = (sw) ? 4'b1111 : |
CMPOp
1 | assign CMPOp = (bne) ? 3'b10 : |
NPC_sel
1 | assign npc_sel = (beq||bne) ? 3'b001 : |
out_sel
1 | assign outE_sel = (lui) ? 2'b01 : |
else
1 | assign RegDst = (alu_type) ? 2'b01 : |
阻塞与转发
Rs_Tuse
1 | assign Rs_T_use = (alu_type||alu_imm||mdu||mt||LdWd||StWd) ? 2'b01 : |
Rt_Tuse
1 | assign Rt_T_use = (alu_type||mdu) ? 2'b01 : |
Tnew_D
1 | assign T_new = ((Status == 2'b01 && (alu_type||alu_imm||mf))|| |
测试方案
乘除法部件的运算逻辑与读写数据都与ALU截然不同。
- 乘除法具有高强度阻塞和特别的时序逻辑,不能和组合逻辑直接混用。
- HI,LO寄存器只有MDU能用到,和ALU混用,内部耦合度提高,会变得混乱
把HI,LO放在E区,与其他寄存器分离开来,能够实现即出即写,避免了读写冲突。
真实的流水线CPU是如何实现乘除法的?
在真实的流水线CPU中,32位乘法是分部分计算的,一个周期可以32位对8位数的乘法,4个周期算完。
除法用试商法,一次试4位,8个周期做完除法。
请结合自己的实现分析,你是如何处理BUsy信号带来的周期阻塞的?
start引发busy,start和busy都能导致阻塞。
通过计数器控制阻塞周期数,在最后一周期,清空busy,cnt,并完成寄存器赋值。
请问采用字节使能信号的方式处理写指令有什么好处?
清晰性:
清晰地表达了究竟要写哪些字节,对每个字节执行独立的赋值。
统一性:
sw,sh,sb都能用同样的按字节赋值的逻辑来执行。
请思考,我们在按字节读和按字节写时,实际从DM获得数据和向DM写入的数据是否是一字节?在什么情况下我们按字节读和按自己写的效率会高于按字节读和写?
写入DM的确实只写了一个字节,但从DM获取的是一整个字,要经过后续处理。
执行lw和sw时按字读写更快,但是执行对半字与字节的操作时,按字节读写效率更高。
为u了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突时有什么样的特点与帮助?
抽象:
对指令进行了归类,并按类地添加和处理指令。
规范:
对于同一类指令,统一为控制信号赋值。
帮助:
在译码和处理数据冲突的时候,按类调整控制信号与T信号,更加方便。
在本实验中你遇到了哪些不同指令类型组合产生的冲突,你又是如何解决的?相应的测试样例是什么样的?
新的冲突只有数据冒险。
阻塞:
在HAzard译码时为各个指令赋好了Tuse_rs,Tuse_rt,Tnew,阻塞正常进行。
转发:
需求者没有改变。
供给者M级有来自MDU的新数据。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16mult $s1, $s2
mfhi $t0 ## 写$t0
add $t1, $t0, $t0 ## 读$t0,M->E需转发,写$t1
mtlo $t1 ## 读$t1,M->E需转发
mflo $t2 ## 写$t2
add $t3 $t2 $t2 ## 读$t2,M->E需转发,写$t3
mthi $t3 ## 读$t3,M->E需转发
mfhi $t4 ## 写$t4
sw $t4 0($0) ## 读$t4,M->E需转发
lw $t5 0($0) ## 写$t5
mtlo $t5 ## 读$t5
mflo $t6 ## 写$t6
label:
or $t6 $t6 $0 ## 表示正在死循环
bne $0 $t6 label ## M->D转发
nop
如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。
我采用了手动构造法。
通过建立策略矩阵,按照T的关系来进行构造。


