Quick Start
Build and run the gpio-button-led example end-to-end. By the end you'll have a working native_sim simulation and a path to real silicon.
What you need
-
The Alp SDK installed
-
The
tanCLI (0.5.1) — the SDK's sole user-facing command. It ships from its own repo (alplabai/tan-cli) on its own version line, and it is a Python program released as a self-contained PyInstaller freeze: no Rust toolchain, no rustup, no host Python interpreter.curl -fsSL https://raw.githubusercontent.com/alplabai/tan-cli/main/install.sh | sh# Windows PowerShell: irm https://raw.githubusercontent.com/alplabai/tan-cli/main/install.ps1 | iex -
A
ZEPHYR_BASEenvironment variable pointing at the Zephyr checkout (set bybootstrap.shon Linux/macOS/WSL2,bootstrap.ps1on native Windows)
Hardware is not required for native_sim. To target real silicon, jump to step 4.
Step 1 — Look at the example
Open alp-sdk/examples/gpio-button-led. Every example carries:
gpio-button-led/
├── board.yaml # declarative project config
├── prj.conf # empty (the loader generates alp.conf)
├── CMakeLists.txt # invokes the orchestrator at configure time
├── src/main.c # the application
└── boards/ # per-board overlay files
The minimum-viable board.yaml:
som:
sku: E1M-AEN801 # your MPN — the SDK ships a preset
# at metadata/e1m_modules/<MPN>.yaml
preset: e1m-evk # stock board preset, or write the board out inline
cores:
m55_hp:
# os: omitted → SoM topology default (Cortex-M → zephyr).
app: ./src
peripherals: [gpio]
diagnostics:
log_level: info
That's the whole config. The orchestrator resolves som.sku to the right silicon, picks up the board preset, and generates the per-slice native config (Zephyr Kconfig fragment, CMake -D flags, or Yocto local.conf) per core declared under cores:. Cores omitted inherit the SoM preset's topology: defaults — see the board.yaml reference for the full schema and the heterogeneous builds walkthrough for dual-OS projects.
See the board.yaml reference for the full schema.
Step 2 — Build under native_sim
cd alp-workspace/alp-sdk/examples/peripheral-io/gpio-button-led
tan run
tan run is the quick single-image loop: it resolves the project from the current directory (walking up to the nearest board.yaml), builds it under native_sim, and then executes the produced binary. --board <board> builds for real hardware instead; add --flash to program it after the build.
Expected output:
*** Booting Zephyr OS build v4.4.1 ***
[gpio] init button=ALP_E1M_GPIO_IO0, led=ALP_E1M_GPIO_IO1
[gpio] led=0 status=0
[gpio] led=1 status=0
...
[gpio] is_pressed -> status=0 pressed=1
[gpio] done
status=0 means ALP_OK. pressed=1 is Zephyr's gpio_emul default "input is low" report.
What tan run did — and the commands around it
tan is the SDK's sole user-facing CLI. ADR-0020 Phase 4 (alp-sdk v0.12.0) retired the SDK-side build executor: no alp binary is installed anywhere any more, and the west alp-build / alp-image / alp-flash / alp-clean / alp-size / alp-renode extensions went with it. alp-sdk is plans-only for this surface — tan consumes alp_orchestrate --emit build-plan and --emit system-manifest, then runs west / bitbake / cmake per slice itself.
tan run is the quick single-image loop. The full multi-slice pipeline — every core board.yaml declares, in one command — is tan build:
| Command | What it does |
|---|---|
tan build | Validates board.yaml, fans out into per-core slices, seeds system-manifest.yaml. |
tan image | Consumes the manifest, assembles a flashable bundle (build/image-bundle/). |
tan flash | Walks the manifest's boot_order: and programs each piece with the right backend. |
tan clean | Removes build outputs. |
tan renode | Boots the image in Renode (headless smoke; --sim-mode for the studio simulator). |
tan size | Reports per-slice flash/RAM footprint against the SoM budget (--fail-over-budget). |
tan generate | Writes the board-derived config files, no build. Nine targets are in the default (--all) set — zephyr-conf, dts-overlay, native-sim-overlay, cmake-args, yocto-conf, carrier-netlist, west-libraries, hw-info-h, os-topology — and three are explicit-only: zephyr-board (requires --core), composed-route-table, ipc-contract-h. |
There is no tan emit verb. Reach for tan generate --target <target> when you want one artefact.
The scaffold / validate / inspect / host-tool verbs (tan init, tan new-som, tan validate, tan doctor, tan monitor, tan model, tan explain, tan faultdecode) ride the same program. tan does not forward any verb to python -m alp_cli — since v0.5.0-rc1 tan is itself Python, and model / monitor / faultdecode / new-som are native Python inside it. See the tan CLI reference.
Four west alp-* commands survive the move. These resolve only when alp-sdk is the workspace's manifest repo — see Installation:
| Command | What it does |
|---|---|
west alp-migrate | Versions and migrates a project's board.yaml. |
west alp-lock | Writes or verifies alp.lock, the dependency lock. |
west alp-quality | Runs the quality-task registry for a profile (JSON / JUnit / SARIF). |
west alp-emit | Prints one generated artefact from board.yaml, no build — the orchestrator subset of the catalogue (system-manifest, ipc-contract-h, dts-reservations, dts-partitions, storage-mounts-c, tfm-sysbuild-conf, build-plan). |
Under the hood tan build runs four steps:
- Validates the app's
board.yaml(JSON Schema + SoM SKU preset + board preset +hw_rev/ SDK-version compatibility + per-sliceperipherals:vs SoC caps +cores:keys against the SoM preset'stopology:+ cross-field validator rules). - Fans out into per-core slice build directories (
build/<core>-zephyr/,build/<core>-yocto/, etc.) and materialises per-slice Kconfig fragments + Yoctolocal.confsnippets. - Emits cross-slice artefacts —
build/system-manifest.yaml,build/generated/alp/system_ipc.h,build/generated/dts-reservations.dtsi— byte-stable across rebuilds. - Delegates to
west/bitbake/cmakeper slice, whichever backend that slice needs.
Step 3 — Read the source
Open examples/gpio-button-led/src/main.c. Every example is annotated as teaching material:
#include <alp/peripheral.h>
#include <alp/e1m_pinout.h>
int main(void) {
alp_gpio_t *led = alp_gpio_open(&(alp_gpio_config_t){
.pin_id = ALP_E1M_GPIO_IO1,
.direction = ALP_GPIO_DIR_OUTPUT,
});
if (led == NULL) {
printk("[gpio] led open failed: err=%d\n", (int)alp_last_error());
return -1;
}
alp_gpio_t *button = alp_gpio_open(&(alp_gpio_config_t){
.pin_id = ALP_E1M_GPIO_IO0,
.direction = ALP_GPIO_DIR_INPUT,
.pull = ALP_GPIO_PULL_UP,
});
for (int i = 0; i < 4; i++) {
alp_gpio_write(led, i & 1);
k_msleep(500);
}
bool pressed;
alp_gpio_read(button, &pressed);
printk("[gpio] is_pressed pressed=%d\n", pressed);
alp_gpio_close(led);
alp_gpio_close(button);
return 0;
}
Key points:
- Handles (
alp_gpio_t *) are opaque and obtained fromalp_*_open(). - Failures stamp
alp_last_error()(thread-local) — check it whenever a handle isNULL. - Instance IDs (
ALP_E1M_GPIO_IO1) are E1M-portable: this code works on every conformant SoM. alp_*_close()releases the handle — observe it for clean shutdown.
See the <alp/peripheral.h> reference.
Step 4 — Target real silicon
There is no -b / --board selector: the target comes from the project's own board.yaml (som.sku + preset), which tan build resolves to the qualified Zephyr board string for you.
For the E1M EVK populated with an AEN SoM (single-OS, Zephyr):
cd alp-workspace/alp-sdk/examples/peripheral-io/gpio-button-led
tan build
tan flash
For the E1M-X EVK populated with a V2N SoM (heterogeneous, A55 Yocto + M33 Zephyr):
cd alp-workspace/alp-sdk/examples/multicore/rpmsg-v2n
tan build # fans out per-core
tan image # assemble the bundle
tan flash # boot_order-aware programming
tan flash walks system-manifest.yaml's boot_order: and dispatches each artefact to the right backend — vendor flasher for the SoC, openocd-via-SWD for the GD32 helper MCU, USB-CDC bootloader for the CC3501E. No developer-side tool-selection.
Each example's boards/ directory carries an overlay that maps the application's alp,pin-array slots to specific EVK pins. The overlay applies automatically when you build for the matching board.
For SoMs without a published EVK board file yet, write your own board file under alplabai/alp-zephyr-modules or a private board layer. See docs/porting-new-som.md.
Step 5 — Explore more examples
Every wrapped peripheral has a corresponding minimal example:
cd alp-workspace
for ex in peripheral-io/pwm-led-fade peripheral-io/adc-voltmeter \
peripheral-io/i2c-scanner peripheral-io/spi-loopback \
peripheral-io/uart-echo peripheral-io/uart-rx-ringbuf \
peripheral-io/can-loopback peripheral-io/qenc-readout \
power-timing/counter-alarm power-timing/rtc-clock \
power-timing/wdt-feed audio/i2s-tone; do
(cd alp-sdk/examples/$ex && tan run)
done
On native_sim most peripherals don't have emul controllers (only I²C / SPI / GPIO / UART do). Examples that target unwrapped peripherals exit after printing the alp_last_error() diagnostic — that's expected and proves the wrapper plumbing compiles and links cleanly.
End-to-end reference apps:
examples/aen/edgeai-vision-aen/— camera → Ethos-U inference → display overlayexamples/iot-connected-camera/— camera → DRP-AI → MQTT publish
Heterogeneous flagships (one board.yaml drives both halves):
examples/rpmsg-v2n/— V2N101 A55 (Yocto) ↔ M33-SM (Zephyr) over RPMsg.examples/rpmsg-aen/— AEN801 A32 (Yocto) ↔ M55-HP (Zephyr).examples/rpmsg-imx93/— iMX93 A55 (Yocto) ↔ M33 (Zephyr).examples/heterogeneous-offload/— A-cluster delegates FFT to the M peer over RPMsg.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
tan: command not found | tan ships separately from the SDK; install it with curl -fsSL https://raw.githubusercontent.com/alplabai/tan-cli/main/install.sh | sh (Windows PowerShell: irm https://raw.githubusercontent.com/alplabai/tan-cli/main/install.ps1 | iex). No Rust toolchain and no host Python interpreter are involved. |
alp: command not found, or west: unknown command "alp-build" | Retired in alp-sdk v0.12.0 (ADR-0020 Phase 4). The alp binary and the west alp-{build,image,flash,clean,size,renode} extensions are gone — use tan instead. west alp-migrate / alp-lock / alp-quality / alp-emit still need a bootstrapped workspace (west init -m https://github.com/alplabai/alp-sdk). |
validate_board_yaml.py exit 2 | som.sku doesn't match any preset under metadata/e1m_modules/, or cross-field validator rule failed. Check spelling and the rule hint. |
validate_board_yaml.py exit 3 | hw_rev incompatible with this SDK version. Update SDK or pick a compatible rev. |
Handle is NULL and alp_last_error() == ALP_ERR_NOSUPPORT | Peripheral wrapper not implemented for this OS/SoM yet. Check the test plan. |
Handle is NULL and alp_last_error() == ALP_ERR_OUT_OF_RANGE | Requested config exceeds the SoC's documented caps (e.g. 16-bit ADC on a 12-bit SoC). |
Full troubleshooting index: docs/troubleshooting.md.
What next?
board.yamlreference — flat schema + the declarative blocks- Heterogeneous builds — dual-OS project walkthrough
- Firmware quickstart — per-SoM patterns
- API reference — including
<alp/rpc.h>for cross-core RPC - Examples catalogue