fence.i
缓存一致性问题
当使用 store
指令修改内存中的数据时候, 我们很可能会去修改一个存储着指令的地址, 而改地址若已经 cached
, 则会导致缓存中所存储的指令与实际存储在目标地址的指令不一致, 导致程序行为的错误, 无法实现预期的效果
// smc.c
int main() {
asm volatile("li a0, 0;"
"li a1, UART_TX;" // change UART_TX to the correct address
"li t1, 0x41;" // 0x41 = 'A'
"la a2, again;"
"li t2, 0x00008067;" // 0x00008067 = ret
"again:"
"sb t1, (a1);"
"sw t2, (a2);"
"j again;"
);
return 0;
}
A
后, 修改标号 again
处的指令为 ret
, 实现退出, 而在我所实现的 npc 中的实际运行效果是不断的输出字母 A
, 这与代码所描述的行为是不一致的, why?
正是因为我们修改了程序代码, 但在 cache 中的备份却没有被修改, 导致了程序行为的不符合预期.
fence.i
指令正是用来解决这样的问题, 在程序访问这个数据块之前, 执行 fence.i
, 表明其之后的取指操作都可以看到其之前的 store
指令修改的结果, fence
正是像一个屏障, 让其之后的指令无法跨越个这个屏障来读取到被 store
指令修改之前的旧数据.
fence.i
指令的实现方式
fence.i
指令可以有多种的实现方式, 只要达到了屏障的目的即可.
一种简单的方式是在执行 fence.i
指令时候, 将 cache 中的所有块都标记为 invalid
, 并且对流水线进行冲刷, 则下一次访问该指令的时候, 就会触发 cache miss
, 重新从存储器中更新缓存 (冲刷缓存与流水线)