<alp/hw_info.h> — Hardware Identification
Runtime structure populated from the on-module EEPROM manifest. Used at boot to confirm the running firmware matches the hardware revision.
The EEPROM manifest is the sole authoritative source of the SoM identity — it travels with the module, so it is the module's identity. There is no ADC resistor-divider cross-check on the SoM side. Carrier boards may still encode their own revision on a board-side BOARD_ID divider; that is a separate, board-side path, independent of the SoM revision.
Header
#include <alp/hw_info.h>
#include "alp_hw_info_build.h" /* generated by --emit hw-info-h */
Read the manifest
alp_hw_info_t info;
if (alp_hw_info_read(&info) != ALP_OK) {
// EEPROM unprogrammed, corrupt, unreachable, or no bus configured —
// decide app policy: halt, degrade, log + continue, ...
}
printk("SoM: %s\n", info.som_sku);
printk("Family: %s\n", info.som_family);
printk("Rev: %s\n", info.som_hw_rev);
printk("Serial: %s\n", info.som_serial);
printk("Mfg: %04u-%02u-%02u\n", info.som_mfg_year, info.som_mfg_month, info.som_mfg_day);
Return codes
| Code | Meaning |
|---|---|
ALP_OK | Valid manifest read — magic, schema_version, and CRC32 all check out. |
ALP_ERR_INVAL | out is NULL. |
ALP_ERR_NOT_PROVISIONED | EEPROM reads back blank/unprogrammed (no ALPH magic). Expected on units that have not been through production test. |
ALP_ERR_IO | Magic present but the manifest is corrupt (bad schema_version / CRC). |
ALP_ERR_NOT_READY | EEPROM/I2C layer reports the device unavailable (NAK, bus fault, missing chip). |
ALP_ERR_NOSUPPORT | No EEPROM bus configured (CONFIG_ALP_SDK_HW_INFO_EEPROM_I2C_BUS_ID unset / < 0). Out-struct is zero-filled. |
ALP_ERR_NOT_PROVISIONED is the one your app should treat as a policy decision rather than a hard fault: a blank EEPROM means an unprovisioned unit, not broken hardware. See the v2n-board-id-readout example for the recommended handling.
Assert at boot
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);
// Halts boot on mismatch; logs the discrepancy.
The ALP_HW_BUILD_* macros come from the build-time companion header emitted by alp_orchestrate.py --emit hw-info-h based on your board.yaml:
ALP_HW_BUILD_SOM_SKU— e.g."E1M-AEN701"ALP_HW_BUILD_SOM_FAMILY— e.g."aen"ALP_HW_BUILD_SOM_HW_REV— e.g."r1"ALP_HW_BUILD_OS— e.g."zephyr"ALP_HW_BUILD_BOARD_NAME— when a board is declared (inline or viapreset:)ALP_HW_BUILD_BOARD_HW_REV
Where the manifest comes from
The 128-byte EEPROM manifest is programmed at production-test time using scripts/program_eeprom.py:
python3 scripts/program_eeprom.py \
--board-yaml board.yaml \
--serial AL-2025-000123 \
--mfg-date 2026-05-13 \
--output build/manifest.bin
The packer reads identifiers from board.yaml, adds serial + mfg date, and emits a binary that the production-test flow burns to the on-module 24C128 EEPROM.
Board-side BOARD_ID ADC
Carrier boards (not SoMs) may carry a single ADC channel fed by a resistor divider that encodes the board's hardware revision. When the board preset declares one, the decode lands in the board_name / board_hw_rev / board_id_mv fields of alp_hw_info_t; the fields stay zero/empty otherwise. This path is independent of the SoM identity — the SoM revision comes only from the EEPROM manifest.
Why one ADC pin instead of GPIO straps: the E1M form factor has no spare GPIO pads for board-ID resistor straps — every pad is allocated by the spec. A single ADC channel distinguishes up to ~8 revisions at ±100 mV bin radius with 1 % resistors on a 1.8 V rail. Per-rev resistor values and nominal mV readings live in the board's hw-revisions table.
SoC identity — silicon-level identifiers
[ABI-EXPERIMENTAL] · new in v0.9
alp_hw_info_t identifies the assembled module (SKU / hw-rev / factory serial from the EEPROM manifest). A companion surface identifies the silicon — the SoC die revision, the factory-fused unique serial, and the version of the secure / system-controller firmware servicing the die. On SoCs whose power / boot / identity services live behind a dedicated controller core, the backend queries that controller; fields it cannot source stay zero/empty.
alp_soc_info_t soc;
alp_status_t s = alp_soc_info_read(&soc); // zero-fills soc, stamps soc_ref first
printf("Silicon: %s\n", soc.soc_ref); // always populated, even on failure
if (s == ALP_OK) {
printf("Secure FW: %s\n", soc.secure_fw_version);
printf("Part/rev: 0x%08x / 0x%08x\n", soc.part_number, soc.revision_id);
printf("Lifecycle: 0x%08x\n", soc.lifecycle);
// soc.serial[0..soc.serial_len) = factory-fused unique serial bytes
}
alp_soc_info_read() zero-fills out, stamps soc_ref from the build-time silicon reference, then asks the active backend to fill the runtime fields. On platforms without a queryable secure / system controller it returns ALP_ERR_NOSUPPORT with soc_ref as the only populated field — so callers that just need "which silicon is this build for" can use that best-effort result unconditionally.
alp_soc_info_t fields
| Field | Type | Notes |
|---|---|---|
soc_ref | char[32] | Build-time silicon reference (ALP_SOC_INFO_REF_LEN = 32), e.g. "alif:ensemble:e8". Always filled, even when the runtime query fails. |
secure_fw_version | char[80] | Secure / system-controller firmware version string, NUL-terminated (ALP_SOC_INFO_FW_VERSION_LEN = 80). Empty when the platform has no queryable controller firmware. |
part_number | uint32_t | SoC part-number code (vendor-defined encoding); 0 when unavailable. |
revision_id | uint32_t | SoC die-revision identifier; 0 when unavailable. |
lifecycle | uint32_t | Secure-lifecycle state code (implementation-defined; see the per-SoM HW reference for the legend); 0 when unavailable. |
serial | uint8_t[16] | Factory-fused SoC unique-serial bytes (ALP_SOC_INFO_SERIAL_MAX_LEN = 16). |
serial_len | uint8_t | Valid bytes in serial (0 when unavailable). |
alp_soc_info_read return codes
| Code | Meaning |
|---|---|
ALP_OK | Full runtime read. |
ALP_ERR_INVAL | out is NULL (out-struct is not written in this case). |
ALP_ERR_NOSUPPORT | No runtime identity source on this build — soc_ref is still valid. |
ALP_ERR_NOT_READY | Controller firmware asleep/unreachable (retryable). |
ALP_ERR_IO | Transport fault or a controller-rejected request; fields read before the fault stay filled. |
alp_soc_secure_fw_ping
if (alp_soc_secure_fw_ping() == ALP_OK) {
// controller answered — safe to trust identity / power-profile reads
}
A bounded liveness round-trip that proves the SoC's secure / system-controller firmware answers before the caller trusts identity or power-profile reads. Purely diagnostic — no state is read or written. Returns ALP_OK (controller answered), ALP_ERR_NOSUPPORT (build has no controller transport, e.g. native_sim), ALP_ERR_NOT_READY (asleep/unreachable, retryable), or ALP_ERR_IO (transport fault).
See also
board.yamlreference- Architecture: hardware identification
- Chip catalogue —
eeprom_24c128