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
+106
View File
@@ -0,0 +1,106 @@
# FABRIC-4.md — forward-looking design notes
**Status:** Not a successor to `FABRIC-3.md` and not a punch-list document. `FABRIC-3.md` is
the current living document (bare metal boot, opened 2026-09-04, successor to the now-closed
`FABRIC-2.md`) — nothing here supersedes it, and this file doesn't follow the
close-and-carry-forward discipline `FABRIC-0.md` → `FABRIC-1.md` → `FABRIC-2.md` → `FABRIC-3.md`
used (that chain triggers on *closing* a document; `FABRIC-3.md` isn't closed). This is a
separate, lower-discipline scratchpad for design ideas caught early — theory-stage,
pre-punch-list, often missing a stated "why" on purpose (captured as raised, not fully
justified yet). An idea graduates out of here into `FABRIC-3.md` (or wherever the living
document is by then) once it has a real scope, not before.
---
## 1. Stadium-level "wheel" — a second clock alongside the heartbeat (2026-09-03)
Raised in conversation with Captain Bob, theory stage — no code, no scope, deliberately not
punch-listed yet.
**The mechanism.** A second clock, independent of but derived from the true heartbeat
(`HeartbeatState`, Loop #7), advanced by a fixed-point (Q48.16, this project's standard)
phase accumulator: each master heartbeat tick adds a `rate` scalar to the wheel's own
`phase`; whenever `phase` crosses `1.0` the wheel fires (advances its own state) and the
crossing remainder carries forward. This is a continuous scalar, not an integer tick-divisor
— `rate` can sit anywhere, including above `1.0`. A wheel with `rate > 1` fires more than
once per master tick (multiple threshold-crossings per tick, remainder still carried); one
with `rate < 1` accumulates across several master ticks before firing once. Nothing about the
mechanism privileges either side of `1.0` — "runs ahead of baseline" and "runs stretched
behind baseline" are the same math with a different scalar.
**What feeds it.** Not a new signal source — this wheel listens to two already-real, already-
built mechanisms: `rolling_window_of_truth.c` (Loop #2's circular-buffer execution history —
`rolling_window_measure_diversity()`, `rolling_window_find_hottest_word()`,
`rolling_window_count_transition()`, `rolling_window_pattern_capture_rate()`,
`rolling_window_is_warm()`) and `inference_engine.c` (Loops #5/#6 — `InferenceOutputs`:
decay-slope, variance-inflection point, fit quality). Both exist today at the **per-VM**
level (each patron/word-owning VM has its own rolling window and runs its own inference
pass) — the wheel itself is meant to live at the **Stadium** level, one layer up. This is a
genuinely new axis: nothing today aggregates RWOT/inference-engine output across the whole
Stadium floor into one fleet-wide signal. **Open, not decided:** what the aggregation actually
is (sum across patrons? mean? the hottest patron's own values only? something else) — flagged
in conversation, not resolved.
**What it's for.** Not a sieve itself. The wheel is a *scheduler/gate*: it decides *when* to
slip an appropriate sieve into a stack of sieves that doesn't exist yet (see §2 below) —
running its own clock, potentially ahead of the true heartbeat, is what buys it lead time to
notice a transition worth reacting to before the baseline heartbeat would get there on its
own. It is not itself the thing doing the sieving.
**Build sequencing, decided in conversation.** `rate` starts as a fixed build parameter (same
shape as `ACL-BASE-TTL`/`Q.SLOT` — a plain constant or Kconfig symbol, not adaptive from day
one). Reasoning, Captain Bob's own: a fixed rate is the only version that can go through this
project's existing DoE apparatus and produce a clean, attributable measurement (rate held
constant, everything else varies) — if the rate self-tunes from day one there's no fixed
baseline to compare "does the wheel actually help" against. Only *after* a real, measured
baseline exists does `rate` itself graduate to adaptive/self-tuning — reusing the same
inference-engine machinery the wheel already listens to, not new plumbing. This mirrors the
L8 Jacquard selector's own real history: hardcoded compudynamics constant first, later
replaced by the VM Fleet Attractor's dynamically-inferred rate
(`docs/working/architecture/VM-FLEET-ATTRACTOR-DESIGN-20260705.md`). Same graduation, one
level up the stack.
**Explicitly not decided yet, per Captain Bob ("we'll talk about why later"):**
- The actual *purpose* — why this wheel, what real problem it solves. Deferred on purpose.
- The sieve stack itself — what a "sieve" is concretely, what gets sieved, how many. A larger,
separate vision (128–256 wheels, each fed by an "appropriate" — possibly learned/neural,
vocabulary not yet resolved against anything this codebase currently has — source, forming
a coincidence-pattern sieve) was raised earlier in the same conversation as a *distinct*,
further-out idea; this single-wheel scheduler is deliberately scoped smaller and doesn't
presuppose that larger picture is right.
- Whether "trivial to add a wheel" (an earlier framing) still matters once there's only one —
a generic `{rate, phase, state}` table is a premature abstraction for a single instance;
build the one wheel concretely first, generalize only if/when a second one is actually
needed (this project's own standing convention — three similar lines beat a premature
abstraction).
## 2. A pinned GPIO VM per real-hardware board (2026-09-04)
Raised in conversation with Captain Bob during `FABRIC-3.md` §IV/§V's bare-metal-boot
planning — theory stage, no code, no scope, deliberately not punch-listed yet.
**The idea, as stated:** the Raspberry Pi 5 will need a special pinned VM dedicated to GPIO —
presumably following the same Tripod precedent Hermes/Artemis already established (a
permanently-pinned, born-at-boot VM with a fixed role, per `FABRIC-2.md` §H.1's "pinned
sessions never leave" model). Whether the Milk-V Mars needs the same treatment is explicitly
**not yet decided** — Captain Bob's own framing: "same for Milk-V (? not sure here)." Worth
noting the Mars does have its own 40-pin GPIO header (§VI), so the question isn't "does it
have GPIO" but whether it needs its own *dedicated pinned VM* for it the same way the Pi 5
does, or whether GPIO access there fits some other shape.
**Why a dedicated VM, not just a C driver, presumably (not yet confirmed as the actual
reasoning — inferred from the Tripod precedent, not stated outright):** GPIO is shared,
stateful hardware that multiple capsules/patrons might want to touch — the same category of
problem Hermes (messaging) and Artemis (block storage) already solve by being a dedicated,
addressable VM other patrons talk to, rather than a bare C primitive anyone can call directly.
**Explicitly not decided yet:**
- Whether Milk-V Mars needs the same pinned-VM treatment, or something else.
- What this VM's own vocabulary/interface would look like (a `GPIO-READ`/`GPIO-WRITE`-shaped
word set? something richer, given the Pi 5's GPIO header also muxes UART/SPI per §IV.1?).
- Whether this is one pinned VM per board doing double duty (GPIO + whatever else that board's
bring-up needs) or GPIO specifically warrants its own separate VM distinct from, say, a
future UART-handling VM.
- How this interacts with §IV.1's own still-open native-boot-flow work — a GPIO VM needs GPIO
register addresses from the DTB the same way the rest of that bring-up does, so this idea
isn't independent of that work, just downstream of it.