在流水线中,何时使用数据转发,何时使用暂停来处理数据冒险(RISC-V架构)

编程语言 2026-07-10

我一直在尝试理解在流水线处理中处理数据冒险(如RAW)时,在哪些情形下转发比阻塞更优,反之亦然。我参考了好几篇资料来理解,其中有些说通常偏向阻塞,因为无法用转发解决所有冒险,另一些则说两者可以互换使用,但转发可能需要更复杂的硬件。

尽管这几处解释了两者的差异,我仍然不清楚究竟在什么情形下应使用转发、什么时候应使用阻塞?还有哪些情形是转发在处理冒险方面无法帮助到阻塞能够处理的?为什么在这些情形下不能使用转发?

例如:

lw s1, 0(0x101)
add s2, s1, zero

声称“并非在所有情形下都能只用转发就解决问题”的来源,将上述情形作为一个例子。我能理解仅靠阻塞就能处理上述RAW冒险,但为什么在上述情形下不能只用转发呢?

注:该流水线属于RISC-V架构,包含5 个阶段:取指(IF)、指令译码(ID)、执行(EX)、数据存储器访问(DM)以及回写(WB)。

解决方案

转发始终优于阻塞,因为转发是“免费的”,而阻塞要耗费一个时钟周期,降低执行速度。

在use-after-load的情形,转发和阻塞通常都会被使用。若不使用转发,该场景在加载与紧随其后的使用之间将需要两次阻塞周期。使用转发时,只需要一个阻塞周期即可。

一般的思路是,指令会写入寄存器组并从寄存器组读取,这是前序指令与后续指令共享结果的方式。但写入发生在WB阶段,读取发生在ID阶段。这意味着在第一条指令完成WB时,第二条指令的ID阶段(寄存器读取)已经在两周期前完成了。这就是为什么你的use-after-load场景需要两个周期的阻塞。

(技术性注解:在同一个时钟周期,ID阶段对同一寄存器的读取可以直接读取到在WB阶段对同一寄存器写入的结果。如果不是这样,那么为了在连续指令之间共享结果,后指令将需要三周期的阻塞,因为它在开始自己的ID之前必须等待前一条指令完成WB。)有些人把这种在ID与 WB重叠的能力称为寄存器文件内部的“内部转发”,也有人说可以使用半周期,但我仍认为这是在同一时钟周期对同一寄存器进行写入和读取所导致的、电路在值定值方面的自然结果。

当所关心的数据值已经在处理器内部的某处可用、尽管尚未写入寄存器文件时,可以进行转发以避免阻塞。这需要在适当的位置放置一些比较器,将ID阶段读取的值替换为来自可用位置的正确值。

因此,在add-后接add的情形中,第一条指令的第一次加法结果在其EX阶段之后就已经可用(但要再过2 个时钟周期才写入寄存器),第二条指令将在紧接着的下一个周期进入EX。因此,转发可以检测到第二条指令在ID阶段读到了过时的值,应将其替换为第一条指令的计算结果。于是使用EX输出到EX输入的转发(简称EX–EX转发)来避免阻塞。若不使用转发,按照上述分析就需要2(或3)个周期的阻塞。

在加载后使用的情形中,加载结果要在数据存储器(DM)阶段之后才会在处理器内可用,但第二条指令在EX阶段就需要用到它。因此,我们能做的最佳方案是将DM输出转发给EX输入,同时让第二条指令等待DM输出完成后再进入EX,从而产生一个阻塞。


另外,检测阻塞与识别转发的电路在很大程度上是同一套。若没有这部分电路,处理器就无法维持自身的契约,因此要么设计成没有流水线阶段的重叠,要么只能用阻塞,或用阻塞与转发的组合。后者才是最符合逻辑的选择。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章