回到文章列表
os-opensbi-timer-interrupt

【OS】OpenSBI 開啟Timer Interrupt

發布於 2026-04-20·15·
OSOpenSBIRISC-V

最近在實作RISC-V OS的Trap時,剛好要做Timer Interrupt,這在QEMU中非常簡單,只要follow xv6的做法就可以實現,但在Milkv開發版上就不太一樣。

因為我的Milkv開發版上不是裸機執行OS,而是透過OpenSBI來載入並降級到S-Mode,且又因為RISC-V的Timer Interrupt 是只能在M-Mode設置,因此做起來就比較麻煩,需要在S-Mode下透過ecall 委託M-Mode的OpenSBI來完成。

xv6在QEMU上的做法

先來快速go through xv6是怎麼做Timer Interrupt的,首先需要先還在M-Mode時就先設置menvcfg (Machine Environment Configuration) 這個csr暫存器的bit 63來允許 S-mode 使用 stimecmp (Sstc or Supervisor-mode Timer Compare extension)。 (xv6-start.c line:59)

然後再設置mcounteren的bit 1,允許 S-mode 讀 time 計數器,沒設的話在執行

c
asm volatile("csrr %0, time" : "=r" (x));

時就會觸發 illegal instruction。

以上兩個都開啟後,就可以在S-Mode中設置stimecmp來開啟timer的計時功能了,時間到就會Trap,然後跳到我們寫好的Trap Handler處理。

Milkv Duo S (OpenSBI) 上的做法

然而因為我目前的Milkv Duo S是跑在OpenSBI下的,所以OS一進來就是S-Mode,沒辦法碰到M-Mode的暫存器,加上他的CPU C906不支援Sstc,不能直接在S-Mode設timer。

但好在OpenSBI有開啟前面兩點,並提供直接設置Timer計數的功能,因此只要ecall呼叫一下OpenSBI請他開始計數即可。

為了實作這個,我寫了一個Helper Funtion如下

C
// SBI Timer Extension
// 請 M-mode(OpenSBI)設定 timer,當 time >= x 時觸發 S-mode timer interrupt
// C906 沒有 Sstc extension,S-mode 不能直接寫 stimecmp,
// 必須透過 SBI ecall 請 M-mode 代勞設定 mtimecmp
//
// SBI ecall 呼叫慣例:
//   a7 = Extension ID(哪個功能模組)
//   a6 = Function ID (該模組的哪個功能)
//   a0 = 第一個參數
//   執行 ecall → 陷入 M-mode → OpenSBI 根據 a7/a6 分發處理
static inline void sbi_set_timer(uint64 x){
    asm volatile(
        "mv a0, %0\n"           // a0 = x(參數:timer 目標時間)
        "li a6, 0\n"            // a6 = FID = 0(set_timer 功能)
        "li a7, 0x54494D45\n"   // a7 = EID = "TIME"(Timer Extension)
        "ecall"                  // 陷入 M-mode,OpenSBI 幫忙設定 mtimecmp
        : : "r"(x) : "a0", "a6", "a7"
        // "r"(x): 把 x 放進暫存器,用 %0 引用
        // clobber list: 告訴 compiler a0/a6/a7 被動過
    );
}

如此一來只要在需要的地方去呼叫就可以了。

c
sbi_set_timer(get_time() + 1000000);