我目前正在构建一个小型 CPU 解释器,它支持多种寻址模式,包括寄存器延迟和位移。它利用经典的 IF-ID-EX-MEM-WB RISC 管道。在流水线的哪个阶段解码的地址模式操作数的值。例如:
添加 r9, (r2), 8(r3)
(r2) 和 8(r3) 在哪个阶段被解码为它们的实际值?
我目前正在构建一个小型 CPU 解释器,它支持多种寻址模式,包括寄存器延迟和位移。它利用经典的 IF-ID-EX-MEM-WB RISC 管道。在流水线的哪个阶段解码的地址模式操作数的值。例如:
添加 r9, (r2), 8(r3)
(r2) 和 8(r3) 在哪个阶段被解码为它们的实际值?
这是一个有趣的问题。
RISC 架构的一个特性是寄存器-寄存器操作。也就是说,计算指令(如 ADD)的所有操作数必须已经在寄存器中。这使 RISC 实现能够享受常规管道,例如您在问题中提到的 IF-ID-EX-MEM-WB 管道。此约束还简化了内存访问和异常。例如,如果从内存中读取数据的唯一指令是加载指令,并且如果这些指令只有寄存器+位移这样的简单寻址模式,那么给定指令最多可以引发一个内存保护异常。
相比之下,CISC 体系结构通常允许丰富的操作数寻址模式,例如间接寄存器,并且如您的问题中那样索引。这些架构的实现通常具有不规则的流水线,这可能会因为在操作数可用于计算(ADD 等)之前发生一次或多次内存访问而停止。
尽管如此,微架构师已经成功地流水线化了 CISC 架构。例如,Intel 486 有一个流水线,可以将操作数和结果读/写到内存中。因此,在实现 ADD [eax],42 时,有一个流水线阶段从 8 KB d-cache 读取 [eax],一个流水线阶段执行添加,另一个流水线阶段将总和写回 [eax] .
由于 CISC 指令和操作数的使用在动态上非常混合且不规则,因此您的管道设计要么必须相当长才能考虑到最坏的情况,例如多次内存读取以访问操作数和一次内存写入以回写结果,或者它必要时必须停止管道以插入额外的内存访问。
因此,为了适应这些 CISCy 寻址模式,您可能需要一个 IF-ID-EA-RD1-RD2-EX-WR 管道(EA=eff addr, RD1=read op 1, RD2=read op 2, WR=write result to RAM或 reg 文件)。
快乐的黑客。
正如 Jan Gray 指出的那样,您提到的 CISC 指令addw r9, (r2), 8(r3)
不会直接映射到 IF-ID-EX-MEM-WB RISC 管道上。
但是,除了创建一个 IF-ID-EA-RD1-RD2-EX-WR 管道(我认为无论如何都不适用于这种情况,至少在我的符号中不是),您还可以考虑将 CISC 指令分解为类 RISC 微指令
tmp1 := load Memory[ r2 ]
tmp2 := load Memory[ 8+r3 ]
r9 := addw tmp1 + tmp2
通过这种 uop(微操作)分解,地址计算 (r2) 和 8(r3) 将在它们各自的 EX 管道级中完成,并且在 MEM 管道级内部和周围进行实际内存访问。
正如 Jan 所提到的,i486 有一个不同的管道,即所谓的加载操作管道:IF-ID-AGU-MEM-EX-WB,其中 AGU 是地址生成单元/管道级。
这允许不同的 uop 分解
tmp1 := load Memory[ r2 ]
r9 := addw tmp1 + load Memory[ 8+r3 ]
地址计算 (r2) 和 8(r3) 在 AGU pipestage 中完成。
正如 Jan Gray 上面提到的,您尝试执行的指令实际上不适用于此管道。您需要在 MEM 阶段加载数据,并在 EX 阶段(在 mem 之前)对其进行一些数学运算。
但是,如果您想回答另一个相关问题,请执行以下操作:
Load R9, 8(R3)
“值修改操作数的值”的值在 EX 阶段计算。