Skip to content

feat(firmware): image identity block on every production STM32 image (#362) - #375

Merged
mottihoresh merged 2 commits into
mainfrom
feat/362-identity-block
Oct 26, 2026
Merged

mottihoresh merged 2 commits into
mainfrom
feat/362-identity-block

Conversation

@mottihoresh

Copy link
Copy Markdown
Collaborator

Closes #362.

Every production STM32 image now carries a 32-byte identity block at image start + 0x200. It holds the magic, layout version, size, image length, image CRC, NODE_ID, NodeKind and firmware version. The bootloader (#363), the uploader (#366) and the client (SkyHawkClient#177) read it to check an image before it is flashed or booted. The bootloader layout (4 KB offset) is staged but stays off until #363.

What changed

  • Libraries/FirmwareImage/ (new, header-only): the Identity struct, its constants and field offsets, the CRC test vectors, and OSH_FIRMWARE_IDENTITY(kind).

  • Role libraries define the block:

    • PanelGroup.cpp → PANEL_GROUP
    • PduNode.cpp → PDU
    • PanelBridge.cpp → BRIDGE

    A sketch author writes nothing.

  • Firmware/shared/ (new):

    • stm32f103.ld: the STM32 core's F103C8/CB linker script, plus .osh_identity at +0x200 with FILL(0xFF) before it.
    • stm32.ini: the shared [env] settings (linker script and post script). The board_build.flash_offset = 0x1000 lines are staged, commented out, for feat(firmware): CAN bootloader — core #363.
    • osh_image.py: the post-build step. It stamps imageLength and imageCrc into the .elf, then rewrites firmware.bin with gaps filled 0xFF, so the .bin, the .elf and the board's flash give one CRC.
    • tests/test_osh_image.py: checks the CRC vectors and field offsets against FirmwareImage.h.
  • Production projects include shared/stm32.ini:

    • Templates/PanelBridge and Templates/PanelGroup
    • PDU_Left, PDU_Right and PDU_Center
    • E2E_DCS_Test PanelBridge, PanelGroup and PanelGroupInputs

    Center_Armament (deprecated, no role library) and WiringCheck (a diagnostic) are left out. SoakRig follows when test(firmware): add SoakRig node sketches for the #291 soak rig #339 lands.

  • Tests: 19 per-library test projects add file://../../Libraries/FirmwareImage to their dependency lists. They keep the core's default linker script, which has no KEEP for the section, so the linker drops the block from test builds.

  • CI and release:

    • Firmware CI's quick job runs the stamp test on PRs.
    • release-please bumps FirmwareImage/library.json.
    • The PlatformIO impact map is regenerated.
  • Spec and docs:

    • Decision D25.
    • FirmwarePlan 08, new section "Image Layout & Identity Block".
    • TechSpec FirmwareImage (new page) and an entry in the TechSpec README.
    • docs/firmware/platformio-setup.md, new section "The production build lines".
    • docs/firmware/build-flags.md: three line-number citations moved after the new lines, plus a new citation for FirmwareImage.h as a NODE_ID reader.

Verification

  • All in-scope projects build and stamp. Examples:
    • bridge: node 0 · kind 3 · 32088 B · 2A9D9F5C
    • Templates PanelGroup: node 1 · kind 1 · 27412 B · 84E99DEF
    • PDUs: nodes 3, 4 and 5 · kind 2 · ~32.4 KB each
  • A trial build with flash_offset = 0x1000 puts the block at 0x08001200.
  • All 19 affected test projects build from clean libdeps.
  • pytest Firmware/shared/tests passes (5 tests). check_doc_refs.py, the impact-map check and check_node_ids.py also pass.
  • Bench (Gateway board, 2026-10-25):
    • The stamped bridge image was flashed over ST-Link and read back. It is byte-for-byte equal to firmware.bin, and the recomputed CRC matches the stamp, so the gap reads 0xFF on the chip.
    • The boot log is the same as the previous image's.
    • The Gateway answers HELLO_ACK.
    • A roster request sent over USB came back as _NODE_STATUS through the full USB → RP2040 → UART → bridge relay.
    • The board was then restored to its original image.

Notes

…362)

Every production STM32 image carries a 32-byte identity block at image
start + 0x200 (magic, layout, size, length, CRC, NODE_ID, NodeKind,
version), defined by the role library and stamped after linking by
Firmware/shared/osh_image.py with gaps filled 0xFF. The bootloader
(#363), uploader (#366) and client read it. The 4 KB bootloader offset
is staged in Firmware/shared/stm32.ini for #363. D25.
@mottihoresh mottihoresh added this to the Firmware v1.0.0 milestone Oct 26, 2026
@mottihoresh mottihoresh added enhancement New feature or request firmware Firmware implementation work priority: medium Wanted and scoped; not blocking today labels Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📝 Docs site may need an update

This PR touches files the docs site is built from. Before merging,
confirm the published pages still match:

  • New hardware (panel / PCB / CAD) has a matching page under docs/
  • Changed design decisions are reflected on the relevant page
    (e.g. architecture/design-decisions.md, the hardware reference)
  • No page now contradicts docs/_source/ or hardware-standards.md
  • Structured tables (NODE_IDs, pinouts) are transcluded, not retyped

Backstop: the weekly docs-drift review. Ignore docs/api/ (auto-generated).

Comment thread Firmware/shared/osh_image.py Fixed
Comment thread Firmware/shared/osh_image.py Fixed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request firmware Firmware implementation work priority: medium Wanted and scoped; not blocking today

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(firmware): image identity block and bootloader-ready app layout

1 participant