Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
148 lines
7.3 KiB
C
148 lines
7.3 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
*/
|
||
|
||
/**
|
||
* capsule_vm_switch_signal.h - New, purpose-built "who runs next" signal
|
||
* for preemptive context switching (FABRIC-3.md §XXVIII, Stage 3,
|
||
* 2026-09-13).
|
||
*
|
||
* Deliberately NOT a repurposing of capsule_vm_physics.c's execution-heat
|
||
* engine -- that measures word-level dispatch fairness over millions of
|
||
* executions on a different timescale, and its own header explicitly
|
||
* documents it as never touched from interrupt context (unlocked, by
|
||
* design). This is a different physical quantity: instant-by-instant
|
||
* run-readiness, consulted from real ISR context (heartbeat_tick()) every
|
||
* timer tick.
|
||
*
|
||
* Concurrency discipline mirrors heartbeat.c's own §21.1-sanctioned
|
||
* pattern for heartbeat_next_period_ns(): single-writer-ISR (tick()) /
|
||
* single-reader-mainline (take_pending(), called from the cooperative
|
||
* checkpoint in execute_colon_word()), no lock, because nothing on this
|
||
* single hart is concurrent with the ISR while it runs.
|
||
*
|
||
* NOT truly interrupt-driven register/stack swapping (that was
|
||
* considered and rejected for this stage -- see FABRIC-3.md §XXVIII
|
||
* Stage 3 for why): the ISR only ever sets a flag. The actual switch
|
||
* (Stage 2's already-proven sk_vm_context_switch()) happens later, at a
|
||
* safe cooperative checkpoint on the mainline, once per word dispatch.
|
||
*
|
||
* Slot table is sized with headroom, not hardcoded to exactly today's 3
|
||
* participants (Hera/Hermes/Artemis) -- extending participation later
|
||
* (Stage 4+) is another sk_vm_switch_signal_register() call, not a
|
||
* redesign.
|
||
*
|
||
* FABRIC-3.6.md task 3.1 (2026-09-21, ruled B2 / FABRIC-3.5.md §XLV.2): the
|
||
* slot table is no longer a fixed SK_SWITCH_MAX_SLOTS=16 compile-time array.
|
||
* It is kmalloc'd at boot by sk_vm_switch_signal_boot_init(), sized from
|
||
* stadium_max_vm_count() -- the same RAM-derived population bound Stadium
|
||
* and session.c already use (session_boot_init() is the direct precedent
|
||
* mirrored here). Every switch-signal participant is a Stadium VM, so
|
||
* reusing that bound directly (rather than re-deriving a separate RAM
|
||
* budget) needs no new sizing formula.
|
||
*/
|
||
|
||
#ifndef STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H
|
||
#define STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H
|
||
|
||
#ifdef __STARKERNEL__
|
||
|
||
#include <stdint.h>
|
||
#include "starkernel/vm_uuid.h"
|
||
|
||
/* Boot-time allocation (FABRIC-3.6.md task 3.1, 2026-09-21): kmalloc's the
|
||
* slot table to stadium_max_vm_count() entries. Must run after
|
||
* stadium_boot_init() (that bound is 0, and this fails, until Stadium has
|
||
* computed it) and before the first sk_vm_switch_signal_register() call.
|
||
* Soft failure -- returns -1 and leaves the table unallocated (capacity 0,
|
||
* so register() below simply refuses every registration) rather than
|
||
* halting boot, same posture as stadium_boot_init()/session_boot_init().
|
||
* Idempotent-unsafe: calling twice leaks the first allocation, so callers
|
||
* must call it exactly once. */
|
||
int sk_vm_switch_signal_boot_init(void);
|
||
|
||
/* Register a VM as a switch-signal participant. Returns its slot index,
|
||
* or -1 if the slot table is full (or sk_vm_switch_signal_boot_init() was
|
||
* never called / failed). Call once per participating VM, after that VM is
|
||
* fully born (never mid-birth -- this stage has no critical-section
|
||
* protection against being switched away mid-setup). */
|
||
int sk_vm_switch_signal_register(VMUuid vm_id);
|
||
|
||
/* Remove a switch-signal participant (FABRIC-3.md §XXVIII Stage 4,
|
||
* 2026-09-14) -- Tripod VMs never need this (they live forever), but
|
||
* WIREBIND-birthed identity VMs cycle through attach/detach repeatedly
|
||
* and must free their slot for reuse, or the bounded table exhausts
|
||
* after sk_vm_switch_signal_slot_capacity() attach/detach cycles. Compacts the table
|
||
* (small, bounded, mutated only at attach/detach -- not a hot path).
|
||
* Clears a pending switch targeting this VM, if any, so the checkpoint
|
||
* never attempts to switch into a no-longer-registered participant.
|
||
* No-op (returns -1) if vm_id was never registered. */
|
||
int sk_vm_switch_signal_unregister(VMUuid vm_id);
|
||
|
||
/* Called from heartbeat_tick() (ISR context) every timer tick. Cheap:
|
||
* iterates only the registered slots (bounded, small). */
|
||
void sk_vm_switch_signal_tick(void);
|
||
|
||
/* Called from the cooperative checkpoint (execute_colon_word(), mainline,
|
||
* once per word dispatch). Returns the VMUuid of a VM that should now be
|
||
* switched to, or vm_uuid_none() if nothing is pending. Clears the
|
||
* pending flag as a side effect -- call at most once per checkpoint. */
|
||
VMUuid sk_vm_switch_signal_take_pending(void);
|
||
|
||
/* Call once, from the same checkpoint, immediately after a switch
|
||
* sk_vm_switch_signal_take_pending() requested actually executes (not if
|
||
* the target turned out invalid/self) -- feeds the DoE CSV counters
|
||
* below. Also resets `target_id`'s own readiness/has_work directly
|
||
* (Stage 3 follow-on correction, 2026-09-14) -- see the .c file's own doc
|
||
* comment on why this can't be left to tick()'s current-slot polling. */
|
||
void sk_vm_switch_signal_note_switch_performed(VMUuid target_id);
|
||
|
||
/* Message-arrival eligibility hook (FABRIC-3.md §XXVIII Stage 3 follow-on,
|
||
* 2026-09-14): mark that VM `vm_id` was just sent a message (the FORTH-side
|
||
* MSG-SEND hook in capsules/common/messaging.4th calls this via the new
|
||
* SWITCH-MARK-WORK primitive, mainline, single-writer). Consulted by
|
||
* sk_vm_switch_signal_tick()'s own readiness->pending decision so a VM
|
||
* with nothing recently sent to it never becomes a switch target purely by
|
||
* sitting idle long enough -- closes the wasteful (but, since the Stage 3
|
||
* stack-ownership fix, no longer corrupting) trampoline-bounce cycle for
|
||
* an idle participant. No-op for an unregistered vm_id. */
|
||
void sk_vm_switch_signal_mark_work(VMUuid vm_id);
|
||
|
||
/* DoE CSV read-only exposure (FABRIC-3.md §XXVIII Stage 3 follow-on,
|
||
* 2026-09-13) -- all of this state already existed for the switch
|
||
* decision itself; these just make it observable. */
|
||
uint64_t sk_vm_switch_signal_switch_count(void); /* cumulative, since boot */
|
||
uint32_t sk_vm_switch_signal_ticks_since_switch(void);
|
||
int sk_vm_switch_signal_current_slot(void); /* -1 = none/unregistered */
|
||
int sk_vm_switch_signal_slot_count(void);
|
||
uint32_t sk_vm_switch_signal_readiness(int slot); /* 0 if slot out of range */
|
||
uint32_t sk_vm_switch_signal_readiness_of(VMUuid vm_id); /* 0 if not registered */
|
||
|
||
/* FABRIC-3.6.md task 3.1 (2026-09-21): the table's boot-time-computed
|
||
* capacity (0 if sk_vm_switch_signal_boot_init() was never called or
|
||
* failed) -- the dynamic replacement for the old compile-time
|
||
* SK_SWITCH_MAX_SLOTS=16. */
|
||
int sk_vm_switch_signal_slot_capacity(void);
|
||
|
||
#endif /* __STARKERNEL__ */
|
||
|
||
#endif /* STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H */
|