Skip to content

Commit ac851ca

Browse files
SebastianBoekartben
authored andcommitted
soc: nordic: uicr: 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. 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]>
1 parent ae191f6 commit ac851ca

File tree

14 files changed

+231
-141
lines changed

14 files changed

+231
-141
lines changed

scripts/ci/check_compliance.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,7 @@ def check_no_undef_outside_kconfig(self, kconf):
12501250
"FOO_LOG_LEVEL",
12511251
"FOO_SETTING_1",
12521252
"FOO_SETTING_2",
1253+
"GEN_UICR_GENERATE_PERIPHCONF", # Used in specialized build tool, not part of main Kconfig
12531254
"HEAP_MEM_POOL_ADD_SIZE_", # Used as an option matching prefix
12541255
"HUGETLBFS", # Linux, in boards/xtensa/intel_adsp_cavs25/doc
12551256
"IAR_BUFFERED_WRITE",

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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config NRF_HALTIUM_GENERATE_UICR
5+
bool "Generate UICR file"
6+
depends on SOC_SERIES_NRF54HX
7+
default y
8+
help
9+
Generate UICR HEX file.

soc/nordic/common/uicr/gen_uicr.py

Lines changed: 67 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@
77

88
import argparse
99
import ctypes as c
10-
import math
11-
import pickle
12-
import re
1310
import sys
14-
from collections import defaultdict
1511
from itertools import groupby
1612

1713
from elftools.elf.elffile import ELFFile
@@ -25,11 +21,6 @@
2521
# Must match the name used in the linker script.
2622
PERIPHCONF_SECTION = "uicr_periphconf_entry"
2723

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-
3324
# Common values for representing enabled/disabled in the UICR format.
3425
ENABLED_VALUE = 0xFFFF_FFFF
3526
DISABLED_VALUE = 0xBD23_28A8
@@ -141,18 +132,6 @@ def main() -> None:
141132
"peripherals, and to protect the device in various ways."
142133
),
143134
)
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-
)
156135
parser.add_argument(
157136
"--in-periphconf-elf",
158137
dest="in_periphconf_elfs",
@@ -165,71 +144,103 @@ def main() -> None:
165144
"by ascending address and cleared of duplicate entries."
166145
),
167146
)
147+
parser.add_argument(
148+
"--out-merged-hex",
149+
required=True,
150+
type=argparse.FileType("w", encoding="utf-8"),
151+
help="Path to write the merged UICR+PERIPHCONF HEX file to",
152+
)
168153
parser.add_argument(
169154
"--out-uicr-hex",
170155
required=True,
171156
type=argparse.FileType("w", encoding="utf-8"),
172-
help="Path to write the generated UICR HEX file to",
157+
help="Path to write the UICR-only HEX file to",
173158
)
174159
parser.add_argument(
175160
"--out-periphconf-hex",
176-
default=None,
177161
type=argparse.FileType("w", encoding="utf-8"),
178-
help="Path to write the generated PERIPHCONF HEX file to",
162+
help="Path to write the PERIPHCONF-only HEX file to",
163+
)
164+
parser.add_argument(
165+
"--periphconf-address",
166+
default=None,
167+
type=lambda s: int(s, 0),
168+
help="Absolute flash address of the PERIPHCONF partition (decimal or 0x-prefixed hex)",
169+
)
170+
parser.add_argument(
171+
"--periphconf-size",
172+
default=None,
173+
type=lambda s: int(s, 0),
174+
help="Size in bytes of the PERIPHCONF partition (decimal or 0x-prefixed hex)",
175+
)
176+
parser.add_argument(
177+
"--uicr-address",
178+
required=True,
179+
type=lambda s: int(s, 0),
180+
help="Absolute flash address of the UICR region (decimal or 0x-prefixed hex)",
179181
)
180182
args = parser.parse_args()
181183

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

186197
uicr.VERSION.MAJOR = UICR_FORMAT_VERSION_MAJOR
187198
uicr.VERSION.MINOR = UICR_FORMAT_VERSION_MINOR
188199

189-
kconfig_str = args.in_config.read()
190-
kconfig = parse_kconfig(kconfig_str)
200+
# Process periphconf data first and configure UICR completely before creating hex objects
201+
periphconf_hex = IntelHex()
191202

192-
edt = pickle.load(args.in_edt_pickle)
203+
if args.out_periphconf_hex:
204+
periphconf_combined = extract_and_combine_periphconfs(args.in_periphconf_elfs)
193205

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

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

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

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

228+
# Create UICR hex object with final UICR data
223229
uicr_hex = IntelHex()
224-
uicr_hex.frombytes(bytes(uicr), offset=uicr_node.regs[0].addr)
230+
uicr_hex.frombytes(bytes(uicr), offset=args.uicr_address)
225231

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

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

239+
merged_hex.fromdict(periphconf_hex.todict())
240+
241+
merged_hex.write_hex_file(args.out_merged_hex)
242+
uicr_hex.write_hex_file(args.out_uicr_hex)
243+
233244
except ScriptError as e:
234245
print(f"Error: {e!s}")
235246
sys.exit(1)
@@ -270,16 +281,5 @@ def extract_and_combine_periphconfs(elf_files: list[argparse.FileType]) -> bytes
270281
return bytes(final_periphconf)
271282

272283

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

0 commit comments

Comments
 (0)