weiqi7777

riscv cpu硬件访问device区域微乱序的影响

1
阅读(1748)

以下代码,是riscv plic处理一个外部中断的handler程序。在handler程序中

  • 首先写irq_generator组件的clear寄存器,将中断源给清除掉
  • 然后写plic的complete寄存器,通知plic,该中断处理完毕

plic_irq_mmode:

li a1, PLIC_M_CLAIM

// a0 save the interrupt id

// tell the irq_generator to clear the interrupt

li a2, 0x10040300

sw a0, (a2)

lw a3, (a2)

// write the complete

sw a0, (a1)

ret

上述的中断处理程序,理论上可以完美工作。

但是在实际的仿真过程中,发现,当该中断处理完毕,异常返回后,该中断被再次的触发,然后又再次进入中断处理程序。这就与预期的不符合,因为发生了一次中断,但是cpu却处理了两次。    

通过抓取仿真的波形,找到了该中断,再次被cpu响应的原因。

下图是波形:

从波形可以看出,写plic的complete寄存器的时间,要早于写irq_generator组件的clear寄存器的时间。因此造成了,PLIC收到core的中断处理完毕请求后,认为该中断处理完毕,因此可以再次接收该中断的外部中断请求,而此时,外部中断请求还没有clear掉,所以PLIC认为又有一个新的中断请求到来,从而记录该中断,然后向cpu上报该中断。

究其原因,是cpu的硬件,将我们写的程序流的仿存顺序,给乱序仿存了。从而造成了程序非预期的结果。因为cpu是流水线工作,而且为了性能上的考虑,会将顺序仿存,变成乱序仿存,从而提高性能。

所以当我们要求,执行的仿存顺序,是强保序的情况下,就需要额外插入fence指令。

在riscv的spec,定义了fence指令的格式。

该fence指令,比较复杂,之后,专门写篇文章介绍这个。

现在把中断处理程序改为:

plic_irq_mmode:

li a1, PLIC_M_CLAIM

// a0 save the interrupt id

// tell the irq_generator to clear the interrupt

li a2, 0x10040300

sw a0, (a2)

lw a3, (a2)

fence o, o

// write the complete

sw a0, (a1)

ret

然后重新仿真,此时看波形,就正确了。

访问PLIC外设,在时间上,是晚于访问irq_generator外设的,正和我们程序预期的是一致的。

cpu在访问外设时,是不一定会保证不同外设之间的访问,是保序的。只是会保证同一个外设的访问,是保序的。

也就是,如果是访问不同的外设,比如如下程序流:

访问外设A

访问外设B

访问外设C

在硬件里面的真实访问顺序,可能为:

访问外设B

访问外设C

访问外设A

因此如果程序想要硬件的访问顺序和程序流一致,就需要显示的增加fence指令。

但是对于访问相同的外设,比如如下程序流:

访问外设A寄存器a

访问外设A寄存器b

访问外设A寄存器c

在硬件里面的真实访问顺序,是保证和程序流的顺序是一致的。