Firmware Engineer Quickstart
This guide takes a firmware engineer from "I have an E1M-X module on the bench" to a working application built against the SDK. It sits alongside the Quick Start walk-through; this page focuses on the choices and patterns that matter when you're targeting a specific SoM and writing real firmware.
Pick a target
| If your hardware is... | som.sku | Carrier default |
|---|---|---|
| E1M-AEN family on the E1M EVK | E1M-AEN701 (etc.) | E1M-EVK |
| E1M-X V2N101 / V2N102 on E1M-X-EVK | E1M-V2N101 | E1M-X-EVK |
| E1M-X V2N-M1 (V2M101 / V2M102) | E1M-V2M101 | E1M-X-EVK |
| E1M-NX9101 (NXP i.MX 93) | E1M-NX9101 | E1M-EVK |
The per-SoM page covers what's populated, which examples target it, and the bring-up flow.
Workspace setup
If you haven't already, follow the Quick Start §1–2. That gets you a alp-workspace/ with alp-sdk/, zephyr/, and the standard modules.
Your first board.yaml
Example for a V2N101 application that exercises the on-module GD32 bridge:
schema_version: 2
som:
sku: E1M-V2N101
carrier:
name: E1M-X-EVK
cores:
m33_sm:
os: zephyr
app: ./src
peripherals: [spi, i2c]
chips:
- gd32g553
diagnostics:
log_level: info
west alp-build validates this, generates the build-time config, and delegates to west build. See the board.yaml reference.
Pick a starting example
The examples/ tree carries reference apps that double as tutorials. Comment density is ~50 % so the source teaches by itself.
Cross-family examples (work on any SoM)
| Example | What it shows |
|---|---|
gpio-button-led | Open a GPIO, set up input + output, basic patterns. |
i2c-scanner | Walk the I²C bus, report devices that ACK. |
pwm-led-fade | PWM channel open + duty sweep. |
adc-voltmeter | Sample an ADC channel; convert to millivolts. |
uart-echo / uart-rx-ringbuf | UART TX / RX, with optional byte-granular ring buffer. |
rtc-clock | Set + read the SoC RTC. |
wdt-feed | Watchdog open + feed cadence. |
V2N / V2N-M1-specific examples
| Example | What it shows |
|---|---|
v2n-gd32-bridge-ping | PING + GET_VERSION round-trip on both SPI + I²C transports |
v2n-board-id-readout | Read the SoM EEPROM manifest + assert SKU |
v2n-ethernet-dual | Bring up both RTL8211FDI PHYs |
v2n-pwm-fan-control | Ramp a GD32-side PWM channel along a five-stop fan curve |
Build any of them:
cd alp-workspace
west alp-build -b <board> alp-sdk/examples/<name>
west build -d build -t run # native_sim
# or:
west flash # real silicon
Idiomatic patterns
Open an I²C bus + talk to a chip
#include <alp/peripheral.h>
#include <alp/chips/tmp112.h>
alp_i2c_t *bus = alp_i2c_open(&(alp_i2c_config_t){
.bus_id = 0u,
.bitrate_hz = 400000u,
});
if (bus == NULL) {
printk("[i2c] open failed: err=%d\n", (int)alp_last_error());
return;
}
tmp112_t temp;
if (tmp112_init(&temp, bus, 0x40) != ALP_OK) {
printk("[tmp112] init failed\n");
}
Every on-module chip driver follows the same shape: <chip>_init(&ctx, bus, addr_or_handle) then per-feature getters / setters. Drivers under chips/<part>/<part>.c; public headers under <alp/chips/<part>.h>.
Use the GD32 bridge from V2N firmware
The V2N's GD32G553 supervisor MCU owns every E1M-standard analog + counter peripheral plus side-channel GPIOs: eight PWM channels, eight-channel ADC bank, both DAC outputs, four quadrature encoders, the Wi-Fi/BT REG_ON pins, the OPTIGA reset, and the cached PMIC status forwarder. Reach it over SPI (fast path) or I²C (management path):
alp_spi_t *spi = alp_spi_open(&(alp_spi_config_t){
.bus_id = 1u,
.freq_hz = 10000000u,
.mode = ALP_SPI_MODE_0,
.bits_per_word = 8u,
.cs_pin_id = 0u,
});
alp_i2c_t *brd_i2c = alp_i2c_open(&(alp_i2c_config_t){
.bus_id = 0u,
.bitrate_hz = 400000u,
});
gd32g553_t bridge;
gd32g553_init(&bridge, spi, brd_i2c, GD32G553_BRIDGE_DEFAULT_I2C_ADDR);
uint32_t period_ns = 1000000u; // 1 kHz
uint32_t duty_ns = 500000u; // 50 %
gd32g553_pwm_set(&bridge, /* channel */ 0u, period_ns, duty_ns);
Full wire spec: docs/gd32-bridge-protocol.md.
Monitor the PMIC fleet
On V2N + V2N-M1 the secondary PMIC's status reflects rail health. Idiomatic supervisor task:
da9292_t pmic;
da9292_init(&pmic, brd_i2c, DA9292_I2C_ADDR_V2N);
while (1) {
da9292_status_t s;
if (da9292_get_status(&pmic, &s) == ALP_OK) {
if (s.temp_warn || s.vin_uvlo || !s.ch1_pg) {
log_warning(&s);
}
}
k_msleep(1000);
}
Dual Ethernet on V2N
static int my_mdio_read(uint8_t phy_addr, uint8_t reg, uint16_t *val, void *user) {
return zephyr_mdio_read(user, phy_addr, reg, val);
}
static int my_mdio_write(uint8_t phy_addr, uint8_t reg, uint16_t val, void *user) {
return zephyr_mdio_write(user, phy_addr, reg, val);
}
rtl8211fdi_t phy0;
rtl8211fdi_init(&phy0, /* phy_addr */ 0,
my_mdio_read, my_mdio_write, mdio_dev);
rtl8211fdi_soft_reset(&phy0, 500000);
rtl8211fdi_restart_autoneg(&phy0);
V2N-M1: bring DEEPX up before opening PCIe
Three extra steps beyond V2N base:
da9292_v2n_m1_enable_deepx_rail(&pmic, 50000)— the 0.75 V DEEPX rail on the secondary PMIC's CH2.- ACK-probe the three DEEPX TPS628640 instances at
0x44 / 0x48 / 0x4Fto confirm population. deepx_dxm1_bring_up(&dxm1, DEEPX_DXM1_DEFAULT_BOOT_US)— the PCIe muxes +M1_RESETsequencer.
Full sequence with code: docs/bring-up-v2n-m1.md.
V2N: share one BRD_I2C handle across the on-module fleet
The V2N's board-management I²C bus (BRD_I2C) hosts both PMICs, the RTC, OPTIGA Trust M, TMP112, the clock generator, and the EEPROM. Open the bus once at boot and pass the same alp_i2c_t * handle into every chip driver — every chips/<part>/ API accepts a borrowed bus pointer; none take ownership.
alp_i2c_t *brd_i2c = alp_i2c_open(&(alp_i2c_config_t){
.bus_id = 0u, /* BRD_I2C — board-management */
.bitrate_hz = 400000u,
});
act8760_t pmic_primary;
da9292_t pmic_secondary;
optiga_t secure_element;
rv3028c7_t rtc;
act8760_init(&pmic_primary, brd_i2c, ACT8760_I2C_ADDR_V2N);
da9292_init(&pmic_secondary, brd_i2c, DA9292_I2C_ADDR_V2N);
optiga_init(&secure_element, brd_i2c, OPTIGA_TRUST_M_I2C_ADDR);
rv3028c7_init(&rtc, brd_i2c);
For V2N-M1, the secondary PMIC's DEEPX-rail management (CH2 voltage step + 0.75 V enable) follows the same pattern — da9292_v2n_m1_enable_deepx_rail() takes the shared handle and gates on a single PMIC instance. See docs/bring-up-v2n-m1.md for the sequencing.
Chip driver opt-in
Every chip driver is gated on CONFIG_ALP_SDK_CHIP_<NAME>=y so unused chips don't cost code size. Two ways to flip the flag:
- Declarative (recommended) — list the chip under
chips:inboard.yaml. The loader sets the Kconfig automatically. - Manual — add
CONFIG_ALP_SDK_CHIP_GD32G553=yetc. to yourprj.conf.
The full chip catalogue is at metadata/chips/. See the chip page for the in-tree summary.
Build for real silicon
west alp-build figures out the cross-compile target from the SoM's silicon: field. Common boards:
# AEN (Alif Ensemble)
west alp-build -b alp_e1m_evk_aen alp-sdk/examples/gpio-button-led
west flash
# V2N (RZ/V2N)
west alp-build -b alp_e1m_evk_v2n alp-sdk/examples/v2n/v2n-gd32-bridge-ping
west flash
The exact <board> argument depends on whether you're using the upstream Zephyr board file or the alplabai/alp-zephyr-modules overlay.
Per-SoM bring-up references
The SDK ships detailed bring-up walk-throughs for each SoM family:
| Family | Bring-up doc |
|---|---|
| E1M-AEN | docs/bring-up-aen.md |
| E1M-X V2N | docs/bring-up-v2n.md |
| E1M-X V2N-M1 | docs/bring-up-v2n-m1.md |
| E1M-N93 | docs/bring-up-imx93.md |
Deep tutorials
For longer end-to-end walk-throughs (the examples teach a single peripheral; tutorials teach a flow), see docs/tutorials/ in the SDK repo:
| # | Tutorial | Scope |
|---|---|---|
| 01 | first-build | Hello-world on native_sim |
| 02 | i2c-scan | I²C scanner + populated-chip reasoning |
| 03 | pwm-fade | PWM channel basics |
| 04 | cross-family-portability | One app, AEN + V2N + i.MX 93 |
| 05 | supervisor-mcu-bridge | V2N's GD32 bridge — protocol + transports |
| 06 | secure-element-sign | OPTIGA Trust M ECDSA |
| 07 | recovering-a-bricked-bridge | Host-driven SWD bit-bang reflash |
| 08 | runtime-board-detection | EEPROM manifest + BOARD_ID ADC |
| 09 | board-yaml-deep-dive | Every block, every override |
| 10 | secure-boot-signing | MCUboot signing on AEN |
| 11 | mqtt-tls-publish | <alp/iot.h> + <alp/security.h> |
| 12 | mender-ota | Yocto OTA (V2N + V2N-M1) |
| 13 | eeprom-provisioning | Production-line manifest programming |
| 14 | audio-loopback | PDM in → I²S out with DSP chain |
| 15 | mproc-mailbox | Multi-proc IPC end-to-end |
| 16 | inference-mobilenet | TFLite-Micro on Ethos-U / DRP-AI / DEEPX |
Getting help
- File issues at github.com/alplabai/alp-sdk/issues. Include the output of
west alp-build --version, yourboard.yaml, and the fullwest buildlog. - For chip-driver bugs: include the chip's
metadata/chips/<part>.yamldriver status (stubchips returnALP_ERR_NOSUPPORTby design). - Community: the Alp Lab Forum.