我们需要t0的地址
寄存器没有地址:它们有名字;它们在寄存器文件中有位置/索引,并且它们保存值。
只有内存有地址。
因为,lw,确实需要 mem[$t0] + 4 的正确值
lw
访问,并且它mem[$t0+4]
需要 $t0 的值以便它可以执行+
.
和指令计算有效地址lw
:sw
ea = R[rs]+SignExtImm
在这里,硬件使用索引 索引寄存器文件,索引是从编码指令rs
调用的 5 位字段中获得的。存储在寄存器中的值是 32 位的,因此 5 位索引用于查找保存在那里的 32 位值。因此,这是对寄存器值的读取。立即数从指令字段中的 16 位符号扩展为 32 位,然后将寄存器的 32 位值和立即数提供给 ALU 以相加。rs
lw
rs
在计算 之后ea
,负载会:
R[rt] = M[ea]
在计算 之后ea
,商店会
M[ea] = R[rt]
为了计算 ea,我们需要 的值R[rs]
,即rs
寄存器中保存的值。
这部分操作几乎和顺序一样:
add $t0, $t1, $t2 # $t0 is written here
addi $s1, $t0, 4 # $t0 is read, ALU computes $t0+4 (result goes to $s1)
lw $s2, 4($t0) # $t0 is read, ALU computes $t0+4 (result is a memory address)
xori $s3, $t0, 8 # $t0 is read, ALU computes $t0^8
add
和addi
对已读取正确的数据依赖关系。这是 ALU/ALU 依赖或 EX/EX,取决于我们是在谈论功能单元还是流水线阶段。这些都是危险的,因为它们是背靠背的。
由于寄存器读取通常发生在 ID(指令解码)阶段,如果rs
通过 WB(回写)阶段的最后一次寄存器更新是早于 3 个周期开始的指令,则此 ID 读取rs
将获取正确的值。任何更少都意味着读入的 ID 将看不到正确的值,因为它还没有进入寄存器。
cycle#: 1 2 3 4 5 6 7 8
instruction #
i1: IF ID EX MM WB
i2: IF ID EX MM WB
i3: IF ID EX MM WB
i4: IF ID EX MM WB
这里 i4 可以毫无风险地读取 i1 的寄存器更新,因为这些操作都发生在周期 5 中——i1 的 WB 发生在时钟 5 的开头,并且 i4 的 ID 能够看到在同一个时钟中写入的值。
但是如果 i2 或 i3 读取 i1 所针对的寄存器,那就有危险了,因为它们的 ID 阶段发生在 i1 的 WB 阶段之前(i2 的 ID 在周期 3,i3 的 ID 在周期 4,两者都太早,无法获得 i1 的 WB在周期 5)。
所以,危险就是这样发生的。但是让我们注意,i2 的 EX 阶段(在第 4 周期)所需的正确值在 CPU 中,并且已经在 i1 的 EX 阶段在第 3 周期中计算出来。转发或绕过替换该正确值以覆盖在 i2 或 i3 的 ID 阶段读取的陈旧值。
查看指令描述和编码的更多细节:
https ://inst.eecs.berkeley.edu/~cs61c/resources/MIPS_Green_Sheet.pdf
查看基本指令格式以查看字段编码,例如lw
和sw
都是 I 型指令,因此它们有一个rs
、一个rt
和一个immediate
字段。
该lw
指令具有一个寄存器源和一个寄存器目标。(它也有一个内存源,但我们不查看内存是否存在数据依赖的 Read-After-Write 危害,我们只查看寄存器。)
该sw
指令有两个寄存器源,没有寄存器目标。(它也有一个内存目标,但我们不查看内存是否存在数据依赖的 Read-After-Write 危害,我们只查看寄存器。)