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
+267
View File
@@ -0,0 +1,267 @@
/*
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.
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 SSM_JACQUARD_H
#define SSM_JACQUARD_H
#include <stdint.h>
#include "compudynamics.h"
#include "starforth_config.h"
/* ============================================================================
* SSM L8: Jacquard Mode Selector (Data-Driven Architecture)
* ============================================================================
*
* Based on 2^7 DoE with 300 reps (128 configurations, 38,400 total runs).
* Top 5% analysis reveals optimal loop combinations for speed + stability.
*
* Architecture (Experimentally Validated):
* - L1 (heat_tracking): DISABLED (harmful in 86% of top configs)
* - L4 (pipelining_metrics): DISABLED (harmful in 100% of top configs)
* - L7 (adaptive_heartrate): ALWAYS ON (beneficial in 71% of top configs)
* - L2, L3, L5, L6: Runtime-controlled by L8 (workload-dependent)
* - L8 (Jacquard): 4-bit selector (16 modes) controlling L2/L3/L5/L6
*
* L8 operates as a multi-dimensional classifier:
* L2 (window): ON if entropy > 0.75 (diversity tracking)
* L3 (decay): ON if temporal_decay > 0.5 (temporal locality)
* L5 (window_inf): ON if cv > 0.15 (variance adaptation)
* L6 (decay_inf): ON if cv > 0.15 AND temporal_decay > 0.3
*
* Top 5% Validated Modes:
* C4 (0100): L2=0, L3=1, L5=0, L6=0 - Temporal locality
* C7 (0111): L2=0, L3=1, L5=1, L6=1 - Full inference
* C9 (1001): L2=1, L3=0, L5=0, L6=1 - Diverse + decay_inf
* C11 (1011): L2=1, L3=0, L5=1, L6=1 - Diverse + inference
* C12 (1100): L2=1, L3=1, L5=0, L6=0 - Diverse + temporal
*/
/* ============================================================================
* L8 Mode Definitions (4-bit: L2/L3/L5/L6)
* ============================================================================
*/
typedef enum {
/* Bits: L2 L3 L5 L6 */
SSM_MODE_C0 = 0x0, /* 0000: Minimal (stable/predictable workloads) */
SSM_MODE_C1 = 0x1, /* 0001: L6 only (decay inference) */
SSM_MODE_C2 = 0x2, /* 0010: L5 only (window inference) */
SSM_MODE_C3 = 0x3, /* 0011: L5+L6 (volatile workloads) */
SSM_MODE_C4 = 0x4, /* 0100: L3 only (temporal locality) ✅ TOP 5% */
SSM_MODE_C5 = 0x5, /* 0101: L3+L6 (temporal + decay_inf) */
SSM_MODE_C6 = 0x6, /* 0110: L3+L5 (temporal + window_inf) */
SSM_MODE_C7 = 0x7, /* 0111: L3+L5+L6 (full inference) ✅ TOP 5% */
SSM_MODE_C8 = 0x8, /* 1000: L2 only (high diversity) */
SSM_MODE_C9 = 0x9, /* 1001: L2+L6 (diverse + decay_inf) ✅ TOP 5% */
SSM_MODE_C10 = 0xA, /* 1010: L2+L5 (diverse + window_inf) */
SSM_MODE_C11 = 0xB, /* 1011: L2+L5+L6 (diverse + inference) ✅ TOP 5% */
SSM_MODE_C12 = 0xC, /* 1100: L2+L3 (diverse + temporal) ✅ TOP 5% */
SSM_MODE_C13 = 0xD, /* 1101: L2+L3+L6 (complex workload) */
SSM_MODE_C14 = 0xE, /* 1110: L2+L3+L5 (full adaptive, no decay_inf) */
SSM_MODE_C15 = 0xF /* 1111: L2+L3+L5+L6 (full adaptive, all on) */
} ssm_l8_mode_t;
/* ============================================================================
* L8 Metrics (Input to Mode Selection)
* ============================================================================
*/
typedef struct {
double entropy; /* Rolling-window entropy/diversity (0.0-1.0) */
double cv; /* Coefficient of variation (short-term volatility) */
double temporal_decay; /* Temporal locality strength (0.0-1.0) */
double stability_score; /* Combined stability metric for hysteresis */
int inference_ran_this_tick; /* 1 if inference engine ran this tick */
int inference_early_exited; /* 1 if inference early-exited (ANOVA stable) */
} ssm_l8_metrics_t;
/* ============================================================================
* SSM Configuration (L1-L7 Mode Bits)
* ============================================================================
*
* L1/L4/L7 were runtime-inert until this field expansion: the adaptive
* table (SsmConfigTable, below) always selected across the full 128-config
* (7-bit) space, but ssm_apply_mode_from_table() and its per-tick scoring
* counterpart only ever wrote 4 of those 7 bits out here, so L1/L4/L7 were computed by
* the bandit's UCB selection and then silently discarded before reaching
* any real gate -- those three loops were governed purely by compile-time
* macros (ENABLE_HOTWORDS_CACHE, ENABLE_PIPELINING, HEARTBEAT_THREAD_ENABLED)
* for the whole process lifetime. Adding these fields, and wiring them at
* their four call sites alongside (not instead of) the compile-time macros,
* makes the bandit's full selection actually take effect. */
typedef struct {
int L1_heat_tracking; /* 1 = hotwords cache active, 0 = off */
int L2_rolling_window; /* 1 = window tracking active, 0 = off */
int L3_linear_decay; /* 1 = decay active, 0 = off */
int L4_pipelining; /* 1 = pipelining metrics active, 0 = off */
int L5_window_inference; /* 1 = window inference active, 0 = off */
int L6_decay_inference; /* 1 = decay inference active, 0 = off */
int L7_adaptive_heartrate; /* 1 = heartbeat thread active, 0 = off */
} ssm_config_t;
/* ============================================================================
* L8 Configuration Thresholds (Data-Driven from DoE)
* ============================================================================
* Defaults live in starforth_config.h (included above), the single source
* of truth for VM build-time defaults; overridable via -D exactly as before.
*/
/* ============================================================================
* Adaptive Table Constants (L8 Heat-Ranked 128-Config Selector)
* One clock only: the heartbeat tick (rev r). Scoring and UCB reselection
* both run every tick -- no derived multi-tick batching unit exists
* anywhere in this mechanism. The score/UCB/reward/weight constants live
* in cd_tuning_word() (compudynamics.h/.c) now, not here -- this is the
* word-level instantiation of the generic compudynamics module (rev s).
* ============================================================================
*/
/* ============================================================================
* 7-bit Config Bit Positions (b6=L1, b5=L2, b4=L3, b3=L4, b2=L5, b1=L6, b0=L7)
* ============================================================================
*/
#define SSM_CFG_L1 0x40u
#define SSM_CFG_L2 0x20u
#define SSM_CFG_L3 0x10u
#define SSM_CFG_L4 0x08u
#define SSM_CFG_L5 0x04u
#define SSM_CFG_L6 0x02u
#define SSM_CFG_L7 0x01u
/* ============================================================================
* L8 State (extended with adaptive table pointer)
* ============================================================================
*
* The adaptive table itself (CDConfigEntry/CDConfigTable) is the generic
* compudynamics bandit (compudynamics.h), sized to 128 configs x 8 regimes
* via cd_tuning_word() -- see ssm_l8_init_table().
*/
typedef struct {
ssm_l8_mode_t current_mode; /* Mirrored from table for diagnostics */
uint32_t hysteresis_counter; /* Legacy threshold mode hysteresis */
ssm_l8_mode_t pending_mode; /* Legacy threshold mode pending */
CDConfigTable *table; /* NULL = legacy threshold; non-NULL = adaptive */
} ssm_l8_state_t;
/* ============================================================================
* L8 API
* ============================================================================
*/
/**
* @brief Initialize L8 state (legacy fields only; call ssm_l8_init_table separately)
*/
void ssm_l8_init(ssm_l8_state_t *state, ssm_l8_mode_t initial_mode);
/**
* @brief Allocate and seed the adaptive config table
* Sets state->table; must be called after ssm_l8_init().
* On allocation failure, state->table remains NULL (legacy mode is used).
*/
void ssm_l8_init_table(ssm_l8_state_t *state);
/**
* @brief Free the adaptive config table
*/
void ssm_l8_free_table(ssm_l8_state_t *state);
/*
* @brief Per-tick update for the adaptive table path
*
* Classifies the current regime, scores the current config from this
* tick's outcome, and UCB-reselects -- every call, no batching -- and
* applies the selected config to ssm_config_t.
*
* The locality component of the regime classification (and of the
* joint-convergence reward signal) is computed here from recent_word_ids
* via cd_classify_ids() -- purely execution-count-derived, no wall-clock
* input. This replaced a decay-slope-derived temporal_decay signal (rev
* r) that made L8's regime classification wall-clock-tainted once it ran
* every tick instead of once per ~2000 ticks, breaking cross-architecture
* dict_hash determinism under QEMU/TCG's differing real emulation speed
* per ISA (rev s).
*
* @param state L8 state (must have state->table != NULL)
* @param metrics Current runtime metrics (entropy, cv, inference status)
* @param config SSM config updated this tick
* @param current_window VM's effective_window_size this tick
* @param recent_word_ids Recent word-execution-ID history, oldest first
* (e.g. from rolling_window_get_recent_sequence())
* @param recent_word_ids_count Number of valid entries in recent_word_ids
*/
void ssm_l8_update_table(ssm_l8_state_t *state, const ssm_l8_metrics_t *metrics,
ssm_config_t *config,
uint32_t current_window,
const uint32_t *recent_word_ids, uint32_t recent_word_ids_count);
/**
* @brief Apply the current table config to ssm_config_t
* Used on first tick to set the initial DoE-seeded config immediately.
*/
void ssm_apply_mode_from_table(const ssm_l8_state_t *state, ssm_config_t *config);
/**
* @brief Force the adaptive table onto an externally-chosen config
* (e.g. a DoE campaign driving a specific tick's config rather than
* letting the bandit's own UCB selection choose it). See definition for
* details on why this stays coherent with the bandit's own reward loop.
*/
void ssm_l8_force_config(ssm_l8_state_t *state, ssm_config_t *config, uint8_t config_idx);
/**
* @brief Update L8 state based on current metrics (legacy threshold path)
*/
void ssm_l8_update(const ssm_l8_metrics_t *metrics, ssm_l8_state_t *state);
/**
* @brief Apply L8 mode to SSM configuration (legacy threshold path)
*/
void ssm_apply_mode(const ssm_l8_state_t *state, ssm_config_t *config);
/**
* @brief Get human-readable mode name
*/
const char* ssm_l8_mode_name(ssm_l8_mode_t mode);
#endif /* SSM_JACQUARD_H */