Merge pull request #12 from blueflare-energy/docs-reposition

docs: reposition as the 86 Panel development environment
This commit is contained in:
2026-08-31 10:09:13 -04:00
committed by GitHub
28 changed files with 246 additions and 206 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# warden-sdk CI. # bfe-core1106-sdk CI.
# #
# Policy (mirrors flare-edge): only GitHub-owned actions get the repo token; the one # Policy (mirrors flare-edge): only GitHub-owned actions get the repo token; the one
# third-party helper (taiki-e/install-action) is pinned and never handed a token. # third-party helper (taiki-e/install-action) is pinned and never handed a token.
+105 -108
View File
@@ -1,137 +1,134 @@
# warden-sdk # bfe-core1106-sdk
[![ci](https://github.com/blueflare-energy/warden-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/blueflare-energy/warden-sdk/actions/workflows/ci.yml) [![ci](https://github.com/blueflare-energy/bfe-core1106-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/blueflare-energy/bfe-core1106-sdk/actions/workflows/ci.yml)
![Lines of code](.github/badges/loc.svg) ![Lines of code](.github/badges/loc.svg)
![Tests](.github/badges/tests.svg) ![Tests](.github/badges/tests.svg)
![Coverage](.github/badges/coverage.svg) ![Coverage](.github/badges/coverage.svg)
The build, driver, and simulation SDK for WardenOS (the Luckfox Pico 86-Panel / A modern, open development environment for the **Luckfox Pico 86 Panel**
RV1106 HMI). A from-scratch replacement for the twice-ported vendor stack (Rockchip RV1106): a current Linux kernel as a reviewable patch series, a
(Rockchip SDK → Luckfox SDK → our patched fork), built to the same standard as hermetic build, a QEMU device simulator, register-level hardware models, and
the rest of the firmware: tested, benchmarked, reproducible, and honest about MC/DC-hardened drivers (Modified Condition/Decision Coverage — the
what runs on real silicon versus what we simulate. avionics-grade test bar). It replaces the vendor stack — a ~2 GB, twice-forked
SDK pinned to Linux 5.10 — with tooling that is tested, benchmarked,
reproducible, and honest about what runs on real silicon versus what is
simulated.
> Status: **bringup.** The hardware **simulator** and its tests, the RV1106 kernel Originally built as the SDK for WardenOS (BlueFlare Energy's wall-panel
> forward-port as a reviewable `patches/` series, the hermetic kernel build, two firmware), but nothing here requires it: if you have an 86 Panel, this repo
> Tier-1 drivers at 100% MC/DC, and the **QEMU device sim** (`qemu/`, ADR-0006: gives you a modern kernel and a way to develop for the board without flashing
> boots the real kernel + real userspace on `-M virt` — check-in/OTA against the it on every change.
> mock portal, watchdog, RS485-to-sim bridge, 720x720 display + touch, all
> emulation-verified) are in. What remains before this is on the production build
> path: having flare-edge consume warden-sdk as a dependency (maintainer-gated).
> Until then, flare-edge still builds firmware from the vendored SDK +
> `sdk-patches/`.
## Why a new SDK ## Why
The vendored SDK is a ~2 GB opaque fork of a fork. Our real changes to it lived, The vendor SDK bakes in absolute paths, silently drops Kconfig options, and
until recently, as uncommitted edits in one working copy (`flare-edge/sdk-patches/` offers **no way to test hardware-dependent
is the tracked form). It bakes absolute paths, needs `python` (not python3), code off the device** — every change means flashing a panel. That is slow and
silently drops Kconfig options, and — the failure that motivated this repo — gives occasionally destructive: a coprocessor load address that collided with
us **no way to test hardware-dependent code off the device.** Every driver change unreserved kernel RAM bricked a bench unit, a mistake a static memory-map
had to be validated by flashing a panel. That is slow, and it is dangerous: it is check would have caught before any flash (`tools/config-lint` now is that
how a boot-loaded-watchdog change bricked a bench unit (the load address collided check). This SDK exists so the board is buildable, testable, and hardenable
with unreserved kernel RAM — a mistake a target-config check or a memory-map model **without a panel in the loop**, on a maintained kernel.
would have caught before any flash).
The SDK's job is to make the firmware **buildable, testable, and hardenable ## What Works
without a panel in the loop**, and to move us onto a modern, maintained kernel.
## Goals (from future-features) A self-built **Linux 6.18.46**, forward-ported from the vendor 5.10.160 tree
as a subsystem-split patch series (`patches/`) and hardware-verified on a
bench panel: clk, pinctrl, eMMC, GMAC, TRNG, OTP, SARADC/TSADC, RTC, USB
host, PWM/backlight, VOP display, GT911 touch, AIC8800 wifi, RGA, I2S audio,
the HPMCU (RISC-V watchdog coprocessor) mailbox, the open NPU driver, and
PVTM. Mainline alone was not
viable (no RV1106 device tree, clock, display, RGA, NPU, or flash-boot support
upstream); see `docs/decisions/0001-kernel-base.md`.
1. **Modern kernel.** A self-built **Linux 6.18.46**, forward-ported directly from ## Quick Start
the vendor 5.10.160 tree (no plan44/OpenWrt code) on our current Buildroot LTS
(2025.02.x). This is **done and hardware-verified on `warden-c8a3`**: essentially
every RV1106 block the 86-Panel uses boots and works — clk, pinctrl, eMMC, GMAC,
TRNG, OTP, SARADC/TSADC, RTC, USB host, PWM/backlight, **VOP display**, **GT911
touch**, **AIC8800 wifi**, **RGA**, **I2S audio**, **HPMCU mailbox**, the **open
NPU driver**, and **PVTM**. Mainline was not viable (no DT/clk/display/RGA/NPU/
flash-boot upstream for RV1106); the direct 5.10→6.18 forward-port reuses the
already-in-mainline rv1126 register data where it matches and carries our deltas
as a reviewable patch series (`patches/`).
2. **Ported, hardened drivers → 100% MC/DC on the code we own.** "100% MC/DC on
100% of drivers" is infeasible as literally stated: ~97% of driver LOC is
vendor blobs (the AIC8800 wifi driver alone is 88.5K lines). So the target is
**tiered**: real MC/DC on *our* hardware code (modbus master, relays, RGA
wrapper, HPMCU supervisor, devmem/reset ladder); fault-injection + branch
hardening for the vendor blobs behind a stable seam.
3. **A proper simulator.** Simulate the hardware the vendor SDK cannot: **RGA**
(2D blitter), the **RISC-V HPMCU** coprocessor, and the **NPU** — plus the
register/SRAM (`/dev/mem`) and sysfs surfaces the drivers touch — so driver and
supervisor logic runs and is tested on the host, in CI, with no panel.
4. **Its own repo, held to firmware standards.** Tests, benchmarks, reproducible
builds, CI. This repo.
## Architecture — one seam, two backends Requirements: `gcc-arm-linux-gnueabihf`, `qemu-system-arm`, `curl`, `cpio`,
`mkfs.ext4`, a bare `python` on PATH (Debian/Ubuntu: `python-is-python3`),
gcc >= 14 (for the MC/DC gate), and Rust (for the simulators' tests).
The organizing idea is a thin **Hardware Abstraction Seam** per hardware block. ```sh
Firmware code talks to the seam (a trait in Rust, a function table in C); the seam # 1. Build the kernel: fetch pinned pristine 6.18.46, apply patches/, emit
has two backends: # zImage + rv1106-warden.dtb. WORK must sit outside any git checkout.
WORK=$HOME/kbuild-out CROSS_COMPILE=arm-linux-gnueabihf- bash build/build-kernel.sh
``` # 2. Boot it in the QEMU device simulator (no hardware needed):
firmware / driver logic bash qemu/mkinitramfs.sh
bash qemu/mkimage.sh
Hardware Abstraction Seam (devmem, hpmcu, rga, npu, modbus, gpio) bash qemu/run.sh --kernel $HOME/kbuild-out/linux-6.18.46/arch/arm/boot/zImage --shell
┌────┴────┐
real backend sim backend # 3. Run the test suites:
(/dev/mem, ioctl, (software model, for d in sim tools/config-lint qemu/rs485-bridge; do
/proc, serial) host-testable) (cd "$d" && cargo test)
done
for d in drivers/*/test; do make -C "$d" check; done # 100% MC/DC gate (gcc >= 14)
``` ```
- **On-device**, the seam binds the real backend (mmap `/dev/mem`, `librga` The kernel variant with the simulator's extra devices (PCI serial, watchdog,
ioctls, the serial port, `/proc/rknpu`). WireGuard, display) adds one env var to step 1:
- **On the host**, it binds the **sim backend** — a faithful software model of the `WARDEN_KCONFIG_FRAGMENT=qemu/configs/virt.fragment`. See `qemu/README.md`
block. The HPMCU sim, for example, runs the SCR1 watchdog firmware's exact state for the scenario tests (portal, OTA apply, display + touch, watchdog).
machine (boot-grace, heartbeat-timeout, fire) against an in-memory mailbox, so
the flared supervisor's arm/beat protocol is exercised end-to-end in a unit test.
The seam is the same object the driver-hardening effort measures MC/DC against,
and the same object the simulator implements — so the two goals reinforce rather
than duplicate each other.
## Layout ## Layout
``` ```
sim/ the hardware simulator (Rust): membus/devmem, HPMCU, CRU, Modbus, RGA, NPU. patches/ the RV1106 forward-port onto pristine linux-6.18.46 (subsystem-split)
qemu/ the device simulator (ADR-0006): QEMU -M virt boots the real kernel and build/ the hermetic kernel build (fetch pinned source -> apply patches -> zImage + dtb)
real userspace; A/B disk layout, RS485 bridge into sim/, scenario tests. qemu/ the device simulator: QEMU -M virt boots the real kernel and real userspace;
drivers/ our own hardened drivers + their seams (relays, freshness; more migrate in). A/B disk layout, RS485 bridge into sim/, scenario tests
patches/ the RV1106 kernel forward-port delta onto pristine linux-6.18.46 (subsystem-split). sim/ register-level hardware models (Rust): membus, HPMCU, CRU, Modbus, RGA, NPU
kernel/ forward-port docs + provenance (rv1106-enablement/, PROVENANCE.md). drivers/ hardened hardware-facing drivers with HAL seams and 100% MC/DC harnesses
build/ the hermetic kernel build (fetch pristine → apply patches → zImage + dtb). kernel/ forward-port provenance and bring-up records (point-in-time; patches/ is canonical)
docs/ architecture + ADRs (decisions/) + ci-cd + generated workflow flowcharts. tools/ config-lint (static memory-map gates) and dev tooling
tools/ dev tooling. config-lint: static target-config gates (MCU-load-vs-reserved-memory, docs/ architecture, ADRs (decisions/), CI/CD
the 0x40000 brick class); flowgen: the workflow-flowchart generator.
.github/ CI (workflows/ci.yml): patches-apply, host tests, coverage, MC/DC, benchmarks, badges.
``` ```
## Architecture
One thin **hardware abstraction seam** per block (a trait in Rust, a function
table in C). Firmware logic talks to the seam; the seam binds a real backend
on the device (`/dev/mem`, ioctls, serial, `/proc`) or a simulated backend on
the host. The same seam is what the driver-hardening effort measures MC/DC
against and what the simulator implements, so the two reinforce rather than
duplicate each other. Full detail: `docs/architecture.md`.
Three simulators, by design not one:
| Simulator | What it runs | What it proves |
|---|---|---|
| `sim/` | register-level Rust models | driver and supervisor logic, with fault injection |
| `qemu/` | the real kernel + real userspace on `-M virt` | boot, init, daemons, networking, OTA, watchdog, display + touch |
| `lvglsim` (downstream) | the LVGL UI on SDL | rendering and UI flows |
The device simulator covers the UI as well: with the production UI binary in
`qemu/payload/`, `run.sh --display on` opens the panel's 720x720 screen in a
window with mouse clicks landing as touch — device and UI in one VM
(headless + scripted: `qemu/tests/ui-shot.sh`).
Boots and passes under emulation are never treated as on-silicon evidence;
the simulators narrow which claims need a panel, they do not replace it.
## Principles ## Principles
Evaluated against the stack philosophy — **openness, hardness, modernness**: - **Open**: open tools over closed ones (`rkdeveloptool`, source-built
components, an open simulator); GPL-2.0-only.
- **Hard**: every seam has a fault-injection path — recovery code is tested
against failure, not just success.
- **Modern**: the newest kernel the hardware can run, current toolchains,
Rust for new host-testable code, reproducible builds.
- **Open** over closed where we can: `rkdeveloptool` over the closed `upgrade_tool`; ## Downstream
source-buildable `librga` over blobs where a source path exists; the simulator is
fully open and ours.
- **Hard**: every seam has a fault-injection path (a wedged SDIO link, a stalled
MCU, an RGA timeout) so recovery code is tested against failure, not just success.
On-device claims still need on-device evidence; the sim narrows *which* claims
need a panel, it does not replace that rule.
- **Modern**: newest kernel we can actually run; current Buildroot LTS; Rust for new
host-testable code; reproducible builds.
## Relationship to flare-edge WardenOS (the 86 Panel firmware this SDK was born for) consumes this repo
from its own private repo, flare-edge; issue references and checkout paths
flare-edge (WardenOS: the LVGL UI + the `flared` daemon) is the product; warden-sdk pointing there are context, not reachable links. The QEMU simulator runs its
is what builds and tests it. flare-edge is BlueFlare's private companion repo — production binaries unmodified — including real over-the-air updates against
not publicly available — so flare-edge issue references and checkout paths in a mock portal.
this repo's docs are context, not reachable links. During bootstrap, flare-edge consumes warden-sdk piece
by piece: first the simulator (as a dev/test dependency), later the image build.
No flare-edge code moves here — only the SDK/build/sim/driver-seam layer.
## License ## License
**GPL-2.0-only**, repo-wide (see `LICENSE`; a per-file SPDX identifier governs **GPL-2.0-only**, repo-wide (see `LICENSE`; a per-file SPDX identifier
where one is present, e.g. a few GPL-2.0-or-later kernel files). The kernel governs where present). The kernel material in `patches/` and `kernel/` is
material in `patches/` and `kernel/rv1106-enablement/` is derivative of the derivative of the Linux kernel and GPL-2.0 vendor code; per-driver origin is
Linux kernel and of GPL-2.0 vendor code either way — per-driver origin and tracked in `kernel/rv1106-enablement/PROVENANCE.md`. Contributions are
license are tracked in `kernel/rv1106-enablement/PROVENANCE.md`. Contributions accepted under the same license (inbound = outbound).
are accepted under the same license (inbound = outbound).
+19 -17
View File
@@ -1,10 +1,12 @@
# warden-sdk architecture # Architecture
How the SDK makes WardenOS buildable, testable, and hardenable without a panel in How the SDK makes the 86 Panel buildable, testable, and hardenable without a
the loop. Grounded in a full survey of the current flare-edge firmware (the seam board in the loop. The seam inventory below comes from a full survey of the
inventory below is from that survey, not aspiration). downstream WardenOS firmware — the SDK's first consumer — not aspiration; the
file paths in it point into that (private) codebase and serve as engineering
context.
## 1. The problem the seams solve ## 1. The Problem
The firmware touches RV1106 hardware through a *grab-bag* of mechanisms, each The firmware touches RV1106 hardware through a *grab-bag* of mechanisms, each
tested (or not) differently. Today: tested (or not) differently. Today:
@@ -26,7 +28,7 @@ socket, misc dev, cmdline, hpmcu fw), and **fails-soft-because-the-path-is-absen
"relay 1 is ON" or "NPU at 80%", only "absent". The SDK's job is to turn all of "relay 1 is ON" or "NPU at 80%", only "absent". The SDK's job is to turn all of
these into **one deliberate seam per block** with a real backend and a sim backend. these into **one deliberate seam per block** with a real backend and a sim backend.
## 2. The seam taxonomy ## 2. Seam Taxonomy
Two seam kinds cover everything above: Two seam kinds cover everything above:
@@ -47,7 +49,7 @@ backend is "a fake `improcess` that records the blits it was asked to do", swapp
behind the same `#if`, so the offload *dispatch* logic gets tested even though the behind the same `#if`, so the offload *dispatch* logic gets tested even though the
blit itself is modelled. blit itself is modelled.
## 3. The simulator (`sim/`) ## 3. The Register Simulator
A host Rust library modelling the hardware the vendor SDK cannot, so driver and A host Rust library modelling the hardware the vendor SDK cannot, so driver and
supervisor logic runs in CI with no panel. supervisor logic runs in CI with no panel.
@@ -65,7 +67,7 @@ supervisor logic runs in CI with no panel.
the boot-mode register's survives-warm-reset / cleared-by-POR behaviour (the MaskRom the boot-mode register's survives-warm-reset / cleared-by-POR behaviour (the MaskRom
recovery maneuver). The matching firmware-side `Bus` seam on flared's `devmem` — so recovery maneuver). The matching firmware-side `Bus` seam on flared's `devmem` — so
the shipped ladder can be asserted to poke the confirmed offset, never the wrong-SoC the shipped ladder can be asserted to poke the confirmed offset, never the wrong-SoC
one — lands when flare-edge consumes warden-sdk (§8 item 3, maintainer-gated), not yet on one — lands when flare-edge consumes this repo (§8 item 3, maintainer-gated), not yet on
flare-edge `main`. flare-edge `main`.
- **`modbus` — RS-485 device end.** Done. `ModbusSlave`: a byte-in/byte-out RTU slave - **`modbus` — RS-485 device end.** Done. `ModbusSlave`: a byte-in/byte-out RTU slave
(CRC16 byte-identical to the master, FC 0x010x06/0x0F/0x10/0x11, exception replies, (CRC16 byte-identical to the master, FC 0x010x06/0x0F/0x10/0x11, exception replies,
@@ -87,17 +89,17 @@ supervisor logic runs in CI with no panel.
Integration with flare-edge: flared implements `MemBus` for `/dev/mem` and gains Integration with flare-edge: flared implements `MemBus` for `/dev/mem` and gains
`#[cfg(test)]` tests driving its real arm/beat logic against `HpmcuSim`. This needs `#[cfg(test)]` tests driving its real arm/beat logic against `HpmcuSim`. This needs
warden-sdk reachable as a Cargo dependency in CI — i.e. a remote for this repo, this repo reachable as a Cargo dependency in CI — i.e. a remote for this repo,
which is a **maintainer go-ahead item** (credential/remote creation). Until then the which is a **maintainer go-ahead item** (credential/remote creation). Until then the
firmware-side seam and a local test double land in flare-edge, unified with `sim/` firmware-side seam and a local test double land in flare-edge, unified with `sim/`
once the dependency exists. No duplication of *logic* — only the tiny trait. once the dependency exists. No duplication of *logic* — only the tiny trait.
## 4. Driver hardening (the "port + harden to MC/DC" goal) ## 4. Driver Hardening
"100% MC/DC on 100% of drivers" is infeasible literally: ~97% of driver LOC is "100% MC/DC on 100% of drivers" is infeasible literally: ~97% of driver LOC is
vendor blobs (AIC8800 wifi = 88.5K lines). Tiered target: vendor blobs (AIC8800 wifi = 88.5K lines). Tiered target:
- **Tier 1 — our own hardware code → real MC/DC.** Method: the proven `tests/uboot-ab` - **Tier 1 — our own hardware code → real MC/DC.** Method: the proven flare-edge `tests/uboot-ab`
pattern — extract the unit behind a small injectable seam, mock its world, build pattern — extract the unit behind a small injectable seam, mock its world, build
`-fcondition-coverage`, enforce with the shared `drivers/enforce-mcdc.sh` (gcc-14 `-fcondition-coverage`, enforce with the shared `drivers/enforce-mcdc.sh` (gcc-14
`gcov --conditions`) in the CI `mcdc` job. **Done here now:** `relays.c` (40/40 `gcov --conditions`) in the CI `mcdc` job. **Done here now:** `relays.c` (40/40
@@ -116,13 +118,13 @@ Every seam gets a fault-injection mode (a wedged SDIO link, a stalled MCU
heartbeat, an RGA timeout, a GPIO write EIO) so recovery code is tested against heartbeat, an RGA timeout, a GPIO write EIO) so recovery code is tested against
failure, not just the happy path. failure, not just the happy path.
## 5. Target-config checks (a class the sim cannot cover) ## 5. Target-Config Checks
The brick was a *memory-map* fault: the boot-loaded MCU's load address (`0x40000`) The brick was a *memory-map* fault: the boot-loaded MCU's load address (`0x40000`)
is a reserved carve-out on Thunder-Boot boards but plain kernel RAM on ours. No is a reserved carve-out on Thunder-Boot boards but plain kernel RAM on ours. No
behavioural sim catches that — it needs a **static check against the target DT**: behavioural sim catches that — it needs a **static check against the target DT**:
"every address the MCU/coprocessor code loads to is inside a `reserved-memory` "every address the MCU/coprocessor code loads to is inside a `reserved-memory`
node." warden-sdk owns these config-lint checks (idblock loader `.ini` vs DT node." This repo owns these config-lint checks (idblock loader `.ini` vs DT
reservations, partition table vs image sizes, vermagic vs kernel) as CI gates, so a reservations, partition table vs image sizes, vermagic vs kernel) as CI gates, so a
mistake is caught before a flash rather than on the bench. mistake is caught before a flash rather than on the bench.
@@ -136,7 +138,7 @@ fails against a DT with no `rtos@40000` node and passes once the reservation is
added. **Next** target-config checks: partition-table-vs-image-size and added. **Next** target-config checks: partition-table-vs-image-size and
vermagic-vs-kernel. vermagic-vs-kernel.
## 6. Kernel forward-port (done — see ADR-0001) ## 6. Kernel Forward-Port
A self-built **Linux 6.18.46**, forward-ported directly from the vendor 5.10.160 tree A self-built **Linux 6.18.46**, forward-ported directly from the vendor 5.10.160 tree
onto our Buildroot LTS/uClibc base — **not** the plan44/OpenWrt 6.6 fork this section onto our Buildroot LTS/uClibc base — **not** the plan44/OpenWrt 6.6 fork this section
@@ -154,7 +156,7 @@ kernel move as one matched boot+oem image, never a partial reflash.
series honest against pristine 6.18.46; provenance is in `patches/README.md` and series honest against pristine 6.18.46; provenance is in `patches/README.md` and
`kernel/rv1106-enablement/`. `kernel/rv1106-enablement/`.
## 7. Device emulation (`qemu/`) — see ADR-0006 ## 7. Device Emulation
The third simulator, deliberately not named "sim": a QEMU VM (`-M virt,highmem=off`, The third simulator, deliberately not named "sim": a QEMU VM (`-M virt,highmem=off`,
one Cortex-A7, 256M — the RV1106G3's shape) that boots the real forward-ported one Cortex-A7, 256M — the RV1106G3's shape) that boots the real forward-ported
@@ -178,14 +180,14 @@ development* stays `lvglsim`; the VM is where processes, the kernel, and the
network meet. §5 still applies — no behavioural sim, this one included, catches network meet. §5 still applies — no behavioural sim, this one included, catches
memory-map faults; and "boots under emulation" is never on-silicon evidence. memory-map faults; and "boots under emulation" is never on-silicon evidence.
## 8. Order of work ## 8. Order of Work
1. **Simulator core**`membus`, `hpmcu`, the `cru` reset ladder, `modbus`, plus the 1. **Simulator core**`membus`, `hpmcu`, the `cru` reset ladder, `modbus`, plus the
`rga`/`npu` models. **Done.** `rga`/`npu` models. **Done.**
2. **C-driver MC/DC harnesses**`relays.c` and `freshness.c` at 100% MC/DC, CI-gated 2. **C-driver MC/DC harnesses**`relays.c` and `freshness.c` at 100% MC/DC, CI-gated
via the shared `drivers/enforce-mcdc.sh`. **Done** (the first C coverage gate). via the shared `drivers/enforce-mcdc.sh`. **Done** (the first C coverage gate).
3. **flared devmem/hpmcu seam + tests** — firmware-side trait, unified with `sim/` 3. **flared devmem/hpmcu seam + tests** — firmware-side trait, unified with `sim/`
once flare-edge consumes warden-sdk (a separate, maintainer-gated step). **Pending.** once flare-edge consumes this repo (a separate, maintainer-gated step). **Pending.**
4. **Config-lint CI gates** (§5) — the brick-class of bug. **Done.** 4. **Config-lint CI gates** (§5) — the brick-class of bug. **Done.**
5. **Hermetic kernel build** (`build/build-kernel.sh` + the `patches-apply` gate). **Done.** 5. **Hermetic kernel build** (`build/build-kernel.sh` + the `patches-apply` gate). **Done.**
6. **Kernel 5.10→6.18.46 forward-port** (§6, ADR-0001). **Done** (hardware-verified). 6. **Kernel 5.10→6.18.46 forward-port** (§6, ADR-0001). **Done** (hardware-verified).
+1 -1
View File
@@ -18,7 +18,7 @@ execute code on private infrastructure (ADR-0007).
| `prune-artifacts` | ubuntu-latest, dispatch-only | Deletes `kernel-rv1106` artifacts beyond the newest 3. | | `prune-artifacts` | ubuntu-latest, dispatch-only | Deletes `kernel-rv1106` artifacts beyond the newest 3. |
| `badges` | ubuntu-latest | Renders loc/tests/coverage shields on push to `main` (`[skip ci]` + `paths-ignore` loop guard). | | `badges` | ubuntu-latest | Renders loc/tests/coverage shields on push to `main` (`[skip ci]` + `paths-ignore` loop guard). |
## History: the self-hosted runner (retired) ## Runner History
`kernel-build` originally ran on a repo-scoped self-hosted runner (ADR-0004, `kernel-build` originally ran on a repo-scoped self-hosted runner (ADR-0004,
2026-08-25, verified end-to-end) because hosted minutes were metered on the 2026-08-25, verified end-to-end) because hosted minutes were metered on the
+1 -1
View File
@@ -1,4 +1,4 @@
# ADR 0001 — Kernel base: direct 5.10→6.18 forward-port # ADR 0001 — Kernel Forward-Port
**Status:** Accepted (2026-08-25). Supersedes the README's original plan44/6.6 goal. **Status:** Accepted (2026-08-25). Supersedes the README's original plan44/6.6 goal.
+1 -1
View File
@@ -1,4 +1,4 @@
# ADR 0002 — Tiered MC/DC coverage policy # ADR 0002 — Tiered MC/DC
**Status:** Accepted (2026-08-25). **Status:** Accepted (2026-08-25).
+3 -3
View File
@@ -1,4 +1,4 @@
# ADR 0003 — warden-sdk is a standalone repo # ADR 0003 — Standalone Repo
**Status:** Accepted (2026-08-25). Repo-visibility half superseded by ADR-0007 **Status:** Accepted (2026-08-25). Repo-visibility half superseded by ADR-0007
(2026-08-30) — warden-sdk went public; the "private for now" consequence below (2026-08-30) — warden-sdk went public; the "private for now" consequence below
@@ -10,13 +10,13 @@ no CI, tests, or versioning of their own. The SDK requirement (future-features-2
§SDK) calls for "its own repo, held to firmware standards." §SDK) calls for "its own repo, held to firmware standards."
## Decision ## Decision
A **private** `warden-sdk` GitHub repo (now `blueflare-energy/warden-sdk` and public A **private** `warden-sdk` GitHub repo (now `blueflare-energy/bfe-core1106-sdk` and public
per ADR-0007), standalone from day one with its own per ADR-0007), standalone from day one with its own
CI/versioning. Work lands on a `bringup` branch; the first commit to `main` is gated CI/versioning. Work lands on a `bringup` branch; the first commit to `main` is gated
on a passing review run, green CI, and the maintainer's fresh explicit go-ahead. on a passing review run, green CI, and the maintainer's fresh explicit go-ahead.
## Consequences ## Consequences
- flare-edge consumes warden-sdk later (flared depending on `warden-sim`, drivers - flare-edge consumes this repo later (flared depending on `warden-sim`, drivers
built from here) — a separate, maintainer-gated integration step; flare-edge is not built from here) — a separate, maintainer-gated integration step; flare-edge is not
edited by the SDK-completion effort. edited by the SDK-completion effort.
- Private for now (references bench devices / in-progress hardening); can be opened - Private for now (references bench devices / in-progress hardening); can be opened
+1 -1
View File
@@ -1,4 +1,4 @@
# ADR 0004 — CI/CD runner: 3rd repo-scoped self-hosted runner on 0640 # ADR 0004 — Self-Hosted Runner
**Status:** Superseded in part by ADR-0007 (2026-08-30) — `kernel-build` moved **Status:** Superseded in part by ADR-0007 (2026-08-30) — `kernel-build` moved
to GitHub-hosted runners for the public repo and the self-hosted registration to GitHub-hosted runners for the public repo and the self-hosted registration
@@ -1,4 +1,4 @@
# ADR 0005 — Hardened drivers live in warden-sdk/drivers/ # ADR 0005 — Driver Source of Truth
**Status:** Accepted (2026-08-25). **Status:** Accepted (2026-08-25).
@@ -9,9 +9,9 @@ currently lives in flare-edge. The kernel driver source lives in an un-versioned
scratch tree (`flare-edge/research/linux-6.18.46/`). scratch tree (`flare-edge/research/linux-6.18.46/`).
## Decision ## Decision
Bring **hardened copies into `warden-sdk/drivers/`** as the canonical source-of-truth, Bring **hardened copies into this repo's `drivers/`** as the canonical source-of-truth,
each with its HAL seam and a 100% MC/DC host harness. The RV1106 kernel deltas are each with its HAL seam and a 100% MC/DC host harness. The RV1106 kernel deltas are
formalized as a patch series in `patches/`. flare-edge consumes warden-sdk later formalized as a patch series in `patches/`. flare-edge consumes this repo later
(separate, maintainer-gated step). (separate, maintainer-gated step).
## Consequences ## Consequences
+1 -1
View File
@@ -1,4 +1,4 @@
# ADR 0006 — QEMU device sim: generic `-M virt`, entered at the kernel # ADR 0006 — QEMU Device Simulator
**Status:** Accepted (2026-08-29). **Status:** Accepted (2026-08-29).
@@ -1,4 +1,4 @@
# ADR 0007 — Public repo: kernel-build moves to GitHub-hosted runners # ADR 0007 — Hosted-Only CI
**Status:** Accepted (2026-08-30). Supersedes the runner half of ADR-0004. **Status:** Accepted (2026-08-30). Supersedes the runner half of ADR-0004.
@@ -0,0 +1,32 @@
# ADR 0008 — Panel Development Environment
**Status:** Accepted (2026-08-30).
## Context
warden-sdk was written as the SDK for WardenOS, and its documentation framed
it that way: a support repo for one product. Since going public (ADR-0007,
GPL-2.0-only), the actual audience is wider — anyone with a Luckfox Pico 86
Panel gets a maintained 6.18 kernel, an off-device development loop, and a
device simulator out of this repo, none of which exists elsewhere for this
board. The product-first framing undersold that and confused the entry point
for outside readers.
## Decision
Position warden-sdk as **a modern, open development environment for the
Luckfox Pico 86 Panel (RV1106)**. WardenOS is documented as the downstream
consumer it is, not the purpose. Documentation follows three rules: lead with
the board, not the product; keep private-repo references clearly marked as
context; keep titles short — a heading names a section, it does not summarize
it.
## Consequences
- README and top-level docs lead with the hardware and the developer loop
(build, simulate, test), with verified quick-start commands.
- WardenOS/flare-edge specifics stay where they are engineering truth (the
seam inventory, scenario payloads) but read as one consumer's usage.
- The honesty rule is unchanged: emulation results are never on-silicon
claims.
- The repo is renamed **`bfe-core1106-sdk`** (2026-08-31): the name leads
with the org and the chip, not the product. `warden-sdk` remains as a
GitHub redirect; crate names (`warden-sim`, `warden-config-lint`), the
`WARDEN_*` env vars, and binary names are unchanged.
+21 -17
View File
@@ -1,4 +1,8 @@
# Can the RV1106 NPU be used for 3D graphics or other graphical tasks? # NPU Graphics Feasibility
> Point-in-time engineering study, written while scoping the `rknpu.ko` 6.18
> port for the downstream WardenOS firmware — "our boards" / "this product"
> below refer to that context. The hardware conclusions apply to any 86 Panel.
**Bottom line: no, not for 3D rendering — not "slower," but genuinely not how the **Bottom line: no, not for 3D rendering — not "slower," but genuinely not how the
hardware works past the first pipeline stage. The RKNPU on RV1106 is a hardware works past the first pipeline stage. The RKNPU on RV1106 is a
@@ -24,9 +28,9 @@ or flagged as general knowledge / needing TRM confirmation.
--- ---
## 1. What the RV1106 NPU actually is ## 1. What the NPU Is
### Identity and generation ### Identity and Generation
- It is the **RKNPU** — Rockchip's 4th-generation NPU IP, exposed to tooling as - It is the **RKNPU** — Rockchip's 4th-generation NPU IP, exposed to tooling as
the "RKNPU2" software generation (same toolchain family as RK3566/68/88), but the "RKNPU2" software generation (same toolchain family as RK3566/68/88), but
@@ -56,7 +60,7 @@ or flagged as general knowledge / needing TRM confirmation.
assigns **420MHz**. No datasheet states a default/rated frequency. assigns **420MHz**. No datasheet states a default/rated frequency.
(`luckfox-pico-86-panel/npu.md:11-13`) (`luckfox-pico-86-panel/npu.md:11-13`)
### Architecture: fixed-function command-stream engine, not a programmable core ### Architecture
This is the load-bearing fact for everything downstream. Reading the vendored This is the load-bearing fact for everything downstream. Reading the vendored
kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`): kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknpu/`):
@@ -106,7 +110,7 @@ kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknp
primitive operations the hardware's fixed-function units implement primitive operations the hardware's fixed-function units implement
underneath. It is not a route to arbitrary per-element or per-pixel code. underneath. It is not a route to arbitrary per-element or per-pixel code.
### Data types, memory, DDR sharing ### Data Types and Memory
- **INT8-only quantization tier** for RV1106/RV1103 (`quantize=8` mandatory at - **INT8-only quantization tier** for RV1106/RV1103 (`quantize=8` mandatory at
conversion time) — inputs/outputs must be int8 and strictly 4-D. conversion time) — inputs/outputs must be int8 and strictly 4-D.
@@ -129,7 +133,7 @@ kernel driver source directly (`flare-edge/sdk/sysdrv/source/kernel/drivers/rknp
the 128256MB shared-DDR budget is the practical ceiling well before compute the 128256MB shared-DDR budget is the practical ceiling well before compute
is. (`luckfox-pico-86-panel/npu.md:38`) is. (`luckfox-pico-86-panel/npu.md:38`)
### Software stack — how it's actually driven ### Software Stack
1. **Kernel driver** (`rknpu.ko`, currently v0.9.2 on our shipped firmware) — 1. **Kernel driver** (`rknpu.ko`, currently v0.9.2 on our shipped firmware) —
exposes `/dev/rknpu` (a DRM device or a misc device, selectable at build time exposes `/dev/rknpu` (a DRM device or a misc device, selectable at build time
@@ -159,7 +163,7 @@ footgun on this board (no regulator wired) — never poll it.
--- ---
## 2. 3D rendering feasibility, stage by stage ## 2. 3D Rendering, Stage by Stage
A conventional 3D pipeline: **vertex transform → primitive assembly → A conventional 3D pipeline: **vertex transform → primitive assembly →
rasterization → depth test → texture sampling → per-pixel shading → rasterization → depth test → texture sampling → per-pixel shading →
@@ -175,7 +179,7 @@ framebuffer write.** Verdict per stage, given everything in §1:
| **Per-pixel shading** (arbitrary per-fragment program) | **No** | The NPU executes one fixed, precompiled graph over a whole tensor — it cannot run per-pixel conditional/arbitrary code. You could contrive a *specific* visual effect that literally is a small CNN (see §3), but that's not "shading" in the pipeline sense — it's a different, narrower thing wearing the name. | | **Per-pixel shading** (arbitrary per-fragment program) | **No** | The NPU executes one fixed, precompiled graph over a whole tensor — it cannot run per-pixel conditional/arbitrary code. You could contrive a *specific* visual effect that literally is a small CNN (see §3), but that's not "shading" in the pipeline sense — it's a different, narrower thing wearing the name. |
| **Framebuffer write** (write final pixels to the display's scanout buffer) | **No** | The NPU has no display/scanout connection at all — no DRM plane, no VOP link. Its only output path is writing tensor data to a DDR buffer, which is not a display pixel format. Something else (CPU or RGA) has to dequantize (`int8 → float → pixel`) and repack it into an actual framebuffer format before it's visible — and even RGA doesn't consume NPU tensor layouts directly (see §4). | | **Framebuffer write** (write final pixels to the display's scanout buffer) | **No** | The NPU has no display/scanout connection at all — no DRM plane, no VOP link. Its only output path is writing tensor data to a DDR buffer, which is not a display pixel format. Something else (CPU or RGA) has to dequantize (`int8 → float → pixel`) and repack it into an actual framebuffer format before it's visible — and even RGA doesn't consume NPU tensor layouts directly (see §4). |
### Verdict: full or hybrid 3D pipeline ### Verdict
**No full pipeline is possible on this hardware — five of six stages have no **No full pipeline is possible on this hardware — five of six stages have no
mapping at all, not a slow one.** A "hybrid" design where only vertex transform mapping at all, not a slow one.** A "hybrid" design where only vertex transform
@@ -214,7 +218,7 @@ they are the wrong tool, full stop.
--- ---
## 3. Other graphical tasks that might fit a CNN accelerator ## 3. CNN-Shaped Image Tasks
Setting 3D aside — a CNN accelerator's real strength is convolution, which Setting 3D aside — a CNN accelerator's real strength is convolution, which
*does* map to some classic image-processing tasks. Evaluated against this *does* map to some classic image-processing tasks. Evaluated against this
@@ -228,7 +232,7 @@ specific 0.51 TOPS-class, 128256MB-shared-DDR, no-camera product:
| **Segmentation-driven UI effects** | Yes, in principle | **Moot — no camera, no visual input of any kind to segment.** | | **Segmentation-driven UI effects** | Yes, in principle | **Moot — no camera, no visual input of any kind to segment.** |
| **2D affine transforms** (rotate/scale/skew as matrix math) | Yes, technically a small matmul | **No — RGA already does this natively, in fixed-function hardware, cheaper.** RGA2-Enhance on this board already does scale (bicubic up / averaging down, to 16× either direction), rotate (90/180/270° on input windows), crop, and color/format conversion as dedicated blit-engine operations — no model compile, no INT8 quantization, no job-submit-and-IRQ-wait round trip, just a register-programmed blit. It is already wired into LVGL (the Monitor-page double-buffer-sync offload, verified 20%→8% CPU on real hardware) and proven in production. (`luckfox-pico-86-panel/rga.md`) | | **2D affine transforms** (rotate/scale/skew as matrix math) | Yes, technically a small matmul | **No — RGA already does this natively, in fixed-function hardware, cheaper.** RGA2-Enhance on this board already does scale (bicubic up / averaging down, to 16× either direction), rotate (90/180/270° on input windows), crop, and color/format conversion as dedicated blit-engine operations — no model compile, no INT8 quantization, no job-submit-and-IRQ-wait round trip, just a register-programmed blit. It is already wired into LVGL (the Monitor-page double-buffer-sync offload, verified 20%→8% CPU on real hardware) and proven in production. (`luckfox-pico-86-panel/rga.md`) |
### A concrete, on-record precedent: this was already considered and rejected once ### Prior Rejection
The product wiki records that the keyboard's touch-bias correction (snapping an The product wiki records that the keyboard's touch-bias correction (snapping an
ambiguous tap to the nearest key) was **explicitly evaluated for NPU ambiguous tap to the nearest key) was **explicitly evaluated for NPU
@@ -241,7 +245,7 @@ problem has no business going through a tensor accelerator's compile-and-submit
pipeline. Nothing in this research changes that conclusion — if anything it pipeline. Nothing in this research changes that conclusion — if anything it
generalizes it. generalizes it.
### Realistic verdict for §3 ### Realistic Verdict
None of the CNN-shaped graphical tasks clear the bar for this specific product. None of the CNN-shaped graphical tasks clear the bar for this specific product.
Where a hardware assist genuinely helps (2D blit/scale/rotate/blend, classic Where a hardware assist genuinely helps (2D blit/scale/rotate/blend, classic
@@ -255,9 +259,9 @@ detection, touch-gesture-pattern classification) — not graphics of any kind.
--- ---
## 4. The driver-porting reality ## 4. Driver Porting
### What porting `rknpu.ko` to 6.18 actually involves ### Porting Scope
- **This is a forward-port of Rockchip's out-of-tree vendor driver, not a - **This is a forward-port of Rockchip's out-of-tree vendor driver, not a
from-scratch write.** The driver already carries version-gated compatibility from-scratch write.** The driver already carries version-gated compatibility
@@ -299,7 +303,7 @@ detection, touch-gesture-pattern classification) — not graphics of any kind.
driver forward against a newer kernel ABI" — bounded, evidence-backed, but driver forward against a newer kernel ABI" — bounded, evidence-backed, but
real engineering, not a version-string bump. (`luckfox-pico-86-panel/rga.md:86`) real engineering, not a version-string bump. (`luckfox-pico-86-panel/rga.md:86`)
### Does "graphics" use need the full RKNN toolchain, or is there a more direct compute submit? ### Direct Submit Path
**It needs the full toolchain.** As established in §1, the raw **It needs the full toolchain.** As established in §1, the raw
`DRM_IOCTL_RKNPU_SUBMIT` path exists at the kernel-ioctl level, but the `DRM_IOCTL_RKNPU_SUBMIT` path exists at the kernel-ioctl level, but the
@@ -313,7 +317,7 @@ slower-to-iterate loop than driving RGA (which is a direct, synchronous
`im2d`-style C API call with no offline compile step at all) or writing plain `im2d`-style C API call with no offline compile step at all) or writing plain
CPU code. CPU code.
### RGA is the existing, already-adequate 2D accelerator ### RGA Comparison
Worth restating plainly since it's the thing the NPU would be compared against: Worth restating plainly since it's the thing the NPU would be compared against:
**RGA2-Enhance already does everything this panel's UI plausibly needs from 2D **RGA2-Enhance already does everything this panel's UI plausibly needs from 2D
@@ -332,7 +336,7 @@ silicon to work in, and it lines up with everything else in this document: RGA
--- ---
## 5. Bottom-line recommendation ## 5. Recommendation
| | Worth prototyping? | Why | | | Worth prototyping? | Why |
|---|---|---| |---|---|---|
@@ -343,7 +347,7 @@ silicon to work in, and it lines up with everything else in this document: RGA
| **Porting `rknpu.ko` to 6.18** | **Conditionally yes — but scope it for inference, not graphics** | Same bounded, evidence-backed effort class as the RGA port; keeps the door open for the platform wiki's actual identified NPU opportunity (a small non-visual classifier: audio, RS-485/sensor anomaly detection, touch-gesture patterns). Do not justify or scope the port around a graphics capability — it doesn't unlock one. | | **Porting `rknpu.ko` to 6.18** | **Conditionally yes — but scope it for inference, not graphics** | Same bounded, evidence-backed effort class as the RGA port; keeps the door open for the platform wiki's actual identified NPU opportunity (a small non-visual classifier: audio, RS-485/sensor anomaly detection, touch-gesture patterns). Do not justify or scope the port around a graphics capability — it doesn't unlock one. |
| **A first NPU spike, if one is wanted for team familiarity** | **Only the already-identified real use case** | A tiny non-visual model (e.g. an RS-485 anomaly classifier) — not a graphics stunt. This is the same conclusion the platform wiki already reached independent of this research. | | **A first NPU spike, if one is wanted for team familiarity** | **Only the already-identified real use case** | A tiny non-visual model (e.g. an RS-485 anomaly classifier) — not a graphics stunt. This is the same conclusion the platform wiki already reached independent of this research. |
### What would need to happen for this document to be wrong ### Falsifiers
Flagging explicitly, per the instruction to distinguish settled facts from Flagging explicitly, per the instruction to distinguish settled facts from
things needing verification: things needing verification:
+7 -7
View File
@@ -1,11 +1,11 @@
# Workflow flowcharts # Workflow Flowcharts
Generated by `tools/flowgen.py` from the modelled decision paths. Generated by `tools/flowgen.py` from the modelled decision paths.
Each is an outcome-first flowchart of a workflow the SDK tests, with its benchmark or MC/DC metric. Each is an outcome-first flowchart of a workflow the SDK tests, with its benchmark or MC/DC metric.
- [HPMCU watchdog: arm → beat → fire](hpmcu-watchdog.md) - [HPMCU Watchdog](hpmcu-watchdog.md)
- [Modbus RTU: read-holding-registers round trip](modbus-read-holding.md) - [Modbus RTU Round Trip](modbus-read-holding.md)
- [CRU reset ladder: cause + boot-mode survival](cru-reset-ladder.md) - [CRU Reset Ladder](cru-reset-ladder.md)
- [RGA 2D offload dispatch](rga-offload.md) - [RGA Offload](rga-offload.md)
- [Relay drive (Tier-1, 100% MC/DC)](relay-drive.md) - [Relay Drive](relay-drive.md)
- [UI freshness contract (Tier-1, 100% MC/DC)](freshness-contract.md) - [UI Freshness Contract](freshness-contract.md)
+2 -2
View File
@@ -1,8 +1,8 @@
# CRU reset ladder: cause + boot-mode survival # CRU Reset Ladder
> **Outcome tested:** Reset cause is attributable and the boot-mode register survives a warm reset. > **Outcome tested:** Reset cause is attributable and the boot-mode register survives a warm reset.
**Benchmark** (`cru_poll`): 23.0 ns/op **Benchmark** (`cru_poll`): 21.4 ns/op
```mermaid ```mermaid
flowchart TD flowchart TD
+1 -1
View File
@@ -1,4 +1,4 @@
# UI freshness contract (Tier-1, 100% MC/DC) # UI Freshness Contract
> **Outcome tested:** The UI never shows a stale number: it holds briefly, then marks unknown. > **Outcome tested:** The UI never shows a stale number: it holds briefly, then marks unknown.
+1 -1
View File
@@ -1,4 +1,4 @@
# HPMCU watchdog: arm → beat → fire # HPMCU Watchdog
> **Outcome tested:** A hung A7/flared ends in a counted reset, not a dark panel. > **Outcome tested:** A hung A7/flared ends in a counted reset, not a dark panel.
+2 -2
View File
@@ -1,8 +1,8 @@
# Modbus RTU: read-holding-registers round trip # Modbus RTU Round Trip
> **Outcome tested:** A well-formed request yields the right registers; a bad one a defined fault. > **Outcome tested:** A well-formed request yields the right registers; a bad one a defined fault.
**Benchmark** (`modbus_read_holding`): 88.0 ns/op **Benchmark** (`modbus_read_holding`): 73.7 ns/op
```mermaid ```mermaid
flowchart TD flowchart TD
+1 -1
View File
@@ -1,4 +1,4 @@
# Relay drive (Tier-1, 100% MC/DC) # Relay Drive
> **Outcome tested:** A relay is exported transparently and driven without disturbing a held contact. > **Outcome tested:** A relay is exported transparently and driven without disturbing a held contact.
+2 -2
View File
@@ -1,8 +1,8 @@
# RGA 2D offload dispatch # RGA Offload
> **Outcome tested:** Blits go to the RGA when it succeeds, and fall back to the CPU when it doesn't. > **Outcome tested:** Blits go to the RGA when it succeeds, and fall back to the CPU when it doesn't.
**Benchmark** (`rga_improcess`): 7.2 ns/op **Benchmark** (`rga_improcess`): 6.7 ns/op
```mermaid ```mermaid
flowchart TD flowchart TD
+4 -4
View File
@@ -1,11 +1,11 @@
# drivers/ — our own hardened, hardware-facing drivers # Hardened Drivers
Per **ADR-0002** (tiered MC/DC) and **ADR-0005** (source-of-truth), our own Per **ADR-0002** (tiered MC/DC) and **ADR-0005** (source-of-truth), our own
hardware-facing code migrates here behind a HAL seam and is hardened. "100% MC/DC on hardware-facing code migrates here behind a HAL seam and is hardened. "100% MC/DC on
100% of drivers" is infeasible (≈97% of kernel-driver LOC is vendor blobs — AIC8800 100% of drivers" is infeasible (≈97% of kernel-driver LOC is vendor blobs — AIC8800
alone is 88.5K lines); the realistic, honest target is tiered. alone is 88.5K lines); the realistic, honest target is tiered.
## Tier 1 — real 100% MC/DC (here now, CI-enforced) ## Tier 1 — 100% MC/DC
Self-contained logic with a clean seam, measured to **100% MC/DC** (gcc-14 Self-contained logic with a clean seam, measured to **100% MC/DC** (gcc-14
`-fcondition-coverage`) by the CI `mcdc` job (`make -C drivers/*/test check`): `-fcondition-coverage`) by the CI `mcdc` job (`make -C drivers/*/test check`):
@@ -23,7 +23,7 @@ build/test.rc` (it derives the driver name from the `.gcov` file, so there is no
per-driver copy to keep in sync). The CI `mcdc` job picks up any per-driver copy to keep in sync). The CI `mcdc` job picks up any
`drivers/*/test/Makefile` automatically. `drivers/*/test/Makefile` automatically.
## Tier 2 — serious testing + fault-injection + benchmarks ## Tier 2 — Fault Injection
Drivers too large or too vendor/UI-coupled for literal MC/DC get fault-injection, Drivers too large or too vendor/UI-coupled for literal MC/DC get fault-injection,
branch coverage, and benchmarks against the simulator instead. Their **hardware side branch coverage, and benchmarks against the simulator instead. Their **hardware side
@@ -38,7 +38,7 @@ is already modelled and tested here** in `../sim/`:
**Why the Tier-2 *source* isn't vendored here yet:** `modbus_engine.c` and **Why the Tier-2 *source* isn't vendored here yet:** `modbus_engine.c` and
`warden_rga.c` pull in shared UI headers (`platform.h`, `settings.h`, `lv_*`) and `warden_rga.c` pull in shared UI headers (`platform.h`, `settings.h`, `lv_*`) and
librga. Copying those in would duplicate exactly the shared surface the librga. Copying those in would duplicate exactly the shared surface the
**flare-edge↔warden-sdk unification** (ADR-0003/0005, a separate maintainer-gated step) is **flare-edge↔SDK unification** (ADR-0003/0005, a separate maintainer-gated step) is
meant to resolve cleanly. So the Tier-2 *models* (the hardware ends) live here now; meant to resolve cleanly. So the Tier-2 *models* (the hardware ends) live here now;
the Tier-2 *driver sources* migrate in with the unification, at which point their the Tier-2 *driver sources* migrate in with the unification, at which point their
existing flare-edge harnesses point at this repo. existing flare-edge harnesses point at this repo.
+2 -2
View File
@@ -1,4 +1,4 @@
# RV1106 kernel patch series (pristine linux-6.18.46 → WardenOS) # The Kernel Patch Series
The delta that forward-ports the vendor Rockchip 5.10.160 RV1106 support onto a The delta that forward-ports the vendor Rockchip 5.10.160 RV1106 support onto a
pristine upstream **linux-6.18.46**, as a reviewable, subsystem-split series. Every pristine upstream **linux-6.18.46**, as a reviewable, subsystem-split series. Every
@@ -9,7 +9,7 @@ reproduces the 223-file / ~136K-line delta that boots and is hardware-verified o
Build it with `../build/build-kernel.sh` (fetch pristine → apply these → configure Build it with `../build/build-kernel.sh` (fetch pristine → apply these → configure
with `../build/warden_defconfig``zImage` + `rv1106-warden.dtb`). with `../build/warden_defconfig``zImage` + `rv1106-warden.dtb`).
## The series ## The Series
| Patch | Scope | | Patch | Scope |
|---|---| |---|---|
+12 -15
View File
@@ -1,19 +1,16 @@
# qemu/ — the WardenOS device simulator # The Device Simulator
A QEMU virtual machine that boots the real forward-ported kernel (`build/` + A QEMU virtual machine that boots the real forward-ported kernel (`build/` +
`patches/`) and real userspace, so the *device* — init, daemons, networking, `patches/`) and real userspace, so the 86 Panel — init, daemons, networking,
OTA, watchdog, display — can be tested off-hardware. The third simulator in OTA, watchdog, display — can be developed and tested with no board attached.
the stack, deliberately not named "sim": It is the third simulator in the stack (the root README's table has the
three-way split), deliberately not named "sim": where `sim/` models
- `lvglsim` (flare-edge) — SDL desktop build of the UI. Rendering only. registers, this runs the whole machine above the kernel entry point on real
- `sim/` (this repo) — register-level Rust models of RV1106 blocks behind binaries — bring your own, or drop prebuilt payloads in `payload/`.
driver seams.
- `qemu/` (this) — the whole machine above the kernel entry point, running
the real binaries.
Decision record: `docs/decisions/0006-qemu-device-sim.md`. Decision record: `docs/decisions/0006-qemu-device-sim.md`.
## The boundary (read this before trusting a green run) ## The Boundary
There is no RV1106 machine model in QEMU and everything below the kernel is There is no RV1106 machine model in QEMU and everything below the kernel is
closed rkbin blobs plus mask ROM, so the VM **enters at `-kernel zImage`** on closed rkbin blobs plus mask ROM, so the VM **enters at `-kernel zImage`** on
@@ -43,7 +40,7 @@ Documented guest deviations from production, set by stage-2 init:
`WARDEN_HPMCU=0` (no mailbox SRAM on virt; flared >= flare-edge#106 fix `WARDEN_HPMCU=0` (no mailbox SRAM on virt; flared >= flare-edge#106 fix
required, or the daemon dies of SIGBUS). required, or the daemon dies of SIGBUS).
## Quick start ## Quick Start
```sh ```sh
# 1. kernel: canonical build boots the VM as-is; the fragment variant adds # 1. kernel: canonical build boots the VM as-is; the fragment variant adds
@@ -64,7 +61,7 @@ README) — `warden-flared`, `warden-modbus`, and `warden-ui` (the LVGL
fbdev+evdev build from flare-edge `tools/build-ui-vm.sh`) are started by fbdev+evdev build from flare-edge `tools/build-ui-vm.sh`) are started by
stage-2 init when present. stage-2 init when present.
## Scenario tests (`qemu/tests/`) ## Scenarios
- `boot-smoke.sh <zImage>` — sentinel-asserting boot; runs in CI inside the - `boot-smoke.sh <zImage>` — sentinel-asserting boot; runs in CI inside the
kernel-build job. kernel-build job.
@@ -98,7 +95,7 @@ stage-2 init when present.
the VM resets ~30 s later (verified). Do NOT combine with a flared payload the VM resets ~30 s later (verified). Do NOT combine with a flared payload
expecting survival: flared pets only while the UI heartbeat is fresh. expecting survival: flared pets only while the UI heartbeat is fresh.
## Gotchas that cost time (so they cost it once) ## Gotchas
- AF_UNIX socket paths cap at ~108 chars — keep `--rs485`/`--qmp` paths short. - AF_UNIX socket paths cap at ~108 chars — keep `--rs485`/`--qmp` paths short.
- A serial port that is closed discards incoming bytes: hold ONE fd open - A serial port that is closed discards incoming bytes: hold ONE fd open
@@ -108,7 +105,7 @@ stage-2 init when present.
which every script (boot smoke included) delegates to. which every script (boot smoke included) delegates to.
- Never pass `earlyprintk`: DEBUG_UART_PHYS is the RV1106's 0xff4c0000. - Never pass `earlyprintk`: DEBUG_UART_PHYS is the RV1106's 0xff4c0000.
## Host requirements ## Requirements
`qemu-system-arm` (Debian 13 ships QEMU 10), `curl`, `cpio`, `mkfs.ext4`, `qemu-system-arm` (Debian 13 ships QEMU 10), `curl`, `cpio`, `mkfs.ext4`,
`gcc-arm-linux-gnueabihf` (kernel build), `python3` (+`cryptography` for the `gcc-arm-linux-gnueabihf` (kernel build), `python3` (+`cryptography` for the
+3 -2
View File
@@ -1,6 +1,7 @@
# qemu/payload/ — guest binaries (never committed) # Guest Payloads
Drop **static musl armv7** binaries here; `qemu/mkimage.sh` copies everything Drop **static musl armv7** binaries in this directory (contents are
gitignored — binaries are never committed); `qemu/mkimage.sh` copies everything
in this directory (except this README) into `/usr/bin/` of both rootfs slots. in this directory (except this README) into `/usr/bin/` of both rootfs slots.
Static musl is the same target the device uses for its Rust daemons, so the Static musl is the same target the device uses for its Rust daemons, so the
exact production binaries run unmodified in the VM. exact production binaries run unmodified in the VM.
+7
View File
@@ -0,0 +1,7 @@
Finished `bench` profile [optimized] target(s) in 0.08s
Running benches/sim_bench.rs (target/release/deps/sim_bench-57835de7b7b68615)
{"bench":"hpmcu_tick","ns_per_op":1.8,"iters":1000000}
{"bench":"cru_poll","ns_per_op":21.4,"iters":1000000}
{"bench":"modbus_read_holding","ns_per_op":73.7,"iters":1000000}
{"bench":"rga_improcess","ns_per_op":6.7,"iters":1000000}
{"bench":"membus_poke_peek","ns_per_op":27.0,"iters":1000000}
+1 -1
View File
@@ -2,7 +2,7 @@
name = "warden-config-lint" name = "warden-config-lint"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
description = "Static target-config checks for WardenOS: catch memory-map faults (the 0x40000 MCU-load brick class) and other flash-time config mistakes before a flash, not on the bench." description = "Static target-config checks for the Luckfox Pico 86 Panel: catch memory-map faults (the 0x40000 MCU-load brick class) and other flash-time config mistakes before a flash, not on the bench."
license = "GPL-2.0-only" license = "GPL-2.0-only"
[[bin]] [[bin]]
+4 -4
View File
@@ -1,9 +1,9 @@
# config-lint — static target-config gates # config-lint
Catches flash-time config faults the behavioural sim cannot: mistakes in the Static target-config gates: catches flash-time config faults the behavioural sim cannot: mistakes in the
*memory map*, not the logic. The first check is the one that would have caught the *memory map*, not the logic. The first check is the one that would have caught the
**c8a3 brick** — a boot-loaded coprocessor firmware dropped at `0x40000`, which is **c8a3 brick** — a boot-loaded coprocessor firmware dropped at `0x40000`, which is
a `reserved-memory` carve-out on Thunder-Boot boards but plain kernel RAM on ours, a `reserved-memory` carve-out on Thunder-Boot boards but plain kernel RAM on the 86 Panel,
so the MCU and the kernel fought over the same DRAM and the board hung before eth0. so the MCU and the kernel fought over the same DRAM and the board hung before eth0.
## The check ## The check
@@ -37,6 +37,6 @@ found; `2` = usage/IO error.
The suite encodes the brick as a regression: the real Thunder-Boot `.ini` The suite encodes the brick as a regression: the real Thunder-Boot `.ini`
(Hpmcu @ `0x40000`) *fails* against a DT with no `rtos@40000` node and *passes* (Hpmcu @ `0x40000`) *fails* against a DT with no `rtos@40000` node and *passes*
once the reservation is added — and our board's non-TB loader (no boot-loaded MCU) once the reservation is added — and the 86 Panel's non-TB loader (no boot-loaded MCU)
always passes. See `../../docs/architecture.md` §5 and, for the hardware hazard, always passes. See `../../docs/architecture.md` §5 and, for the hardware hazard,
the `boot-loaded-mcu-0x40000-hazard` note. the `boot-loaded-mcu-0x40000-hazard` note.
+7 -7
View File
@@ -27,7 +27,7 @@ OUT = os.path.join(REPO, "docs", "workflows")
WORKFLOWS = [ WORKFLOWS = [
{ {
"name": "hpmcu-watchdog", "name": "hpmcu-watchdog",
"title": "HPMCU watchdog: arm → beat → fire", "title": "HPMCU Watchdog",
"outcome": "A hung A7/flared ends in a counted reset, not a dark panel.", "outcome": "A hung A7/flared ends in a counted reset, not a dark panel.",
"metric": ("bench", "hpmcu_tick"), "metric": ("bench", "hpmcu_tick"),
"mermaid": """flowchart TD "mermaid": """flowchart TD
@@ -42,7 +42,7 @@ WORKFLOWS = [
}, },
{ {
"name": "modbus-read-holding", "name": "modbus-read-holding",
"title": "Modbus RTU: read-holding-registers round trip", "title": "Modbus RTU Round Trip",
"outcome": "A well-formed request yields the right registers; a bad one a defined fault.", "outcome": "A well-formed request yields the right registers; a bad one a defined fault.",
"metric": ("bench", "modbus_read_holding"), "metric": ("bench", "modbus_read_holding"),
"mermaid": """flowchart TD "mermaid": """flowchart TD
@@ -58,7 +58,7 @@ WORKFLOWS = [
}, },
{ {
"name": "cru-reset-ladder", "name": "cru-reset-ladder",
"title": "CRU reset ladder: cause + boot-mode survival", "title": "CRU Reset Ladder",
"outcome": "Reset cause is attributable and the boot-mode register survives a warm reset.", "outcome": "Reset cause is attributable and the boot-mode register survives a warm reset.",
"metric": ("bench", "cru_poll"), "metric": ("bench", "cru_poll"),
"mermaid": """flowchart TD "mermaid": """flowchart TD
@@ -71,7 +71,7 @@ WORKFLOWS = [
}, },
{ {
"name": "rga-offload", "name": "rga-offload",
"title": "RGA 2D offload dispatch", "title": "RGA Offload",
"outcome": "Blits go to the RGA when it succeeds, and fall back to the CPU when it doesn't.", "outcome": "Blits go to the RGA when it succeeds, and fall back to the CPU when it doesn't.",
"metric": ("bench", "rga_improcess"), "metric": ("bench", "rga_improcess"),
"mermaid": """flowchart TD "mermaid": """flowchart TD
@@ -84,7 +84,7 @@ WORKFLOWS = [
}, },
{ {
"name": "relay-drive", "name": "relay-drive",
"title": "Relay drive (Tier-1, 100% MC/DC)", "title": "Relay Drive",
"outcome": "A relay is exported transparently and driven without disturbing a held contact.", "outcome": "A relay is exported transparently and driven without disturbing a held contact.",
"metric": ("mcdc", "relays.c — 40/40 conditions, 100% MC/DC (CI-enforced)"), "metric": ("mcdc", "relays.c — 40/40 conditions, 100% MC/DC (CI-enforced)"),
"mermaid": """flowchart TD "mermaid": """flowchart TD
@@ -102,7 +102,7 @@ WORKFLOWS = [
}, },
{ {
"name": "freshness-contract", "name": "freshness-contract",
"title": "UI freshness contract (Tier-1, 100% MC/DC)", "title": "UI Freshness Contract",
"outcome": "The UI never shows a stale number: it holds briefly, then marks unknown.", "outcome": "The UI never shows a stale number: it holds briefly, then marks unknown.",
"metric": ("mcdc", "freshness.c — 66/66 conditions, 100% MC/DC (CI-enforced)"), "metric": ("mcdc", "freshness.c — 66/66 conditions, 100% MC/DC (CI-enforced)"),
"mermaid": """flowchart TD "mermaid": """flowchart TD
@@ -148,7 +148,7 @@ def metric_line(metric, bench):
def main(): def main():
bench = load_bench(sys.argv) bench = load_bench(sys.argv)
os.makedirs(OUT, exist_ok=True) os.makedirs(OUT, exist_ok=True)
index = ["# Workflow flowcharts", "", index = ["# Workflow Flowcharts", "",
"Generated by `tools/flowgen.py` from the modelled decision paths.", "Generated by `tools/flowgen.py` from the modelled decision paths.",
"Each is an outcome-first flowchart of a workflow the SDK tests, with its" "Each is an outcome-first flowchart of a workflow the SDK tests, with its"
" benchmark or MC/DC metric.", ""] " benchmark or MC/DC metric.", ""]