Skip to main content

<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.

#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

CodeMeaning
ALP_OKValid manifest read — magic, schema_version, and CRC32 all check out.
ALP_ERR_INVALout is NULL.
ALP_ERR_NOT_PROVISIONEDEEPROM reads back blank/unprogrammed (no ALPH magic). Expected on units that have not been through production test.
ALP_ERR_IOMagic present but the manifest is corrupt (bad schema_version / CRC).
ALP_ERR_NOT_READYEEPROM/I2C layer reports the device unavailable (NAK, bus fault, missing chip).
ALP_ERR_NOSUPPORTNo 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 via preset:)
  • 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

FieldTypeNotes
soc_refchar[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_versionchar[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_numberuint32_tSoC part-number code (vendor-defined encoding); 0 when unavailable.
revision_iduint32_tSoC die-revision identifier; 0 when unavailable.
lifecycleuint32_tSecure-lifecycle state code (implementation-defined; see the per-SoM HW reference for the legend); 0 when unavailable.
serialuint8_t[16]Factory-fused SoC unique-serial bytes (ALP_SOC_INFO_SERIAL_MAX_LEN = 16).
serial_lenuint8_tValid bytes in serial (0 when unavailable).

alp_soc_info_read return codes

CodeMeaning
ALP_OKFull runtime read.
ALP_ERR_INVALout is NULL (out-struct is not written in this case).
ALP_ERR_NOSUPPORTNo runtime identity source on this build — soc_ref is still valid.
ALP_ERR_NOT_READYController firmware asleep/unreachable (retryable).
ALP_ERR_IOTransport 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

Questions about this page? Discuss in Community Forum