Skip to content

NXP: add SPSDK as runner and support i.MX95 firmware build and flashing #80507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions boards/common/spsdk.board.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0

board_set_flasher_ifnset(spsdk)
board_finalize_runner_args(spsdk)
8 changes: 8 additions & 0 deletions boards/nxp/common/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

config BOARD_NXP_SPSDK_IMAGE
bool "Generate bootable firmware image with SPSDK"
help
Generate bootable firmware image with NXP Secure
Provisioning SDK (SPSDK).
113 changes: 113 additions & 0 deletions boards/nxp/imx95_evk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,118 @@ if (CONFIG_SOF AND CONFIG_BOARD_IMX95_EVK_MIMX9596_M7_DDR)
)
endif()

if(CONFIG_BOARD_NXP_SPSDK_IMAGE OR (DEFINED ENV{USE_NXP_SPSDK_IMAGE}
AND "$ENV{USE_NXP_SPSDK_IMAGE}" STREQUAL "y"))
find_program(7Z_EXECUTABLE 7z REQUIRED)
set(FIRMWARE_RELEASE "imx95-19x19-lpddr5-evk-boot-firmware-0.1")
# Parse SPSDK version
execute_process(
COMMAND spsdk --version
OUTPUT_VARIABLE SPSDK_VERSION_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" SPSDK_VERSION "${SPSDK_VERSION_OUTPUT}")
message(STATUS "SPSDK version is ${SPSDK_VERSION}")

if(CONFIG_BOARD_IMX95_EVK_MIMX9596_M7)
set(AHAB_CONFIG_FILE "imx95_evk_mimx9596_m7_ahab.yaml")

file(WRITE ${CMAKE_BINARY_DIR}/zephyr/${AHAB_CONFIG_FILE}
"
family: mimx9596
revision: a1
target_memory: standard
output: ${CMAKE_BINARY_DIR}/zephyr/flash.bin
containers:
- binary_container:
path: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/mx95a0-ahab-container.img
- container:
srk_set: none
images:
- lpddr_imem: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_imem_v202311.bin
Copy link
Contributor

@hakehuang hakehuang May 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIRMWARE_RELEASE need check availability, and need provide a default value?

lpddr_imem_qb: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_imem_qb_v202311.bin
lpddr_dmem: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_dmem_v202311.bin
lpddr_dmem_qb: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_dmem_qb_v202311.bin
oei_ddr: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/oei-m33-ddr.bin
- oei_tcm: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/oei-m33-tcm.bin
- system_manager: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/m33_image-mx95alt.bin
- cortex_m7_app: ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.bin
- v2x_dummy: true
")
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
COMMAND ${7Z_EXECUTABLE} x ${ZEPHYR_HAL_NXP_MODULE_DIR}/zephyr/blobs/imx-boot-firmware/${FIRMWARE_RELEASE}.bin -o./imx-boot-firmware -aos -bso0 -bse1
COMMAND ${7Z_EXECUTABLE} x imx-boot-firmware/${FIRMWARE_RELEASE} -aos -bso0 -bse1
COMMAND nxpimage ahab export -c ${CMAKE_BINARY_DIR}/zephyr/${AHAB_CONFIG_FILE}
)
elseif(CONFIG_SOC_MIMX9596_A55)
file(WRITE ${CMAKE_BINARY_DIR}/zephyr/imx95_evk_mimx9596_a55_ahab_primary.yaml
"
family: mimx9596
revision: a1
target_memory: standard
output: ${CMAKE_BINARY_DIR}/zephyr/primary_ahab.bin
containers:
- binary_container:
path: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/mx95a0-ahab-container.img
- container:
srk_set: none
images:
- lpddr_imem: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_imem_v202311.bin
lpddr_imem_qb: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_imem_qb_v202311.bin
lpddr_dmem: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_dmem_v202311.bin
lpddr_dmem_qb: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/lpddr5_dmem_qb_v202311.bin
oei_ddr: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/oei-m33-ddr.bin
- oei_tcm: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/oei-m33-tcm.bin
- system_manager: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/m33_image-mx95evk.bin
- spl: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/u-boot-spl.bin-imx95-19x19-lpddr5-evk-sd
- v2x_dummy: true
")
file(WRITE ${CMAKE_BINARY_DIR}/zephyr/imx95_evk_mimx9596_a55_ahab_secondary.yaml
"
family: mimx9596
revision: a1
target_memory: standard
output: ${CMAKE_BINARY_DIR}/zephyr/secondary_ahab.bin
containers:
- container:
srk_set: none
images:
- atf: ${CMAKE_BINARY_DIR}/zephyr/${FIRMWARE_RELEASE}/bl31-imx95-zephyr.bin
- image_path: ${CMAKE_BINARY_DIR}/zephyr/${CONFIG_KERNEL_BIN_NAME}.bin
load_address: '${CONFIG_SRAM_BASE_ADDRESS}'
entry_point: '${CONFIG_SRAM_BASE_ADDRESS}'
image_type: executable
core_id: cortex-a55
is_encrypted: false
")
file(WRITE ${CMAKE_BINARY_DIR}/zephyr/imx95_evk_mimx9596_a55_ahab_flash_template.yaml
"
family: mimx9596
revision: latest
memory_type: serial_downloader
primary_image_container_set: ${CMAKE_BINARY_DIR}/zephyr/imx95_evk_mimx9596_a55_ahab_primary.yaml
secondary_image_container_set: ${CMAKE_BINARY_DIR}/zephyr/imx95_evk_mimx9596_a55_ahab_secondary.yaml
")
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
COMMAND ${7Z_EXECUTABLE} x ${ZEPHYR_HAL_NXP_MODULE_DIR}/zephyr/blobs/imx-boot-firmware/${FIRMWARE_RELEASE}.bin -o./imx-boot-firmware -aos -bso0 -bse1
COMMAND ${7Z_EXECUTABLE} x imx-boot-firmware/${FIRMWARE_RELEASE} -aos -bso0 -bse1
)
if(SPSDK_VERSION VERSION_GREATER_EQUAL "3.0.0")
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
COMMAND nxpimage bootable-image export -c ${CMAKE_BINARY_DIR}/zephyr/imx95_evk_mimx9596_a55_ahab_flash_template.yaml -o flash.bin
)
else()
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
COMMAND nxpimage bootable-image merge -c ${CMAKE_BINARY_DIR}/zephyr/imx95_evk_mimx9596_a55_ahab_flash_template.yaml -o flash.bin
)
endif()
else()
message(FATAL_ERROR "SPSDK Image not supported on the platform!")
endif()
zephyr_blobs_verify(FILES
${ZEPHYR_HAL_NXP_MODULE_DIR}/zephyr/blobs/imx-boot-firmware/${FIRMWARE_RELEASE}.bin
REQUIRED)
endif()

zephyr_library()
zephyr_library_sources(board.c)
4 changes: 4 additions & 0 deletions boards/nxp/imx95_evk/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

source "boards/nxp/common/Kconfig"
16 changes: 16 additions & 0 deletions boards/nxp/imx95_evk/board.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,19 @@
if (CONFIG_SOF AND CONFIG_BOARD_IMX95_EVK_MIMX9596_M7_DDR)
board_set_rimage_target(imx95)
endif()

if(CONFIG_BOARD_NXP_SPSDK_IMAGE OR (DEFINED ENV{USE_NXP_SPSDK_IMAGE}
AND "$ENV{USE_NXP_SPSDK_IMAGE}" STREQUAL "y"))
board_set_flasher_ifnset(spsdk)

board_runner_args(spsdk "--family=mimx9596")
board_runner_args(spsdk "--bootloader=${CMAKE_BINARY_DIR}/zephyr/imx95-19x19-lpddr5-evk-boot-firmware-0.1/imx-boot-imx95-19x19-lpddr5-evk-sd.bin-flash_all")
board_runner_args(spsdk "--flashbin=${CMAKE_BINARY_DIR}/zephyr/flash.bin")
if(CONFIG_CPU_CORTEX_A55)
board_runner_args(spsdk "--containers=two")
else()
board_runner_args(spsdk "--containers=one")
endif()

include(${ZEPHYR_BASE}/boards/common/spsdk.board.cmake)
endif()
171 changes: 128 additions & 43 deletions boards/nxp/imx95_evk/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,88 +115,139 @@ Programming and Debugging (A55)

.. zephyr:board-supported-runners::
Use this configuration to run basic Zephyr applications and kernel tests,
for example, with the :zephyr:code-sample:`synchronization` sample:
There are multiple methods to program and run Zephyr on the A55 core:

Option 1. Boot Zephyr by Using SPSDK Runner
===========================================

SPSDK runner leverages SPSDK tools (https://spsdk.readthedocs.io), it builds an
bootable flash image ``flash.bin`` which includes all necessary firmware components,
such as ELE+V2X firmware, System Manager, TCM OEI, TF-A images etc. Using west flash
command will download the boot image flash.bin to DDR memory, SD card or eMMC flash.
By using flash.bin, as no U-Boot image is available, so TF-A will boot up Zephyr on
the first Cortex-A55 Core directly.

In order to use SPSDK runner, it requires fetching binary blobs, which can be achieved
by running the following command:

.. code-block:: console
1. Build and run the Non-SMP application
west blobs fetch hal_nxp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as for now, we need manually unzip the blobs, so need add steps

.. note::

It is recommended running the command above after :file:`west update`.

SPSDK runner is enabled by configure item :kconfig:option:`CONFIG_BOARD_NXP_SPSDK_IMAGE`, currently
it is not enabled by default for i.MX95 EVK board, so use this configuration to enable
it, for example, with the :zephyr:code-sample:`synchronization` sample:

.. zephyr-app-commands::
:zephyr-app: samples/synchronization
:host-os: unix
:board: imx95_evk/mimx9596/a55
:goals: build
:gen-args: -DCONFIG_BOARD_NXP_SPSDK_IMAGE=y

This will build an image (zephyr.bin) with the synchronization sample app.
If :kconfig:option:`CONFIG_BOARD_NXP_SPSDK_IMAGE` is available and enabled for the board variant,
``flash.bin`` will be built automatically. The programming could be through below commands.
Before that, switch SW7[1:4] should be configured to 0b1001 for usb download mode
to boot, and USB1 and DBG ports should be connected to PC. There are 4 serial ports
enumerated (115200 8n1), and we use the first for M7 and the fourth for M33 System Manager.
(The flasher is spsdk which already installed via scripts/requirements.txt.
On linux host, usb device permission should be configured per Installation Guide
of https://spsdk.readthedocs.io)

Copy the compiled ``zephyr.bin`` to the first FAT partition of the SD card and
plug the SD card into the board. Power it up and stop the u-boot execution at
prompt.
.. code-block:: none
Use U-Boot to load and kick zephyr.bin to Cortex-A55 Core1:
# load and run without programming. for next flashing, execute 'reset' in the
# fourth serail port
$ west flash
.. code-block:: console
# program to SD card, then set SW7[1:4]=0b1011 to reboot
$ west flash --bootdevice sd
fatload mmc 1:1 0xd0000000 zephyr.bin; dcache flush; icache flush; cpu 1 release 0xd0000000
# program to emmc card, then set SW7[1:4]=0b1010 to reboot
$ west flash --bootdevice=emmc
Or use the following command to kick zephyr.bin to Cortex-A55 Core0:
Option 2. Boot Zephyr by Using U-Boot Command
=============================================

.. code-block:: console
U-Boot "go" command can be used to start Zephyr on A55 core0 and U-Boot "cpu" command
is used to load and kick Zephyr to the other A55 secondary Cores. Currently "cpu" command
is supported in : `Real-Time Edge U-Boot`_ (use the branch "uboot_vxxxx.xx-y.y.y,
xxxx.xx is uboot version and y.y.y is Real-Time Edge Software version, for example
"uboot_v2023.04-2.9.0" branch is U-Boot v2023.04 used in Real-Time Edge Software release
v2.9.0), and pre-build images and user guide can be found at `Real-Time Edge Software`_.

fatload mmc 1:1 0xd0000000 zephyr.bin; dcache flush; icache flush; go 0xd0000000
.. _Real-Time Edge U-Boot:
https://github.com/nxp-real-time-edge-sw/real-time-edge-uboot
.. _Real-Time Edge Software:
https://www.nxp.com/rtedge

Step 1: Download Zephyr Image into DDR Memory
---------------------------------------------

It will display the following console output:
Firstly need to download Zephyr binary image into DDR memory, it can use tftp:

.. code-block:: console
*** Booting Zephyr OS build v3.6.0-4569-g483c01ca11a7 ***
thread_a: Hello World from cpu 0 on imx95_evk!
thread_b: Hello World from cpu 0 on imx95_evk!
thread_a: Hello World from cpu 0 on imx95_evk!
thread_b: Hello World from cpu 0 on imx95_evk!
thread_a: Hello World from cpu 0 on imx95_evk!
tftp 0xd0000000 zephyr.bin
Or copy the Zephyr image ``zephyr.bin`` SD card and plug the card into the board, for example
if copy to the FAT partition of the SD card, use the following U-Boot command to load the image
into DDR memory (assuming the SD card is dev 1, fat partition ID is 1, they could be changed
based on actual setup):

2. Build and run the SMP application
.. code-block:: console
fatload mmc 1:1 0xd0000000 zephyr.bin;
Step 2: Boot Zephyr
-------------------

Use this configuration to run basic Zephyr applications and kernel tests,
for example, with the :zephyr:code-sample:`synchronization` sample:

.. zephyr-app-commands::
:zephyr-app: samples/synchronization
:host-os: unix
:board: imx95_evk/mimx9596/a55/smp
:board: imx95_evk/mimx9596/a55
:goals: build

This will build an image (zephyr.bin) with the synchronization sample app.

Copy the compiled ``zephyr.bin`` to the first FAT partition of the SD card and
plug the SD card into the board. Power it up and stop the u-boot execution at
prompt.

Use the following command to kick zephyr.bin to Cortex-A55 Core0:
Then use the following command to boot Zephyr on the core0:

.. code-block:: console
fatload mmc 1:1 0xd0000000 zephyr.bin; dcache flush; icache flush; go 0xd0000000
dcache off; icache flush; go 0xd0000000;
Or use "cpu" command to boot from secondary Core, for example Core1:

.. code-block:: console
dcache flush; icache flush; cpu 1 release 0xd0000000
It will display the following console output:

.. code-block:: console
*** Booting Zephyr OS build v3.7.0-rc3-15-g2f0beaea144a ***
Secondary CPU core 1 (MPID:0x100) is up
Secondary CPU core 2 (MPID:0x200) is up
Secondary CPU core 3 (MPID:0x300) is up
Secondary CPU core 4 (MPID:0x400) is up
Secondary CPU core 5 (MPID:0x500) is up
thread_a: Hello World from cpu 0 on imx95_evk!
thread_b: Hello World from cpu 4 on imx95_evk!
thread_a: Hello World from cpu 0 on imx95_evk!
thread_b: Hello World from cpu 3 on imx95_evk!
*** Booting Zephyr OS build v3.6.0-4569-g483c01ca11a7 ***
thread_a: Hello World from cpu 0 on imx95_evk!
thread_b: Hello World from cpu 1 on imx95_evk!
thread_b: Hello World from cpu 0 on imx95_evk!
thread_a: Hello World from cpu 0 on imx95_evk!
thread_b: Hello World from cpu 5 on imx95_evk!
thread_b: Hello World from cpu 0 on imx95_evk!
thread_a: Hello World from cpu 0 on imx95_evk!
thread_b: Hello World from cpu 2 on imx95_evk!
Option 3. Boot Zephyr by Using Remoteproc under Linux
=====================================================

When running Linux on the A55 core, it can use the remoteproc framework to load and boot Zephyr,
refer to Real-Time Edge user guide for more details. Pre-build images and user guide can be found
at `Real-Time Edge Software`_.


Programming and Debugging (M7)
******************************
Expand All @@ -213,13 +264,47 @@ To program M7, an i.MX container image ``flash.bin`` must be made, which contain
multiple elements required, like ELE+V2X firmware, System Manager, TCM OEI, Cortex-M7
image and so on.

The steps making flash.bin and programming should refer to ``Getting Started with
SPSDK runner is used to build ``flash.bin``, and it requires fetching binary blobs, which
can be achieved by running the following command:

.. code-block:: console
west blobs fetch hal_nxp
.. note::

It is recommended running the command above after :file:`west update`.

Two methods to build and program ``flash.bin``.

1. If :kconfig:option:`CONFIG_BOARD_NXP_SPSDK_IMAGE` is not available for the board variant,
the steps making flash.bin and programming should refer to ``Getting Started with
MCUXpresso SDK for IMX95LPD5EVK-19.pdf`` in i.MX95 `MCUX SDK release`_. Note that
for the DDR variant, one should use the Makefile targets containing the ``ddr`` keyword.

See ``4.2 Run an example application``, just rename ``zephyr.bin`` to ``m7_image.bin``
to make flash.bin and program to SD/eMMC.

2. If :kconfig:option:`CONFIG_BOARD_NXP_SPSDK_IMAGE` is available and enabled for the board variant,
``flash.bin`` will be built automatically. The programming could be through below commands.
Before that, switch SW7[1:4] should be configured to 0b1001 for usb download mode
to boot, and USB1 and DBG ports should be connected to PC. There are 4 serial ports
enumerated (115200 8n1), and we use the first for M7 and the fourth for M33 System Manager.
(The flasher is spsdk which already installed via scripts/requirements.txt.
On linux host, usb device permission should be configured per Installation Guide
of https://spsdk.readthedocs.io)

.. code-block:: none
# load and run without programming. for next flashing, execute 'reset' in the
# fourth serail port
$ west flash
# program to SD card, then set SW7[1:4]=0b1011 to reboot
$ west flash --bootdevice sd
# program to emmc card, then set SW7[1:4]=0b1010 to reboot
$ west flash --bootdevice=emmc
Copy link
Contributor

@hakehuang hakehuang Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$ west flash --bootdevice=emmc
$ west flash --bootdevice=emmc
Need switch the board to SD/MMC mode, SW7 1010, and power cycle board to boot with it.

Zephyr supports two M7-based i.MX95 boards: ``imx95_evk/mimx9596/m7`` and
``imx95_evk/mimx9596/m7/ddr``. The main difference between them is the memory
used. ``imx95_evk/mimx9596/m7`` uses TCM (ITCM for code and, generally, read-only
Expand Down
Loading
Loading