Skip to main content

AEN bench + bring-up tools

The examples/aen/ tree holds a second class of app that sits outside everything else on this site.

These are not portable examples. None of them carries a board.yaml. That single fact has consequences worth stating plainly:

  • They are not in the 95-example catalog — the catalog is generated from board.yaml, so an app without one is invisible to it by construction.
  • They are outside the portable-example contract and the example-portability lint, which both key off board.yaml. Nothing stops them reaching straight for Zephyr driver headers, vendor HAL clients, or raw register bases — and most of them do.
  • They are standalone Zephyr apps, built directly against a board target with no alp_project.py flow.

They exist to validate E8 silicon, not to be copied. Many say so in their own source: "BENCH-VALIDATION app — not a customer teaching example."

:::tip Building an application? Start elsewhere Use the portable examples: hello-world to prove your toolchain, then i2c-master, spi-master, uart-hello-world. For AEN-specific application patterns see edgeai-vision-aen or aen-npu-inference.

This page is for people bringing up silicon, debugging a driver, or trying to understand what the bench actually proved. :::

The aen-*-regcheck suite belongs to the same class and has its own page. Everything below is the non-regcheck remainder.

How they're observed

Most of these run on a scopeless bench: no oscilloscope, and the app UART is often not routed to USB. Three recurring mechanisms follow from that:

  • RAM console. The app prints to a ram_console_buf symbol that the operator reads over J-Link mem8 and ASCII-decodes.
  • Global SRAM0 beacons. Liveness is stamped into fixed, master-agnostic addresses in SRAM0 that J-Link can read from whichever debug AP it attached to. This is how the dual-core apps prove a second core is alive when its own AP isn't reachable.
  • RESULT PASS: / RESULT FAIL: — one line per app, greppable.

Source tree: examples/aen/.

Dual-core + IPC

The E8 has two M55 cores (HP and HE). At power-on only one is released — these apps establish which, and how to start the other.

ExampleWhat it proves
aen-dualcore-probeThe decisive "does a dual-entry ATOC boot both M55s?" test. One app, role-by-board: each core stamps a magic word at its own global-SRAM0 beacon then advances a heartbeat forever. Both advance → the Secure Enclave booted both from one power-on. Only HP advances → single-core boot.
aen-dualcore-masterThe answer to the above: the SES-booted core starts the other M55 at runtime via the portable alp_mproc_boot_core(), instead of relying on a dual-boot ATOC. The portable call resolves through the backend registry to the SoM's boot authority — on AEN, the Secure Enclave's boot service. Application code carries no vendor include.
aen-dualcore-doorbellThe HE→HP MHU-1 doorbell — testable only once both cores boot. The HP master releases HE, then HE rings HP. Beacons count doorbells sent vs received; if the received count advances, the doorbell propagates.
aen-hp-core-smokeFirst light for the M55-HP core — the first SDK image targeted at the second M55 (every prior bench app ran on HE). It writes a liveness beacon to global SRAM0 rather than relying on the HP core's own RAM console, because the bench reads memory over the HE/AXI debug AP.
aen-alp-rpcThe same dual-M55 ping/pong as aen-rpc-pingpong, but driven through the portable <alp/rpc.h> surface instead of raw Zephyr ipc_service calls. The teaching point: customer code calls a handful of vendor-clean alp_rpc_* functions while the backend does the identical ipc_service dance over the same MHUv2 mailbox + shared SRAM0 vring.

TrustZone + secure element

ExampleWhat it proves
aen-tz-secure-log-probeSilicon-proven. The question: on one M55, can the application be hardware-blocked from writing the MRAM log window? The answer is core-local TrustZone-M. The probe configures the SAU + TGU to carve a Non-Secure chunk out of DTCM, drops into a tiny NS stub, and has it store to the default-Secure MRAM log window. Pass = that store raises a SecureFault (SFSR set, SFAR = the log address) while a Secure store to scratch succeeds first. The app cannot lift the block — SAU/TGU registers are Secure-only.
aen-tz-secure-log-appendSilicon-proven. The end-to-end enforced append. The Non-Secure app cannot write the log store, so it appends by writing a request into a shared NS mailbox and returning to the Secure owner, which performs the privileged write. This is the single-core mirror of the two-core owner/client split. Pass = the owner appended on the app's behalf and the app's own direct store raised SFSR.AUVIOL. Notably it uses only the S→NS call direction, because Alif silicon does not attribute a custom NSC veneer window.
aen-se-service-infoStaging check of the Secure Enclave service transport. Deliberately vendor-specific — the sanctioned bring-up escape hatch. It asserts the DT nodes bind at the fork's reg/IRQ facts and drives the hal_alif client directly, which no portable surface can or should express.
aen-se-service-queryThe portable companion to the above, and it carries no vendor include. Where -info proves the SE mailboxes bind, this shows what customer code calls once they do: alp_soc_secure_fw_ping / alp_soc_info_read (<alp/hw_info.h>), alp_power_profile_get (<alp/power.h>), alp_random_bytes (<alp/security.h>). All read-only.
aen-se-cryptoThe compute surface riding the same SE mailbox: a known-answer SHA-256, an AES-128-GCM round-trip, and a TRNG pull — all through the vendor-clean alp_hash_* / alp_aead_* / alp_random_bytes API. Nothing in the app names the SE: the SE-CryptoCell backend registers at the E8's silicon ref with a priority that wins over the portable MbedTLS-PSA backend, so the work lands on dedicated silicon transparently.

:::note Secure-element scope The SE backends are scoped to E8 only. Separately, the OPTIGA Trust M driver is probe-only — signing and key generation return ALP_ERR_NOSUPPORT. :::

NPU + vision

ExampleWhat it proves
aen-npu-inference-alifMatched-runtime Ethos-U85 inference on a hermetic tiny_int8.tflite fixture, through the Arm InferenceProcess wrapper → TFLM MicroInterpreterethosu_invoke(). This is the example whose silicon bring-up fixed the long-standing ethosu_invoke=1 residual — via two in-app strong weak-overrides: ethosu_address_remap (the CPU→NPU address translation any TCM-resident buffer needs) and an ethosu_config_select() returning the SRAM AXI port, because Alif's SRAM-only memory mode places model/arena/IO in SRAM0 while the Arm default routes region 0 and the command stream to the EXT port and aborts.
aen-npu-inference-person-mramBench-verified on E8 (2026-06-17.) The production-scale companion: the real person_detect MobileNet resident in MRAM slot0. Vela'd for the U85 it is ~263 KiB and overflows the 256 KiB ITCM RAM-run region, so it links into slot0 and is SE-booted from MRAM. Vela reports CPU operators = 0 (0.0 %) / NPU operators = 44 (100.0 %), ~7.1 M MACs; runJob completes with a populated output.
aen-dsi-displayThe pixels-on-glass successor to aen-dsi-regcheck (which only proved the chain binds). It turns on the CDC200 + DSI display-class drivers and renders a solid-colour framebuffer to a Rocktech 720×1280 MIPI-DSI panel. The 720×1280 RGB565 framebuffer is 1.84 MB, so it lives in SRAM0 — it does not fit ITCM, where the RAM-run links code.
aen-gpu2d-benchThat the portable <alp/gpu2d.h> surface runs correctly on the real M55-HE — through the pure-C software-fallback backend, not a 2D engine. Every op is checked against an exact expected pixel value derived from the fallback's own formulas. See the note below.

:::warning What aen-gpu2d-bench does and doesn't prove It validates the software fallback, and its own source says why: a plain build selects sw_fallback because the Alif D/AVE 2D hardware backend is opt-in and bench-unverified. The DAVE2D backend is self-declared BENCH-UNVERIFIED / STRUCTURAL and needs a proprietary, un-vendored Alif pack — so GPU2D acceleration is effectively unavailable today, and the CPU pixel path is what actually runs.

That still makes the bench useful: it confirms the same dispatcher + CPU path that every no-2D-engine target (V2N, i.MX 93, native_sim) uses executes correctly on Alif silicon, not just on the host. :::

Power

ExampleWhat it proves
aen-power-smokeStage A. A plain architectural Cortex-M55 WFI via the kernel idle path, with no PM subsystem, woken by SysTick, with the kernel surviving the round trip. A beacon in global SRAM0 plus k_uptime_get() both advance across rounds — i.e. it really slept and really woke, repeatedly.
aen-power-iwicStage B. Brings the Alif PM layer up over pinned Zephyr and proves the CONFIG_PM suspend-to-idle (deep IWIC sleep) path: it reports the PM wiring as a bind-check, then blocks long enough that the PM policy selects PM_STATE_SUSPEND_TO_IDLE, so pm_state_set() programs WICCONTROL and the core parks in WFI until SysTick wakes it.
aen-aipm-readReads the live RUN and STANDBY operating-point profiles through the portable <alp/power.h> profile surface. The teaching point: on an SoC like the E8 a system controller owns the power and clock tree — a core never pokes PLL / DC-DC / power-domain registers, it reads and writes a per-core profile instead. On the E8 that controller is the Secure Enclave and the feature is aiPM.

Storage + memory

ExampleWhat it proves
aen-mcuboot-smokeThe SES → MCUboot → slot0 → app boot chain. Built with --sysbuild; MCUboot signs the image into slot0. If this app's banner appears in the RAM console, MCUboot verified and chain-loaded it from slot0 — that's the whole proof. It also reports VTOR to characterise where the app runs from after chain-load: slot0 XIP vs copied-to-TCM.
aen-mram-flash-validateExercises the write side of the MRAM flash driver through the portable Zephyr flash_* class API — erase → readback equals erase value, write → readback equals pattern, read → byte-exact verify, and out-of-unit erase/write rejected with -EINVAL. See the scope note below.
aen-sdcard-readoutSD Host Controller bring-up plus a microSD probe via the Zephyr disk-access API. On the E1M EVK the microSD sits behind a mux whose enable and select are both on the CC3501E side, reachable only over the inter-chip SPI bridge. Until that bridge routes the mux, no card is reachable from the Alif side — so this validates controller/driver bring-up, and card init is expected to fail with "no card".

:::warning aen-mram-flash-validate — scope this claim carefully The source frames it as an on-silicon erase/program/readback proof, and it is designed as one: a RESULT PASS/FAIL line plus a J-Link mem32 readback as silicon ground truth.

But its twister row is build_only: true, with the comment "the erase/program/readback check is a bench flash, not a CI run" — and it is filtered out of the native_sim gate entirely (it only builds against the AEN801 HE board target). What CI proves is that the driver and DT node compile on the real board target. There is no bench-matrix row and no changelog entry behind a "validated on silicon" claim. Treat the erase/program verdict as a bench-operated result, not a standing one. :::

Peripheral bench

ExampleWhat it proves
aen-gpio-benchGPIO over the upstream DesignWare driver: drive a safe, uncontended pin as a push-pull output, set it high then low through the portable Zephyr GPIO API, and read back the data + direction registers after each step.
aen-uart-ns16550-loopbackThe upstream ns16550 driver on a spare UART instance — deliberately not the carrier console, not the DevKit console. The overlay sets loopback;, so the driver programs the 16550 MCR LOOP bit and TX is internally fed back to RX. A uart_poll_outuart_poll_in byte-compare exercises the real TX FIFO/THR + RX RDR path, not just registers.
aen-pwm-utimer-pwmledsThe consumer-node variant of the UTIMER PWM regcheck. Same channel, same registers — the difference is how the controller device is obtained. Going through PWM_DT_SPEC_GET on an LED child node sidesteps a codegen hazard: the alif,pwm binding re-declares #pwm-cells, so gen_defines treats the controller node as if it owned a pwms property and emits a phantom reference that can make DEVICE_DT_GET on that node fail codegen or never resolve.
aen-wdt-feedThe CMSDK watchdog over the upstream Zephyr driver (Tier-1, no vendoring). Installs a timeout, feeds it a few times, then disables it — it never lets the timeout fire, because a reset would re-boot the SES and tear down the bench RAM-console read. The API path is what this proves; the exact timeout duration rides a watchdog clock rate that is still a TRM placeholder.
aen-qenc-readoutA quadrature encoder on the UTIMER via the Zephyr sensor API. The driver scales the raw counter to degrees, so the value ranges 0–359, not raw counts. Pass needs the angle to actually change — the shaft has to be turned. Motion smaller than ~1° won't move the reading.
aen-rtc-calendarThe portable calendar surface alp_rtc_set_time() / alp_rtc_get_time() (<alp/rtc.h>) on a part with no Zephyr-RTC-class peripheral at all. The only timekeeping hardware is the always-on LPRTC — a bare free-running 32-bit counter at 32768 Hz, not a date/time register file. The SDK's calendar shim bridges the two by keeping a software epoch base plus a counter snapshot. See aen-rtc-regcheck for the bare-counter check underneath.
aen-i2s-amp-alifDrives a tone out of the E8 audio I²S through the standard Zephyr I²S API. Note what it does not prove: on the EVK the signal reaches the two TAS2563 smart-amps through a mux whose select is a CC3501E GPIO over the inter-chip bridge. So this validates the I²S controller + clock path; audible output additionally needs the mux routed and the TAS2563 configured.
aen-pdm-mic-alifCapture from the EVK's four MP34DT05 PDM microphones via the Zephyr DMIC API. Pass needs blocks with non-zero, non-constant samples — live acoustic energy, so tap or speak near the mics. A run that configures and reads cleanly but sees only silence is reported PARTIAL: the driver path is proven; check mic routing or gain rather than the driver.
aen-usb-firstlightxHCI USB host enumeration — and a good illustration of how entangled the EVK is. The E8's USB passes through a mux whose select is a CC3501E GPIO, so before the xHCI can see a device the app must bring up the CC3501E bridge and drive that pad over the SPI GPIO-proxy. Only then does it run uhc_init (DWC3 + xHCI first light) and uhc_enable (rings, Enable Slot, port reset, Address Device, EP0 GET_DESCRIPTOR).

A theme worth noticing across the last few rows: several E8 peripherals are only reachable once the CC3501E coprocessor routes a board mux for them. See aen-cc3501e-gpio for how that proxy works.

Build

Like the regchecks, these are standalone Zephyr apps — no board.yaml, no alp_project.py flow:

export ZEPHYR_BASE=<zephyr-base>
west build -p always -b alp_e1m_aen801_m55_he/ae822fa0e5597ls0/rtss_he \
examples/aen/<name> -d build/<name> -- \
"-DEXTRA_ZEPHYR_MODULES=<alp-sdk>;<hal_alif>"

Each app's boards/ overlay auto-applies for the matching board target. Apps that outgrow the ITCM RAM-run region (the NPU person-detect model, the DSI framebuffer) link into MRAM slot0 and are SE-booted instead.

See also

Questions about this page? Discuss in Community Forum