Contenu principal

Run and Debug C/C++ Tests on Target Using TRACE32

R2026b
Since R2026b

TRACE32 is a hardware and software toolset from Lauterbach that lets you load programs onto a target, run them, and inspect them during execution. It can work through a hardware debug probe connected to a physical board, or through a built-in Instruction Set Simulator (ISS) that runs entirely as software.

The TRACE32 toolset supports various ways of communicating with the target hardware. One of the supported ways is the Fast Data Exchange or FDX protocol. In this protocol, the test code reads and writes designated memory locations on the target, and the debug probe continuously moves that data between the target and your computer.

This topic shows how to create a Polyspace® Test™ target registration package that uses FDX for host-target communication. The example uses a NXP S32K344 board (Cortex-M7) but you can adapt the example to any board that is supported for TRACE32-based execution.

Prerequisites

Before you begin, you need:

  • TRACE32 PowerView installed on your machine. Download the software from the Lauterbach Portal. This example uses <T32Install> as the TRACE32 software installation folder. For instance:

    • FDX source files from your TRACE32 installation, t32fdx.c and t32fdx.h, are located under <T32Install>\demo\arm\fdx\.

    • To open the PowerView software, you can double-click the appropriate executable in <T32Install>\bin.

  • NXP S32 Design Studio installed on your machine. It provides both the cross-compiler (GCC for ARM) and the SDK files (Real-Time Drivers, startup code, and peripheral headers) used in this example.

  • (Optional) A Lauterbach debug probe, such as CombiProbe or PowerDebug, connected to your target board via USB. If you are running tests in the Instruction Set Simulator mode, you do not need the debug probe unless you are also obtaining a TRACE32 license through the probe.

Package Overview

Your target registration package for FDX communication contains the following files:

NXP_S32K3X4EVB_Q257_Package/
├── pstestPreferences.json
├── NXP_S32K3X4EVB_Q257_Preferences.m
├── NXP_S32K3X4EVB_Q257_PackageRegister.m
├── NXP_S32K3X4EVB_Q257_PackageUnregister.m
├── NXP_S32K3X4EVB_Q257_DebugIOTool.m
├── NXP_S32K3X4EVB_Q257_BoardDefinition.m
├── NXP_S32K3X4EVB_Q257_ToolchainDefinition.m
├── S32K344_Trace32_config.t32
├── s32k344evb-q257.cmm
├── src/
│   ├── rtiostream_fdx.c
│   ├── rtiostream_fdx.h
│   ├── NXP_S32K3X4EVB_Q257_board.c
│   ├── NXP_S32K3X4EVB_Q257_board.h
│   └── ...  (SDK/driver files for your board)

The file NXP_S32K3X4EVB_Q257_PackageRegister.m registers your target package while the file NXP_S32K3X4EVB_Q257_PackageUnregister.m unregisters the package. The remaining MATLAB® files contain definitions of entities that are part of the package (board, toolchain, and debug tool).

The .t32 file and the .cmm file control how TRACE32 connects to the target and sets up FDX communication. Sample versions of these files are provided with a TRACE32 installation.

The src/ folder contains target-side source files:

  • The files rtiostream_fdx.c and rtiostream_fdx.h implement the rtIOStream API using FDX ring buffers. The rtIOStream API enables host-target communication when you run or debug tests.

  • The files NXP_S32K3X4EVB_Q257_board.c and NXP_S32K3X4EVB_Q257_board.h contain the board initialization code (clock setup, GPIO configuration). These files are specific to your hardware and are typically generated from your board's SDK or IDE (for instance, NXP S32 Design Studio for this board). In the Instruction Set Simulator mode, these files are not needed.

Create TRACE32 Files

The .t32 file and the .cmm files control how TRACE32 connects to the specific hardware board. The key parts of the files are outlined below. For more information on the syntaxes, see TRACE32 documentation.

Create TRACE32 Configuration File

The TRACE32 configuration file (.t32) determines how the TRACE32 software is launched. The settings differ depending on whether you use physical hardware or the Instruction Set Simulator.

Physical Hardware.  For physical hardware, the configuration file can be the following:

; USB debugger with auto-reconnect
PBI=USB
CONNECTIONMODE=AUTOCONNECT

; System paths
OS=
ID=TRACE32
SYS=C:\T32\bin\windows64

; Remote API (used internally by the test framework)
IC=NETASSIST
PORT=20000
PACKLEN=1024

The file has three sections:

  • Probe connection — The PBI= block specifies how TRACE32 connects to the debug hardware. PBI=USB selects a USB-attached probe. CONNECTIONMODE=AUTOCONNECT forces the connection even if a previous session did not release the probe cleanly — without it, subsequent runs fail with the message:

    TRACE32 device already used by other GUI

  • System paths — OS= and ID=TRACE32 are standard TRACE32 settings (host OS detection and session name). Set SYS= to your TRACE32 installation directory if it is not in the default location.

  • Remote API — The IC=NETASSIST block opens a UDP port (here 20000) that the framework uses to send control commands to TRACE32.

Instruction Set Simulator.  For the Instruction Set Simulator, replace the probe connection block with:

; Obtain simulator license from USB probe
PBI=*SIM
USB
INSTANCE=AUTO
PBI=*SIM starts TRACE32 in simulator mode. The line USB indicates a licensing configuration that obtains the simulator license from a USB-attached probe. If your setup uses a different license type such as a node-locked or floating license, you do not need this line.

The system paths and remote API sections remain the same as for physical hardware.

Create Startup Script

Create a PRACTICE script (.cmm) that configures the CPU, loads the test binary, sets up FDX channels, and starts execution. Use IF (!SIMULATOR()) guards to skip hardware-specific commands when running in the Instruction Set Simulator mode. The script receives four arguments from the framework: an ELF file path, pipe names for input and output, and a debug flag.

ENTRY &ELF_FILE &pipeIn &pipeOut &isDebug

IF (!OS.FILE(&ELF_FILE))
(
    PRINT %ERROR "The target binary location must be passed to the script."
    ENDDO
)

; CPU and debug port configuration (adapt to your target)
SYStem.CPU S32K344-M7
IF (!SIMULATOR())
(
    SYStem.CONFIG.DEBUGPORTTYPE SWD
    IF COMBIPROBE()||UTRACE()
    (
        SYStem.CONFIG.CONNECTOR MIPI20T
    )
    SYStem.Option DUALPORT ON
    SYStem.MemAccess DAP
    SYStem.JtagClock 10MHz
    Trace.DISable
)
SYStem.Up
SYStem.MemAccess DAP

; Load binary
IF (!SIMULATOR())
(
    ; Flash programming (adapt to your target)
    DO ~~/demo/arm/flash/s32k3.cmm PREPAREONLY
    FLASH.ReProgram ALL
    Data.LOAD.Elf "&ELF_FILE"
    FLASH.ReProgram OFF
)
ELSE
(
    Data.LOAD.Elf "&ELF_FILE"
)

; FDX configuration
FDX.RESet
FDX.METHOD BUFFERE

FDX.OutChannel FdxSendBuffer
FDX.InChannel FdxReceiveBuffer

FDX.PipeWRITE FdxSendBuffer "&pipeOut"
FDX.PipeREAD FdxReceiveBuffer "&pipeIn"
FDX.ENableChannel

; Optional: breakpoint for debug mode
if "&isDebug"=="1"
(
    Break.Set pst_run_test_case_function
)

Break.Set rtIOStreamClose /OnChip

Go

ENDDO

The script proceeds in four phases:

  • CPU and debug port setup — This phase selects the CPU type and brings the debug session up with SYStem.Up. Hardware debug port commands (SWD configuration, JTAG clock speed) are guarded with IF (!SIMULATOR()) because the simulator does not support these commands.

  • Binary loading — On physical hardware, the binary is programmed into flash using FLASH.ReProgram. In the Instruction Set Simulator (ISS) mode, the ELF is loaded directly into RAM.

  • FDX channel configuration — FDX.METHOD BUFFERE selects the ring buffer transfer method. FDX.OutChannel and FDX.InChannel bind FDX channels to symbols in target memory (FdxSendBuffer and FdxReceiveBuffer, defined in rtiostream_fdx.c). FDX.PipeWRITE and FDX.PipeREAD expose those channels as Windows named pipes whose names are provided by the framework.

  • Execution — Break.Set rtIOStreamClose /OnChip places a hardware breakpoint that halts the CPU when the test completes. Go starts execution and the script exits with ENDDO. From this point, the framework exchanges test data through the named pipes while the target runs.

Create Preferences Files

You can abstract away details specific to your target board and TRACE32 installation in a JSON file.

Create Preferences JSON

Create a pstestPreferences.json file with paths to your cross-compiler, TRACE32 files, and FDX source files:

{
    "GnuARMPath": "C:/path/to/your/gcc-arm-compiler",
    "SDK_Base_Path": "C:/path/to/your/sdk/base",
    "SDK_Platform_Path": "C:/path/to/your/sdk/platform",

    "T32Config": "C:/path/to/your/S32K344_Trace32_config.t32",
    "T32StartupScript": "C:/path/to/your/s32k344evb-q257.cmm",
    "T32Executable": "C:/T32/bin/windows64/t32marm.exe",

    "T32FdxFiles" : ["C:/T32/demo/arm/fdx/t32fdx.c",
                     "C:/T32/demo/arm/fdx/t32fdx.h"]
}

The JSON contains two groups of settings:

  • TRACE32 and FDX (required for all FDX setups) — T32Config and T32StartupScript point to the .t32 and .cmm files described in the previous sections. T32Executable is the path to the TRACE32 PowerView executable for your target architecture (for example, t32marm.exe for ARM). T32FdxFiles lists the t32fdx.c and t32fdx.h files from the TRACE32 installation. The framework compiles these into the target binary alongside your rtiostream_fdx.c.

  • Board-specific paths (vary by target) — GnuARMPath points to the cross-compiler. SDK_Base_Path and SDK_Platform_Path are specific to the NXP S32K3 RTD package and provide the headers and drivers for this board. Other boards would have different keys here depending on their SDK structure.

    For instance, the following are example paths from an NXP Design Studio installation:

        "GnuARMPath": "C:/NXP/S32DS.3.6.8/S32DS/build_tools/gcc_v11.4/gcc-11.4-arm32-eabi",
        "SDK_Base_Path": "C:/NXP/SW32K3_RTD_4.4_R21-11_3.0.0/eclipse/plugins/BaseNXP_TS_T40D34M30I0R0",
        "SDK_Platform_Path": "C:/NXP/SW32K3_RTD_4.4_R21-11_3.0.0/eclipse/plugins/Platform_TS_T40D34M30I0R0"

Create Preferences Class to Read JSON

Create a preferences class NXP_S32K3X4EVB_Q257_Preferences that inherits from pstest.target.Trace32.TargetPreferences:

classdef NXP_S32K3X4EVB_Q257_Preferences < pstest.target.Trace32.TargetPreferences
    properties(Constant)
        ToolChainName = "GCC ARM Cortex M | NXP_S32K3X4EVB-Q257"
        BoardName = "NXP_S32K3X4EVB_Q257 TRACE32"
    end
    methods(Access=public)
        function this = NXP_S32K3X4EVB_Q257_Preferences(jsonName)
            arguments
                jsonName (1,1) string = "pstestPreferences.json"
            end
            jsonFullPath = fullfile(fileparts(which(mfilename)), jsonName);
            this@pstest.target.Trace32.TargetPreferences(jsonFullPath);
        end
    end
end
This class reads the JSON file and adds constant properties (ToolChainName and BoardName) used during board and toolchain registration. The parent class pstest.target.Trace32.TargetPreferences adds properties for the FDX gateway — a background process that bridges TCP communication between the framework and the FDX named pipes exposed by TRACE32.

Instantiate Preferences Class From Package Registration File

In your packageRegister.m, instantiate the NXP_S32K3X4EVB_Q257_Preferences class you just created.

addpath(fileparts(mfilename('fullpath')));
prefs = NXP_S32K3X4EVB_Q257_Preferences();

if savepath
    warning('Error while saving path!');
end
rehash toolboxcache;
You can now use the variable prefs to access JSON settings or constants defined in the preferences file. For instance:

  • prefs.GnuARMPath allows you to access the GNU ARM compiler path specified through the JSON key GnuARMPath.

  • prefs.ToolChainName allows you to access the toolchain name defined in the NXP_S32K3X4EVB_Q257_Preferences preferences class.

The parent preferences class pstest.target.Trace32.TargetPreferences also provides a method getGatewayArgs(), which returns the TCP port for host-target communication. You can use prefs.getGatewayArgs() to invoke this method.

Implement Target-Side Data Transfer

Create a file rtiostream_fdx.c that implements the rtIOStream API using FDX. This file runs on the target and is responsible for exchanging data with the host through memory locations that the debug probe monitors.

The FDX API is provided by t32fdx.c and t32fdx.h from your TRACE32 installation (listed in T32FdxFiles in the JSON preferences).

#include <stddef.h>
#include "t32fdx.h"
#include "rtiostream_fdx.h"

#define RTIOSTREAM_NO_ERROR  (0)
#define RTIOSTREAM_ERROR    (-1)

/* FDX channel buffers (4096 bytes each) */
T32_Fdx_DefineChannel(FdxSendBuffer, 4096);     /* target -> host */
T32_Fdx_DefineChannel(FdxReceiveBuffer, 4096);   /* host -> target */

void initFDX(void) {
    T32_Fdx_InitChannel(FdxSendBuffer);
    T32_Fdx_InitChannel(FdxReceiveBuffer);
    T32_Fdx_EnableChannel(FdxSendBuffer);
    T32_Fdx_EnableChannel(FdxReceiveBuffer);
}

int rtIOStreamOpen(int argc, void *argv[])
{
    (void)argc;
    (void)argv;
    initFDX();
    return RTIOSTREAM_NO_ERROR;
}

int rtIOStreamClose(int streamID)
{
    (void)streamID;
    T32_Fdx_DisableChannel(FdxSendBuffer);
    T32_Fdx_DisableChannel(FdxReceiveBuffer);
    return RTIOSTREAM_NO_ERROR;
}

int rtIOStreamSend(int streamID, const void *src, size_t size, size_t *sizeSent)
{
    (void)streamID;
    int sent = T32_Fdx_Send(&FdxSendBuffer, (void *)src, (int)size);
    if (sent > 0) {
        *sizeSent = (size_t)sent;
        return RTIOSTREAM_NO_ERROR;
    }
    *sizeSent = 0;
    return RTIOSTREAM_ERROR;
}

int rtIOStreamRecv(int streamID, void *dst, size_t size, size_t *sizeRecvd)
{
    (void)streamID;
    int recvd = T32_Fdx_Receive(&FdxReceiveBuffer, dst, (int)size);
    *sizeRecvd = (size_t)(recvd > 0 ? recvd : 0);
    return RTIOSTREAM_NO_ERROR;
}

The implementation has three layers:

  • Channel declaration — The function T32_Fdx_DefineChannel() reserves a block of target memory (here 4096 bytes) as a data buffer. The symbol names (FdxSendBuffer, FdxReceiveBuffer) used for this block must match the names used in the .cmm script so TRACE32 knows where to read and write.

  • Initialization — The function initFDX(), called via the function rtIOStreamOpen(), initializes and enables the channels used for communication. This runs once at the start of the test.

  • Data transfer — The function rtIOStreamSend() writes test data into the send buffer and the function rtIOStreamRecv() reads incoming data from the receive buffer. The debug probe continuously moves data between these memory regions and the host. The function rtIOStreamClose() disables both channels when the test finishes.

Create Execution Tool

Create a class NXP_S32K3X4EVB_Q257_DebugIOTool that inherits from pstest.target.Trace32.DebugIOTool:

classdef NXP_S32K3X4EVB_Q257_DebugIOTool < pstest.target.Trace32.DebugIOTool
    methods
        function this = NXP_S32K3X4EVB_Q257_DebugIOTool
            this@pstest.target.Trace32.DebugIOTool(NXP_S32K3X4EVB_Q257_Preferences());
        end
    end
end

This class is a thin wrapper around the parent class pstest.target.Trace32.DebugIOTool, which handles the full lifecycle: launching TRACE32, running the startup script, managing the gateway process, and shutting everything down after the test. All it needs from you is the preferences object so it knows which executable, config file, and script to use.

Register Board and Communication

Create a board definition file NXP_S32K3X4EVB_Q257_BoardDefinition.m that registers the board, its processor, source files, execution service, and communication settings. In this file, define a function that creates the connection and board objects:

function NXP_S32K3X4EVB_Q257_BoardDefinition()
You call this function from the package registration file.

The function body has five sections:

  • Board and processor — Creates the board and processor objects with the appropriate language implementation:

    prefs = NXP_S32K3X4EVB_Q257_Preferences();
    
    board = target.create("Board", "Name", prefs.BoardName);
    processor = target.create("Processor", "Name", "NXP_S32K3X4EVB_Q257", ...
        "Manufacturer", "NXP");
    langImpl = target.create("LanguageImplementation", ...
        "Name", "ARM Cortex-M Compatible", "Copy", "GNU GCC ARM 32-bit");
    processor.LanguageImplementations = langImpl;
    board.Processors = processor;

  • Source files and includes — Lists the source files for the target. On physical hardware, this includes board initialization code, vendor startup code, and SDK driver files. In the Instruction Set Simulator mode, only a minimal set of files is needed because vendor startup code typically initializes hardware peripherals that do not exist in the simulator.

    • Physical hardware:

      baseSoftDir = [strrep(fileparts(mfilename('fullpath')), '\','/') '/src'];
      
      mainFunction = target.create("MainFunction", "Name", prefs.BoardName + " - Test Main");
      mainFunction.IncludeFiles = {'NXP_S32K3X4EVB_Q257_board.h'};
      mainFunction.InitializationCode = sprintf('init_NXP_S32K3X4EVB_Q257();\n');
      board.MainFunctions = mainFunction;
      
      mainFunction.BuildDependencies.SourceFiles = { ...
          [baseSoftDir, '/NXP_S32K3X4EVB_Q257_board.c'] ...
          [baseSoftDir, '/rtiostream_fdx.c'] ...
          ... % NXP SDK driver files (Clock_Ip, startup, nvic, etc.)
      };
      
      mainFunction.BuildDependencies.IncludePaths = { ...
          baseSoftDir, ...
          [baseSoftDir, '/NXP'], ...
          [prefs.SDK_Base_Path, '/include'], ...
          [prefs.SDK_Base_Path, '/header'], ...
          [prefs.SDK_Platform_Path, '/startup/include'] ...
      };
      InitializationCode inserts a C function call at the start of main, before any test runs. Here, it calls the board initialization function defined in NXP_S32K3X4EVB_Q257_board.c (clock setup, GPIO configuration). On the ISS, this property is not set because vendor hardware initialization is not needed.

    • Instruction Set Simulator:

      baseSoftDir = [strrep(fileparts(mfilename('fullpath')), '\','/') '/src'];
      
      mainFunction = target.create("MainFunction", "Name", prefs.BoardName + " - Test Main");
      board.MainFunctions = mainFunction;
      
      mainFunction.BuildDependencies.SourceFiles = { ...
          [baseSoftDir, '/rtiostream_fdx.c'] ...
          [baseSoftDir, '/syscalls.c'] ...
          [baseSoftDir, '/sysmem.c'] ...
      };
      
      mainFunction.BuildDependencies.IncludePaths = { baseSoftDir };
      The syscalls.c and sysmem.c files provide minimal stubs for system calls (_write, _read, _sbrk, etc.) that the C library expects. These stubs are typically part of your toolchain's bare-metal support files or can be generated from your IDE.

    In both cases, the FDX files from the TRACE32 installation are added to the build:

    % Add FDX files from TRACE32 installation
    for ii = 1:numel(prefs.T32FdxFiles)
        [folder, ~, ext] = fileparts(prefs.T32FdxFiles{ii});
        if strcmp(ext, '.h')
            mainFunction.BuildDependencies.IncludePaths{end+1} = folder;
        else
            mainFunction.BuildDependencies.SourceFiles{end+1} = prefs.T32FdxFiles{ii};
        end
    end

    In the Instruction Set Simulator mode, vendor startup and SDK driver files are not needed. Use a minimal startup file (such as the Lauterbach crt0.S) and a RAM-only linker script instead of vendor startup code that initializes clock controllers and other peripherals. See the toolchain section for the corresponding linker changes.

  • Execution service — Registers the DebugIOTool class as the execution tool for this board:

    className = 'NXP_S32K3X4EVB_Q257_DebugIOTool';
    customExecutionService = target.create("APIImplementation", ...
        "Name", prefs.BoardName + " - Execution Service implementation", ...
        "API", target.get("API", "ExecutionTool"), ...
        "BuildDependencies", target.create("MATLABDependencies", "Classes", className));
    
    executionTool = target.create("ExecutionService", ...
        "Name", prefs.BoardName + " - Target process execution", ...
        "APIImplementation", customExecutionService);
    board.Tools.ExecutionTools = executionTool;
    The class is named DebugIOTool but registers under the ExecutionTool API. In the FDX workflow, the startup script manages execution (issues Go and sets breakpoints), so the framework treats it as a standard execution tool.

  • Communication — Sets up the protocol stack and TCP connection to the gateway process. The gateway is a background process launched automatically by the DebugIOTool class. It bridges TCP (used by the framework to send and receive test data) and the FDX named pipes (exposed by TRACE32 in the startup script).

    % Internal protocol (polls for breakpoint hits)
    pilProtocol = target.internal.create("PILDebugIOProtocol", ...
        "Name", prefs.BoardName + " - PIL DebugIOProtocol");
    pilProtocol.BreakpointPollingWaitTime = 10000;
    board.Inner.CommunicationProtocolStacks = pilProtocol;
    
    % External protocol (timeouts for receive and open)
    pilProtocolExt = target.create("PILProtocol", ...
        "Name", prefs.BoardName + " - PIL Protocol");
    pilProtocolExt.ReceiveTimeout = 10000;
    pilProtocolExt.OpenTimeout = 30000;
    board.CommunicationProtocolStacks = pilProtocolExt;
    
    % TCP connection to gateway
    connection = target.create("TargetConnection", "Name", prefs.BoardName);
    connection.CommunicationChannel = target.create("TCPChannel", ...
        "Name", prefs.BoardName + " - TCPChannel");
    connection.CommunicationChannel.IPAddress = "localhost";
    gatewayArgs = prefs.getGatewayArgs();
    connection.CommunicationChannel.Port = num2str(gatewayArgs.Port);
    connection.Target = board;
    
    target.add(connection, "UserInstall", true, "SuppressOutput", false);
    getGatewayArgs() is a method inherited from pstest.target.Trace32.TargetPreferences. It returns a struct whose Port field is the TCP port the gateway listens on. The framework connects to this port to exchange test data.

A complete sample board definition file is provided here:

function NXP_S32K3X4EVB_Q257_BoardDefinition()

prefs = NXP_S32K3X4EVB_Q257_Preferences();
toolName = @(desc)prefs.BoardName + " - " + desc;

%% 1: Board and processor
board = target.create("Board", "Name", prefs.BoardName);
processor = target.create("Processor", "Name", "NXP_S32K3X4EVB_Q257", ...
    "Manufacturer", "NXP");
langImpl = target.create("LanguageImplementation", ...
    "Name", "ARM Cortex-M Compatible", "Copy", "GNU GCC ARM 32-bit");
processor.LanguageImplementations = langImpl;
board.Processors = processor;

%% 2: Source files, includes, defines
baseSoftDir = [strrep(fileparts(mfilename('fullpath')), '\','/') '/src'];

mainFunction = target.create("MainFunction", "Name", toolName("Test Main"));
board.MainFunctions = mainFunction;

mainFunction.BuildDependencies.SourceFiles = { ...
    [baseSoftDir, '/syscalls.c'], ...
    [baseSoftDir, '/sysmem.c'], ...
    [baseSoftDir, '/NXP_S32K3X4EVB_Q257_board.c'] ...
    [baseSoftDir, '/rtiostream_fdx.c'] ...
    [baseSoftDir, '/NXP/crt0.S'] ...
    [baseSoftDir, '/NXP/startup.c'] ...
    [baseSoftDir, '/NXP/system.c'] ...
    ... % Add remaining NXP SDK driver files (Clock_Ip, OsIf, Port, etc.)
};

mainFunction.BuildDependencies.IncludePaths = { ...
    baseSoftDir, ...
    [baseSoftDir, '/NXP'], ...
    [prefs.SDK_Base_Path, '/include'], ...
    [prefs.SDK_Base_Path, '/header'], ...
    [prefs.SDK_Platform_Path, '/startup/include'] ...
};

mainFunction.BuildDependencies.Defines = { ...
    'NXP_S32K3X4EVB_Q257', 'USE_HAL_DRIVER' ...
};

for ii = 1:numel(prefs.T32FdxFiles)
    fCurr = prefs.T32FdxFiles{ii};
    [folder, ~, ext] = fileparts(fCurr);
    if strcmp(ext, '.h')
        mainFunction.BuildDependencies.IncludePaths{end+1} = folder;
    else
        mainFunction.BuildDependencies.SourceFiles{end+1} = fCurr;
    end
end

%% 3: Execution tool
className = 'NXP_S32K3X4EVB_Q257_DebugIOTool';
customExecutionService = target.create("APIImplementation", ...
    "Name", toolName("Execution Service implementation"), ...
    "API", target.get("API", "ExecutionTool"), ...
    "BuildDependencies", target.create("MATLABDependencies", "Classes", className));

executionTool = target.create("ExecutionService", ...
    "Name", toolName("Target process execution"), ...
    "APIImplementation", customExecutionService);
board.Tools.ExecutionTools = executionTool;

%% 4: Board communication
pilProtocol = target.internal.create("PILDebugIOProtocol", ...
    "Name", toolName("PIL DebugIOProtocol"));
pilProtocol.BreakpointPollingWaitTime = 10000;
board.Inner.CommunicationProtocolStacks = pilProtocol;

comms = target.create("CommunicationInterface", ...
    "Name", toolName("Communication Interface"));
comms.Channel = "TCPChannel";
comms.APIImplementations = target.create("APIImplementation", ...
    "Name", toolName("APIImplementation"));
comms.APIImplementations.API = target.get("API", "rtiostream");
comms.APIImplementations.BuildDependencies = target.create("BuildDependencies");
board.CommunicationInterfaces = comms;

pilProtocolExt = target.create("PILProtocol", ...
    "Name", toolName("PIL Protocol"));
pilProtocolExt.ReceiveTimeout = 10000;
pilProtocolExt.OpenTimeout = 30000;
board.CommunicationProtocolStacks = pilProtocolExt;

%% 5: Host connection
connection = target.create("TargetConnection", "Name", prefs.BoardName);
connection.CommunicationChannel = target.create("TCPChannel", ...
    "Name", toolName("TCPChannel"));
connection.CommunicationChannel.IPAddress = "localhost";
gatewayArgs = prefs.getGatewayArgs();
connection.CommunicationChannel.Port = num2str(gatewayArgs.Port);
connection.Target = board;

target.add(connection, "UserInstall", true, "SuppressOutput", false);
end

Define Toolchain

Create a toolchain definition file NXP_S32K3X4EVB_Q257_ToolchainDefinition.m that registers the cross-compiler, linker, assembler, and archiver with the appropriate flags for your target processor. In this file, define a function that creates the toolchain:

function NXP_S32K3X4EVB_Q257_ToolchainDefinition()
You call this function from the package registration file.

The toolchain definition is not specific to TRACE32 or FDX — it defines how to compile, link, and produce an ELF binary for your target architecture. A sample toolchain definition file is provided here:

function NXP_S32K3X4EVB_Q257_ToolchainDefinition()

    baseSoftDir = [strrep(fileparts(mfilename('fullpath')), '\','/') '/src'];
    prefs = NXP_S32K3X4EVB_Q257_Preferences();
    toolName = @(desc)prefs.ToolChainName + " " + desc;
    gnuARMPath = prefs.GnuARMPath;
    
    % Create the Toolchain
    tc = target.create("Toolchain", "Name", prefs.ToolChainName);
    tc.Builder = target.create("MakefileBuilder", "GMake");

    if ispc
        tc.HostOperatingSystemSupport = target.HostOperatingSystemSupport.WindowsOnly;
        exeExt = '.exe';
        hostKind = "Windows";
    else
        tc.HostOperatingSystemSupport = target.HostOperatingSystemSupport.LinuxOnly;
        exeExt = '';
        hostKind = "Unix";
    end

    basictools1 = target.get("Toolset", hostKind + " system tools for Makefiles");
    tc.Tools(end+1) = basictools1;
    maketool = target.create("BuildTool", "Make Tool", "gmake -j$(NUM_CORES)", ...
                            "Name", "GNU GMake for " + prefs.ToolChainName);
    maketool.FileSep = '/';
    tc.Tools(end+1) = maketool;

    % Common flags for C/C++ compiler, linker, and assembler
    commonFlags = {
        '-mcpu=cortex-m7', ...
        '-march=armv7e-m', ...
        '-mthumb', ...
        '-mlittle-endian', ...
        '-mfloat-abi=hard', ...
        '-mfpu=fpv5-sp-d16', ...
        '-fno-common', ...
        '-pedantic', ...
        '-ggdb3', ...
        '-specs=nano.specs', ...
        '-specs=nosys.specs', ...
        ['--sysroot=', gnuARMPath, '/lib']};

    optiLevel = '-O0';
    commonFlags{end+1} = '-g';

    tc.BuildRequirements.CompilerFlags = [commonFlags, ...
        {'-std=gnu11'}, ...
        {'-std=c99'}, ...
        {'-funsigned-char'}, ...
        {'-fstack-usage'}, ...
        {'-fomit-frame-pointer'}, ...
        {'-funsigned-bitfields'}, ...
        {'-fno-short-enums'}, ...
        {'-DS32K3XX'}, ...
        {'-DS32K344'}, ...
        {'-DCPU_S32K344'}, ...
        {'-Wall'}];

    % Linker flags
    linkerScript = [baseSoftDir '/NXP/linker_flash_s32k344.ld'];
    entryPoint = '--entry=Reset_Handler';
    
    tc.BuildRequirements.LinkerFlags = [commonFlags, { ...
        '-Wl,--gc-sections', ...
        '-nostartfiles', ...
        entryPoint, ...
        '-lc -lm -lgcc', ...
        ['-T"' linkerScript '"']}];

    % C Compiler
    cCompiler = target.create("BuildTool", "C Compiler", ...
        [gnuARMPath '/bin/arm-none-eabi-gcc' exeExt], ...
        "Name", toolName("C Compiler"));
    cCompiler.setDirective("CommandFile", "@");
    cCompiler.setDirective("Debug", "-g");
    cCompiler.setDirective("EnableOptimization", optiLevel);
    cCompiler.setDirective("DisableOptimization", "-O0");
    cCompiler.setFileExtensions("Source", {'.c', '.C'});
    cCompiler.FileSep = '/';
    tc.Tools(end+1) = cCompiler;

    % C++ Compiler
    cxxCompiler = target.create("BuildTool", "C++ Compiler", ...
        [gnuARMPath '/bin/arm-none-eabi-g++' exeExt], ...
        "Name", toolName("C++ Compiler"));
    cxxCompiler.setDirective("CommandFile", "@");
    cxxCompiler.setDirective("Debug", "-g");
    cxxCompiler.setDirective("DisableOptimization", "-O0");
    cxxCompiler.FileSep = '/';
    cxxCompiler.Command.Arguments = {'-std=gnu++14'};
    cxxCompiler.setFileExtensions("Source", {'.cpp', '.cxx', '.CPP', '.CXX'});
    tc.Tools(end+1) = cxxCompiler;

    % Assembler (uses gcc with -x assembler-with-cpp for preprocessor support)
    assembler = target.create("BuildTool", "Assembler", ...
        [gnuARMPath '/bin/arm-none-eabi-gcc' exeExt], ...
        "Name", toolName("Assembler"));
    assembler.setFileExtensions("Source", {'.s', '.S'});
    assembler.FileSep = '/';
    assembler.Command.Arguments = [commonFlags, {'-c', '-x', 'assembler-with-cpp'}];
    tc.Tools(end+1) = assembler;

    % C Linker
    cLinker = target.create("BuildTool", "Linker", ...
        [gnuARMPath '/bin/arm-none-eabi-gcc' exeExt], ...
        "Name", toolName("C Linker"));
    cLinker.setDirective("LibraryGroup", '-Wl,--start-group', '-Wl,--end-group');
    cLinker.setDirective("CommandFile", "@");
    cLinker.setDirective("Debug", "-g");
    cLinker.setFileExtensions("Executable", {'.elf'});
    cLinker.FileSep = '/';
    tc.Tools(end+1) = cLinker;

    % C++ Linker
    cxxLinker = target.create("BuildTool", "C++ Linker", ...
        [gnuARMPath '/bin/arm-none-eabi-g++' exeExt], ...
        "Name", toolName("C++ Linker"));
    cxxLinker.setDirective("LibraryGroup", ...
        "-o $(PRODUCT).elf -Wl,--start-group", "-Wl,--end-group");
    cxxLinker.setDirective("CommandFile", "@");
    cxxLinker.setDirective("Debug", "-g");
    cxxLinker.setFileExtensions("Executable", {'.elf'});
    cxxLinker.FileSep = '/';
    tc.Tools(end+1) = cxxLinker;

    % Archiver
    ar = target.create("BuildTool", "Archiver", ...
        [gnuARMPath '/bin/arm-none-eabi-ar' exeExt], ...
        "Name", toolName("Archiver"));
    ar.setDirective("LibrarySearchPath", '');
    ar.setDirective("OutputFlag", '');
    ar.setFileExtensions("Object", {'.o'});
    ar.setFileExtensions("Static Library", {'.lib'});
    ar.FileSep = '/';
    ar.Command.Arguments = {'rcs'};
    tc.Tools(end+1) = ar;

    % Associate the processor to this toolchain
    proc = target.get('Processor', 'Name', 'NXP_S32K3X4EVB_Q257', ...
        'Manufacturer', 'NXP');
    tc.SupportedHardware = target.create('HardwareComponentSupport', ...
        'Component', proc);

    % Register the toolchain
    target.add(tc, "UserInstall", true, "SuppressOutput", false);
end

For a detailed walkthrough of creating a toolchain definition, see the toolchain section in Create Target Registration Packages for C/C++ Test Execution on Targets.

Note that in the Instruction Set Simulator mode, you must provide a simplified linker script. For instance:

  • The example shows a toolchain definition for test execution on physical hardware. The toolchain uses a flash linker script linker_flash_s32k344.ld provided by the board vendor. The --entry linker flag points to a function in this file.

  • If you are compiling and linking for the Instruction Set Simulator mode, replace the linker script with a RAM-only linker script with a minimal entry point (for example, S32K344_ram.ld). The RAM linker script should allocate sufficient stack space for the test harness — at least 8 KB is recommended.

Bring It All Together in Package Registration

The package registration file NXP_S32K3X4EVB_Q257_PackageRegister.m is the entry point that ties all the pieces together. When you register the package, this file runs and calls the board definition and toolchain definition:

addpath(fileparts(mfilename('fullpath')));
if savepath
    warning('Error while saving path!');
end
rehash toolboxcache;
prefs = NXP_S32K3X4EVB_Q257_Preferences();
NXP_S32K3X4EVB_Q257_BoardDefinition();
NXP_S32K3X4EVB_Q257_ToolchainDefinition();

This file:

  • Adds the package folder to the MATLAB path so the framework can find your classes.

  • Saves the path and refreshes the toolbox cache so the registration persists across Polyspace Test sessions.

  • Calls NXP_S32K3X4EVB_Q257_BoardDefinition() to register the board, processor, source files, execution tool, and communication settings.

  • Calls NXP_S32K3X4EVB_Q257_ToolchainDefinition() to register the cross-compiler and build tools.

Define a package unregistration file NXP_S32K3X4EVB_Q257_PackageUnregister.m to unregister the package when no longer necessary:

prefs = NXP_S32K3X4EVB_Q257_Preferences();
target.remove("Toolchain", prefs.ToolChainName, 'IncludeAssociations', true);
target.remove("TargetConnection", prefs.BoardName, 'IncludeAssociations', true);
rmpath(fileparts(mfilename('fullpath')));

Your full target registration package should now have all these files defined:

NXP_S32K3X4EVB_Q257_Package/
├── pstestPreferences.json
├── NXP_S32K3X4EVB_Q257_Preferences.m
├── NXP_S32K3X4EVB_Q257_PackageRegister.m
├── NXP_S32K3X4EVB_Q257_PackageUnregister.m
├── NXP_S32K3X4EVB_Q257_DebugIOTool.m
├── NXP_S32K3X4EVB_Q257_BoardDefinition.m
├── NXP_S32K3X4EVB_Q257_ToolchainDefinition.m
├── S32K344_Trace32_config.t32
├── s32k344evb-q257.cmm
├── src/
│   ├── rtiostream_fdx.c
│   ├── rtiostream_fdx.h
│   ├── NXP_S32K3X4EVB_Q257_board.c
│   ├── NXP_S32K3X4EVB_Q257_board.h
│   └── ...  (SDK/driver files for your board)

Register and Run

To register and use the target:

  1. Open a project configuration in the Polyspace Platform user interface. Select Manage Boards and register your package using the NXP_S32K3X4EVB_Q257_PackageRegister.m file.

  2. On the Build tab, select your board name for the option Target board name (Testing).

  3. Build and run your tests. TRACE32 PowerView opens automatically, loads the binary, configures FDX channels, and starts execution. Test results are transferred through FDX pipes and displayed in the user interface.

Troubleshooting

SymptomLikely Cause
"FATAL ERROR from PBI-driver. TRACE32 device already used by other GUI."Add CONNECTIONMODE=AUTOCONNECT to the .t32 config file after the USB line. This forces the connection to the probe even if a previous session did not release it cleanly.
No data transfer or test hangsVerify that initFDX() is called in rtIOStreamOpen. Open the FDX channel window in TRACE32 PowerView and check that channels show "EnableChannel" selected and pipes are connected.
Flash programming errorPower-cycle both the debug probe and the target board to reset the hardware state.
(ISS) Target never reaches main, test hangs with no data transferVendor startup code is trying to initialize hardware peripherals (such as clock controllers) that do not exist on the simulator. Replace the vendor startup assembly file and flash linker script with a minimal startup (such as Lauterbach crt0.S) and a RAM-only linker script.
(ISS) PowerView freezes or crashes on startup for an uncommon target architectureSome target architectures are not fully supported by the TRACE32 simulator with FDX. As an alternative, you can use the T32XIL toolbox workflow which communicates over TCP instead of FDX. See Run and Debug C/C++ Tests on Target Using TRACE32 (T32XIL Toolbox Workflow).

See Also

Topics