board.yaml Reference
board.yaml is the single declarative file that describes a firmware project: which SoM SKU, which carrier, which runtime on each on-die core, which inference backend, which optional libraries, which IPC carve-outs. The SDK's orchestrator compiles it down to per-core build slices (Zephyr Kconfig, Yocto local.conf, plain CMake), a system manifest, and generated headers — your build plumbing stays unchanged.
Schema v2 landed in v0.6 alongside heterogeneous OS orchestration. The top-level
os:field is gone; runtime is declared per-core undercores.<id>. The v1 schema is no longer accepted by the loader — there are no shipping customers on v1, so every in-repo example was rewritten in the same redesign. See Heterogeneous Builds for the dual-OS walkthrough.
Minimum-viable example
schema_version: 2
som:
sku: E1M-AEN701 # your MPN — preset under metadata/e1m_modules/<MPN>.yaml
carrier:
name: E1M-EVK # stock preset or your custom carrier
cores:
m55_hp:
os: zephyr
app: ./src
peripherals: [gpio]
That's the whole config for a vanilla "E1M-AEN701 on the EVK running Zephyr on the M55-HP" build. All other blocks are optional. Cores omitted from cores: inherit the SoM preset's topology: defaults; use os: off to skip a peer core explicitly.
Heterogeneous example (V2N: A55 Yocto + M33 Zephyr)
schema_version: 2
som:
sku: E1M-V2N101
hw_rev: r1
carrier:
name: E1M-X-EVK
cores:
a55_cluster:
os: yocto
app: ./linux
image: alp-image-edge
peripherals: [ethernet, usb, emmc]
libraries: [mbedtls, nlohmann_json]
iot: { wifi: true, mqtt: true, tls: true }
m33_sm:
os: zephyr
app: ./m33_sm
peripherals: [adc, pwm, i2c, gpio]
libraries: [cmsis_dsp]
inference: { backend: cpu }
ipc:
- kind: rpmsg
endpoints: [a55_cluster, m33_sm]
carve_out_kb: 512
name: alp_default_rpmsg
diagnostics:
log_level: info
One declaration drives both halves. The orchestrator fans out into per-slice build directories, allocates the RPMsg carve-out from the SoM preset's memory_map:, and emits <alp/system_ipc.h> with the resolved addresses + endpoint IDs that both halves #include.
File location
your-app/
├── board.yaml # this file
├── linux/ # Yocto slice (path matches cores.a55_cluster.app)
│ └── CMakeLists.txt
└── m33_sm/ # Zephyr slice (path matches cores.m33_sm.app)
├── prj.conf # empty; the loader generates alp.conf
└── src/main.c
Slice sub-directory names are conventions, not magic — they're bound by cores.<id>.app:. Matching the core ID keeps the layout readable. Single-OS projects keep a flat src/ layout under one core.
Single source of truth
Pre-board.yaml you tracked configuration across prj.conf, CMake -D flags, local.conf, vendor Kconfig knobs, and library opt-ins. board.yaml collapses all of that into one place.
- Don't edit
prj.confdirectly. It stays empty; the loader emitsalp.confand Zephyr layers it on viaOVERLAY_CONFIG. - Don't pass SDK
-Dflags tocmake. The loader emits the right set per slice. - Don't hand-edit
local.conf'sMACHINE. The orchestrator picks the per-cluster MACHINE (e.g.e1m-v2n101-a55) from the SoM preset. - Don't type endpoint IDs or carve-out addresses in app code. They live in the generated
<alp/system_ipc.h>that both slices include.
If board.yaml can't express something you need, file an issue — the right fix is to extend the schema, not bypass it.
Released MPNs
Paste any of these into som.sku:
| Family | MPNs |
|---|---|
| Alif Ensemble | E1M-AEN301, AEN401, AEN501, AEN601, AEN701, AEN801 |
| Renesas RZ/V2N | E1M-V2N101, V2N102 |
| RZ/V2N + DEEPX | E1M-V2M101, V2M102 |
| NXP i.MX 93 | E1M-NX9101 (hardware-fact fields carry TBD pending HW config) |
Stock carriers
| Carrier name | Form factor | Hosts |
|---|---|---|
E1M-EVK | 35 × 35 | E1M-AEN family, E1M-N93 family |
E1M-X-EVK | 45 × 65 | E1M-X V2N family, V2N-M1 family |
Schema
The authoritative JSON Schema is at metadata/schemas/board-config-v2.schema.json. Top-level fields:
| Field | Required | What it picks |
|---|---|---|
schema_version | yes | Constant 2. v1 is no longer accepted. |
som | yes | SoM SKU + per-component / memory overrides. |
carrier | no | Carrier name + populated-chip deltas. Omit for headless builds. |
cores | yes | Per-core os / app / peripherals / libraries / inference. |
ipc | no | Cross-core shared-memory carve-outs (RPMsg / raw shmem / mailbox). |
chips | no | Project-level chip drivers not pre-populated by the carrier. |
diagnostics | no | alp_last_error() + log level (project-wide). |
som block
som:
sku: E1M-AEN701 # required
hw_rev: r1 # optional — defaults to the preset's default_hw_rev.
# Validated build-time against the family's
# hw_revisions table; cross-checked at runtime via
# the BOARD_ID ADC.
overrides: # rare — custom SoM variants only
secure_element: none # custom AEN without the OPTIGA Trust M
memory: # custom DRAM / flash populations
flash_mbit: 65536
The SoM block describes the module itself — silicon, on-module radio, on-module secure element / RTC / temperature sensor / EEPROM. Fixed at SoM-fab time; you can't DNI on-module parts after order.
carrier block
carrier:
name: E1M-EVK # stock preset, or any unique name for a custom carrier
hw_rev: r1 # optional — same enforcement rules as som.hw_rev
populated: # delta vs the carrier preset's defaults
lsm6dso: false # DNI'd on this custom assembly
tas2563: false # no speaker amps populated
The carrier block describes the carrier board — IMUs, barometers, OLEDs, cameras, microphones, speaker amps, current monitors, I/O expanders. Different per carrier design; custom carriers DNI any component.
Each populated.<name>: true becomes CONFIG_ALP_SDK_CHIP_<NAME>=y in the generated Kconfig fragment.
cores block (per-core runtime)
The core of v2. Keys are canonical core IDs from the SoM preset's topology: block (a55_cluster, m33_sm, m55_hp, m55_he, a32_cluster, m33). The topology key set is cross-checked by pr-metadata-validate.yml — a typo in a core ID fails the build before any compile work.
Per-core fields:
| Field | Values | Notes |
|---|---|---|
os | zephyr / yocto / baremetal / off | off skips the core entirely. |
app | Path to the app source directory | Zephyr/baremetal: CMakeLists.txt. Yocto: recipe dir. |
image | Yocto image recipe name | Yocto slices only (alp-image-edge is the stock reference). |
peripherals | List (per-slice, not project-wide) | Zephyr subsystems or Yocto packages this slice needs. |
libraries | List | Scoped to this slice's include path + link line. |
inference | { backend: auto/cpu/ethos_u/drpai/deepx_dx } | NPU dispatcher per-slice. |
iot | { wifi: bool, mqtt: bool, ble: bool, tls: bool } | Connectivity toggles per-slice. |
Which cores does my SoM expose?
| SoM family | A-class | M-class | Heterogeneous? |
|---|---|---|---|
| AEN E3/E4 | — | m55_hp, m55_he (Zephyr) | No — RTOS-only |
| AEN E5..E8 | a32_cluster (Yocto) | m55_hp, m55_he (Zephyr) | Yes |
| V2N / V2N-M1 | a55_cluster (Yocto) | m33_sm (Zephyr) | Yes |
| iMX93 (N93) | a55_cluster (Yocto) | m33 (Zephyr) | Yes |
A bare som: { sku: <MPN> } plus an empty cores: block produces a working dual-image build for every heterogeneous SoM — the per-core OS defaults come from the SoM preset.
ipc block (cross-core carve-outs)
ipc:
- kind: rpmsg # rpmsg | raw_shmem | mailbox_only
endpoints: [a55_cluster, m33_sm]
carve_out_kb: 512
name: alp_default_rpmsg
Each entry declares a shared-memory region the orchestrator allocates from the SoM preset's memory_map: (filtered by accessible_from: for the listed endpoints). Addresses + endpoint IDs + mailbox channel macros are emitted into build/generated/alp/system_ipc.h, which both halves #include. App code uses <alp/rpc.h> — never raw addresses.
peripherals / chips
cores:
m55_hp:
peripherals: [i2c, pwm, adc] # per-slice
chips:
- lsm6dso # project-wide chip drivers
- ssd1306
The orchestrator gates each peripheral and chip driver on a Kconfig symbol so unused code doesn't cost flash. Listing a chip in board.yaml is equivalent to setting CONFIG_ALP_SDK_CHIP_<NAME>=y by hand — but with validation that the SoM actually routes the bus the chip needs.
libraries
cores:
m55_hp:
libraries:
- etl
- fmt
- nlohmann_json
- lvgl
- mbedtls
- cmsis_dsp
- littlefs
Listed libraries become available on the include path + link line, with their native API (no wrapping). The SDK ships profile headers under metadata/library-profiles/<lib>/ that pre-tune each library for the embedded environment (no exceptions, no STL on M-class, etc).
| Library | Profile sets |
|---|---|
etl | ETL_NO_STL, ETL_NO_EXCEPTIONS, C++17 target. |
fmt | FMT_HEADER_ONLY=1, FMT_USE_IOSTREAM=0, FMT_EXCEPTIONS=0. |
nlohmann_json | JSON_NOEXCEPTION=1, JSON_USE_IMPLICIT_CONVERSIONS=0. |
diagnostics
diagnostics:
log_level: info # debug | info | warn | error | trace
last_error: true # alp_last_error() thread-local slot
Project-wide, applies to every slice.
How the orchestrator compiles the file
scripts/alp_orchestrate.py (driven by west alp-build) reads board.yaml, validates against the v2 schema, resolves the SoM SKU + carrier presets + per-core topology, applies overrides, and emits per-slice native configs plus cross-slice generated artefacts.
Generated artefacts (deterministic, byte-stable across rebuilds)
| Path | What it carries |
|---|---|
build/system-manifest.yaml | Per-slice status, log paths, artefact paths, boot order. |
build/generated/alp/system_ipc.h | Endpoint IDs, addresses, mailbox channel macros — shared by all slices. |
build/generated/dts-reservations.dtsi | reserved-memory: carve-outs shipped into Linux + Zephyr DTs. |
build/<core>-zephyr/alp.conf | Kconfig fragment layered onto each Zephyr slice's prj.conf. |
build/<core>-yocto/conf/alp-generated.conf | local.conf snippet consumed by bitbake (MACHINE=…, IMAGE_INSTALL). |
The system manifest is consumed downstream by west alp-image (assembles the bundle), west alp-flash (walks boot_order: per artefact), west alp-renode (boots the dual-OS image in simulation), and OTA (<alp/ota.h>).
Per-slice emit modes (driven manually if you bypass west alp-build)
--emit mode | Output |
|---|---|
zephyr-conf | alp.conf — Kconfig fragment, layered over prj.conf via OVERLAY_CONFIG. |
cmake-args | A list of -D flags for plain CMake / bare-metal. |
yocto-conf | local.conf snippet (MACHINE=…, IMAGE_INSTALL deltas). |
dts-overlay | Zephyr DTS overlay with alp,pin-array + bus aliases. |
hw-info-h | <alp_hw_info_build.h> with ALP_HW_BUILD_* macros. |
west-libraries | west.yml fragment pinning the libraries board.yaml requested. |
Build-time hw_info header
#include "alp/hw_info.h"
#include "alp_hw_info_build.h" /* generated by --emit hw-info-h */
alp_hw_info_t info;
alp_hw_info_read(&info);
alp_hw_info_assert_matches_build(&info,
ALP_HW_BUILD_SOM_SKU,
ALP_HW_BUILD_SOM_HW_REV);
ALP_HW_BUILD_SOM_SKU, ALP_HW_BUILD_SOM_FAMILY, ALP_HW_BUILD_SOM_HW_REV, and (when a carrier is declared) ALP_HW_BUILD_CARRIER_NAME + ALP_HW_BUILD_CARRIER_HW_REV are all emitted per project. Per-slice ALP_HW_BUILD_CORE_ID + ALP_HW_BUILD_OS live in each slice's generated header.
Hardware revision tracking
Every released SoM family and carrier carries a hw_revisions: table. The SDK uses it to detect "wrong firmware for this hardware" two ways:
- Build-time — the orchestrator reads
metadata/sdk_version.yamland fails-fast if the chosenhw_rev's[min_sdk_version, max_sdk_version]window doesn't cover the current SDK version. Validator exit code3. - Runtime — the SDK boots into a board-ID check that uses one ADC pin (SoM-side and carrier-side) fed by a resistor divider from a 1.8 V rail. A second tier reads the SoM's on-module 24C128 EEPROM for an authoritative MPN string + serial + mfg date. Mismatch on either tier halts boot.
Modular SoMs: optional chip populations
The SoM YAML carries a per-chip assembled: flag for every entry in its i2c_devices: topology so the SDK can express SoMs that ship in multiple BOM variants:
assembled: | Meaning |
|---|---|
true (default) | Chip is always populated on every BOM variant of this SKU. |
false | DNI — the chip footprint exists but is empty. |
"optional" | Per-BOM-variant — some units have it, some don't. |
Customer code that uses an optional chip MUST handle alp_*_init returning ALP_ERR_NOT_READY gracefully (skip the demo, log a clear message, fall back) instead of crashing.
Versioning
schema_version: 2 is the only valid value today. v1 was removed in the v0.6 redesign — there were no shipping customers on v1, so every in-repo example was migrated in the same PR. Breaking changes in the future bump to 3; the loader will support both for at least one minor cycle so consumers can migrate.
See also
- Heterogeneous Builds — dual-OS project walkthrough
<alp/rpc.h>— framed RPC over OpenAMP/RPMsgmetadata/templates/board.yaml— the canonical commented template- Architecture — how the orchestrator fits into the build
- Quick start — concrete walk-through
- JSON Schema — authoritative