RISC-V特權架構 - 機器模式下的異常處理
- 1 進入異常
- 1.1 從mtvec 定義的PC 地址開始執行
- 1.2 更新CSR 寄存器mcause
- 1.3 更新CSR 寄存器mepc
- 1.4 更新CSR 寄存器mtval
- 1.5 更新CSR 寄存器mstatus
- 2 退出異常
- 2.1 從mepc 定義的PC 地址開始執行
- 2.2 更新CSR 寄存器mstatus
- 3 異常服務程序
本文屬于《 RISC-V指令集基礎系列教程》之一,歡迎查看其它文章。
狹義的中斷和異常,均可以被歸于廣義的異常范疇,因此本文自此將用“異常”作為統一概念進行論述,其包含了狹義上的“中斷”和“異常”。
RISC-V 定義的三種模式 User、Supervisor 和 Machine,均可發生異常,但是只有特權模式 Supervisor 和 Machine 才能處理異常,因為處理異常需要 CSR 寄存器。默認情況下,RISC-V所有的異常,都在Machine模式下處理。除此以外,還可以通過委托機制,將異常委托給Supervisor模式下處理。
這里本文介紹的是Machine模式下的異常處理機制。
1 進入異常
進入異常時,RISC-V 架構規定的硬件行為,可以簡述如下:
(1)停止執行當前程序流,轉而從CSR 寄存器mtvec 定義的PC 地址開始執行。
(2)進入異常,不僅會讓處理器跳轉到,上述的PC 地址開始執行,還會讓硬件,同時更新其他幾個CSR 寄存器,分別是以下4 個寄存器:
- 機器模式異常原因寄存器mcause(Machine Cause Register)
- 機器模式異常PC 寄存器mepc(Machine Exception Program Counter)
- 機器模式異常值寄存器mtval(Machine Trap Value Register )
- 機器模式狀態寄存器mstatus(Machine Status Register)
下文將分別予以詳述。
1.1 從mtvec 定義的PC 地址開始執行
RISC-V 架構規定,在處理器的程序執行過程中,一旦遇到異常發生,則終止當前的程序流,處理器被強行跳轉到一個新的PC 地址。該過程在RISC-V 的架構中定義為“陷阱(trap)”,字面含義為“跳入陷阱”,更加準確的意譯為“進入異常”。
RISC-V 處理器trap 后跳入的PC 地址,由一個叫做機器模式異常入口基地址寄存器mtvec(Machine Trap-Vector Base-Address Register)的CSR 寄存器指定,其要點如下:
(1)mtvec 寄存器是一個可讀可寫的CSR 寄存器,因此軟件可以編程更改其中的值。
(2)mtvec 寄存器的詳細格式,如下圖所示,其中的最低2 位是MODE 域,高30 位是BASE 域。(32位架構下,XLEN為32;64位架構下,XLEN為64。)
- 假設MODE 的值為0,則所有的異常響應時,處理器均跳轉到BASE 值指示的PC 地址。
- 假設MODE 的值為1,則狹義的異常發生時,處理器跳轉到BASE 值指示的PC 地址;狹義的中斷發生時,處理器跳轉到BASE+4*CAUSE 值指示的PC 地址。CAUSE的值,表示中斷對應的異常編號(Exception Code),如下圖所示(Machine cause register (mcause) values)。譬如機器計時器中斷(Machine Timer Interrupt)的異常編號為7,則其跳轉的地址為BASE+4×7=BASE+28= BASE+0x1c。
1.2 更新CSR 寄存器mcause
RISC-V 架構規定,在進入異常時,機器模式異常原因寄存器mcause(Machine Cause Register)被同時更新,以反映當前的異常種類,軟件可以通過讀此寄存器,查詢造成異常的具體原因。
mcause 寄存器的詳細格式,如下圖所示,其中最高1 位為Interrupt 域,低31 位為異常編號域。
此兩個域的組合表示值,如下圖所示,用于指示RISC-V 架構定義的12 種中斷類型和16 種異常類型。
1.3 更新CSR 寄存器mepc
RISC-V 架構定義,異常的返回地址由機器模式異常PC 寄存器mepc(Machine Exception Program Counter)保存。
在進入異常時,硬件將自動更新mepc 寄存器的值,為當前遇到異常的指令PC 值(即當前程序的停止執行點)。該寄存器將作為異常的返回地址,在異常結束之后,能夠使用它保存的PC 值,回到之前被停止執行的程序點。
(1)值得注意的是,雖然mepc 寄存器會在異常發生時,自動被硬件更新,但是mepc 寄存器,本身也是一個可讀可寫的寄存器,因此軟件也可以直接寫該寄存器以修改其值。
(2)對于狹義的中斷和狹義的異常而言,RISC-V 架構定義其返回地址(更新的mepc 值)有些細微差別:
- 出現中斷時,中斷返回地址mepc 的值,被更新為下一條尚未執行的指令。
- 出現異常時,中斷返回地址mepc 的值,被更新為當前發生異常的指令PC。
注意:
如果異常由ecall 或ebreak 產生,由于mepc 的值被更新為ecall 或ebreak 指令自己的PC。因此在異常返回時,如果直接使用mepc 保存的PC值作為返回地址,則會再次跳回ecall 或者ebreak指令,從而造成死循環(執行ecall 或者ebreak 指令導致重新進入異常)。正確的做法是,在異常處理程序中,軟件改變mepc指向下一條指令,由于現在ecall/ebreak(或c.ebreak)是4(或2)字節指令,因此改寫設定 mepc=mepc+4(或+2) 即可。
1.4 更新CSR 寄存器mtval
RISC-V 架構規定,在進入異常時,硬件將自動更新機器模式異常值寄存器mtval(Machine Trap Value Register ),以反映引起當前異常的存儲器訪問地址或者指令編碼。
- 如果是由存儲器訪問造成的異常,譬如遭遇硬件斷點、取指令、存儲器讀寫造成的異常,則將存儲器訪問的地址,更新到mtval 寄存器中。
- 如果是由非法指令造成的異常,則將該指令的指令編碼,更新到mtval 寄存器中。
注意:mtval 寄存器,又名mbadaddr 寄存器,在某些早期版本的RISC-V 編譯器中僅識別mbadaddr 名稱。
1.5 更新CSR 寄存器mstatus
RISC-V 架構規定,在進入異常時,硬件將自動更新機器模式狀態寄存器mstatus(Machine Status Register)的某些域。
(1)mstatus 寄存器的詳細格式,如上圖所示,其中的MIE 域,表示在Machine Mode 下中斷全局使能。
- 當該MIE 域的值為1 時,表示Machine Mode 下所有中斷的全局打開。
- 當該MIE 域的值為0 時,表示Machine Mode 下所有中斷的全局關閉。
(2)RISC-V 架構規定,異常發生時有如下情況。
- MPIE 域的值,被更新為異常發生前MIE 域的值。MPIE 域的作用,是在異常結束之后,能夠使用MPIE 的值,恢復出異常發生之前的MIE 值。
- MIE 的值,則被更新成為0(意味著進入異常服務程序后,中斷被全局關閉,所有的中斷都將被屏蔽不響應)。也就是說,RISC-V架構默認不支持中斷嵌套。
- MPP 的值,被更新為異常發生前的模式。MPP 域的作用,是在異常結束之后,能夠使用MPP 的值,恢復出異常發生之前的工作模式。對于只支持機器模式(Machine Mode Only)的處理器核,則MPP 的值永遠為二進制值11。
注意:由于為簡化知識模型,在此僅介紹“只支持機器模式”的架構,因此對SIE、UIE、SPP、SPIE 等不做贅述。
2 退出異常
當程序完成異常處理之后,最終需要從異常服務程序中退出,并返回主程序。RISC-V架構定義了一組專門的退出異常指令(Trap-Return Instructions),包括MRET、SRET、和URET。
- 其中MRET 指令是必備的;
- 而SRET 和URET 指令,僅在支持監督模式和用戶模式的處理器中使用。
注意:由于為簡化知識模型,在此僅介紹“只支持機器模式”的架構,對SRET 和URET 指令不做贅述。
在機器模式下,退出異常時,軟件必須使用MRET 指令。
RISC-V 架構規定,處理器執行MRET 指令后的硬件行為,如下:
- 停止執行當前程序流,轉而從CSR 寄存器mepc 定義的PC 地址開始執行。
- 執行MRET 指令,不僅會讓處理器跳轉到上述的PC 地址開始執行,還會讓硬件同時更新CSR 寄存器機器模式狀態寄存器mstatus(Machine Status Register)。
下文將分別予以詳述。
2.1 從mepc 定義的PC 地址開始執行
在上文中曾經提及,在進入異常時,mepc 寄存器被同時更新,以反映當時遇到異常的指令的PC 值。通過這個機制,意味著MRET 指令執行后,處理器回到了當時遇到異常的指令的PC 地址,從而可以繼續執行之前被中止的程序流。
2.2 更新CSR 寄存器mstatus
mstatus 寄存器的詳細格式,如上圖所示。RISC-V 架構規定,在執行MRET 指令后,硬件將自動更新機器模式狀態寄存器mstatus(Machine Status Register)的某些域。
RISC-V 架構規定,執行MRET 指令退出異常時有如下情況:
- mstatus 寄存器MIE 域的值,被更新為當前MPIE 的值(恢復)。
- mstatus 寄存器MPIE 域的值,則被更新為1。
在上文中曾提及,在進入異常時,MPIE 的值曾經被更新為異常發生前的MIE 值。而MRET 指令執行后,再次將MIE 域的值更新為MPIE 的值。通過這個機制,則意味著MRET指令執行后,處理器的MIE 值被恢復成異常發生之前的值(假設之前的MIE 值為1,則意味著中斷被重新全局打開)。
3 異常服務程序
如上文中所述,當處理器進入異常后,即開始從mtvec 寄存器定義的PC 地址執行新的程序。該程序通常為異常服務程序,并且程序還可以通過查詢mcause 中的異常編號(Exception Code)決定進一步跳轉到更具體的異常服務程序。
譬如當程序查詢mcause 中的值為0x2,則得知該異常,是非法指令錯誤(Illegal Instructions)引起的,因此可以進一步跳轉到,非法指令錯誤異常服務子程序中去。
下圖所示為一異常入口程序實例片段,程序通過讀取mcause 的值,進而判斷異常的類型,從而進入不同的異常服務子程序。
注意:
由于RISC-V 架構規定的進入異常和退出異常機制中沒有硬件自動保存和恢復上下文的操作,因此需要軟件明確地使用指令進行上下文的保存和恢復。
參考文檔:
- 《手把手教你設計CPU.RISC-V處理器》