53 lines
1.6 KiB
Plaintext
53 lines
1.6 KiB
Plaintext
use crate::hal::arch::riscv::{asm, MIE_MTIE, MSTATUS_MIE};
|
|
use crate::NCPU;
|
|
use core::ptr::addr_of;
|
|
|
|
// Core Local Interrupter (CLINT), which contains the timer.
|
|
pub const CLINT: usize = 0x2000000;
|
|
const CLINT_MTIME: usize = CLINT + 0xbff8;
|
|
|
|
extern "C" {
|
|
pub fn timervec();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub static mut timer_scratch: [[u64; 5]; NCPU] = [[0u64; 5]; NCPU];
|
|
|
|
fn clint_mtimecmp(hartid: usize) -> *mut u64 {
|
|
(CLINT + 0x4000 + (8 * hartid)) as *mut u64
|
|
}
|
|
|
|
/// Arrange to receive timer interrupts.
|
|
///
|
|
/// They will arrive in machine mode at
|
|
/// at timervec in kernelvec.S,
|
|
/// which turns them into software interrupts for
|
|
/// devintr() in trap.c.
|
|
pub unsafe fn timerinit() {
|
|
// Each CPU has a separate source of timer interrupts.
|
|
let id = asm::r_mhartid() as usize;
|
|
|
|
// Ask the CLINT for a timer interrupt.
|
|
// cycles, about 1/10th second in qemu
|
|
let interval = 1_000_000u64;
|
|
*clint_mtimecmp(id) = *(CLINT_MTIME as *const u64) + 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.
|
|
let scratch: &mut [u64; 5] = &mut timer_scratch[id];
|
|
scratch[3] = clint_mtimecmp(id) as usize as u64;
|
|
scratch[4] = interval;
|
|
asm::w_mscratch(addr_of!(scratch[0]) as usize as u64);
|
|
|
|
// Set the machine-mode trap handler.
|
|
asm::w_mtvec(timervec as usize as u64);
|
|
|
|
// Enable machine-mode interrupts.
|
|
asm::w_mstatus(asm::r_mstatus() | MSTATUS_MIE);
|
|
|
|
// Enable machine-mode timer interrupts.
|
|
asm::w_mie(asm::r_mie() | MIE_MTIE);
|
|
}
|