Skip to content

Commit 546e163

Browse files
committed
[nrf toup] nordic: Reorganize how gen_uicr.py is invoked
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. gen_uicr.py has been modified to no longer create a seperate hex file for periphconf as the runner infrastructure expects to only flash one hex file. Also, we simplify gen_uicr.py by moving parsing of Kconfig/DT from gen_uicr.py to CMake. Ref: NCSDK-NONE Signed-off-by: Sebastian Bøe <[email protected]>
1 parent 8f6f73e commit 546e163

File tree

13 files changed

+191
-143
lines changed

13 files changed

+191
-143
lines changed

scripts/west_commands/runners/nrf_common.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -433,26 +433,6 @@ def program_hex(self):
433433
core='Application',
434434
)
435435

436-
if self.build_conf.getboolean("CONFIG_NRF_HALTIUM_GENERATE_UICR"):
437-
zephyr_build_dir = Path(self.cfg.build_dir) / 'zephyr'
438-
439-
self.op_program(
440-
str(zephyr_build_dir / 'uicr.hex'),
441-
'ERASE_NONE',
442-
None,
443-
defer=True,
444-
core='Application',
445-
)
446-
447-
if self.build_conf.getboolean("CONFIG_NRF_HALTIUM_UICR_PERIPHCONF"):
448-
self.op_program(
449-
str(zephyr_build_dir / 'periphconf.hex'),
450-
'ERASE_NONE',
451-
None,
452-
defer=True,
453-
core='Application',
454-
)
455-
456436
if not self.erase and regtool_generated_uicr:
457437
self.exec_op('erase', core=core, kind='uicr')
458438
else:

soc/nordic/Kconfig.sysbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
config HAS_NORDIC_VPR_LAUNCHER_IMAGE
55
bool
66

7+
rsource "common/uicr/Kconfig.sysbuild"
78
rsource "common/vpr/Kconfig.sysbuild"
89
orsource "*/Kconfig.sysbuild"

soc/nordic/common/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
add_subdirectory_ifdef(CONFIG_RISCV_CORE_NORDIC_VPR vpr)
55

6-
if(CONFIG_NRF_PERIPHCONF_SECTION OR CONFIG_NRF_HALTIUM_GENERATE_UICR)
6+
if(CONFIG_NRF_PERIPHCONF_SECTION)
77
add_subdirectory(uicr)
88
endif()
99

soc/nordic/common/uicr/CMakeLists.txt

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,3 @@
44
if(CONFIG_NRF_PERIPHCONF_SECTION)
55
zephyr_linker_sources(SECTIONS uicr.ld)
66
endif()
7-
8-
if(CONFIG_NRF_HALTIUM_GENERATE_UICR)
9-
if(CONFIG_NRF_PERIPHCONF_SECTION)
10-
set(in_periphconf_elf_arg
11-
--in-periphconf-elf $<TARGET_FILE:${ZEPHYR_LINK_STAGE_EXECUTABLE}>
12-
)
13-
endif()
14-
15-
if(CONFIG_NRF_HALTIUM_UICR_PERIPHCONF)
16-
set(periphconf_hex_file ${PROJECT_BINARY_DIR}/periphconf.hex)
17-
set(out_periphconf_hex_arg
18-
--out-periphconf-hex ${periphconf_hex_file}
19-
)
20-
list(APPEND optional_byproducts ${periphconf_hex_file})
21-
endif()
22-
23-
set(uicr_hex_file ${PROJECT_BINARY_DIR}/uicr.hex)
24-
set_property(GLOBAL APPEND PROPERTY extra_post_build_commands
25-
COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${ZEPHYR_BASE}/scripts/dts/python-devicetree/src
26-
${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/gen_uicr.py
27-
--in-config ${DOTCONFIG}
28-
--in-edt-pickle ${EDT_PICKLE}
29-
${in_periphconf_elf_arg}
30-
${out_periphconf_hex_arg}
31-
--out-uicr-hex ${uicr_hex_file}
32-
)
33-
set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts
34-
${uicr_hex_file} ${optional_byproducts}
35-
)
36-
endif()

soc/nordic/common/uicr/Kconfig

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,9 @@
11
# Copyright (c) 2025 Nordic Semiconductor ASA
22
# SPDX-License-Identifier: Apache-2.0
33

4-
config NRF_HALTIUM_GENERATE_UICR
5-
bool "Generate UICR file"
6-
depends on SOC_NRF54H20_CPUAPP
7-
default y
8-
help
9-
Generate UICR HEX file.
10-
11-
if NRF_HALTIUM_GENERATE_UICR
12-
13-
config NRF_HALTIUM_UICR_PERIPHCONF
14-
bool "Initialize global domain peripherals"
15-
default y
16-
help
17-
Generates a blob containing static global domain peripheral initialization
18-
values extracted from the build artifacts, and configures UICR.PERIPHCONF
19-
to point at the blob. The initialization values are then loaded ahead of
20-
ahead of the application boot.
21-
22-
endif
23-
244
config NRF_PERIPHCONF_SECTION
255
bool "Populate global peripheral initialization section"
26-
default y if SOC_NRF54H20_CPUAPP
6+
default y if SOC_NRF54H20_CPUAPP || SOC_NRF54H20_CPURAD
277
depends on LINKER_DEVNULL_SUPPORT
288
imply LINKER_DEVNULL_MEMORY
299
help
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
config NRF_HALTIUM_GENERATE_UICR
2+
bool "Generate UICR file"
3+
depends on SOC_SERIES_NRF54HX
4+
default y
5+
help
6+
Generate UICR HEX file.
7+

soc/nordic/common/uicr/gen_uicr.py

Lines changed: 30 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
import argparse
99
import ctypes as c
1010
import math
11-
import pickle
12-
import re
1311
import sys
14-
from collections import defaultdict
1512
from itertools import groupby
1613

1714
from elftools.elf.elffile import ELFFile
@@ -25,11 +22,6 @@
2522
# Must match the name used in the linker script.
2623
PERIPHCONF_SECTION = "uicr_periphconf_entry"
2724

28-
# Expected nodelabel of the UICR devicetree node, used to extract its location from the devicetree.
29-
UICR_NODELABEL = "uicr"
30-
# Nodelabel of the PERIPHCONF devicetree node, used to extract its location from the devicetree.
31-
PERIPHCONF_NODELABEL = "periphconf_partition"
32-
3325
# Common values for representing enabled/disabled in the UICR format.
3426
ENABLED_VALUE = 0xFFFF_FFFF
3527
DISABLED_VALUE = 0xBD23_28A8
@@ -141,18 +133,6 @@ def main() -> None:
141133
"peripherals, and to protect the device in various ways."
142134
),
143135
)
144-
parser.add_argument(
145-
"--in-config",
146-
required=True,
147-
type=argparse.FileType("r"),
148-
help="Path to the .config file from the application build",
149-
)
150-
parser.add_argument(
151-
"--in-edt-pickle",
152-
required=True,
153-
type=argparse.FileType("rb"),
154-
help="Path to the edt.pickle file from the application build",
155-
)
156136
parser.add_argument(
157137
"--in-periphconf-elf",
158138
dest="in_periphconf_elfs",
@@ -169,13 +149,25 @@ def main() -> None:
169149
"--out-uicr-hex",
170150
required=True,
171151
type=argparse.FileType("w", encoding="utf-8"),
172-
help="Path to write the generated UICR HEX file to",
152+
help="Path to write the generated merged UICR+PERIPHCONF HEX file to (typically zephyr.hex)",
173153
)
174154
parser.add_argument(
175-
"--out-periphconf-hex",
155+
"--periphconf-address",
176156
default=None,
177-
type=argparse.FileType("w", encoding="utf-8"),
178-
help="Path to write the generated PERIPHCONF HEX file to",
157+
type=lambda s: int(s, 0),
158+
help="Absolute flash address of the PERIPHCONF partition (decimal or 0x-prefixed hex)",
159+
)
160+
parser.add_argument(
161+
"--periphconf-size",
162+
default=None,
163+
type=lambda s: int(s, 0),
164+
help="Size in bytes of the PERIPHCONF partition (decimal or 0x-prefixed hex)",
165+
)
166+
parser.add_argument(
167+
"--uicr-address",
168+
required=True,
169+
type=lambda s: int(s, 0),
170+
help="Absolute flash address of the UICR region (decimal or 0x-prefixed hex)",
179171
)
180172
args = parser.parse_args()
181173

@@ -186,49 +178,27 @@ def main() -> None:
186178
uicr.VERSION.MAJOR = UICR_FORMAT_VERSION_MAJOR
187179
uicr.VERSION.MINOR = UICR_FORMAT_VERSION_MINOR
188180

189-
kconfig_str = args.in_config.read()
190-
kconfig = parse_kconfig(kconfig_str)
191-
192-
edt = pickle.load(args.in_edt_pickle)
181+
# Create a single hex object that will contain both UICR and periphconf data
182+
merged_hex = IntelHex()
193183

194-
try:
195-
periphconf_partition = edt.label2node[PERIPHCONF_NODELABEL]
196-
except LookupError as e:
197-
raise ScriptError(
198-
"Failed to find a PERIPHCONF partition in the devicetree. "
199-
f"Expected a DT node with label '{PERIPHCONF_NODELABEL}'."
200-
) from e
184+
if args.in_periphconf_elfs: # Check if periphconf data is provided
185+
periphconf_combined = extract_and_combine_periphconfs(args.in_periphconf_elfs)
201186

202-
flash_base_address = periphconf_partition.flash_controller.regs[0].addr
203-
periphconf_address = flash_base_address + periphconf_partition.regs[0].addr
204-
periphconf_size = periphconf_partition.regs[0].size
187+
padding_len = args.periphconf_size - len(periphconf_combined)
188+
periphconf_final = periphconf_combined + bytes([0xFF for _ in range(padding_len)])
205189

206-
periphconf_combined = extract_and_combine_periphconfs(args.in_periphconf_elfs)
207-
padding_len = periphconf_size - len(periphconf_combined)
208-
periphconf_final = periphconf_combined + bytes([0xFF for _ in range(padding_len)])
190+
# Add periphconf data to the merged hex
191+
merged_hex.frombytes(periphconf_final, offset=args.periphconf_address)
209192

210-
if kconfig.get("CONFIG_NRF_HALTIUM_UICR_PERIPHCONF") == "y":
211193
uicr.PERIPHCONF.ENABLE = ENABLED_VALUE
212-
uicr.PERIPHCONF.ADDRESS = periphconf_address
213-
uicr.PERIPHCONF.MAXCOUNT = math.floor(periphconf_size / 8)
214-
215-
try:
216-
uicr_node = edt.label2node[UICR_NODELABEL]
217-
except LookupError as e:
218-
raise ScriptError(
219-
"Failed to find UICR node in the devicetree. "
220-
f"Expected a DT node with label '{UICR_NODELABEL}'."
221-
) from e
194+
uicr.PERIPHCONF.ADDRESS = args.periphconf_address
195+
uicr.PERIPHCONF.MAXCOUNT = math.floor(args.periphconf_size / 8)
222196

223-
uicr_hex = IntelHex()
224-
uicr_hex.frombytes(bytes(uicr), offset=uicr_node.regs[0].addr)
197+
# Add UICR data to the merged hex
198+
merged_hex.frombytes(bytes(uicr), offset=args.uicr_address)
225199

226-
uicr_hex.write_hex_file(args.out_uicr_hex)
227-
228-
if args.out_periphconf_hex is not None:
229-
periphconf_hex = IntelHex()
230-
periphconf_hex.frombytes(periphconf_final, offset=periphconf_address)
231-
periphconf_hex.write_hex_file(args.out_periphconf_hex)
200+
# Write the merged hex file containing both UICR and periphconf data
201+
merged_hex.write_hex_file(args.out_uicr_hex)
232202

233203
except ScriptError as e:
234204
print(f"Error: {e!s}")
@@ -270,16 +240,5 @@ def extract_and_combine_periphconfs(elf_files: list[argparse.FileType]) -> bytes
270240
return bytes(final_periphconf)
271241

272242

273-
def parse_kconfig(content: str) -> dict[str, str | None]:
274-
result = defaultdict(None)
275-
match_iter = re.finditer(
276-
r"^(?P<config>(SB_)?CONFIG_[^=\s]+)=(?P<value>[^\s#])+$", content, re.MULTILINE
277-
)
278-
for match in match_iter:
279-
result[match["config"]] = match["value"]
280-
281-
return result
282-
283-
284243
if __name__ == "__main__":
285244
main()
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
#
3+
# The code in this CMakeLists.txt constructs the arguments for gen_uicr.py
4+
# and creates a flashable zephyr.hex file containing UICR data (no C code compiled)
5+
#
6+
7+
cmake_minimum_required(VERSION 3.20.0)
8+
9+
# Instead of adding all of Zephyr we add just the subset that is
10+
# required to generate uicr.hex.
11+
#
12+
# The generation of uicr.hex is configured by this image, so we
13+
# include modules from zephyr_default up until kconfig.
14+
15+
find_package(Zephyr
16+
COMPONENTS zephyr_default:kconfig
17+
REQUIRED HINTS $ENV{ZEPHYR_BASE}
18+
)
19+
20+
project(uicr)
21+
22+
# Use CMAKE_VERBOSE_MAKEFILE to silence an unused-variable warning.
23+
if(CMAKE_VERBOSE_MAKEFILE)
24+
endif()
25+
26+
set(periphconf_args)
27+
set(periphconf_elfs)
28+
set(zephyr_hex_file ${APPLICATION_BINARY_DIR}/zephyr/zephyr.hex)
29+
30+
# Compute UICR absolute address from this image's devicetree
31+
dt_nodelabel(uicr_path NODELABEL "uicr")
32+
33+
if(NOT DEFINED uicr_path)
34+
message(FATAL_ERROR "uicr node not found in devicetree")
35+
endif()
36+
37+
dt_reg_addr(UICR_ADDRESS PATH ${uicr_path})
38+
39+
if(NOT DEFINED UICR_ADDRESS)
40+
message(FATAL_ERROR "Failed to read UICR address from DT")
41+
endif()
42+
43+
if(CONFIG_UICR_SAMPLE_GENERATE_PERIPHCONF)
44+
# gen_uicr.py parses all zephyr.elf files. To find these files (which
45+
# have not been built yet) we scan sibling build directories for
46+
# zephyr.dts
47+
get_filename_component(SYSBUILD_DIR ${APPLICATION_BINARY_DIR} DIRECTORY)
48+
file(GLOB _siblings LIST_DIRECTORIES true "${SYSBUILD_DIR}/*")
49+
foreach(_dir ${_siblings})
50+
get_filename_component(_name ${_dir} NAME)
51+
if(_name STREQUAL "uicr")
52+
# This image is an exception to the rule. It has a zephyr.dts, but
53+
# no zephyr.elf
54+
continue()
55+
endif()
56+
57+
if(EXISTS ${_dir}/zephyr/zephyr.dts)
58+
list(APPEND periphconf_elfs ${_dir}/zephyr/zephyr.elf)
59+
endif()
60+
endforeach()
61+
62+
# Compute PERIPHCONF absolute address and size from this image's devicetree
63+
dt_nodelabel(periph_path NODELABEL "periphconf_partition")
64+
if(NOT DEFINED periph_path)
65+
message(FATAL_ERROR "periphconf_partition not found in devicetree")
66+
endif()
67+
68+
dt_reg_addr(PERIPHCONF_OFFSET PATH ${periph_path})
69+
dt_reg_size(PERIPHCONF_SIZE PATH ${periph_path})
70+
71+
if(NOT DEFINED PERIPHCONF_OFFSET OR NOT DEFINED PERIPHCONF_SIZE)
72+
message(FATAL_ERROR "Failed to read periphconf addresses from DT")
73+
endif()
74+
75+
# Calculate absolute periphconf address
76+
math(EXPR PERIPHCONF_ADDRESS "${CONFIG_FLASH_BASE_ADDRESS} + ${PERIPHCONF_OFFSET}" OUTPUT_FORMAT HEXADECIMAL)
77+
78+
# Set up periphconf arguments for gen_uicr.py
79+
list(APPEND periphconf_args --periphconf-address ${PERIPHCONF_ADDRESS})
80+
list(APPEND periphconf_args --periphconf-size ${PERIPHCONF_SIZE})
81+
82+
foreach(elf ${periphconf_elfs})
83+
list(APPEND periphconf_args --in-periphconf-elf ${elf})
84+
endforeach()
85+
endif(CONFIG_UICR_SAMPLE_GENERATE_PERIPHCONF)
86+
87+
# Generate zephyr.hex file
88+
add_custom_command(
89+
OUTPUT ${zephyr_hex_file}
90+
COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${ZEPHYR_BASE}/scripts/dts/python-devicetree/src
91+
${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/soc/nordic/common/uicr/gen_uicr.py
92+
--uicr-address ${UICR_ADDRESS}
93+
--out-uicr-hex ${zephyr_hex_file}
94+
${periphconf_args}
95+
DEPENDS ${periphconf_elfs}
96+
WORKING_DIRECTORY ${APPLICATION_BINARY_DIR}
97+
COMMENT "Using gen_uicr.py to generate ${zephyr_hex_file} from ${periphconf_elfs}"
98+
)
99+
100+
# Add zephyr subdirectory to handle flash configuration with correct paths
101+
add_subdirectory(zephyr)
102+
103+
add_custom_target(gen_uicr ALL DEPENDS ${zephyr_hex_file})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
source "Kconfig.zephyr"
4+
5+
menu "UICR generator options"
6+
7+
config UICR_SAMPLE_GENERATE_PERIPHCONF
8+
bool "Generate PERIPHCONF hex alongside UICR"
9+
default y
10+
help
11+
When enabled, the UICR generator will emit periphconf.hex in addition to uicr.hex.
12+
13+
endmenu

soc/nordic/common/uicr/gen_uicr/prj.conf

Whitespace-only changes.

0 commit comments

Comments
 (0)