From d1ad4f8d4cb91d789b35ea4f0827af78f25a9d76 Mon Sep 17 00:00:00 2001 From: Markus Maiwald Date: Tue, 6 Jan 2026 20:54:22 +0100 Subject: [PATCH] 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. --- apps/init/init.nim | 8 ++------ core/kernel.nim | 24 ++++++++++++++-------- core/sched.nim | 2 +- hal/entry_riscv.zig | 49 +++++++++++++++++++++++++++++++++------------ 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/apps/init/init.nim b/apps/init/init.nim index 5291422..828110e 100644 --- a/apps/init/init.nim +++ b/apps/init/init.nim @@ -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() diff --git a/core/kernel.nim b/core/kernel.nim index ffdd906..02d88ab 100644 --- a/core/kernel.nim +++ b/core/kernel.nim @@ -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]") diff --git a/core/sched.nim b/core/sched.nim index 536a545..d2f78de 100644 --- a/core/sched.nim +++ b/core/sched.nim @@ -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): diff --git a/hal/entry_riscv.zig b/hal/entry_riscv.zig index 73520f8..ec66323 100644 --- a/hal/entry_riscv.zig +++ b/hal/entry_riscv.zig @@ -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)),