在 MIPS 中使用 NOP 和停顿有什么区别

What is the difference using NOP and stalls in MIPS

用NOP代替stall有什么区别。 在流水线的情况下,两者恰好执行相同的任务。看不懂

我认为您混淆了术语。

处理器将停顿注入流水线以解决数据危险(处理指令所需的数据尚不可用的情况。NOP只是一条没有副作用的指令。


摊位

回忆5个流水线阶段classic RISC pipeline:

  1. IF - 指令获取(从内存中获取下一条指令)
  2. ID - 指令解码(找出这是哪条指令以及操作数是什么)
  3. EX - 执行(执行动作)
  4. MEM - 内存访问(存储或读取内存)
  5. WB - Write back(将结果写回寄存器)

考虑代码片段:

add $t0, $t1, $t1
sub $t2, $t0, $t0

从这里可以明显看出第二条指令依赖于第一条指令的结果。这是 data hazard: Read After Write (RAW); 真正的依赖.

sub 在其 EX 阶段需要 add 的值,但 add 仅在其 MEM 阶段 - 该值在 WB 之前不可用阶段:

+------------------------------+----+----+----+-----+----+---+---+---+---+
|                              |         CPU Cycles                      |
+------------------------------+----+----+----+-----+----+---+---+---+---+
|         Instruction          | 1  | 2  | 3  | 4   | 5  | 6 | 7 | 8 | 9 |
+------------------------------------------------------------------------+
|       0 | add $t0, $t1, $t1  | IF | ID | EX | MEM | WB |   |   |   |   |
|       1 | sub $t2, $t0, $t0  |    | IF | ID | EX  |    |   |   |   |   |
+---------+--------------------+----+----+----+-----+----+---+---+---+---+

One solution 此问题是处理器插入停顿或 冒泡 管道,直到数据可用。

+------------------------------+----+----+----+-----+----+----+-----+---+----+
|                              |         CPU Cycles                          |
+------------------------------+----+----+----+-----+----+----+-----+----+---+
|         Instruction          | 1  | 2  | 3  | 4   | 5  | 6  | 7   | 8  | 9 |
+----------------------------------------------------------------------------+
|        0 | add $t0, $t1, $t1 | IF | ID | EX | MEM | WB |    |     |    |   |
|        1 | sub $t2, $t0, $t0 |    | IF | ID | S   | S  | EX | MEM | WB |   |
+----------+-------------------+----+----+----+-----+----+---+---+---+-------+

NOP

A NOP 是一条什么都不做的指令(没有副作用)。 MIPS 汇编程序通常支持 nop 指令,但在 MIPS this is equivalent to sll $zero $zero 0.

这条指令将占用流水线的所有5个阶段。它最常用于填充 branch delay slot 的跳转或分支,当没有其他有用的东西可以在该插槽中完成时。

j label
nop # nothing useful to put here

如果您使用的是 MIPS 模拟器,您可能需要启用分支延迟槽模拟才能看到这一点。 (例如,在 spim 中使用 -delayed_branches 参数)

我们不应该使用 NOP 代替停顿,反之亦然。

当存在导致管道的特定阶段等待直到它获得所需数据的依赖性危险时,我们将使用停顿,而在停顿的情况下使用 NOP 它只会通过该阶段什么都不做的指令。但是,在使用 NOP 完成该阶段后,该阶段所需的数据可用,我们需要从头开始执行指令,这会增加处理器的平均 CPI,导致性能下降。此外,在某些情况下,该指令所需的数据可能会在重新启动指令之前被另一条指令修改,这将导致执行错误。

另外,如果我们在NOP 的地方使用stall,也是一样的。 每当在执行阶段发生不可屏蔽的中断(如除以零)时,我们需要在不更改处理器状态的情况下通过异常后的阶段,这里我们使用 NOP 来通过管道的其余阶段而不进行任何更改处理器状态(比如将某些内容写入寄存器或内存,这是异常生成的错误值)。

在这里,我们不能使用 stall,因为下一条指令将等待 stall 完成,而 stall 将不会完成,因为它是一个不可屏蔽的中断(用户无法控制这些类型的指令)并且管道进入死锁。