Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure

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>
This commit is contained in:
rajames
2026-10-01 15:40:09 -04:00
co-authored by Junie
parent 3c709c115b
commit a8b70e88d3
630 changed files with 2646 additions and 2036 deletions
+240
View File
@@ -0,0 +1,240 @@
/*
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.
*/
#ifndef COMPUDYNAMICS_H
#define COMPUDYNAMICS_H
#include <stdint.h>
/* ============================================================================
* Compudynamics: Generic Entity-Agnostic Physics Engine
* ============================================================================
*
* Four operational levels observe and adapt to their own execution history:
* - words (dictionary entries, driven by ssm_jacquard.c) -- LIVE
* - VMs (Tripod fleet, driven by capsule_vm_physics.c) -- reserved
* - blocks (Artemis block storage) -- reserved
* - messages (Hermes event routing) -- reserved
*
* This module provides the entity-agnostic machinery shared by all four:
* a deterministic regime classifier over an entity-ID touch history, and a
* generic UCB1 bandit over a caller-sized config space. "Entity-ID" means
* whatever the level touches -- word_id for words, vm_id for VMs, block
* LBN for blocks, message type for messages.
*
* Determinism is the entire point of this module. Every input is an
* execution count or an entity-ID sequence -- never a wall-clock timestamp,
* never a duration. Two architectures executing the same instruction
* sequence at different real speeds (e.g. QEMU/TCG amd64 vs aarch64) must
* produce byte-identical classifier output. This was violated once
* (ssm_jacquard.c's temporal_decay input, derived from a wall-clock-tainted
* decay slope) and broke cross-architecture dict_hash parity; this module
* exists so that mistake has nowhere to hide the second time.
*
* Word-level and VM-level are the only two levels with real, instantiated
* CDTuning constants today (CD_TUNING_WORD, CD_TUNING_VM). Only word-level
* is actually wired through this module (ssm_jacquard.c); VM-level's own
* fleet mechanism (capsule_vm_physics.c, VMFleetWindow) is a separate,
* already-validated, already-accepted design that legitimately depends on
* real elapsed time for its rate-recovery math (fleet_transfer_slope_q48)
* and is out of scope here. CD_TUNING_VM documents what this module's
* classifier-side parameters would be if VM-level regime classification
* were ever migrated onto it; its bandit-side fields are zeroed and
* unused because VM level has no config-space bandit today. Block and
* message levels have no Artemis/Hermes compudynamics implementation yet
* -- CD_TUNING_BLOCK and CD_TUNING_MESSAGE are deliberately not defined
* here rather than filled with invented numbers.
* ============================================================================
*/
/* Hard cap on how many recent entity-ID touches cd_classify_ids() will look
* at. Callers pass a shorter slice via count; this cap bounds the classifier's
* O(depth^2) worst case and the stack scratch space it uses internally. */
#define CD_MAX_CLASSIFY_DEPTH 64u
/* ============================================================================
* Deterministic Regime Classification
* ============================================================================
*/
typedef struct {
uint32_t diversity_q16; /* distinct IDs in window / window size, Q16 [0,65536] */
uint32_t volatility_q16; /* dispersion (coefficient of variation) of per-ID
* touch counts within the window, Q16, unclamped
* above 65536 is possible and meaningful (high CV) */
uint32_t locality_q16; /* fraction of touches that repeat an ID seen within
* the preceding locality_lookback touches, Q16 [0,65536] */
} CDRegimeMetrics;
/**
* @brief Classify a recent entity-ID touch sequence into diversity/volatility/
* locality, purely from execution counts -- no wall-clock input anywhere.
*
* @param ids Recent entity IDs, oldest first, chronological order
* @param count Number of valid entries in ids (clamped internally
* to CD_MAX_CLASSIFY_DEPTH)
* @param locality_lookback How many preceding touches count as "recent" for
* the locality measure (clamped to count)
* @return Classified regime metrics; all-zero if count == 0
*/
CDRegimeMetrics cd_classify_ids(const uint32_t *ids, uint32_t count,
uint32_t locality_lookback);
/* ============================================================================
* Per-Level Tuning Knobs
* ============================================================================
*/
typedef struct {
/* --- Classifier knobs (used by cd_classify_ids callers) --- */
uint32_t id_window_depth; /* touches to classify over, <= CD_MAX_CLASSIFY_DEPTH */
uint32_t locality_lookback; /* recurrence lookback within id_window_depth */
uint32_t diversity_high_threshold_q16;
uint32_t volatility_high_threshold_q16;
uint32_t locality_high_threshold_q16;
/* --- Bandit knobs (used by cd_config_table_*); 0 where the level has
* no config-space bandit (e.g. VM level today -- see file header) --- */
uint32_t num_configs; /* size of the config space, e.g. 128 */
uint32_t num_regimes; /* size of the regime space, e.g. 8 */
uint32_t score_max;
uint32_t score_min;
uint32_t score_decay_factor_q16; /* per-tick exponential decay of benefit_score */
uint32_t ucb_k; /* UCB1 exploration coefficient */
uint32_t reward_gain; /* max per-tick score delta magnitude */
uint32_t joint_weight_q16; /* weight of window/locality joint-convergence
* signal in the combined reward, Q16 */
uint32_t anova_weight_q16; /* weight of the caller-supplied stability
* signal (e.g. ANOVA early-exit) in the
* combined reward, Q16 */
} CDTuning;
/* Tuning knobs are returned by value, not exposed as addressable globals.
* This freestanding kernel's boot-time loader does not reliably relocate
* the address of an extern const aggregate referenced from a different
* translation unit (confirmed by direct QEMU/gdb-style fault tracing:
* &CD_TUNING_WORD-style access faulted on an unrelocated pointer, while
* cross-TU function calls -- including struct-return-by-value, which
* uses a caller-local hidden pointer, never a global's address --
* worked correctly). Every existing scalar tunable elsewhere in this
* codebase already avoids this by being a #define immediate rather than
* a referenced global; these accessors are the aggregate-typed
* equivalent of that same constraint. */
/* Word level (ssm_jacquard.c): 128 configs (7-bit L1-L7), 8 regimes
* (3-bit diversity/volatility/locality). Values match the constants
* ssm_jacquard.c/.h used before this module existed -- this is a lift,
* not a retune. */
CDTuning cd_tuning_word(void);
/* VM level (capsule_vm_physics.c): classifier-side parameters only, for a
* future migration off Hera's word-level regime proxy. Not consumed by
* any call site today -- see file header. Bandit fields are zero: VM
* level modulates a continuous multiplier (fleet_transfer_slope_q48)
* rather than selecting from a discrete config space. */
CDTuning cd_tuning_vm(void);
/* ============================================================================
* Generic UCB1 Config-Space Bandit
* ============================================================================
*
* Stratified by regime: each (regime, config) pair has its own benefit
* score and tick count, so the bandit learns "best config per regime"
* rather than one global best. One clock only: cd_config_table_tick()
* scores the currently-active config and reselects on every call -- no
* derived multi-tick batching unit exists anywhere in this mechanism.
* ============================================================================
*/
typedef struct {
uint8_t config_bits;
uint8_t has_prev; /* 1 once prev_window/prev_locality_q16 hold real data */
uint8_t _pad[2];
uint32_t benefit_score;
uint32_t tick_count;
uint32_t prev_window;
uint32_t prev_locality_q16;
} CDConfigEntry;
typedef struct {
CDConfigEntry *entries; /* num_configs entries, malloc'd */
uint32_t *regime_scores; /* num_regimes * num_configs, row-major [regime][config] */
uint32_t *regime_ticks; /* same shape as regime_scores */
uint32_t *total_regime_ticks; /* num_regimes entries */
uint32_t num_configs;
uint32_t num_regimes;
uint8_t current_config;
uint8_t current_regime;
} CDConfigTable;
/**
* @brief Allocate a config table sized for tuning->num_configs / num_regimes.
* @return 0 on success, -1 on malloc failure (table left zeroed, unusable)
*/
int cd_config_table_init(CDConfigTable *table, const CDTuning *tuning);
/**
* @brief Free all memory owned by a config table. Safe on a zeroed table.
*/
void cd_config_table_free(CDConfigTable *table);
/**
* @brief Seed every (regime, config) score with initial_scores[config], and
* set the starting active config to initial_config.
* @param initial_scores Array of tuning->num_configs initial benefit scores
*/
void cd_config_table_seed(CDConfigTable *table, const CDTuning *tuning,
const uint32_t *initial_scores, uint8_t initial_config);
/**
* @brief Score the currently-active config from this tick's outcome and
* UCB1-reselect this tick's config, for the given regime.
*
* Combined reward = joint_weight * window/locality-convergence-stability
* + anova_weight * anova_stability_q16
* Joint-convergence is skipped (falls back to ANOVA-only) on a config's
* first tick, or when use_joint_convergence is false (the caller's config
* bits don't include loops whose convergence this signal is meant to
* track -- word level's L5/L6, see ssm_jacquard.c).
*
* @param table Config table (must be initialised)
* @param tuning Tuning knobs matching this table's sizing
* @param regime Current regime index, < tuning->num_regimes
* @param anova_stability_q16 Caller-supplied stability signal, Q16 [0,65536]
* @param use_joint_convergence Whether to include the window/locality signal
* @param current_window Caller's current window-width metric
* @param current_locality_q16 Caller's current locality_q16 from cd_classify_ids
* @return The newly-selected config's config_bits
*/
uint8_t cd_config_table_tick(CDConfigTable *table, const CDTuning *tuning,
uint8_t regime, uint32_t anova_stability_q16,
int use_joint_convergence,
uint32_t current_window, uint32_t current_locality_q16);
/**
* @brief Force the table onto an externally-chosen config (as if UCB1 had
* picked it). One-tick nudge, not a hold -- see ssm_l8_force_config().
*/
void cd_config_table_force(CDConfigTable *table, uint8_t config_idx);
#endif /* COMPUDYNAMICS_H */