Skip to content

[nrf toup] nordic: Reorganize how gen_uicr.py is invoked #3202

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

SebastianBoe
Copy link
Contributor

Reorganize how gen_uicr.py is invoked

Ref: NCSDK-NONE

@SebastianBoe SebastianBoe force-pushed the ncs_ps branch 2 times, most recently from dc098ca to 546e163 Compare August 18, 2025 10:20
@SebastianBoe SebastianBoe changed the title [NOT FOR REVIEW] [FOR CI TESTING ONLY] [nrf toup] nordic: Reorganize how gen_uicr.py is invoked [nrf toup] nordic: Reorganize how gen_uicr.py is invoked Aug 18, 2025
@@ -169,13 +149,25 @@ def main() -> None:
"--out-uicr-hex",
required=True,
type=argparse.FileType("w", encoding="utf-8"),
help="Path to write the generated UICR HEX file to",
help="Path to write the generated merged UICR+PERIPHCONF HEX file to (typically zephyr.hex)",
Copy link
Contributor

Choose a reason for hiding this comment

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

These artifacts are useful to have separate depending on your application, so the choice to keep them separate was deliberate. Can we keep them as two different outputs and use something like mergehex afterwards instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How are they useful? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is possible to have them separate. But it would complicate the build system, and also to a certain degree the user experience.

So I would prefer to not keep them separate unless necessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

The PERIPHCONF area in MRAM can be modified even when UICR is locked, as long as you haven't put it in the PROTECTEDMEM region. So there is a use case for pulling out just the periphconf.hex part and including it in a DFU package without the UICR part.

Copy link
Contributor

Choose a reason for hiding this comment

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

customers might bundle the periphconf in their ota image but not the rest of the uicr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see, I did not think of that, will fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose even in non-DFU use-cases, you may have locked your UICR, and which to west flash a new periphconf, but not the UICR.

I could make this configurable via KConfig.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hex files restored

f"Expected a DT node with label '{UICR_NODELABEL}'."
) from e
uicr.PERIPHCONF.ADDRESS = args.periphconf_address
uicr.PERIPHCONF.MAXCOUNT = math.floor(args.periphconf_size / 8)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: noticed this earlier, but this could just be args.periphconf_size // 8

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How about we error-out when it's not divisible by 8?

Copy link
Contributor

@karstenkoenig karstenkoenig left a comment

Choose a reason for hiding this comment

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

Ah super nice, but please split into independent hex files again


menu "UICR generator options"

config UICR_SAMPLE_GENERATE_PERIPHCONF
Copy link
Contributor

Choose a reason for hiding this comment

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

What does the sample in the name stand for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is trying to communicate to the reader that this Kconfig option is specific to this gen_uicr "sample".

As opposed to most other Kconfigs in ~/ncs/zephyr/soc, which are read by all zephyr images.

Maybe

GEN_UICR_SAMPLE_GENERATE_PERIPHCONF

Would be clearer?

Or do you have another idea?

Copy link
Contributor

Choose a reason for hiding this comment

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

sample in the sense of example? That's what I assume and find confusing, this shouldn't be example code right?
Or in sample the sense of this one unit? image or target?

Wouldn't GEN_UICR_GENERATE_PERIPHCONF be enough? Or would that be confusing because it only applies to the uicr target and not actually be set in the app or radio build?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

@nordicjm nordicjm left a comment

Choose a reason for hiding this comment

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

Looks good!

default y
help
Generate UICR HEX file.

Copy link
Contributor

Choose a reason for hiding this comment

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

trailing newline

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 25 to 28
dt_nodelabel(partition_path NODELABEL ${partition_nodelabel})
if(NOT DEFINED partition_path)
message(FATAL_ERROR "${partition_nodelabel} not found in devicetree")
endif()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
dt_nodelabel(partition_path NODELABEL ${partition_nodelabel})
if(NOT DEFINED partition_path)
message(FATAL_ERROR "${partition_nodelabel} not found in devicetree")
endif()
dt_nodelabel(partition_path NODELABEL ${partition_nodelabel} REQUIRED)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice!

Was not aware.

Comment on lines +46 to +38
# Use CMAKE_VERBOSE_MAKEFILE to silence an unused-variable warning.
if(CMAKE_VERBOSE_MAKEFILE)
endif()
Copy link
Contributor

Choose a reason for hiding this comment

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

?

Copy link
Contributor Author

@SebastianBoe SebastianBoe Aug 20, 2025

Choose a reason for hiding this comment

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

I got 5 lines of warning in the logs when this variable went unreferenced.

I assume SYSBUILD passes it to each image, and Zephyr usually references it, but since I have this minimal Zephyr it goes unreferenced.


set(periphconf_args)
set(periphconf_elfs)
set(merged_hex_file ${APPLICATION_BINARY_DIR}/zephyr/zephyr.hex)
Copy link
Contributor

Choose a reason for hiding this comment

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

use the Kconfig for this for the bin name

Copy link
Contributor Author

@SebastianBoe SebastianBoe Aug 21, 2025

Choose a reason for hiding this comment

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

fixed

I couldn't find neither a variable nor a Kconfig that contained the hex suffix unfortunately.

@@ -0,0 +1,13 @@
# SPDX-License-Identifier: Apache-2.0

source "Kconfig.zephyr"
Copy link
Contributor

Choose a reason for hiding this comment

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

nit. non-blocking - might want to put this at bottom so that the application Kconfigs appear at the top

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

Choose a reason for hiding this comment

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

don't think prj.conf is needed and it will fallback to empty.conf? Could be wrong on that though

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I got an error without it earlier, but I'll retest.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I get this error without it:

-- Board: nrf54h20dk, Revision: 0.9.0, qualifiers: nrf54h20/cpurad
CMake Error at /home/sebo/ncs/zephyr/cmake/modules/extensions.cmake:2919 (message):
  No prj.conf file(s) was found in the
  /home/sebo/ncs/zephyr/soc/nordic/common/uicr/gen_uicr folder(s), please
  read the Zephyr documentation on application development.
Call Stack (most recent call first):
  /home/sebo/ncs/zephyr/cmake/modules/configuration_files.cmake:40 (zephyr_file)
  /home/sebo/ncs/zephyr/cmake/modules/zephyr_default.cmake:131 (include)
  /home/sebo/ncs/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:75 (include)
  /home/sebo/ncs/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
  CMakeLists.txt:15 (find_package)


-- Configuring incomplete, errors occurred!
CMake Error at cmake/modules/sysbuild_extensions.cmake:530 (message):
  CMake configure failed for Zephyr project: uicr

  Location: /home/sebo/ncs/zephyr/soc/nordic/common/uicr/gen_uicr
Call Stack (most recent call first):
  cmake/modules/sysbuild_images.cmake:43 (ExternalZephyrProject_Cmake)
  cmake/modules/sysbuild_default.cmake:21 (include)
  /home/sebo/ncs/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:75 (include)
  /home/sebo/ncs/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate)
  /home/sebo/ncs/zephyr/share/sysbuild-package/cmake/SysbuildConfig.cmake:8 (include)
  template/CMakeLists.txt:10 (find_package)


-- Configuring incomplete, errors occurred!

Copy link
Contributor

Choose a reason for hiding this comment

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

keep the empty file, maybe something to look at in future @tejlmand


# Ensure UICR is configured and built after the default image so EDT/ELFs exist.
sysbuild_add_dependencies(CONFIGURE uicr ${DEFAULT_IMAGE})
add_dependencies( uicr ${DEFAULT_IMAGE})
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
add_dependencies( uicr ${DEFAULT_IMAGE})
add_dependencies(uicr ${DEFAULT_IMAGE})

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@jonathannilsen jonathannilsen self-requested a review August 20, 2025 13:39
Comment on lines 216 to 219
# Update UICR hex with the modified UICR data
uicr_data = bytes(uicr)
uicr_hex = IntelHex() # Reset and rebuild with updated UICR
uicr_hex.frombytes(uicr_data, offset=args.uicr_address)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this might flow a bit cleaner if we populated each hex object just once, instead of populating them with initial data first and then overwriting it. For example:

        periphconf_hex = IntelHex()

        if args.in_periphconf_elfs:  # Check if periphconf data is provided
            periphconf_combined = extract_and_combine_periphconfs(args.in_periphconf_elfs)

            padding_len = args.periphconf_size - len(periphconf_combined)
            periphconf_final = periphconf_combined + bytes([0xFF for _ in range(padding_len)])

            # Add periphconf data to separate and merged hex files
            periphconf_hex.frombytes(periphconf_final, offset=args.periphconf_address)

            uicr.PERIPHCONF.ENABLE = ENABLED_VALUE
            uicr.PERIPHCONF.ADDRESS = args.periphconf_address
            uicr.PERIPHCONF.MAXCOUNT = args.periphconf_size // 8

        uicr_hex = IntelHex()
        uicr_hex.frombytes(bytes(uicr), offset=args.uicr_address)

        merged_hex = IntelHex()
        merged_hex.fromdict(uicr_hex.todict())
        if periphconf_hex:
            merged_hex.fromdict(periphconf_hex.todict())

        # Write the three hex files
        merged_hex.write_hex_file(args.out_merged_hex)
        uicr_hex.write_hex_file(args.out_uicr_hex)
        if args.out_periphconf_hex:
            periphconf_hex.write_hex_file(args.out_periphconf_hex)

(I think IntelHex.todict() works there, but haven't tried running it)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

much better!

Comment on lines 202 to 203
if args.in_periphconf_elfs: # Check if periphconf data is provided
periphconf_combined = extract_and_combine_periphconfs(args.in_periphconf_elfs)
Copy link
Contributor

@jonathannilsen jonathannilsen Aug 20, 2025

Choose a reason for hiding this comment

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

I think we should retain the ability to generate an empty (0xFFFF_FFFFs) periphconf blob if no elf is provided here - an empty periphconf is still valid

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@SebastianBoe
Copy link
Contributor Author

@karstenkoenig , @jonathannilsen , @nordicjm :

All feedback addressed, will reupload to upstream zephyr soon.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be a license header in this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

endif()

if(EXISTS ${_dir}/zephyr/zephyr.dts)
list(APPEND periphconf_elfs ${_dir}/zephyr/zephyr.elf)
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this should also take into account whether KERNEL_BIN_NAME has been changed in the image it's getting from? Not sure how that is best handled

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't expect this to ever be anything but zephyr

Reorganize how gen_uicr.py is invoked.

Instead of invoking it from one of the Zephyr images we invoke it from
a new special Zephyr image called uicr.

This uicr Zephyr image is flashed in the same way as normal Zephyr
images so special handling in the runner is no longer necessary.

Also, we simplify gen_uicr.py by moving parsing of Kconfig/DT from
gen_uicr.py to CMake.

Signed-off-by: Sebastian Bøe <[email protected]>
@SebastianBoe
Copy link
Contributor Author

These patches are now up for review upstream in zephyrproject-rtos/zephyr#94796

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants