指令集

分类:

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
2
3
4
5
6
assign ALUOp = (add||addi||StWd||LdWd) ? 4'b0000 :
(sub) ? 4'b0001 :
(AND||andi) ? 4'b0010 :
(OR||ori) ? 4'b0011 :
(slt) ? 4'b0100 :
(sltu) ? 4'b0101 : 4'b1111;

MDUOp

1
2
3
4
5
6
7
8
assign MDUOp = (mult)  ? 4'b0001 :
(multu) ? 4'b0010 :
(div) ? 4'b0011 :
(divu) ? 4'b0100 :
(mfhi) ? 4'b0101 :
(mflo) ? 4'b0110 :
(mthi) ? 4'b0111 :
(mtlo) ? 4'b1000 : 4'b0000;

lwOp

1
2
3
assign lwOp = (lw) ? 3'b000 :
(lb) ? 3'b010 :
(lh) ? 3'b100 : 3'b111;

Byteen

1
2
3
assign MemType = (sw) ? 4'b1111 :
(sh) ? (addr[1] ? 4'b1100 : 4'b0011) :
(sb) ? (4'b1 << addr) : 4'b0000;

CMPOp

1
2
assign CMPOp = (bne) ? 3'b10 :
(beq) ? 3'b01 : 3'b00;

NPC_sel

1
2
3
assign npc_sel = (beq||bne) ? 3'b001 :
(jal||j) ? 3'b010 :
(jr) ? 3'b011 : 3'b0;

out_sel

1
2
3
4
5
assign outE_sel = (lui) ? 2'b01 :
(jal) ? 2'b10 : 2'b00;
assign outM_sel = (alu_type||alu_imm) ? 2'b01 :
(mf) ? 2'b10 : 2'b00;
assign outW_sel = (LdWd) ? 2'b01 : 2'b00;

else

1
2
3
4
5
6
assign RegDst = (alu_type) ? 2'b01 :
(jal) ? 2'b10 : 2'b00;
assign ALUsrc = (alu_imm||LdWd||StWd);
assign RegWrite = (alu_type||alu_imm||lui||LdWd||jal||mf);
assign ExtOp = (lui) ? 2'b10 :
(LdWd||StWd) ? 2'b01 : 2'b00;

阻塞与转发

Rs_Tuse

1
2
assign Rs_T_use = (alu_type||alu_imm||mdu||mt||LdWd||StWd) ? 2'b01 :
(beq||bne||jr||lui) ? 2'b00 : 2'b11;

Rt_Tuse

1
2
3
assign Rt_T_use = (alu_type||mdu) ? 2'b01 :
(StWd) ? 2'b10 :
(beq||bne) ? 2'b00 : 2'b11;

Tnew_D

1
2
3
assign T_new = ((Status == 2'b01 && (alu_type||alu_imm||mf))||
(Status == 2'b10 && LdWd)) ? 2'b01 :
(Status == 2'b01 && LdWd) ? 2'b10 : 2'b00;

测试方案

  1. 按分类逐条测试新增指令
  2. 考虑阻塞和转发按情况测试指令

    思考题

    为什么需要有单独的乘除法部件而不是整合进ALU?为何需要有独立的HI,LO寄存器

乘除法部件的运算逻辑与读写数据都与ALU截然不同。

  1. 乘除法具有高强度阻塞和特别的时序逻辑,不能和组合逻辑直接混用。
  2. 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
16
mult $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的关系来进行构造。