BSP, boards & projects
This page catalogs the board/SoC support layer that sits beneath the
nv:: firmware and the buildable projects the Build System
turns into images. Three source trees cooperate, and the build’s three
main selectors map onto them one-for-one:
| Selector | Source tree | Provides |
|---|---|---|
PROJECT |
src/projects/<name>/ |
Per-core main.cpp, config.h, sign params, linker/core-image glue |
PLATFORM |
src/sys/<soc>/ (+ project sys/<soc>/) |
SoC drivers, startup, FreeRTOSConfig.h, linker script |
BOARD |
src/boards/<name>/ |
Pin mux, clocks, peripherals (from an .mex) |
flowchart TD
subgraph sel[Build selectors]
P[PROJECT=mcxn547helloworld]
PL[PLATFORM=mcxn556-both]
B[BOARD=mcxn547helloworld]
end
P --> PRJ["src/projects/mcxn547helloworld<br/>core0/ + core1/ + config.h"]
PL --> SYS["src/sys/mcxn556<br/>SoC drivers + startup"]
PL --> PSYS["project sys/mcxn556<br/>FreeRTOSConfig + linker + start.S"]
B --> BRD["src/boards/mcxn547helloworld<br/>pin_mux / clock_config / peripherals"]
PRJ --> IMG[[signed image]]
SYS --> IMG
PSYS --> IMG
BRD --> IMG
The example selectors above are the ones the Build System chapter uses
(mcu_build.sh:22); note that the mcxn547helloworld project is built
here on the mcxn556 platform against the mcxn547helloworld board —
project, platform and board are chosen independently.
SoC BSPs (src/sys/)
Each supported SoC has a parallel subsystem tree under src/sys/<soc>/sys/
(adc/, bootloader/, common/, crypto/, flash/, gpio/, i2c/,
i3c/, ipc/, spi/, uart/, usb/, watchdog/, …). The nv::
firmware calls into these through sys:: interfaces, so swapping the
platform swaps the whole driver tree.
| BSP | Role | Notes |
|---|---|---|
src/sys/mcu/ |
Cross-SoC shared code | Currently only a placeholder sys/i3c/driver.cpp (license-only, 17 lines) — shared home for future common drivers (interpretation) |
src/sys/mcxn236/ |
NXP MCXN236 (single-core) BSP | Full driver set; used by the testrunner project |
src/sys/mcxn556/ |
NXP MCXN556S BSP (baseline MCU) | Adds dual-core support: sys/c2c_mailbox/, sys/ipc_bm/, sys/smartdma/, and an NC-SI-over-USB stack in lib/ncsi/ |
src/sys/x86/ |
Host build for emulation / unit tests | Same subsystem tree compiled for x86, plus a FreeRTOS shim in freertos/ |
Fact: common/system.h defines the SoC-neutral System class
(src/sys/mcxn556/sys/common/system.h:22) that each project starts with
common::System::inst().scheduler_start().
The x86 host build and FreeRTOS shim
src/sys/x86/ lets the firmware build and run on a Linux host for
emulation and unit tests. Instead of a real RTOS port it uses a shim under
src/sys/x86/freertos/: FreeRTOS tasks are implemented on POSIX threads
(class Thread, src/sys/x86/freertos/port.cpp:42, using <pthread.h>,
src/sys/x86/freertos/port.cpp:26), and blocking waits use a
pthread condition-variable Event (src/sys/x86/freertos/event.h:23).
The host System::scheduler_start() just calls vTaskStartScheduler()
(src/sys/x86/sys/common/system.cpp:38) and shutdown maps onto
std::exit() (src/sys/x86/sys/common/system.cpp:63) so a test run
returns a process exit code. See Reference for how
these host builds feed CI.
Boards (src/boards/)
A board directory pairs an NXP MCUXpresso .mex configuration with the C
sources generated from it. Only the two .mex/board/ pairs below are in
scope:
| Board | .mex processor |
board/ sources |
|---|---|---|
src/boards/mcxn547helloworld/ |
MCXN547 (src/boards/mcxn547helloworld/mcxn547helloworld.mex:2) |
pin_mux.c, clock_config.c, peripherals.c, plus a trustzone/resource_config.c |
src/boards/p3957_cxx/ |
MCXN556S (src/boards/p3957_cxx/p3957_cxx.mex:2) |
pin_mux.c, clock_config.c, peripherals.c |
The .mex is the source of truth (pin labels such as THERM_OVERT and
BMC I2C SDA live in the .mex and its generated pin_mux.c), and the
generated code exposes the BOARD_Init* entry points the projects call:
BOARD_InitBootPins (src/boards/mcxn547helloworld/board/pin_mux.c:73,
src/boards/p3957_cxx/board/pin_mux.c:149), with clocks and peripherals
in the sibling clock_config.c / peripherals.c. Board-specific
electrical differences are documented per board, e.g.
src/boards/mcxn547helloworld/note.md:3 records FLEXCOMM0 I²C at 100 kHz.
Projects (src/projects/)
A project is what PROJECT= selects. It owns the main() entry point(s),
the big compile-time config.h, the signing parameters, and the
linker/startup glue — one sys/<soc>/ subdirectory per platform it can
target.
Per-core main.cpp
main() follows the same shape everywhere: disable IRQs, run the board’s
BOARD_Init* functions (src/projects/mcxn547helloworld/core0/main.cpp:243),
start the watchdog, create each subsystem task (mctp::Task::make() …,
src/projects/mcxn547helloworld/core0/main.cpp:266), then hand control to
FreeRTOS via common::System::inst().scheduler_start()
(src/projects/mcxn547helloworld/core0/main.cpp:313).
mcxn547helloworld is dual-core, so it has both core0/ and core1/.
Core0 is the full application; core1 is deliberately minimal — it inits
MCMGR, makes only its IPC task (src/projects/mcxn547helloworld/core1/main.cpp:157)
and starts the scheduler, with every other *::Task::make() commented out
(src/projects/mcxn547helloworld/core1/main.cpp:173). The single-core
p3957_cxx project has only a core0/.
config.h — the compile-time product definition
Each project’s config.h is a large constexpr description of the
product: task/queue/event tables, GPIO map, USB VID/PID, telemetry, and
feature flags. The EnableDualCore flag is what distinguishes the two
board projects — true for mcxn547helloworld
(src/projects/mcxn547helloworld/config.h:144) and false for
p3957_cxx (src/projects/p3957_cxx/config.h:156). The per-task core
assignment (TaskInfos) pins Ipc1 to Core1 and everything else to
Core0 (src/projects/mcxn547helloworld/config.h:206). See
Architecture
for how these tables drive the runtime.
Linker & core1-image glue
For dual-core targets, core0’s image embeds core1’s binary. Core0 declares
a .core1_image section — a placeholder byte array at build time
(src/projects/mcxn547helloworld/core0/core1_image.c:18) that the build
replaces with the real core1 image. The linker keeps that section
(src/projects/mcxn547helloworld/core0/sys/mcxn556/linker_script.ld:66)
and INCLUDEs a generated address map
(src/projects/mcxn547helloworld/core0/sys/mcxn556/linker_script.ld:23)
whose CORE1_RAM_START / CORE1_TEXT_START symbols
(src/projects/mcxn547helloworld/core0/sys/mcxn556/generated_core1.ld:18)
place core1. Core0’s main() then boots core1 from
__core1_text_start__ (src/projects/mcxn547helloworld/core0/sys/mcxn556/linker_script.ld:34),
passing that address into its IPC task
(src/projects/mcxn547helloworld/core0/main.cpp:300). Alongside the
linker script, each sys/<soc>/ subdir carries the platform’s
FreeRTOSConfig.h, start.S, and gpio_interrupt_handler.cpp.
Signing parameters
Two JSON files per platform tell the signer (see
Build System) how to package
the image: an MBI (masked-boot-image) descriptor and an SB (secure-binary)
descriptor. For example the MBI file sets "mbi": true
(src/projects/mcxn547helloworld/core0/sign-parameters_mbi_mcxn556.json:2)
while the SB file sets "sb4": true and lists the flash erase/load
commands (src/projects/mcxn547helloworld/core0/sign-parameters_sb_mcxn556.json:3).
Files are suffixed by SoC (_mcxn547, _mcxn556, _mcxn236) so one
project can be signed for several platforms.
testrunner — the unit-test project
testrunner is the project used for host and on-target tests rather than a
shipping product. Its x86 main() boots a minimal task set (MCTP, PLDM,
flash, USB, logger), then creates every queue/event and runs the
supervisor to completion, returning a pass/fail exit code
(src/projects/testrunner/sys/x86/main.cpp:135). It also demonstrates the
Ada/SPARK toolchain: test.ads exports an Ada procedure to C++ as
ada_print_hello (src/projects/testrunner/test.ads:25), test.adb
implements it by calling Nv.Common.Console.Print
(src/projects/testrunner/test.adb:24), and the C++ side declares and
calls it after adainit()
(src/projects/testrunner/sys/x86/main.cpp:137). A Lattice CPLD stub
stands in for hardware (src/projects/testrunner/sys/x86/main.cpp:44),
configured from config.h (src/projects/testrunner/config.h:62).
Like a board project it also carries a regconfig.cpp/regconfig.h
register table (src/projects/testrunner/regconfig.cpp:26) and a
powersensor.h. Its on-target variant lives under
src/projects/testrunner/sys/mcxn236/main.cpp and its host variant under
src/projects/testrunner/sys/x86/.
SDK overrides (src/overrides/)
src/overrides/ holds local replacements for standard-library / SDK
sources that must run in the firmware’s freestanding environment. The
chrono, thread, cstring, and cwchar headers are trimmed partial
implementations — e.g. src/overrides/chrono:20 and
src/overrides/thread:24 both note they are “partial implementation[s]
that can run in freestanding mode”. src/overrides/libc.cpp is a small C
runtime shim. Interpretation: these are put on the include path ahead of
the SDK copies so the firmware gets the stripped-down versions without
patching the SDK in place.
Next
- Platform / BSP & Projects — section overview.
- Build System — how
PROJECT/PLATFORM/BOARDbecome an image. - Architecture — the
nv::/sys::split and dual-core composition.
</content>