流水线CPU设计文档
设计草稿及模块安排
整体模块架构
mips
D_Controller
E_Controller
M_Controller
W_Controller
Blocker
D_Controller
E_Controller
M_Controller
F_IFU
FD_REG
D_GRF
D_ext
D_cmp
D_NPC
DE_REG
E_ALU
EM_REG
M_DM
MW_REG
模块安排
mips
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
mips模块是整个系统的主模块,其主要承担整体架构中各分模块的线路连接任务,其中对流水线D,E,M,W每一级调用模块Controller来输出控制信号并以此为依据控制转发,并调用模块Blocker处理阻塞情况。 |
F_IFU
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
en | 1 | in | 使能信号 |
next_pc | 32 | in | 下一条执行指令地址 |
pc | 32 | out | 当前执行指令地址 |
instr | 32 | out | 当前32位指令 |
F_IFU即F级的取指令器,根据指令地址在指令存储器中取指令;由于流水线CPU可能存在的阻塞处理,这里使用en端实现,需阻塞则置为0,暂停更新pc值。
FD_REG
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
flush | 1 | in | 刷新信号,置1则输出全0 |
en | 1 | in | 使能信号 |
F_pc | 32 | in | F级当前执行指令的地址 |
F_instr | 32 | in | F级当前的执行指令 |
D_pc | 32 | out | D级当前需执行指令的地址 |
D_instr | 32 | out | D级当前需执行的指令 |
FD_REG即保存前一周期F级得到的指令及状态并在本周期将其传送到D级的寄存器,其中引入flush即刷新信号也是为了服务于阻塞机制,但在目前的指令集下,其在FD_REG中无作用,将在DE_REG中介绍;需要注意的是,阻塞发生时,该寄存器的en也应置为0。
D_GRF
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
pc | 32 | in | W级指令地址 |
A1 | 5 | in | 读取的寄存器1编号 |
A2 | 5 | in | 读取的寄存器2编号 |
A3 | 5 | in | 需写入的寄存器编号 |
WD | 32 | in | 需写入寄存器的数据 |
RD1 | 32 | out | 从寄存器1读出的数据 |
RD2 | 32 | out | 从寄存器2读出的数据 |
D_GRF即D级的寄存器文件,值得注意的是其中pc、A3和WD来自W级,且可能产生冒险行为,这里采用寄存器内部转发来解决W级的回写与D级读寄存器地址冲突的情况,采用外部转发解决E,M与D级产生的数据冲突;具体操作见冲突处理一节。
D_ext
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
imm16 | 16 | in | 16位立即数输入 |
extop | 1 | in | 扩展方式控制:0:0扩展;1:符号扩展 |
imm32 | 32 | out | 扩展后的32位立即数 |
D_ext即安排在D级的立即数扩展模块
D_cmp
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
rs_data | 32 | in | 从寄存器1读出的数据(可能是转发过来的) |
rt_data | 32 | in | 从寄存器2读出的数据(可能是转发过来的) |
cmpop | 3 | in | 选择比较方式,对应beq、bne等指令 |
jump | 1 | out | 根据指令比较方式和比较结果决定是否跳转,跳转则置1 |
D_cmp即D级的比较器,为了减少判断跳转指令可能带来的流水线上的无效指令,将分支判断提前到D级,那么即使发生跳转,需要作废的指令只有F级,此时若跳转也约定F级指令不作废,即得到延时槽。
D_NPC
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
NPCop | 3 | in | 根据指令选择的下一条指令地址的操作选择信号 |
F_pc | 32 | in | 当前F级的指令地址 |
D_pc | 32 | in | 当前D级的指令地址 |
b_jump | 1 | in | 来自D_cmp的跳转判断信号 |
imm16 | 16 | in | 16位地址偏移量 |
imm26 | 26 | in | 26位伪直接寻址的指令地址 |
rs_data | 32 | in | 从寄存器1读出的数据(可能是转发过来的) |
next_pc | 32 | out | 经判断计算得到的下一条执行指令的地址 |
D_NPC即D级的指令更新器,值得注意的是若处理b这一类指令满足条件应在跳转至D_pc + 4 + {{14{imm16[15]}},imm16,2'b00}
,而若不需要跳转则下一条指令地址为F_pc+4
。值得注意的一点是若imm16 = 0则由于延时槽的存在且D_pc+4 = F_pc该跳转指令的下一条指令会被执行两次。
DE_REG
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
flush | 1 | in | 刷新信号,置1则输出全0 |
en | 1 | in | 使能信号 |
D_pc | 32 | in | D级正在执行的指令地址 |
D_instr | 32 | in | D级正在执行的指令 |
D_rs_data | 32 | in | 从寄存器1读出的数据(可能是转发过来的) |
D_rt_data | 32 | in | 从寄存器2读出的数据(可能是转发过来的) |
D_imm32 | 32 | in | 在D级被扩展得到的32位立即数 |
D_b_jump | 1 | in | 在D级经判断得到的b指令跳转信号 |
E_pc | 32 | out | E级需执行的指令地址 |
E_instr | 32 | out | E级需执行的指令 |
E_rs_data | 32 | out | 传递到E级的寄存器1数据 |
E_rt_data | 32 | out | 传递到E级的寄存器2数据 |
E_imm32 | 32 | out | 传递到E级的32位立即数 |
E_b_jump | 1 | out | 传递到E级的b指令跳转信号 |
DE_REG即保存前一周期D级得到的指令及状态并在本周期将其传送到E级的寄存器,需要注意的是只要处于阻塞状态,该寄存器的flush置1,即在流水线中产生“气泡”,“气泡”随流水线传递,达到等待直至阻塞状态解除的目的。
E_ALU
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
A | 32 | in | 操作数1 |
B | 32 | in | 操作数2 |
ALUCtrl | 4 | in | ALU运算控制信号 |
ALUResult | 32 | out | 运算结果 |
E_ALU即安排在E级的运算单元。
EM_REG
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
flush | 1 | in | 刷新信号,置1则输出全0 |
en | 1 | in | 使能信号 |
E_pc | 32 | in | E级正在执行的指令地址 |
E_instr | 32 | in | E级正在执行的指令 |
E_ALUResult | 32 | in | E级ALU的运算结果 |
E_rt_data | 32 | in | E级保存的寄存器2数据 |
E_imm32 | 32 | in | E级保存的32位立即数 |
E_b_jump | 1 | in | E级保存的b指令跳转信号 |
M_pc | 32 | out | M级需执行的指令地址 |
M_instr | 32 | out | M级需执行的指令 |
M_ALUResult | 32 | out | 传递到M级的ALU的运算结果 |
M_rt_data | 32 | out | 传递到M级的寄存器2数据 |
M_imm32 | 32 | out | 传递到M级的32位立即数 |
M_b_jump | 1 | out | 传递到M级的b指令跳转信号 |
EM_REG即保存前一周期E级得到的指令及状态并在本周期将其传送到M级的寄存器。
M_DM
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
pc | 32 | in | M级执行指令地址 |
MemAddr | 32 | in | 写/读数据存储器的地址 |
dataIn | 32 | in | 写入存储器的数据 |
DMop | 3 | in | 存储器读/写数据操作选择信号:字/半字/字节 |
WriteEnable | 1 | in | 写使能 |
dataOut | 32 | out | 从存储器中读出的数据 |
M_DM安排在M级的数据存储器。
MW_REG
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
clk | 1 | in | 系统时钟信号 |
reset | 1 | in | 复位信号 |
flush | 1 | in | 刷新信号,置1则输出全0 |
en | 1 | in | 使能信号 |
M_pc | 32 | in | M级正在执行的指令地址 |
M_instr | 32 | in | M级正在执行的指令 |
M_ALUResult | 32 | in | M级保存的ALU的运算结果 |
M_dataOut | 32 | in | M级读出的存储器数据 |
M_b_jump | 1 | in | M级保存的b指令跳转信号 |
W_pc | 32 | out | W级需执行的指令地址 |
W_instr | 32 | out | W级需执行的指令 |
W_ALUResult | 32 | out | 传递到W级的ALU的运算结果 |
W_dataOut | 32 | out | 传递到W级的存储器读出数据 |
W_b_jump | 1 | out | 传递到W级的b指令跳转信号 |
MW_REG即保存前一周期M级得到的指令及状态并在本周期将其传送到W级的寄存器。
Controller
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
instr | 32 | in | 32位指令 |
b_jump | 1 | in | b指令跳转信号 |
rs | 5 | out | 指令的21-25位,寄存器1编号 |
rt | 5 | out | 指令的16-20位,寄存器2编号 |
rd | 5 | out | 指令的11-15位,寄存器3编号 |
shamt | 5 | out | 指令的6-10位,多用于移位指令的位移量 |
imm16 | 16 | out | 指令的0-15位,16位立即数 |
imm26 | 26 | out | 指令的0-25位,26位立即数 |
ALUCtrl | 4 | out | ALU运算操作选择信号 |
ALU_Asel | 2 | out | ALU操作数1选择信号 |
ALU_Bsel | 2 | out | ALU操作数2选择信号 |
cmpop | 3 | out | 比较操作选择信号 |
extop | 1 | out | 位拓展操作选择信号 |
NPCop | 3 | out | 指令地址更新操作选择信号 |
DMop | 3 | out | 存储器读/写数据操作选择信号:字/半字/字节 |
DM_WriteEnable | 1 | out | 数据存储器写使能 |
GRF_A3 | 5 | out | 寄存器文件写数据地址 |
WDSel | 2 | out | 寄存器写入数据选择信号 |
load | 1 | out | 读取数据存储器指令识别信号 |
store | 1 | out | 写入数据存储器型指令识别信号 |
cal_r | 1 | out | 寄存器操作计算指令识别信号 |
cal_i | 1 | out | 立即数操作计算指令识别信号 |
branch | 1 | out | b型跳转指令识别信号 |
j_reg | 1 | out | 从寄存器获取跳转地址的跳转指令识别信号 |
j_imm | 1 | out | 以立即数为跳转地址的跳转指令识别信号 |
j_link | 1 | out | 跳转并链接指令识别信号 |
Controller即通用控制器,在mips模块中,在D,E,M,W每一级被调用,接收在相应级所需的状态信息,达到分布式译码的目的,以但前级的指令和状态为依据,发出控制信号,操作数据通路,控制转发操作;在Blocker模块中,在D,E,M级调用,发出指令识别信号以计算 Tuse 和 Tnew ,为是否阻塞提供依据。
Blocker
管脚名 | 位数 | in/out | 功能解释 |
---|---|---|---|
D_instr | 32 | in | D级正在执行的指令 |
E_instr | 32 | in | E级正在执行的指令 |
M_instr | 32 | in | M级正在执行的指令 |
ifBlock | 1 | out | 阻塞操作使能信号,需阻塞置1 |
Blocker即阻塞控制器,根据Controller的译码结果,计算D_Tuse_rs,D_Tuse_rt,E_Tnew,M_Tnew(由于当前指令集W_Tnew恒为0故先不作计算),再结合寄存器读写信息使用AT模型控制输出的阻塞信号。
冲突处理
在流水线CPU中,由于多条指令同时存在于流水线上,且在同一周期内执行不同的指令操作,这可能引发由于硬件资源重叠和指令间依赖性而导致的冲突问题;当前指令集下冲突有以下2种可能:
- 寄存器文件中的寄存器被同时读写
- 后面指令在需要使用数据时,前面供给的数据还没有存入寄存器堆
本节主要讨论阻塞和转发处理冲突的情况判断和实现方式。
阻塞操作
阻塞,顾名思义使流水线停在某一指令,需等待某种条件解除阻塞状态。
何时阻塞
后面指令(记为B)在需要使用数据时,前面指令(记为A)供给的数据还没有产生并写入流水级寄存器,这时转发无源头 (转发的源头都是流水级寄存器存储的数据,故不能认为数据产生后可被立即转发) ,唯一的方法是让B等待,直到A在流水线某一级产生所需数据时解除,再考虑转发或直接使用。
这里采用Tuse–Tnew模型判断。
- Tuse:某指令位于 D 级的时候,再经过多少个时钟周期就必须要使用相应的数据。
- Tnew:位于某个流水级的某个指令,它经过多少个时钟周期可以算出结果并且存储到流水级寄存器里。
具体各指令的Tuse,Tnew参见文件夹内表格Tuse&&Tnew。
由此,可以得到结论 Tuse < Tnew 且读写地址重叠(均不为$0) 时必须进行相应阻塞操作。
当前指令集需阻塞的情况代码表示如下:
wire E_ifBlock_rs = (E_GRF_A3 == D_rs && D_rs != 0) && (D_Tuse_rs < E_Tnew);
wire E_ifBlock_rt = (E_GRF_A3 == D_rt && D_rt != 0) && (D_Tuse_rt < E_Tnew);
wire M_ifBlock_rs = (M_GRF_A3 == D_rs && D_rs != 0) && (D_Tuse_rs < M_Tnew);
wire M_ifBlock_rt = (M_GRF_A3 == D_rt && D_rt != 0) && (D_Tuse_rt < M_Tnew);
assign ifBlock = E_ifBlock_rs | E_ifBlock_rt | M_ifBlock_rs | M_ifBlock_rt;
如何阻塞
自此,我们得到了阻塞信号ifBlock
以此为依据操作阻塞情况的数据通路:
- 将F_IFU和FD_REG的使能信号(en)置为0,不再更新并发送新的指令信号,达到使进入流水线的指令滞留在D级的目的。
- 将DE_REG的刷新信号(flush)置为1,使向E级发送的指令为nop,即产生“气泡”填充流水线;注意,仅需在此寄存器刷新,因为只要处于阻塞状态,该寄存器不断产生“气泡”,而这些“气泡”随时钟周期向后移动填充流水线各级。
- 当 Tuse >= Tnew 时解除阻塞状态,使能信号置为1,刷新信号置为0,开始考虑转发,继续流水。
具体代码实现如下:
assign PC_en = !ifBlock;
assign FD_REG_en = !ifBlock;
assign DE_REG_en = 1;
assign EM_REG_en = 1;
assign MW_REG_en = 1;
assign FD_REG_flush = 0;
assign DE_REG_flush = ifBlock;
assign EM_REG_flush = 0;
assign MW_REG_flush = 0;
转发操作
转发,即将先进入流水线的指令产生的数据根据条件发送给后进入的指令。
何时转发
前面指令供给的数据,而后面指令在需要使用数据时,前面供给的数据已经产生且写入流水级寄存器但还没有存入寄存器堆,导致后面的指令在GRF中取不到正确的值,故当两个流水级出现读写寄存器的重叠时,(在无需阻塞或阻塞完成时)应考虑转发。
如何转发
在当前指令级下,仅存在:
- W向D级转发(寄存器内自转发)
- E,M向D级转发
- M,W向E级转发
- W向M级转发
具体转发关系见文件夹下表格 hazard_and_relocate
具体代码实现如下:
//寄存器内自转发
assign RD1 = (A1==0) ? 0 :
(A1==A3 && A1!=0) ? WD :
regFile[A1];
assign RD2 = (A2==0) ? 0 :
(A2==A3 && A2!=0) ? WD :
regFile[A2];
//向D级转发
assign D_Forward_rs_data = (D_rs == 0) ? 0 :
(D_rs == E_GRF_A3) ? E_WD :
(D_rs == M_GRF_A3) ? M_WD :
D_rs_data;
assign D_Forward_rt_data = (D_rt == 0) ? 0 :
(D_rt == E_GRF_A3) ? E_WD :
(D_rt == M_GRF_A3) ? M_WD :
D_rt_data;
//向E级转发
assign E_Forward_rs_data = (E_rs == 0) ? 0 :
(E_rs == M_GRF_A3) ? M_WD :
(E_rs == W_GRF_A3) ? W_WD :
E_rs_data;
assign E_Forward_rt_data = (E_rt == 0) ? 0 :
(E_rt == M_GRF_A3) ? M_WD :
(E_rt == W_GRF_A3) ? W_WD :
E_rt_data;
//向M级转发
assign M_Forward_rt_data = (M_rt == 0) ? 0 :
(M_rt == W_GRF_A3) ? W_WD :
M_rt_data;
值得注意的是,这种转发方式的正确性是由阻塞机制和转发优先级决定的。
所谓优先级即向每一级转发时,依次沿流水线检索此级的下级,满足条件即转发,若都无需转发,则采用本级读出的寄存器值,这在代码中有所体现。
但可能存在这样两个问题
1.如需要向D级转发某数据,此数据在E级产生但未写入流水级寄存器,直至下个时钟上升沿才写入EM_REG(M级),则按优先级优先转发了DE_REG(E级)保存的数据,这是否会导致错误?
答:实际上不会,由于阻塞机制的存在,当数据在E级产生但未写入流水级寄存器时,流水线被阻塞,指令停滞,此时转发的数据也起不到作用,待到下个时钟上升沿才写入EM_REG(M级),转发来自M级的数据会直接将错误值覆盖,阻塞状态解除,流水线正常执行。
2.如果转发到的寄存器在此指令期间不被读(可以不转发)将一个值存入其中会不会有影响?
答:不会,若该寄存器不被读,则其要么被写,要么不参与本指令的执行,那么对于第一种情况,该指令写入时会将原本转发的值覆盖;对于第二种情况,该寄存器在流水级中的表现实际是相当于提前被写入了(实际上写入还是要到转发的源头指令的W级)。
测试方案
综合测试
mips指令代码:
ori $a0,$0,1999
ori $a1,$a0,111
lui $a2,12345
lui $a3,0xffff
lui $t0,0xffff
beq $a3,$t0,eee
add $s7,$0,$a0
nop
ori $a3,$a3,0xffff
add $s0,$a0,$a1
add $s1,$a3,$a3
add $s2,$a3,$s0
beq $s2,$s3,eee
sub $s0,$a0,$s2
sub $s1,$a3,$a3
eee:
sub $s2,$a3,$a0
sub $s3,$s2,$s1
ori $t0,$0,0x0000
sw $a0,0($t0)
nop
sw $a1,4($t0)
sw $s0,8($t0)
sw $s1,12($t0)
sw $s2,16($t0)
sw $s5,20($t0)
lw $t1,20($t0)
lw $t7,0($t0)
lw $t6,20($t0)
sw $t6,24($t0)
lw $t5,12($t0)
jal end
ori $t0,$t0,1
ori $t1,$t1,1
ori $t2,$t2,2
beq $t0,$t2,eee
lui $t3,1111
jal out
end:
add $t0,$t0,$t7
jr $ra
out:
add $t0,$t0,$t3
ori $t2,$t0,0
beq $t0,$t2,qqq
lui $v0,10
qqq:
lui $v0,11
j www
nop
www:
lui $ra,100
机器码:
340407cf
3485006f
3c063039
3c07ffff
3c08ffff
10e80009
0004b820
00000000
34e7ffff
00858020
00e78820
00f09020
12530002
00928022
00e78822
00e49022
02519822
34080000
ad040000
00000000
ad050004
ad100008
ad11000c
ad120010
ad150014
8d090014
8d0f0000
8d0e0014
ad0e0018
8d0d000c
0c000c25
35080001
35290001
354a0002
110affec
3c0b0457
0c000c27
010f4020
03e00008
010b4020
350a0000
110a0001
3c02000a
3c02000b
08000c2e
00000000
3c1f0064
思考题解答
1、我们使用提前分支判断的方法尽早产生结果来减少因不确定而带来的开销,但实际上这种方法并非总能提高效率,请从流水线冒险的角度思考其原因并给出一个指令序列的例子。
答:
以beq指令为例,我们为了减少不确定带来的开销将比较操作提前到了D级,这直接导致beq指令的D_Tuse_rt和D_Tuse_rs为0,又因为Tuse < Tnew 时需阻塞流水线,则beq指令被阻塞的概率较大(阻塞情况参加表格hazard_and_relocate),可能会在效率方面与提前判断分支带来的提升抵消,甚至降低效率。
示例如下:
beq $t1, $t2, tag1
add $t1, $s1, $s2 // lw $t1, 0($s1)
...
tag1:
...
2、因为延迟槽的存在,对于 jal 等需要将指令地址写入寄存器的指令,要写回 PC + 8,请思考为什么这样设计?
答:
因为存在延时槽,在跳转指令执行之后,下一条指令(地址为pc + 4)执行,执行完毕后再跳转至目标指令,故若需返回时,应返回 pc + 8 ,否则位于 pc + 4 的指令又会被执行一次,则会产生错误。
3、我们要求大家所有转发数据都来源于流水寄存器而不能是功能部件(如 DM 、 ALU ),请思考为什么?
答:
如果从功能部件转发,那么某一级的执行总延迟就会增加,从而影响整条流水线的时序设计,不得不延长时钟周期,总效率反而降低,得不偿失。
4、我们为什么要使用 GPR 内部转发?该如何实现?
答:
主要是为了处理W级和D级可能产生的寄存器重叠的冲突而进行的转发;实现方式即直接将WD端口的输入赋值到相应输出端口。
代码实现如下:
assign RD1 = (A1==0) ? 0 :
(A1==A3 && A1!=0) ? WD : //内部转发
regFile[A1];
assign RD2 = (A2==0) ? 0 :
(A2==A3 && A2!=0) ? WD : //内部转发
regFile[A2];
5、我们转发时数据的需求者和供给者可能来源于哪些位置?共有哪些转发数据通路?
答:
参见冲突处理一节的转发操作和表格hazard_and_relocate。
6、在课上测试时,我们需要你现场实现新的指令,对于这些新的指令,你可能需要在原有的数据通路上做哪些扩展或修改?提示:你可以对指令进行分类,思考每一类指令可能修改或扩展哪些位置。
答:
扩展指令的思路:
- 在defination中添加指令的识别和控制信号的宏定义。
- 明确扩展指令的所属类型,在Controller中添加该指令的识别操作,并将该指令归类(load,store…),并以此为依据修改(或增添)控制信号的选择逻辑。
- 根据扩展指令需执行的操作在各个操作模块中添加相应的操作(和控制指令)。
- 若扩展指令存在跨级的操作,还要添加相应的流水级寄存器中添加传递的数据和控制信号。
- 最后考虑阻塞和转发信号的修改。
寄存器计算指令(cal_r):
- 在defination中添加指令的识别和控制信号的宏定义。
- 在Controller中添加相应的识别和控制操作(主要有ALU的运算选择信号,ALU的操作数选择信号,GRF的写入选择信号)。
- 根据在Controller中新增的信号索引到流水级的各个模块(ALU、GRF)进行操作和控制信号的添加。
- 与add等指令形制相同则阻塞转发一般无需修改。
立即数运算指令(cal_i):
- 在defination中添加指令的识别和控制信号的宏定义。
- 在Controller中添加相应的识别和控制操作(主要有ext的立即数扩展信号,ALU的运算选择信号,ALU的操作数选择信号,GRF的写入选择信号)。
- 根据在Controller中新增的信号索引到流水级的各个模块(ext、ALU、GRF)进行操作和控制信号的添加。
- 与ori指令形制相同则阻塞转发一般无需修改。
写数据存储器指令(store):
- 在defination中添加指令的识别和控制信号的宏定义。
- 在Controller中添加相应的识别和控制操作(主要有DM的读写模式信号)。
- 修改DM模块的读写操作。
- 与sw指令形制相同则阻塞转发一般无需修改。
读数据存储器指令(load):
- 在defination中添加指令的识别和控制信号的宏定义。
- 在Controller中添加相应的识别和控制操作(主要有DM的读写模式信号,GRF的写入数据选择和写入地址选择信号)。
- 修改DM模块的读写操作。
- 与lw指令形制相同则阻塞转发一般无需修改。
j类型跳转指令(j_imm,j_reg,j_link):
- 在defination中添加指令的识别和控制信号的宏定义。
- 在Controller中添加相应的识别和控制操作(主要有NPC的指令地址更新信号,GRF的写入数据选择和写入地址选择信号)。
- 增添NPC模块的地址更新操作和GRF模块的写入操作。
- 阻塞转发一般无需修改。(注意j_reg的Tuse_rs = 0)
b类型跳转指令(branch)
- 在defination中添加指令的识别和控制信号的宏定义。
- 在Controller中添加相应的识别和控制操作(主要有cmp的比较选择操作,NPC的指令地址更新选择操作)。
- 在cmp模块中添加比较操作,NPC模块中添加指令地址更新操作。
- 阻塞转发一般无需修改。(注意branch的Tuse_rs = Tuse_rt = 0)
7、简要描述你的译码器架构,并思考该架构的优势以及不足。
答:
我采用的分布式译码的控制信号驱动型:
分布式译码:每一级都部署一个控制器,负责译出当前级所需控制信号。这种方法较为灵活,“现译现用”有效降低了流水级间传递的信号量,但是需要实例化多个控制器,增加了后续流水级的逻辑复杂度。
控制信号驱动型:为每个指令定义一个 wire 型变量,使用或运算描述组合逻辑,对每个控制信号进行单独处理。这种方法在指令数量较多时适用,且代码量易于压缩,缺陷是如错添或漏添了某条指令,很难锁定出现错误的位置。