Build System

OpenSMA’s build has two layers: the top-level orchestrator mcu_build.sh, and the UBS make system under corepdk/ubs/ that it invokes through the ./ubs wrapper. This chapter traces both.

Entry point: mcu_build.sh

mcu_build.sh parses KEY=value arguments (mcu_build.sh:20); the usage banner lists them all (mcu_build.sh:6):

Selector Meaning Example
PROJECT Buildable project (src/projects/<name>) mcxn547helloworld
PLATFORM SoC/core platform mcxn556-both
BOARD Board variant (src/boards/<name>) mcxn547helloworld
MODE Build mode (opt/debug) rel, dev
SIGN_KEYSET Signing keyset local, local-prod, S0
VERSION Firmware version string 02.0020.0000
STREAMBOOT Emit multi-binary streaming image 1
RUN_LOCAL Run outside the UBS Docker image 1
BUILD_ONLY Build without signing/downloading 1

Unset selectors fall back to *_ENV environment variables (mcu_build.sh:36). The toolchain root defaults to /toolchain (mcu_build.sh:44) and the build/sign container defaults to a pinned UBS_DOCKER image (mcu_build.sh:45).

The build flow

run_build_flow() (mcu_build.sh:61) assembles the per-core environment and drives the build. For the dual-core “both” platforms it builds core1 first and embeds it into core0 — see Architecture.

The actual build is a UBS make invocation assembled into BUILD_CMD="./ubs build" (mcu_build.sh:211) with the selectors appended, then eval‘d. ./ubs is a symlink to corepdk/ubs/ubs, the make front-end; the build tree, plugins, and share files live under corepdk/ubs/.

flowchart LR
    A[mcu_build.sh] --> B[run_build_flow]
    B --> C["./ubs build<br/>(corepdk/ubs)"]
    C --> D{STREAMBOOT=1?}
    D -->|yes| E["./ubs multi-bin"]
    D -->|no| F
    E --> F{BUILD_ONLY=1?}
    F -->|yes| Z[stop]
    F -->|no| G["./ubs local-sign<br/>(if SIGN_KEYSET=local*)"]
    G --> H[signed image in build/]

Make fragments (etc/)

UBS composes a build from make fragments under etc/:

Directory Role
etc/platforms/ Per-platform makefiles (mcxn236.mk, mcxn556-core0.mk, mcxn556-core1.mk, mcxn547-*.mk, x86.mk) and configs (nvheader-cfg.json, pldmfw-cfg-*.json)
etc/projects/ Per-project/module makefiles (mcxn547helloworld*.mk, module-mctp-cpp.mk, module-pldm-fd.mk, module-spdm.mk, nvheader.mk)
etc/plugins/ Build plugins: local-sign.mk, sign-request.mk, sign-download.mk, flash.mk, multi-bin.mk, nv-coverage-check.mk, ubs-format.mk, ubs-module.mk
etc/features.mk Feature toggles (etc/features.mk)

This fragment layout is why adding a platform or project is mostly a matter of dropping the right .mk files under etc/platforms/ and etc/projects/.

Signing and keysets

After a successful build (and unless BUILD_ONLY=1, mcu_build.sh:253), signing runs. core1-only platforms are not signed (mcu_build.sh:258). Local signing triggers when SIGN_KEYSET starts with local (mcu_build.sh:265) and assembles ./ubs local-sign (mcu_build.sh:268):

  • SIGN_KEYSET=local / local-debugKEY_TYPE=DEBUG
  • SIGN_KEYSET=local-prodKEY_TYPE=PROD

The signing make logic lives in etc/plugins/local-sign.mk, which pulls in etc/plugins/sign-request.mk (etc/plugins/local-sign.mk:19) and branches per chip. Non-local keysets (e.g. S0) route to the request/download signing plugins (sign-request.mk, sign-download.mk) instead. Signing uses NXP’s spsdk (installed in Setup).

Streaming boot & multi-binary

With STREAMBOOT=1, mcu_build.sh additionally runs ./ubs multi-bin (mcu_build.sh:232) to emit the multi-binary streaming image, governed by etc/plugins/multi-bin.mk.

Docker vs local

By default the build/sign steps run inside the pinned UBS Docker image (mcu_build.sh:45); RUN_LOCAL=1 runs them directly on the host instead. The image is built from libexec/ubs.dockerfile (see Setup).

Next


Built with Just the Docs. Source-traced against NVIDIA/OpenSMA at 9b183d3 (v02.0020.0000). Methodology under appendices/trace-methodology.

This site uses Just the Docs, a documentation theme for Jekyll.