starkernel: converge the tick path and wire the adaptive heartbeat (item 0.8)
Introduces src/starkernel/heartbeat.c as the shared top/bottom-half implementation of heartbeat_init/tick/service/ticks/trust/state, replacing the per-architecture duplicates in amd64/riscv64/aarch64 timer.c. Each arch's timer.c now contributes only heartbeat_read_counter() (rdtsc / rdtime / CNTPCT_EL0). Per the GAP-A1 ruling the top half stays counter+ latch only; heartbeat_service() (called every REPL idle iteration, unconditionally per FABRIC.md's fidelity note) does the window/variance/ trust work outside interrupt context. vm_tick()'s call sites are unchanged -- the engine still runs on the virtual tick. Per FABRIC.md §26 (ruled 2026-08-03): wires Loop #7's execution-derived stable/volatile signal into the physical re-arm period. vm_runtime.c's existing Loop #7 site now calls heartbeat_set_adaptive_period_ns() with tick_target_ns ratio-rescaled onto a 10ms kernel base (not the hosted 10us HEARTBEAT_TICK_NS -- see §26.3 for the scale mismatch). Each architecture's re-arm function (apic_timer_rearm() on amd64/aarch64, riscv64_timer_rearm()) now converts heartbeat_next_period_ns() to its own raw counter units instead of a fixed constant; amd64 gained a rearm function it didn't previously need, since periodic-mode auto-reload never required one before this item. Verified: all three architectures build with no new warnings and boot cleanly to ok> with dict_hash=0x3d4e1daf289da94f, unchanged from the pre-change baseline -- no regression. Verified NOT achieved: live re-arm period variation under load. A temporary diagnostic (added and reverted) confirmed Loop #7 never actually fired during a live QEMU session -- a synthetic word-execution loop drove ~6,500 executions, past the 1000-tick inference frequency, without tripping vm_tick_inference_engine()'s pre-existing !vm->rolling_window.is_warm gate. That gate predates this item and was not investigated -- out of scope. FABRIC.md's Done-when is amended to record this honestly rather than claim it. Punch list §25 item 0.8 complete (per amended, weaker acceptance -- see the item's own annotation). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
da4cb14702
commit
3699be964d
@@ -154,11 +154,37 @@ const timer_calibration_record_t *timer_calibration_record(void);
|
||||
void heartbeat_init(uint64_t tsc_hz, uint64_t tick_hz);
|
||||
|
||||
/**
|
||||
* Called by heartbeat ISR on each tick.
|
||||
* Updates TIME-TICKS, samples TSC, updates rolling window and TIME-TRUST.
|
||||
* Top half. Called directly from each architecture's ISR (punch-list item
|
||||
* 0.8) -- one call site per architecture, unchanged from before this item.
|
||||
* Does exactly three things: reads the raw counter via
|
||||
* @c heartbeat_read_counter(), increments TIME-TICKS, and latches the
|
||||
* sample for @c heartbeat_service() to pick up. No window math, no
|
||||
* variance, no loops -- this must stay cheap enough for interrupt context.
|
||||
*/
|
||||
void heartbeat_tick(void);
|
||||
|
||||
/**
|
||||
* Bottom half (punch-list item 0.8). Services one pending sample if
|
||||
* @c heartbeat_tick() has latched one since the last call: computes the
|
||||
* inter-tick deviation, updates the rolling window, and (architecture
|
||||
* permitting -- see @c heartbeat.c) recomputes variance and TIME-TRUST.
|
||||
* Never runs in interrupt context. Call from the mainline, as frequently
|
||||
* as convenient -- a stale/skipped service call degrades the window's
|
||||
* fidelity but affects nothing else, since TIME-TRUST is diagnostic only
|
||||
* and never gates execution.
|
||||
*/
|
||||
void heartbeat_service(void);
|
||||
|
||||
/**
|
||||
* Read the raw hardware counter this architecture's heartbeat is paced
|
||||
* against -- the same clock @c timer_now_ns() and calibration already use
|
||||
* internally (rdtsc on amd64, the `time` CSR on riscv64, CNTPCT_EL0 on
|
||||
* aarch64), not a separate/different source. Implemented once per
|
||||
* architecture in that architecture's timer.c; consumed only by
|
||||
* @c heartbeat_tick() in the shared heartbeat.c.
|
||||
*/
|
||||
uint64_t heartbeat_read_counter(void);
|
||||
|
||||
/**
|
||||
* Get current TIME-TICKS (monotonic heartbeat count).
|
||||
*/
|
||||
@@ -174,4 +200,20 @@ time_trust_t heartbeat_trust(void);
|
||||
*/
|
||||
const TimeTrustState *heartbeat_state(void);
|
||||
|
||||
/**
|
||||
* Set the adaptive re-arm period, in nanoseconds (punch-list item 0.8,
|
||||
* FABRIC.md §26). Called from the mainline execution path only (Loop #7's
|
||||
* site in vm_runtime.c) -- never from interrupt context. Clamped to
|
||||
* [1/4x, 4x] of the kernel's base period internally; a caller need not
|
||||
* pre-clamp.
|
||||
*/
|
||||
void heartbeat_set_adaptive_period_ns(uint64_t ns);
|
||||
|
||||
/**
|
||||
* Read the period the next hardware re-arm should use. Called from
|
||||
* interrupt context by each architecture's re-arm function in place of a
|
||||
* fixed constant.
|
||||
*/
|
||||
uint64_t heartbeat_next_period_ns(void);
|
||||
|
||||
#endif /* STARKERNEL_TIMER_H */
|
||||
|
||||
Reference in New Issue
Block a user