在p5中,我们需要设计一个支持10条指令的5级全速流水线cpu,今天刚刚写完,交了一发,惊喜地直接通过了弱测/中测/中强测,然而孩子p4课上考试还没过(哭)。趁着写代码的激情和记忆还在,写一篇也是第一篇博客记录一下设计的思路。
1.充分理解流水线的暂停转发机制和对应的代码实现方式,这一点课件上阐述的非常全面。
2.若对每个信号控制信号每一级都流水,那么D,E,M,W流水寄存器的接口将会非常多。但是我们可以想到,需要流水的所有的信息都是包含在指令里的,那么,通过只流水指令,转而在执行部分对指令进行译码,每一阶段只需得到想要的信号即可,这就是所谓的“分布式译码”。
3、将控制流和数据流分离。在写程序之初就常常听到过,要提高内聚,降低耦合。指的就是每一个模块执行一个特定的功能,再通过有限的有利于理解和记忆的接口将不同的模块组合起来,很像一个搭积木的过程,对吧。在本例中,最明显的体现就是,通过Hazard_unit模块产生转发和阻塞信号,而不必将Tuse,Tnew在流水线中不停的传递,因为在cpu里,指令就包含了所有的信息。
4.延时槽。不得不吐槽,高老板的课件中对于这部分的解释过于简略,甚至概念的定义都是由英文的形式给出,“Whether or not we take the branch, always execute the instruction immediately following the branch”,嗯,就这一句话,就讲完了。以一个跟我一样天赋普通的计科学生的视角,看见这个定义第一时间一定是不知所云。以beq指令为例,
beq $t1 $t2 label
nop
subu.....
label: addu ......
如果跳转指令成立,如果没有延时槽,IF/ID寄存器第一时间被清除,nop不会被执行,如果加了延时槽,不管是否跳转,nop都会被执行,再跳转至addu指令。具体在代码中实现就是,对于J型指令,将PC+8存入31号寄存器而非PC+4。
测试指令(主要针对特定的转发和暂停)
// An highlighted block
add $s1 $s2 $s3
sw $s1 0($0) //
____________________
add $s1 $s2 $s3
sw $0 0($s1)
___________________
jal label
nop
label: add $1 $31 $0
__________________
jal label
label:add $1 $31 $0
nop
___________________
addu $s0 $s1 $s2
subu $s3 $s4 $s5
sw $s0 0($0)
___________________
jal label
nop
label: jr $31
nop
___________________
add $31 $1 $2
jr $31
nop
____________
jal target
nop
target:beq $31 $0 target2
nop
target2: ....