// generic_timer: generic ARM timer driver. // // The tick deadline is maintained as an absolute CNTP_CVAL value and advanced // by a fixed period each interrupt. Re-arming relative to the current counter // (CNTP_TVAL) would rebase every deadline to handler-entry time, letting // interrupt latency accumulate into the tick period and drift the cadence on // real hardware. const SYS_FREQ u32 = 54_000_000 extern fn setup_CNTP_CTL() void extern fn set_CNTP_CVAL(cval u64) void extern fn get_sys_count() u64 extern fn get_sys_freq() u64 var next_deadline u64 = 0 // Seconds since boot, for SYS_UPTIME. Divides the architectural counter // by its runtime frequency (CNTFRQ_EL0) — deliberately NOT the SYS_FREQ // tick constant, which is only the re-arm period and can differ from the // counter rate on another board. A zero CNTFRQ would be a firmware bug; // guard the divide so a misconfigured board reports 0 instead of faulting. export fn uptime_seconds() u64 { const freq = get_sys_freq() if freq == 0 { return 0 } return get_sys_count() / freq } export fn generic_timer_init() void { setup_CNTP_CTL() next_deadline = get_sys_count() +% SYS_FREQ set_CNTP_CVAL(next_deadline) } export fn handle_generic_timer() void { next_deadline +%= SYS_FREQ // If the new deadline already lies in the past (the handler ran very // late), rebase from the current counter so the timer does not refire // immediately for every missed period in a burst. const now = get_sys_count() if (#as(i64, #bitCast(next_deadline -% now)) <= 0) { next_deadline = now +% SYS_FREQ } set_CNTP_CVAL(next_deadline) }