问题描述
对于下面的RISC-V代码序列,我试图确定数据转发无法解决的数据危险。通过重新排序代码序列可以克服危险吗?如果是,任何人都可以显示新序列吗?
这是我的代码:
loop: slli s2,s1,2
add s3,s2,s0
lw t0,0(s3)
add t1,t2,t0
sw t1,20(s3)
addi s1,1
beq s1,s5,loop
解决方法
通过转发可以解决哪些数据危险完全取决于您所使用的RISC-V体系结构的特定实现。我将假设使用Patterson&Hennessey教科书中介绍的具有转发功能的传统5阶段RISC-V管道,并为您的问题加分。
此流水线具有阶段指令提取(IF),指令解码(ID),执行(EX),内存(MEM)和写回(WB)。执行阶段计算寄存器-寄存器和寄存器立即操作的值,并计算存储器(lw / sw)和控制传输(分支/跳转)操作的地址,并且存储器级读取lw的数据并写入sw的数据。数据可以从执行,存储或写回阶段的末尾转发到上一个阶段的开头,以用于后续指令。
在您包含的代码段中,有5种数据危害:
(1)slli s2,s1,2
和add s3,s2,s0
,用于寄存器s2
在这里,寄存器s2
由slli
写入并由add
读取。新值是在第一条指令的EX阶段结束时计算的,而在下一条指令的EX阶段开始时需要的,因此可以通过转发来解决。
(2)add s3,s0
和lw t0,0(s3)
用于寄存器s3
在这里,寄存器s3
由add
写入并由lw
读取。新值是在第一条指令的EX阶段结束时计算的,而在下一条指令的EX阶段开始时需要的,因此可以通过转发来解决。
(3)寄存器lw t0,0(s3)
的{{1}}和add t1,t2,t0
在这里,寄存器t0
由t0
写入并由lw
读取。新值是在第一条指令的MEM阶段结束时计算的,而在下一条指令的EX阶段开始时需要此新值,因此无法通过转发解决。
(4)寄存器add
的{{1}}和add t1,t0
在这里,寄存器sw t1,20(s3)
由t1
写入并由t1
读取。新值是在第一条指令的EX阶段结束时计算的,而在下一条指令的MEM阶段开始时需要的,因此可以通过转发来解决。
(5)寄存器add
的{{1}}和sw
在这里,寄存器addi s1,1
由beq s1,s5,loop
写入并由s1
读取。在第一条指令的EX阶段结束时计算新值。通常,对于没有对控制危险进行任何优化的RISC-V管道,对于分支指令,在EX期间将寄存器与程序计数器+偏移量的计算并行进行比较,以获得分支目标。这里的控制危险是一个单独的问题,但是可以通过转发解决数据危险。
因此,在5种危险中,只有一种是无法通过数据转发解决的。可以通过指令重新排序解决吗?是。看一下s1
。在addi
之前,第一个beq
之后的任何指令都不会读写addi s1,1
。如果我们移动该指令,那么生成的代码将如下所示:
addi
这样,s1
检索要写入slli
的数据的指令将在{{}需要新的loop: slli s2,2
add s3,s0
lw t0,0(s3)
addi s1,1
add t1,t0
sw t1,20(s3)
beq s1,loop
值时完成MEM阶段。 1}}指令。
通过这种重新排序,可以通过转发解决所有数据危害,并且不需要停顿。