Skip to content

Commit f55f142

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 f55f142

File tree

13 files changed

+221
-140
lines changed

13 files changed

+221
-140
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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +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)
7-
add_subdirectory(uicr)
8-
endif()
6+
add_subdirectory(uicr)
97

108
# Let SystemInit() be called in place of soc_reset_hook() by default.
119
zephyr_linker_symbol(SYMBOL soc_reset_hook EXPR "@SystemInit@")

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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
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.

soc/nordic/common/uicr/gen_uicr.py

Lines changed: 65 additions & 66 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",
@@ -165,71 +145,101 @@ def main() -> None:
165145
"by ascending address and cleared of duplicate entries."
166146
),
167147
)
148+
parser.add_argument(
149+
"--out-merged-hex",
150+
required=True,
151+
type=argparse.FileType("w", encoding="utf-8"),
152+
help="Path to write the merged UICR+PERIPHCONF HEX file to",
153+
)
168154
parser.add_argument(
169155
"--out-uicr-hex",
170156
required=True,
171157
type=argparse.FileType("w", encoding="utf-8"),
172-
help="Path to write the generated UICR HEX file to",
158+
help="Path to write the UICR-only HEX file to",
173159
)
174160
parser.add_argument(
175161
"--out-periphconf-hex",
176-
default=None,
177162
type=argparse.FileType("w", encoding="utf-8"),
178-
help="Path to write the generated PERIPHCONF HEX file to",
163+
help="Path to write the PERIPHCONF-only HEX file to",
164+
)
165+
parser.add_argument(
166+
"--periphconf-address",
167+
default=None,
168+
type=lambda s: int(s, 0),
169+
help="Absolute flash address of the PERIPHCONF partition (decimal or 0x-prefixed hex)",
170+
)
171+
parser.add_argument(
172+
"--periphconf-size",
173+
default=None,
174+
type=lambda s: int(s, 0),
175+
help="Size in bytes of the PERIPHCONF partition (decimal or 0x-prefixed hex)",
176+
)
177+
parser.add_argument(
178+
"--uicr-address",
179+
required=True,
180+
type=lambda s: int(s, 0),
181+
help="Absolute flash address of the UICR region (decimal or 0x-prefixed hex)",
179182
)
180183
args = parser.parse_args()
181184

182185
try:
186+
# Validate argument dependencies
187+
if args.out_periphconf_hex:
188+
if args.periphconf_address is None:
189+
raise ScriptError("--periphconf-address is required when --out-periphconf-hex is used")
190+
if args.periphconf_size is None:
191+
raise ScriptError("--periphconf-size is required when --out-periphconf-hex is used")
192+
183193
init_values = DISABLED_VALUE.to_bytes(4, "little") * (c.sizeof(Uicr) // 4)
184194
uicr = Uicr.from_buffer_copy(init_values)
185195

186196
uicr.VERSION.MAJOR = UICR_FORMAT_VERSION_MAJOR
187197
uicr.VERSION.MINOR = UICR_FORMAT_VERSION_MINOR
188198

189-
kconfig_str = args.in_config.read()
190-
kconfig = parse_kconfig(kconfig_str)
191-
192-
edt = pickle.load(args.in_edt_pickle)
199+
# Process periphconf data first and configure UICR completely before creating hex objects
200+
periphconf_hex = IntelHex()
193201

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
202+
if args.out_periphconf_hex:
203+
periphconf_combined = extract_and_combine_periphconfs(args.in_periphconf_elfs)
201204

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
205+
padding_len = args.periphconf_size - len(periphconf_combined)
206+
periphconf_final = periphconf_combined + bytes([0xFF for _ in range(padding_len)])
205207

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)])
208+
# Add periphconf data to periphconf hex object
209+
periphconf_hex.frombytes(periphconf_final, offset=args.periphconf_address)
209210

210-
if kconfig.get("CONFIG_NRF_HALTIUM_UICR_PERIPHCONF") == "y":
211+
# Configure UICR with periphconf settings
211212
uicr.PERIPHCONF.ENABLE = ENABLED_VALUE
212-
uicr.PERIPHCONF.ADDRESS = periphconf_address
213-
uicr.PERIPHCONF.MAXCOUNT = math.floor(periphconf_size / 8)
213+
uicr.PERIPHCONF.ADDRESS = args.periphconf_address
214+
215+
# MAXCOUNT is given in number of 8-byte peripheral
216+
# configuration entries and periphconf_size is given in
217+
# bytes. When setting MAXCOUNT based on the
218+
# periphconf_size we must first assert that
219+
# periphconf_size has not been misconfigured.
220+
if args.periphconf_size % 8 != 0:
221+
raise ScriptError(
222+
f"args.periphconf_size was {args.periphconf_size}, but must be divisible by 8"
223+
)
214224

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
225+
uicr.PERIPHCONF.MAXCOUNT = args.periphconf_size // 8
222226

227+
# Create UICR hex object with final UICR data
223228
uicr_hex = IntelHex()
224-
uicr_hex.frombytes(bytes(uicr), offset=uicr_node.regs[0].addr)
229+
uicr_hex.frombytes(bytes(uicr), offset=args.uicr_address)
225230

226-
uicr_hex.write_hex_file(args.out_uicr_hex)
231+
# Create merged hex by combining UICR and periphconf hex objects
232+
merged_hex = IntelHex()
233+
merged_hex.fromdict(uicr_hex.todict())
227234

228-
if args.out_periphconf_hex is not None:
229-
periphconf_hex = IntelHex()
230-
periphconf_hex.frombytes(periphconf_final, offset=periphconf_address)
235+
if args.out_periphconf_hex:
231236
periphconf_hex.write_hex_file(args.out_periphconf_hex)
232237

238+
merged_hex.fromdict(periphconf_hex.todict())
239+
240+
merged_hex.write_hex_file(args.out_merged_hex)
241+
uicr_hex.write_hex_file(args.out_uicr_hex)
242+
233243
except ScriptError as e:
234244
print(f"Error: {e!s}")
235245
sys.exit(1)
@@ -270,16 +280,5 @@ def extract_and_combine_periphconfs(elf_files: list[argparse.FileType]) -> bytes
270280
return bytes(final_periphconf)
271281

272282

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-
284283
if __name__ == "__main__":
285284
main()

0 commit comments

Comments
 (0)