mini-riscv-os 05-Preemptive
void timer_init() { // 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. *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)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. reg_t *scratch = &timer_scratch[id][0]; scratch[3] = CLINT_MTIMECMP(id); scratch[4] = interval; w_mscratch((reg_t)scratch); // enable machine-mode timer interrupts. w_mie(r_mie() | MIE_MTIE); }
qemu-system-riscv64 -M virt -kernel kernel.img -bios none -serial stdio -display none OS start OS start loop OS: Activate next task Task0: Created! Task0: Running... Task0: Running... Task0: Running... Task0: Running... Task0: Running... Task0: Running... Task0: Running... Task0: Running... Task0: Running... Task0: Running... Sync exceptions! , code = 7 OOPS! What can I do!
reg_t trap_handler(reg_t epc, reg_t cause) { reg_t return_pc = epc; reg_t cause_code = cause & 0xfff; if (cause & 0x80000000) { /* Asynchronous trap - interrupt */ switch (cause_code) { case 3: lib_puts("software interruption!\n"); break; case 7: lib_puts("timer interruption!\n"); // disable machine-mode timer interrupts. w_mie(~((~r_mie()) | (1 << 7))); timer_handler(); return_pc = (reg_t)&os_kernel; // enable machine-mode timer interrupts. w_mie(r_mie() | MIE_MTIE); break; case 11: lib_puts("external interruption!\n"); break; default: lib_puts("unknown async exception!\n"); break; } } else { /* Synchronous trap - exception */ // https://juejin.cn/post/7006347707144994853 lib_printf("Sync exceptions! , code = %d\n", cause_code); lib_puts("OOPS! What can I do!"); while (1) { /* code */ } } return return_pc; }
原来是trap_vector 处理有问题
.globl trap_vector # the trap vector base address must always be aligned on a 4-byte boundary .align 4 trap_vector: # save context(registers). csrrw t6, mscratch, t6 # swap t6 and mscratch reg_save t6 csrw mscratch, t6 # call the C trap handler in trap.c csrr a0, mepc csrr a1, mcause call trap_handler # trap_handler will return the return address via a0. csrw mepc, a0 # load context(registers). csrr t6, mscratch reg_load t6 mret
更改timervec
#include "timer.h" #include "lib.h" // a scratch area per CPU for machine-mode timer interrupts. reg_t timer_scratch[NCPU][5]; #define interval 20000000 // cycles; about 2 second in qemu. // assembly code in kernelvec.S for machine-mode timer interrupt. extern void timervec(); void timer_init() { // 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. *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)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. reg_t *scratch = &timer_scratch[id][0]; scratch[3] = CLINT_MTIMECMP(id); scratch[4] = interval; w_mscratch((reg_t)scratch); //set the machine-mode trap handler. w_mtvec((reg_t)timervec); // // // enable machine-mode interrupts. w_mstatus(r_mstatus() | MSTATUS_MIE); // enable machine-mode timer interrupts. w_mie(r_mie() | MIE_MTIE); } static int timer_count = 0; void timer_handler() { lib_printf("timer_handler: %d\n", ++timer_count); int id = r_mhartid(); *(reg_t *)CLINT_MTIMECMP(id) = *(reg_t *)CLINT_MTIME + interval; }