Skip to content

Latest commit

 

History

History

adr

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

@page docs_adr_index Architecture Decision Records

Architecture Decision Records

This directory captures the why behind significant architectural choices in the Alp SDK. Each ADR is a short markdown file describing one decision: the context that forced it, the alternatives considered, the choice made, and the consequences (good and bad).

ADRs are append-only — a decision is never edited, only superseded by a later ADR that links back to it. The index below lists the current set in chronological order.

Format

# NNNN. <title>

Status: Proposed | Accepted | Superseded by NNNN | Deprecated
Date: YYYY-MM-DD

## Context
What forced the decision.  What problem we were facing.

## Decision
What we chose.

## Alternatives
What else we considered, and why we didn't pick them.

## Consequences
Good: ...
Bad / costs: ...

Index

ADR Title Status
0001 Why Alp SDK wraps Zephyr (and why the wrapper stays thin) Accepted
0002 alp_last_error() + compile-time SoC capability validation Accepted — amended 2026-08-27 to record the ALP_ERR_NOT_READY/ALP_ERR_INVAL convention measured across the dispatch layer and correct a scope misreading; the decision itself unchanged
0003 Wrap 12 peripheral classes at v0.2, not just I2C/SPI/GPIO/UART Accepted — partially superseded by 0023 (Ethernet-into-<alp/iot.h> row only; 0023 itself is Proposed)
0004 E1M-spec instance counts as the portability bound Accepted
0005 alp-sdk vs alp-studio repo boundary (dual-use acid test) Accepted
0006 Secure boot (vendor-native) + secure OTA (MCUboot + Mender) Accepted, partially superseded (2026-05-11 amendment: Mender replaces RAUC on Linux side; AEN-Zephyr OTA client choice pending)
0007 Wave-2 DSP: pipeline stages, not standalone primitives Accepted
0008 GPU2D: portable shim under <alp/gpu2d.h> even for single-silicon Accepted
0009 Mender Zephyr client deferred to v1.1 Accepted
0010 Heterogeneous OS orchestration: Zephyr + Yocto as peers, not alternatives Accepted
0011 Intra-family portability is load-bearing; cross-form-factor portability is intentionally not a goal Accepted
0012 alp-sdk developer host is cross-platform; Linux required ONLY for Yocto Accepted
0013 TF-M trust boundary on AEN: TrustZone-M split on M55-HP, not a dedicated M55-HE Accepted
0014 Deterministic, versioned build-plan-v1 contract between planning and execution Accepted — runtime mechanism superseded by 0020's Python-port amendment; contract remains the reference/parity and --plan-from seam
0015 CC3501E bridge firmware is embedded in alp-sdk (like gd32-bridge), not a separate repo Accepted
0016 Cross-core peripheral proxy wire schema: the GD32 bridge command frame carried over RPMsg (A55→M33→GD32) Proposed
0017 alp-sdk rides over the vendor SDK — no rewritten vendor drivers; Tier-1 upstream-native / Tier-1.5 in-tree-thin (Apache HAL / upstream core) / Tier-2 vendor-SDK-consumed / Tier-3 SE-mediated Accepted (amended 2026-06-15)
0018 Curated third-party libraries: one libraries: flag in the project YAML, capability-gated manifests under metadata/libraries/, Tier A curated / Tier B recipe-only, never forked Accepted
0019 Vendor a verbatim e1m-spec pinout snapshot under metadata/e1m/ (generated by sync_e1m_spec.py, gated offline by check_e1m_pinout.py) so per-SoM pinout tables are cross-checked against the standard; supersedes PR #582's hand-transcribed footprint Accepted
0020 A standalone tan CLI owns the command surface and execution; Python Tan now also carries the relocated in-process planner, while alp-sdk retains the reference emitters; the six west alp-{build,flash,image,size,renode,clean} extensions + fan_out retired (read-only west alp-emit remains); supersedes 0014 mechanism + 84-87 Accepted — amended 2026-08-03 for the Python port; only migrate, lock, and quality still forward to west; the cross-repo repository_dispatch trigger shipped as .github/workflows/dispatch-tan-parity.yml (see ADR Amendment, 2026-08-26) — partially superseded by 0022 (the Rust-executor language clause, and — after 0022's 2026-08-07 withdrawal was itself superseded by its Amendment 2 on 2026-08-17 — the renode command-surface entries as well; the plans-vs-executes split, three-repo boundary, and contract still live)
0021 Toolchain provisioning: pin upstream URLs + sha256 rather than rehosting binaries, key ~/.alp/toolchains/ by artifact not SDK version, signed package manager for small host tools + hermetic archives for the large compilers, absolute-path injection instead of PATH mutation, tiered consent (A no-elevation / B elevation / C licence-gated), and per-lane onboarding (prebuilt-blink ‖ Zephyr-on-M ‖ Yocto container); automates what 0012 documented Proposed
0022 tan ships as Python (PyInstaller freeze of alp-tan), not Rust, from v0.5.0; Renode is retired completely — the command surface, alp-sdk's four pr-renode-* CI gates and their Renode v1.16.1 pin, the metadata/renode/ models, the tests/renode/ fixtures and examples/aen/aen-sim-vision — with no replacement simulator; narrowly supersedes 0020's Rust-executor language clause + its renode command-surface entries Accepted — amended twice: 2026-08-07 withdrew the Renode-retirement clause, then Amendment 2 (2026-08-17) superseded that withdrawal, re-instated the retirement and widened it to alp-sdk's own Renode CI + assets and to examples/aen/aen-sim-vision (Decision point 4 resolved: deleted); diagnostics.sim_console is retained; the Python-executor clause shipped and stands throughout
0023 Ethernet stays out of the <alp/*> surface — compile-time capability + form-factor port identity + ring-2 PHY chip drivers is the whole contract; no <alp/net.h>; link state may return additively if alp-studio needs it; narrowly supersedes ADR 0003's Ethernet-into-<alp/iot.h> row Proposed
0024 V2N/V2M ADC, PWM, DAC, counter, and qenc stay served exclusively by the GD32 bridge — no native RZ/V2N leg, because no SoC pin reaches an E1M-standard analog/counter pad; the six-surface HiL quarantine has mostly moved on and is not the driving concern Proposed
0025 V2N DMAC0 stays CM33-exclusive (whole unit) — the CM33 FSP reserves channels 0-1 for the SCI7 SPI DMA fast path, channels 2-7 are unclaimed margin, and Linux must never re-enable &dmac0; no Linux-side per-channel mask is fabricated for lack of evidence one works Proposed
0026 Tan owns the planner outright -- alp-sdk's scripts/alp_orchestrate/ is retired as a second producer and python/tan/planner/ becomes the single implementation; alp-sdk keeps metadata/schemas/examples (hardware truth) and build-plan-v1 survives as the seam; amends 0020's 2026-08-03 amendment clause 3 only Accepted — amended 2026-08-30: the decision stands, but migration step 3's deletion unit was wrong as scoped (scripts/alp_orchestrate/ is planner + emitters + the board.yaml loader whose SdkRevisionNotBuildable gate refuses unbuildable silicon, and is imported by validate_board_yaml.py, the script tan spawns on its own default validate path), a missing split step is inserted before it, the surviving-oracle checks are re-homed to tan-cli because parity-seam1.yml diffs the frozen oracle against a LIVE SDK emit, clause 4's frozen-crates/ bullet is struck as already stale (no top-level crates/ in tan-cli as of b9aa697), and the parity axis may be DELETED only once no alp-sdk release tan still accepts carries the second producer; decided 2026-09-04 (§C/§D): alp-sdk keeps the configure-time --emit zephyr-conf call via a surviving emitter core, not repointed at tan, and stays canonical for rendered-artefact bytes, so tan consumes plan-embedded bytes instead of re-implementing the renderers
0027 board.yaml storage regions are declared by role: (settings/app_data/log/ota_cache), a closed enum the planner resolves to a concrete region per SoM; flash_device: survives as an explicit, non-portable PIN and a pinned region missing after a SoM swap is a REFUSAL naming the region, the SoM and the replacing role -- never a silent relocation of persisted data Proposed
0028 Tan owns the model engine -- proposes retiring scripts/alp_model/ in favor of python/tan/model/ as the single implementation; alp-sdk keeps the NPU truth (metadata/npu_ops/, metadata/model_zoo/, metadata/schemas/, the inference_arena_sram_kib budgets) and the on-device .alpmodel reader, and the .alpmodel container format survives as the seam; applies 0026's hardware-truth-vs-consumer principle to the second duplicated surface Proposed
0029 Cross-repo pins (tan-cli's fork-audit hashes, alp-sdk's propagated Zephyr version, SUPPORTED_CLI_VERSION, PINNED_SDK_TAG) become typed lock-manifest entries (value / intent: tracking or frozen / reason-issue link / proof reference / site list) instead of a literal plus a prose comment; acceptance moves from spelling-agreement to a property gate -- no pin entry moves without a green run on the new value, via a new pin-move-verify repository_dispatch to alplabai/alp-e2e Proposed
0030 E1M-AEN SE firmware: the SERAM image on the module and the hal_alif se_services library are one versioned pair that moves together, and E1M-AEN tracks Alif's latest released SERAM (floor and current baseline v110); a module below the floor is unsupported and untriageable because an API break admits any behaviour, so se_service_get_se_revision() is checked before any other AEN SE investigation Accepted
0031 The CC3501E and GD32 bridge firmwares live in their own public repositories, each with its own CI, security policy and protected main; the wire contract stays SINGLE-SOURCED -- neither mirrors the protocol header, both compile alp-sdk's canonical one via an explicit ALP_SDK_ROOT, and firmware-side CI fails on drift. alp-sdk keeps the host halves. Supersedes 0015 Accepted
0032 Vendor data the vendor PERMITS us to redistribute but that cannot sit under Apache-2.0 (e.g. the Alif Ensemble SVDs) is carried in a segregated subtree -- metadata/svd/<vendor>/ for register descriptions, vendors/<name>/ for vendored source -- with the vendor's unmodified licence beside it and a NOTICE entry naming it. That split is load-bearing: check_public_private.py:76-82 excludes vendors/ from the leak scan while metadata/ is a scan root, and an SVD is dense register and pad detail, so it belongs on the scanned side. The title deliberately avoids "licence-gated", since this repo already uses that term for the opposite case (SETOOLS/BSPs, not redistributed) and ADRs are append-only, so the filename would have locked the inversion in; the term keeps its existing meaning where the document does use it. The NOTICE half of the pattern is convention rather than enforcement: nothing reads NOTICE and seven vendored components already have no entry, so Decision 3 repairs a drift rather than continuing a practice. NOT by extending library-v1.schema.json license.enum, which gates only what a project selects through libraries: and whose ADR 0018 guard would be widened to admit something that never travels that path. The root NOTICE currently states Alif/Renesas/NXP/DEEPX vendor material is NOT redistributed, so the carve-out is an amendment to a published representation and lands with the first file it describes, never ahead of it. Extends 0018 Proposed
0033 The CC3501E wire protocol is versioned MAJOR.MINOR and only MAJOR gates the link. MAJOR moves only when an unchanged host would be MISREAD (reusing a reserved byte or flag bit, changing framing or a struct layout); everything additive is MINOR and does NOT refuse a link. Features are discovered with CMD_GET_CAPABILITIES (0x06) rather than inferred from a version number -- the bitmap is composed from the same compile-time switches that decide which HAL bodies link, so it reports what a BUILD implements rather than what its number implies. The raw integer went v5 -> v9 in one week, forcing a lockstep reflash each time, when only v7 and v8 could actually misread an old host; v6 and v9 were additive and cost customers a reflash for nothing. Retroactive mapping v5=1.0, v6=1.1, v7=2.0, v8=3.0, v9=3.1; v9's 3.1 was BYTE-IDENTICAL to what shipped as v8, but since the #2035 CRC-trailer bump (RESP_OK 0x00 -> 0x5A plus a CRC-16 trailer) the current wire is 4.0 -- see the ledger in the ADR body, which is the source of truth for the current version. MAJOR 0 is reserved so a pre-scheme firmware answering with a raw integer is distinguishable from a corrupt one. Costs one final breaking bump, deliberately taken now at one customer rather than at ten Proposed
0034 alp-sdk DECLARES support policy as versioned schema-backed data, and exactly ONE normative evaluator turns a declared policy into a verdict; consumers call it or carry a hash-pinned audited port, never a prose re-derivation. The evaluator lives in alp-sdk scripts/ because 96 configure-time CMakeLists.txt invocations and the four west alp-* commands reach load_board_yaml with no tan in the process -- so a plain west build keeps its refusals. Declared policy is constrained (exact values only, derived closed codomain, no catch-all, totality proved by a gate rather than by ordering) so the evaluator stays a small total lookup and a port is provable. A refusal that enforces a SUPPORT decision must say so rather than assert a hardware limit: the Cortex-A -> Yocto / Cortex-M -> Zephyr pairing is a product choice, not physics -- upstream Zephyr v4.4.1 ships arch/arm/core/cortex_a_r/. Narrows 0026 clause 2's justification, not its decision Proposed
0035 micro-ROS cross-core transport on the RZ/V2N CM33<->A55 pair: recommends a custom XRCE transport over the ADR 0016 RPMsg channel (A55 bridge feeding an unmodified micro-ROS agent) over UDP on a virtual net or XRCE serial-over-RPMsg; no custom RMW is needed; bench-gated, maintainer decides Proposed

When to write an ADR

Write one when the decision:

  • Is hard to reverse (changes the public API shape, the build system, the OS-pivot story, the way the studio talks to the SDK).
  • Has a non-obvious "why" — six months from now the next contributor will look at the code and ask "why isn't this done the simpler way?"
  • Closes off an alternative path that someone could plausibly try again ("we considered X, here's why we didn't do it").

Don't write one for ordinary feature work or routine bugfixes — the changelog is the right place for those.