Skip to content

2 3 RISC V::中斷與異常處理 異常篇

Ian Chen edited this page Sep 23, 2021 · 8 revisions

一般在修讀 Operating System 時,都會學習到 Interrupt 的概念,此外,電腦在運作時也會碰到大大小小的問題。你可曾否好奇在硬體上是如何排除這些問題的呢? 在本篇文章中,筆者會向大家介紹 RISC-V 處理器是如何處理中斷以及異常問題的。

RISC-V 擁有不同的特權模式,供不同層級的程式運作在對應的平台上,如: Machine ModeUser ModeSupervisor Mode 等等。而本系列文只會專注在具有完整硬體存取權限的 Machine Mode 上,如果讀者對特權模式有興趣,可以參考 RISC-V 的官方文件: riscv-privileged-v1.10

CSR 暫存器

RISC-V 架構定義了許多暫存器,部分暫存器被定義為控制和狀態暫存器,也就是標題所指出的 CSR (Control and status registers),它被用於配置或是紀錄處理器的運作狀況。 筆者先將 RISC-V 架構中 (Machine Mode) 與中斷、異常有關的暫存器都列出來,方便待會兒進行解說:

  • CSR
    • mtvec 當進入異常時,PC (Program counter) 會進入 mtvec 所指向的地址並繼續運行。
    • mcause 紀載異常的原因。
    • mtval 紀載異常訊息。
    • mepc 進入異常前 PC 所指向的地址。若異常處理完畢,Program counter 可以讀取該位址並繼續執行。
    • mstatus 進入異常時,硬體會更新 mstatus 寄存器的某些域值。
    • mie 決定中斷是否被處理。
    • mip 反映不同類型中斷的等待狀態。
  • Memory Address Mapped
    • mtime 紀錄計時器的值。
    • mtimecmp 儲存計時器的比較值。
    • msip 產生或結束軟體中斷。
    • PLIC

Exception

我們都知道,處理器是藉由接收指令進行運作的,若處理器在執行指令流的時候遇到無法預期的情況,便稱之為異常

其實異常與中斷非常相似,最主要的差別是異常發生於硬體、程式上的故障。

異常的類型

異常主要分成兩大類型:

同步異常

要判斷異常屬於同步或是異步最簡單的方式是: 在同樣的程式以及環境執行 n 次都能將同樣的異常狀態重現,該異常就可以被歸類到同步異常。常見狀況有:

  • 非法指令產生的錯誤
  • 在 Debugger 設置斷點
  • 擷取指令時訪問到非法的地址空間
  • 訪問地址的屬性出現錯誤
  • 存取指令地址時產生的非對齊錯誤

異步異常

異步異常又能在被細分為兩種狀態: 精確的異步異常以及不精確的異步異常。 由於簡體文字書籍對於精確/非精確異步異常的介紹相當模糊。因此,筆者直接整理下表方便大家釐清:

異常 描述
精確的異步異常 外部中斷
不精確的異步異常 讀寫內存時出錯

 !讀寫內存時出錯說明:  常見在當處理器將資料寫進快取後,等到該資料在快取中被替換(存回外部記憶體)時才發現錯誤,此時處理器已經又處理了上百萬條指令。在這個狀況中,我們很難找出罪魁禍首(出錯的指令)是誰。因此,該狀況被歸類在不精確的異步異常

異常處理

1. Trap

當異常發生時,處理器會停止手邊的工作,再將 Program counter 的位址指向 mtvec 所指的位址並開始執行。這樣的行為就好像是主動跳入陷阱一樣,因此,在 RISC-V 的架構中將這個動作定義為 Trap

補充: mtvec 是一個可讀可寫的暫存器,開發者可以透過軟體修改其值。

  • 當 MODE 為 0 時,轉跳至 BASE 值表示的位址。
  • 當 MODE 為 1 時(同步異常),會將 PC 的位址指向: BASE + 4 X CAUSE !這邊的 CAUSE 值等同於 mcause register 所表示的值。

2. 更新 CSR Register (mcause)

在 RISC-V 架構中有定義: 當進入異常時硬體會更新 mcause register 的值,開發者可以透過軟體去讀取 mcause 以分析造成異常的具體原因。

mcause 的最高位 (MSB) 用來記錄是否為 interrupt,其餘 31 位都用來表示異常編號。

3. 更新 CSR Register (mepc)

mepc register 被用來存放跳入 Trap 前 PC 指向的指令位址,待異常處理結束後,PC 會將 mepc 存放的指令位址讀入並開始執行。 需要注意的是,mepc 在中斷和異常發生時會有不同的行為:

  • 異常發生時 異常發生時,mepc 會記錄 Program Counter 指向的指令位址,也就是出現異常的那條指令。
  • 中斷發生時 當中斷發生時,mepc 會記錄 Program Counter 指向的位址的下一個指令位址,例如:
    1. 中斷發生! (PC = 0x1c)
    2. mepc = PC + 4;
    3. 異常處理
    4. 處理完成,跳回正常狀態 (PC = mepc)
  • 特例 若異常是由 ecall 或是 ebreak 造成的,則參照中斷發生時的情況處理。否則會造成 ecall 以及 ebreak 重複呼叫形成無窮迴圈。

    關於 ecall 以及 ebreak,請參考 RV32I 指令集。 補充: mepc register 也是一個可讀可寫的暫存器。因此,我們同樣可以利用軟體修改它的值。

4. 更新 CSR Register (mtval)

mtval register 又稱為 mbadaddr register,在 RISC-V 的規格書有定義:

當進入異常時,硬體會自動更新 mtval register 的值,以紀錄造成異常發生的暫存器訪問地址或是指令編碼。

從上面的敘述就可以知道,mtval 會有兩種寫入的 Case,分別是:

  • 暫存器訪問造成的異常 包括如硬件斷點、存取指令、儲存器讀寫時造成的異常。這時,硬體會將儲存器訪問的地址記錄到 mtval 當中。

  • 非法指令造成的異常

    除了 RVC 指令集外,其他合法 RISC-V 指令集的 OPCODE 末兩碼都是 11。

    在這個情況中,硬體會將非法的指令編碼記錄到 mtval 當中。

5. 更新 CSR Register (mstatus)

mstatus 紀載了大量的資料,根據 RISC-V 架構的規定: 當進入異常時,硬體會自動更新 mstatus 的某些域值。

format of mstatus

  • MIE 當 MIE 域的值為 1 時為 Enable,反之為 Disable。

簡單來說,就是決定處理器要不要受理中斷請求。

  • MPIE MPIE 用來存放異常發生前 MIE 域的值。當異常結束後就可以利用 MPIE 還原 MIE 的值。

  • MPP 紀錄異常發生前的工作模式,在 RISC-V 規格書中,有以下幾種模式:

    Level Encoding Name Abbreviation
    0 00 User/Application U
    1 01 Supervisor S
    2 10 Reserved -
    3 11 Machine M

6. 退出異常狀態

當異常處理程式完成後,需要從異常服務退出。在 RISC-V 架構中定義了一組用於退出異常的指令 (Trap-Return Instruction),包括:

  1. MRET
  2. SRET
  3. URET

分別對應了 Machine Mode, Supervisor Mode 以及 User Mode。 使用 MRET 指令退出異常後,硬體會做兩件事情:

  1. 從 mepc 指向的指令位址開始執行

  2. 更新 mstatus register 同樣以 Machine Mode 為例:

    • 將 MIE 更新為 MPIE 的值。
    • 將 MPIE 域的值更新為 1。

    需要注意的是: MIE 域僅是反映中斷是否接受處理,其控制權仍取決於 MIE register 中的 MEIE 域。

總結

在先前導讀過的 rv32emu-next 專案中,也包含了異常的處理,如果有興趣可以在自行閱讀原始碼。經過上面的介紹後,就能輕鬆的看懂這個 RISC-V 的 Emulator 是如何運作的了(嗎?)

Reference

Clone this wiki locally