Skip to content

Commit f0f4ba9

Browse files
committed
[nrf fromtree] soc: nordic: Add UICR generation tooling for nRF71
Add supporting scripts and build-system integration for UICR generation. Signed-off-by: Dhanoo Surasarang <dhanoo.surasarang@nordicsemi.no> (cherry picked from commit 9d68b8a)
1 parent 487fca5 commit f0f4ba9

5 files changed

Lines changed: 210 additions & 0 deletions

File tree

soc/nordic/nrf71/Kconfig.sysbuild

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2026 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config SOC_NRF71_GENERATE_UICR
5+
bool "Generate nRF71 UICR hex"
6+
depends on SOC_SERIES_NRF71
7+
default y
8+
help
9+
When enabled, a UICR generator image is included in the build.
10+
This generates a standalone hex containing UICR values derived from
11+
the devicetree (nordic,nrf71-uicr binding).
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright (c) 2026 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
cmake_minimum_required(VERSION 3.20.0)
5+
6+
find_package(Zephyr
7+
COMPONENTS zephyr_default:boards
8+
REQUIRED HINTS $ENV{ZEPHYR_BASE}
9+
)
10+
11+
project(uicr LANGUAGES NONE)
12+
13+
# Override the runners.yaml path to use CMAKE_CURRENT_BINARY_DIR/zephyr
14+
# instead of PROJECT_BINARY_DIR, this ensures runners.yaml is generated
15+
# at <build>/uicr/zephyr where west expects it
16+
set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/zephyr)
17+
18+
set(gen_script ${CMAKE_CURRENT_LIST_DIR}/gen_uicr.py)
19+
set(uicr_hex ${PROJECT_BINARY_DIR}/zephyr.hex)
20+
21+
zephyr_get(DEFAULT_IMAGE_EDT_PICKLE SYSBUILD GLOBAL)
22+
23+
if(NOT DEFAULT_IMAGE_EDT_PICKLE)
24+
message(FATAL_ERROR
25+
"DEFAULT_IMAGE_EDT_PICKLE not found. "
26+
"The UICR generator must be built via sysbuild."
27+
)
28+
endif()
29+
30+
add_custom_command(
31+
OUTPUT ${uicr_hex}
32+
COMMAND ${PYTHON_EXECUTABLE} ${gen_script}
33+
--zephyr-base ${ZEPHYR_BASE}
34+
--edt-pickle ${DEFAULT_IMAGE_EDT_PICKLE}
35+
--output ${uicr_hex}
36+
DEPENDS ${DEFAULT_IMAGE_EDT_PICKLE} ${gen_script}
37+
COMMENT "Generating UICR hex: ${uicr_hex}"
38+
)
39+
40+
add_custom_target(gen_uicr ALL DEPENDS ${uicr_hex})
41+
42+
# Create the runners_yaml_props_target that flash system expects
43+
add_custom_target(runners_yaml_props_target)
44+
set_target_properties(runners_yaml_props_target PROPERTIES
45+
hex_file "zephyr.hex"
46+
)
47+
48+
# Copy over Kconfig and dts files from the main image
49+
get_filename_component(default_image_binary_dir ${DEFAULT_IMAGE_EDT_PICKLE} DIRECTORY)
50+
zephyr_file_copy(${default_image_binary_dir}/.config ${PROJECT_BINARY_DIR}/.config
51+
ONLY_IF_DIFFERENT
52+
)
53+
zephyr_file_copy(${default_image_binary_dir}/edt.pickle ${PROJECT_BINARY_DIR}/edt.pickle
54+
ONLY_IF_DIFFERENT
55+
)
56+
import_kconfig(CONFIG_ ${default_image_binary_dir}/.config)
57+
58+
# Manually include board configuration to enable automatic runners.yaml generation
59+
foreach(dir ${BOARD_DIRECTORIES})
60+
include(${dir}/board.cmake OPTIONAL)
61+
endforeach()
62+
63+
# Include flash support to automatically generate runners.yaml
64+
include(${ZEPHYR_BASE}/cmake/flash/CMakeLists.txt)
65+
66+
# Silent unused variable warnings
67+
set(EXTRA_KCONFIG_TARGETS)
68+
set(FORCED_CONF_FILE)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Copyright 2026 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""
5+
Generate an Intel HEX file containing nRF71 UICR register values
6+
read from a Zephyr edtlib.EDT pickle.
7+
"""
8+
9+
import argparse
10+
import pickle
11+
import struct
12+
import sys
13+
from pathlib import Path
14+
15+
from intelhex import IntelHex
16+
17+
UICR_COMPATIBLE = "nordic,nrf71-uicr"
18+
19+
# ── Field table ──────────────────────────────────────────────────────
20+
#
21+
# Each entry describes one UICR register field configurable via DTS.
22+
#
23+
# property – DTS property name (must match the binding YAML)
24+
# offset – byte offset from UICR base
25+
# mask – bit mask of the field within the 32-bit register
26+
# encoding – maps DTS string values to integer field values
27+
# reset – 32-bit reset value
28+
#
29+
# The full register word is computed as:
30+
# word = (reset & ~mask) | (encoding[value] & mask)
31+
#
32+
33+
UICR_FIELDS = [
34+
{
35+
"property": "supply-config1v8",
36+
"offset": 0x400,
37+
"mask": 0x00000003,
38+
"encoding": {
39+
"normal": 0,
40+
"external": 1,
41+
"high-load": 2,
42+
},
43+
"reset": 0xFFFFFFFF,
44+
},
45+
]
46+
47+
48+
def setup_devicetree_path(zephyr_base):
49+
"""Add the devicetree package to sys.path so EDT can be unpickled."""
50+
51+
devicetree_path = Path(zephyr_base) / "scripts/dts/python-devicetree/src"
52+
if not devicetree_path.is_dir():
53+
sys.exit(f"Devicetree path does not exist: {devicetree_path}")
54+
sys.path.insert(0, str(devicetree_path))
55+
56+
57+
def parse_uicr(args):
58+
setup_devicetree_path(args.zephyr_base)
59+
60+
with open(args.edt_pickle, "rb") as f:
61+
edt = pickle.load(f)
62+
63+
# Find the UICR node.
64+
uicr_nodes = edt.compat2okay.get(UICR_COMPATIBLE, [])
65+
if not uicr_nodes:
66+
IntelHex().write_hex_file(args.output)
67+
return
68+
69+
uicr = uicr_nodes[0]
70+
base = uicr.regs[0].addr
71+
72+
ih = IntelHex()
73+
74+
for field in UICR_FIELDS:
75+
prop = uicr.props.get(field["property"])
76+
if prop is None:
77+
continue
78+
79+
val_str = prop.val
80+
encoding = field["encoding"]
81+
if val_str not in encoding:
82+
valid = ", ".join(f'"{k}"' for k in encoding)
83+
sys.exit(f"Unknown value '{val_str}' for '{field['property']}'. Expected: {valid}")
84+
85+
mask = field["mask"]
86+
word = (field["reset"] & ~mask) | (encoding[val_str] & mask)
87+
addr = base + field["offset"]
88+
ih.puts(addr, struct.pack("<I", word))
89+
90+
ih.write_hex_file(args.output)
91+
92+
93+
if __name__ == "__main__":
94+
parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
95+
parser.add_argument(
96+
"--zephyr-base",
97+
required=True,
98+
help="Path to the Zephyr base directory",
99+
)
100+
parser.add_argument(
101+
"--edt-pickle",
102+
required=True,
103+
help="Path to the main application's EDT pickle file",
104+
)
105+
parser.add_argument(
106+
"--output",
107+
required=True,
108+
help="Path for the output Intel HEX file",
109+
)
110+
args = parser.parse_args()
111+
112+
parse_uicr(args)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) 2026 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Add UICR generator as a utility image
5+
ExternalZephyrProject_Add(
6+
APPLICATION uicr
7+
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/gen_uicr
8+
)
9+
10+
sysbuild_add_dependencies(CONFIGURE uicr ${DEFAULT_IMAGE})
11+
12+
# Forward the default image's EDT pickle path to the UICR image.
13+
sysbuild_cache_set(VAR DEFAULT_IMAGE_EDT_PICKLE
14+
${CMAKE_BINARY_DIR}/${DEFAULT_IMAGE}/zephyr/edt.pickle
15+
)

soc/nordic/sysbuild.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ endif()
3333
if(SB_CONFIG_NRF_HALTIUM_GENERATE_UICR)
3434
include(${CMAKE_CURRENT_LIST_DIR}/common/uicr/sysbuild.cmake)
3535
endif()
36+
37+
if(SB_CONFIG_SOC_NRF71_GENERATE_UICR)
38+
include(${CMAKE_CURRENT_LIST_DIR}/nrf71/uicr/sysbuild.cmake)
39+
endif()

0 commit comments

Comments
 (0)