feat(hal/core): implement heartbeat of iron (real-time SBI timer driver)

- Implemented RISC-V SBI timer driver in HAL (entry_riscv.zig).

- Integrated timer into the Harmonic Scheduler (kernel.nim/sched.nim).

- Re-enabled the Silence Doctrine: system now enters low-power WFI state during idle.

- Confirmed precise nanosecond wakeup and LwIP pump loop stability.

- Updated kernel version to v1.1.2.
This commit is contained in:
Markus Maiwald 2026-01-06 20:54:22 +01:00
parent 96ee0a0112
commit 77b4cb55c7
4 changed files with 55 additions and 28 deletions

View File

@ -62,12 +62,8 @@ proc main() =
if loop_count mod 1 == 0:
print(cstring("[INIT] Heartbeat\n"))
# Busy Wait Sleep (10ms) to bypass broken WFI/Timer
# TODO: Restore nanosleep once HAL Timer Driver is implemented
proc sys_now(): uint32 {.importc, cdecl.}
let start_sleep = sys_now()
while sys_now() - start_sleep < 10:
discard syscall(0x100, 0, 0, 0) # YIELD
# Sleep 10ms using Timer Driver (System Call)
discard syscall(0x65, 10000000'u64)
when isMainModule:
main()

View File

@ -19,6 +19,10 @@ const
MAX_FIBER_STACK* = 128 * 1024
SYSTABLE_BASE* = 0x83000000'u64
# Export Nim Timer Handler for HAL (Zig calls this)
proc rumpk_timer_handler() {.exportc, cdecl, used.} =
discard
# --- EXTERNAL SYMBOLS ---
proc ion_get_phys(id: uint16): uint64 {.importc, cdecl.}
proc ion_alloc_raw*(out_id: ptr uint16): uint64 {.importc, cdecl.}
@ -286,7 +290,7 @@ proc ion_fiber_entry() {.cdecl.} =
fiber_child.satp_value = mm_create_worker_map(cast[uint64](addr stack_child[0]), uint64(sizeof(stack_child)), SYSTABLE_BASE, cell_base, cell_size)
kprintln("[ION] Child fiber spawned successfully")
else: discard
fiber_sleep(100)
fiber_sleep(10_000_000) # 10ms
proc fiber_yield*() {.exportc, cdecl.} =
proc rumpk_yield_guard() {.importc, cdecl.}
@ -303,7 +307,7 @@ proc fiber_netswitch_entry() {.cdecl.} =
if chan_netswitch_rx.recv(pkt):
ion_free_raw(pkt.id)
else:
fiber_sleep(100)
fiber_sleep(10_000_000) # 10ms
fiber_yield()
proc ion_ingress*(id: uint16, len: uint16) {.exportc, cdecl.} =
@ -455,7 +459,7 @@ proc kmain() {.exportc, cdecl.} =
var next_mmio_addr {.importc: "virtio_pci_next_mmio_addr", nodecl.}: uint32
kprint("\n[Kernel] next_mmio_addr check: ")
kprint_hex(uint64(next_mmio_addr))
kprintln("\nNexus Sovereign Core v1.1.1 Starting...")
kprintln("\nNexus Sovereign Core v1.1.2 Starting...")
ion_pool_init()
proc mm_init() {.importc, cdecl.}
proc mm_enable_kernel_paging() {.importc, cdecl.}
@ -607,11 +611,15 @@ proc kmain() {.exportc, cdecl.} =
if not sched_tick_spectrum(active_fibers_arr.toOpenArray(0, 5)):
# The Silence Doctrine: Wait for Interrupt
let next_wake = sched_get_next_wakeup(active_fibers_arr.toOpenArray(0, 5))
if next_wake != 0xFFFFFFFFFFFFFFFF'u64:
proc sched_arm_timer(ns: uint64) {.importc, cdecl.}
# kprint("[Sleep] "); kprint_hex(next_wake); kprintln("")
sched_arm_timer(next_wake)
let now = sched_get_now_ns()
if next_wake > now and next_wake != 0xFFFFFFFFFFFFFFFF'u64:
proc rumpk_timer_set_ns(ns: uint64) {.importc, cdecl.}
# kprint("Interval: "); kprint_hex(next_wake - now); kprintln("")
rumpk_timer_set_ns(next_wake - now) # Pass interval
else:
# No timer needed (or overdue), just WFI for other interrupts (IO)
discard
asm "csrsi sstatus, 2"
asm "wfi"
# kprintln("[Wake]")

View File

@ -47,7 +47,7 @@ proc sched_get_now_ns*(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
proc fiber_can_run_on_channels*(id: uint64, mask: uint64): bool {.importc, cdecl.}
proc is_runnable(f: ptr FiberObject, now: uint64): bool =
if f == nil: return false
if f == nil or f.state.sp == 0: return false # Can only run initialized fibers
if now < f.sleep_until: return false
if f.is_blocked:
if fiber_can_run_on_channels(f.id, f.blocked_on_mask):

View File

@ -265,6 +265,9 @@ export fn rss_trap_handler(frame: *TrapFrame) void {
:
: [mask] "r" (@as(usize, 1 << 5)),
);
// Call Nim Handler
rumpk_timer_handler();
}
k_check_deferred_yield();
return;
@ -303,6 +306,7 @@ export var stack_bytes: [64 * 1024]u8 align(16) = undefined;
const hud = @import("hud.zig");
extern fn kmain() void;
extern fn NimMain() void;
extern fn rumpk_timer_handler() void;
export fn zig_entry() void {
uart.init_riscv();
@ -360,30 +364,49 @@ export fn rumpk_halt() noreturn {
}
}
export fn rumpk_timer_now_ns() u64 {
// RISC-V Time Constants
const TIMEBASE: u64 = 10_000_000; // QEMU 'virt' machine (10 MHz)
const SBI_TIME_EID: u64 = 0x54494D45;
fn rdtime() u64 {
var ticks: u64 = 0;
asm volatile ("rdtime %[ticks]"
: [ticks] "=r" (ticks),
);
// QEMU Virt machine is 10MHz -> 1 tick = 100ns
return ticks * 100;
return ticks;
}
export fn sched_arm_timer(deadline_ns: u64) void {
// 1 tick = 100ns (10MHz)
const deadline_ticks = deadline_ns / 100;
// Use SBI Time Extension (0x54494D45) to set timer
// FID=0: sbi_set_timer(stime_value)
fn sbi_set_timer(stime_value: u64) void {
asm volatile (
\\ ecall
:
: [arg0] "{a0}" (deadline_ticks),
[eid] "{a7}" (0x54494D45),
[fid] "{a6}" (0),
: [arg0] "{a0}" (stime_value),
[eid] "{a7}" (SBI_TIME_EID),
[fid] "{a6}" (0), // FID 0 = set_timer
: .{ .memory = true });
}
// Enable STIE (Supervisor Timer Interrupt Enable) in sie (bit 5)
export fn rumpk_timer_now_ns() u64 {
return rdtime() * 100; // 10MHz = 100ns/tick
}
export fn rumpk_timer_set_ns(interval_ns: u64) void {
if (interval_ns == std.math.maxInt(u64)) {
sbi_set_timer(std.math.maxInt(u64));
// Disable STIE
asm volatile ("csrc sie, %[mask]"
:
: [mask] "r" (@as(usize, 1 << 5)),
);
return;
}
const ticks = interval_ns / 100; // 100ns per tick for 10MHz
const now = rdtime();
const next_time = now + ticks;
sbi_set_timer(next_time);
// Enable STIE (Supervisor Timer Interrupt Enable)
asm volatile ("csrs sie, %[mask]"
:
: [mask] "r" (@as(usize, 1 << 5)),