FreeRTOS on the ARM Automotive Kit (NXP S32Z2)

Status note

This page records hardware bring-up design notes for the freertos-s32z2 target. It is intentionally lower-level than the user guide and may reference NXP-generated/private files or bench scripts that are not present in the public repository. For the supported build entry point and current user workflow, see FreeRTOS S32Z2.

This document records the design of Phase 5 of the FreeRTOS port: bringing the actuation firmware to the ARM Automotive Kit (X-S32Z27X-DC, NXP S32Z2, Cortex-R52). Issue: autowarefoundation/autoware-safety-island#1.

Scope

The kit runs FreeRTOS with feature parity to the existing Zephyr backend on the same SoC:

  • Cortex-R52 RTU0, lock-step mode (reset default).

  • FreeRTOS V11.1.0 via NXP’s Real-Time Drivers (RTD) Cortex-R52 port.

  • lwIP socket layer, BSD-socket-compatible, driving the NETC Ethernet controller.

  • CycloneDDS cross-compiled statically into the firmware image.

  • DDS_ONLY control-command output mode; CAN is not validated on the kit in Phase 5.

  • 150 ms periodic controller loop, identical to the Zephyr backend.

Out of scope: eSync OTA, Linux-side Autoware integration, CAN frame observation, Phase 6 tuning, an on-hardware CI job, migration to the Phase 2 stack (issue #14).

Build tree layout

The Phase 5 build is an additional CMake entry point under actuation_module/freertos_s32z2/ that does not modify the existing actuation_module/freertos/ POSIX-simulator build, the Zephyr boards under actuation_module/boards/, or the PAL backend headers under actuation_module/include/platform/freertos/*.h. The intended hardware path may add S32Z2-specific variant headers under actuation_module/include/platform/freertos/s32z2/ when carrying the lwIP bring-up forward.

The intended hardware networking path uses an S32Z2-specific FreeRTOS network backend. In the public tree, platform_network.h still dispatches FreeRTOS targets through platform/freertos/freertos_network.h; update this dispatcher and the matching backend when carrying the S32Z2 lwIP bring-up path forward.

Boot bring-up (B-1)

board_init() calls Mcu_Init / Platform_Init / Port_Init from the RTD, brings UART9 up at 115200 8N1, and registers the PIT as the FreeRTOS tick source. configCPU_CLOCK_HZ matches the clock programmed by Mcu_Init. Newlib’s _write is retargeted to UART9 so printf reaches the FT232RQ serial port exposed as /dev/ttyUSB0 on the carrier’s J11 USB-C console (the OpenSDA debug probe exposes /dev/ttyACM0 separately).

A heartbeat task prints actuation alive ticks=N every 150 ms. The B-1 acceptance gate is the appearance of at least five such lines within ten seconds of reset.

PB configuration prerequisite

The NXP RTD MCAL drivers (Mcu, Clock_Ip, Siul2_Port_Ip, Uart, Pit) expect post-build configuration structures (*_PBcfg.c) that are generated by S32 Design Studio’s S32 Config Tools from a .mex board configuration. The driver packages ship those structures as EB Tresos templates under $S32_RTD_PATH/RTD/<driver>_TS_*/generate_PB/src/; the templates contain [!...!] markers that must be expanded by S32 Config Tools. Until the generated files exist, the firmware does not link.

Workflow:

  1. In S32 Design Studio 3.6.x, import or recreate the lwip_S32Z27X_FreeRTOS_R52 example shipped at $LWIP_PATH/examples/S32Z270/lwip_S32Z27X_FreeRTOS_R52/ as a starting point.

  2. Open the project’s .mex in the S32 Configuration Tools editor and configure clock ≥ 240 MHz, UART9 at 115200 8N1, PIT channel 0 as the tick, and Ethernet 0 enabled.

  3. Use the Update Code action to expand the templates into the project’s generate/src/*.c, generate/include/*.h and board/ directories.

  4. Point S32CT_GENERATED_DIR at that project root; the build consumes generate/src, generate/include and board. The generated files inherit the NXP Confidential and Proprietary header from the RTD templates, so they are not committed to this public repository: they are bundled as the private s32ct_config submodule, or regenerated locally by developers without submodule access (see the README).

Networking and DDS (B-2)

The intended S32Z2 configure_network() path dispatches to an lwIP bring-up function that calls tcpip_init(), registers the NETC driver via ethif_ethernetif_init (from the NXP eth_port.c glue, not the generic upstream ethernetif_init) / netif_add, requests DHCP, and blocks on a semaphore until a lease arrives. Keep the dispatcher and backend implementation in sync before treating this as current behavior.

CycloneDDS is cross-compiled as a static library for Cortex-R52 with the upstream WITH_FREERTOS=ON and WITH_LWIP=ON options enabled (CycloneDDS ships dedicated sync/freertos.h and sockets/posix.h + lwIP backends — no patches required). Security, SSL, shared memory, IPv6, source-specific multicast, and network partitions are all disabled (see actuation_module/freertos_s32z2/scripts/build-cdds-target.sh). The host idlc built by build.sh --platform freertos-s32z2 is reused for IDL → C generation of autoware_msgs.

The cross-build needs two non-obvious workarounds:

  • -D__int64_t_defined=1 — Ubuntu 24.04’s libnewlib-arm-none-eabi 4.4.0 has a header-name mismatch in machine/_default_types.h (defines ___int64_t_defined with three leading underscores) so inttypes.h never exposes PRId64 / PRIu64. Pre-defining the symbol works around the packaging bug.

  • -include inttypes.h plus -DUSING_RTD=1 -DS32Z27 — the NXP-supplied lwIP arch/cc.h has a #ifdef USING_RTD branch that pulls in the RTD-friendly Devassert.h path instead of the toolchain-dependent device_registers.h path.

The Edge ECU peer is the development host itself: dds_pub and dds_sub (unchanged) compile with host gcc against the host CycloneDDS and run as edge_ecu_pub / edge_ecu_sub. The acceptance gates are:

  • Controller Node Started and Actuation Safety Island is Live appear on UART9.

  • edge_ecu_sub receives STEERING REPORT at least twice.

  • actuation_main does not return during the verification window.

These are the same string markers used by the POSIX-simulator smoke (PRs #10 / #13).

Flash workflow

The kit is flashed via the NXP s32dbg debugger through west debug (the nxp_s32dbg runner does not implement the flash command). The driver scripts live in an out-of-tree MRM_repo/ working directory on the dev host (the copy used for the production demo rig). MRM_repo/run_before.sh STEP 4 starts an Xvfb on DISPLAY=:99 before STEP 5 invokes west_debug.shs32dbg is a GUI tool that aborts at SoC connect with CCS: connection to server refused when no X display is available, even in batch mode. From inside a Zephyr workspace whose runners.yaml selects nxp_s32dbg (the s32z270dc2_rtu0_r52@D board config does):

export DISPLAY=:99
pgrep -x Xvfb >/dev/null || \\
    nohup Xvfb :99 -screen 0 1024x768x24 > /tmp/xvfb.log 2>&1 &
source ~/zephyr-env/bin/activate
west debug \\
    --s32ds-path=/usr/local/NXP/S32DS.3.6.2 \\
    -d build/actuation_module \\
    --tool-opt='--batch'

The --tool-opt='--batch' flag makes the GDB session non-interactive so the firmware is loaded, the entry breakpoint is released, and control returns to the shell after the kit starts running. A successful run loads roughly 1.2 MB in 6 s and shortly afterwards /dev/ttyUSB0 prints Starting Controller Node..., Controller Node Started and Actuation Safety Island is Live.

When the SoC connection still fails after Xvfb is up, clean up residual debugger state per the demo’s retry recipe (pkill -9 -f '(gta|s32dbg|arm-none-eabi-gdb)', rm -rf /tmp/*nxp_s32dbg*, free TCP/45000) and retry. If three retries fail, the SoC is unreachable for physical reasons: power, J6 JTAG seating, J14 jumper, J17/J18 boot mode, or the front-panel S2 reset.

References