xv6TimerInterrupt - ccc-sp/riscv2os GitHub Wiki
xv6: 時間中斷時,會發生甚麼事?
5.4 Timer interrupts
Xv6 uses timer interrupts to maintain its clock and to enable it to switch among compute-bound processes; the yield calls in usertrap and kerneltrap cause this switching. Timer interrupts come from clock hardware attached to each RISC-V CPU. Xv6 programs this clock hardware to interrupt each CPU periodically.
RISC-V requires that timer interrupts be taken in machine mode, not supervisor mode. RISCV machine mode executes without paging, and with a separate set of control registers, so it’s not practical to run ordinary xv6 kernel code in machine mode. As a result, xv6 handles timer interrupts completely separately from the trap mechanism laid out above.
kernel/start.c
// set up to receive timer interrupts in machine mode,
// which arrive at timervec in kernelvec.S,
// which turns them into software interrupts for
// devintr() in trap.c.
void
timerinit() // 設定並啟動時間中斷
{
// each CPU has a separate source of timer interrupts.
int id = r_mhartid();
// ask the CLINT for a timer interrupt.
int interval = 1000000; // cycles; about 1/10th second in qemu.
*(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval; // 設定下次中斷時間
// prepare information in scratch[] for timervec.
// scratch[0..2] : space for timervec to save registers.
// scratch[3] : address of CLINT MTIMECMP register.
// scratch[4] : desired interval (in cycles) between timer interrupts.
uint64 *scratch = &timer_scratch[id][0]; // 設定 scratch 暫存區,[0..2] 保存 a1, a2, a3 等暫存器
scratch[3] = CLINT_MTIMECMP(id); // 放入 CLINT_MTIMECMP 到 scratch[3] 給 timervec 使用
scratch[4] = interval; // 放入 interval 到 scratch[4] 給 timervec 使用
w_mscratch((uint64)scratch); // 寫入到 msratch 暫存器
// set the machine-mode trap handler.
w_mtvec((uint64)timervec); // 設定時間中斷函數 (M-mode)
// enable machine-mode interrupts.
w_mstatus(r_mstatus() | MSTATUS_MIE); // 啟動中斷 (M-mode)
// enable machine-mode timer interrupts.
w_mie(r_mie() | MIE_MTIE); // 啟動時間中斷 (M-mode)
}