riscv cpu硬件訪問device區域微亂序的影響

以下代碼,是riscv plic處理一個外部中斷的handler程序。在handler程序中

  1. 首先寫irq_generator組件的clear寄存器,將中斷源給清除掉
  2. 然後寫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

在硬件裏面的真實訪問順序,是保證和程序流的順序是一致的。面的真實訪問順序,是保證和程序流的順序是一致的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章