Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions dts/bindings/arm/nordic,nrf71-uicr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright (c) 2026 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

description: Nordic nRF71 UICR (User Information Configuration Registers)

compatible: "nordic,nrf71-uicr"

include: base.yaml

properties:
reg:
required: true

supply-config1v8:
type: string
enum:
- "normal"
- "external"
- "high-load"
description: |
Configuration of the VDD_AO_1V8 supply rail.

normal - Autonomous control by the internal power management block.
The LDO_VBAT_1V8 regulator operates in its default mode.
external - 1.8 V supplied externally on VDD_AO_1V8 pin.
Internal LDO_VBAT_1V8 regulator disabled to prevent
conflict with external supply.
high-load - Up to 10mA can be consumed from 1V8 supply for other
devices/IO. Keep LDO_VBAT_1V8 in high power mode at
all times (including system OFF).

This setting, once applied, can only be unset by erasing the UICR
registers. Refer to the nRF7120 Product Specification for details.
2 changes: 1 addition & 1 deletion dts/vendor/nordic/nrf7120_enga.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
/* Intentionally empty because uicr is hardware fixed to Secure */
#else
uicr: uicr@ffd000 {
compatible = "nordic,nrf-uicr";
compatible = "nordic,nrf71-uicr";
reg = <0xffd000 0x1000>;
ranges = <0x0 0xffd000 0x1000>;
#address-cells = <1>;
Expand Down
18 changes: 18 additions & 0 deletions soc/nordic/nrf71/Kconfig.sysbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2026 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

if SOC_NRF7120_ENGA_CPUFLPR

config HAS_NORDIC_VPR_LAUNCHER_IMAGE
default y

endif

config SOC_NRF71_GENERATE_UICR
bool "Generate nRF71 UICR hex"
depends on SOC_SERIES_NRF71
default y
help
When enabled, a UICR generator image is included in the build.
This generates a standalone hex containing UICR values derived from
the devicetree (nordic,nrf71-uicr binding).
70 changes: 70 additions & 0 deletions soc/nordic/nrf71/uicr/gen_uicr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2026 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr
COMPONENTS zephyr_default:boards
REQUIRED HINTS $ENV{ZEPHYR_BASE}
)

project(uicr LANGUAGES NONE)

# Override the runners.yaml path to use CMAKE_CURRENT_BINARY_DIR/zephyr
# instead of PROJECT_BINARY_DIR, this ensures runners.yaml is generated
# at <build>/uicr/zephyr where west expects it
set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/zephyr)

set(gen_script ${CMAKE_CURRENT_LIST_DIR}/gen_uicr.py)
set(uicr_hex ${PROJECT_BINARY_DIR}/zephyr.hex)

zephyr_get(DEFAULT_IMAGE_EDT_PICKLE SYSBUILD GLOBAL)

if(NOT DEFAULT_IMAGE_EDT_PICKLE)
message(FATAL_ERROR
"DEFAULT_IMAGE_EDT_PICKLE not found. "
"The UICR generator must be built via sysbuild."
)
endif()

add_custom_command(
OUTPUT ${uicr_hex}
COMMAND ${PYTHON_EXECUTABLE} ${gen_script}
--zephyr-base ${ZEPHYR_BASE}
--edt-pickle ${DEFAULT_IMAGE_EDT_PICKLE}
--output ${uicr_hex}
DEPENDS ${DEFAULT_IMAGE_EDT_PICKLE} ${gen_script}
COMMENT "Generating UICR hex: ${uicr_hex}"
)

add_custom_target(gen_uicr ALL DEPENDS ${uicr_hex})

# Create the runners_yaml_props_target that flash system expects
add_custom_target(runners_yaml_props_target)

# Copy over Kconfig and dts files from the main image
get_filename_component(default_image_binary_dir ${DEFAULT_IMAGE_EDT_PICKLE} DIRECTORY)
zephyr_file_copy(${default_image_binary_dir}/.config ${PROJECT_BINARY_DIR}/.config
ONLY_IF_DIFFERENT
)
zephyr_file_copy(${default_image_binary_dir}/edt.pickle ${PROJECT_BINARY_DIR}/edt.pickle
ONLY_IF_DIFFERENT
)
import_kconfig(CONFIG_ ${default_image_binary_dir}/.config)

# Manually include board configuration to enable automatic runners.yaml generation
foreach(dir ${BOARD_DIRECTORIES})
include(${dir}/board.cmake OPTIONAL)
endforeach()

# Override hex_file after board.cmake to ensure it always flash its own output
set_target_properties(runners_yaml_props_target PROPERTIES
hex_file "zephyr.hex"
)

# Include flash support to automatically generate runners.yaml
include(${ZEPHYR_BASE}/cmake/flash/CMakeLists.txt)

# Silent unused variable warnings
set(EXTRA_KCONFIG_TARGETS)
set(FORCED_CONF_FILE)
112 changes: 112 additions & 0 deletions soc/nordic/nrf71/uicr/gen_uicr/gen_uicr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Copyright 2026 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

"""
Generate an Intel HEX file containing nRF71 UICR register values
read from a Zephyr edtlib.EDT pickle.
"""

import argparse
import pickle
import struct
import sys
from pathlib import Path

from intelhex import IntelHex

UICR_COMPATIBLE = "nordic,nrf71-uicr"

# ── Field table ──────────────────────────────────────────────────────
#
# Each entry describes one UICR register field configurable via DTS.
#
# property – DTS property name (must match the binding YAML)
# offset – byte offset from UICR base
# mask – bit mask of the field within the 32-bit register
# encoding – maps DTS string values to integer field values
# reset – 32-bit reset value
#
# The full register word is computed as:
# word = (reset & ~mask) | (encoding[value] & mask)
#

UICR_FIELDS = [
{
"property": "supply-config1v8",
"offset": 0x400,
"mask": 0x00000003,
"encoding": {
"normal": 0,
"external": 1,
"high-load": 2,
},
"reset": 0xFFFFFFFF,
},
]


def setup_devicetree_path(zephyr_base):
"""Add the devicetree package to sys.path so EDT can be unpickled."""

devicetree_path = Path(zephyr_base) / "scripts/dts/python-devicetree/src"
if not devicetree_path.is_dir():
sys.exit(f"Devicetree path does not exist: {devicetree_path}")
sys.path.insert(0, str(devicetree_path))


def parse_uicr(args):
setup_devicetree_path(args.zephyr_base)

with open(args.edt_pickle, "rb") as f:
edt = pickle.load(f)

# Find the UICR node.
uicr_nodes = edt.compat2okay.get(UICR_COMPATIBLE, [])
if not uicr_nodes:
IntelHex().write_hex_file(args.output)
return

uicr = uicr_nodes[0]
base = uicr.regs[0].addr

ih = IntelHex()

for field in UICR_FIELDS:
prop = uicr.props.get(field["property"])
if prop is None:
continue

val_str = prop.val
encoding = field["encoding"]
if val_str not in encoding:
valid = ", ".join(f'"{k}"' for k in encoding)
sys.exit(f"Unknown value '{val_str}' for '{field['property']}'. Expected: {valid}")

mask = field["mask"]
word = (field["reset"] & ~mask) | (encoding[val_str] & mask)
addr = base + field["offset"]
ih.puts(addr, struct.pack("<I", word))

ih.write_hex_file(args.output)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
parser.add_argument(
"--zephyr-base",
required=True,
help="Path to the Zephyr base directory",
)
parser.add_argument(
"--edt-pickle",
required=True,
help="Path to the main application's EDT pickle file",
)
parser.add_argument(
"--output",
required=True,
help="Path for the output Intel HEX file",
)
args = parser.parse_args()

parse_uicr(args)
15 changes: 15 additions & 0 deletions soc/nordic/nrf71/uicr/sysbuild.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2026 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

# Add UICR generator as a utility image
ExternalZephyrProject_Add(
APPLICATION uicr
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/gen_uicr
)

sysbuild_add_dependencies(CONFIGURE uicr ${DEFAULT_IMAGE})

# Forward the default image's EDT pickle path to the UICR image.
sysbuild_cache_set(VAR DEFAULT_IMAGE_EDT_PICKLE
${CMAKE_BINARY_DIR}/${DEFAULT_IMAGE}/zephyr/edt.pickle
)
4 changes: 4 additions & 0 deletions soc/nordic/sysbuild.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ endif()
if(SB_CONFIG_NRF_HALTIUM_GENERATE_UICR)
include(${CMAKE_CURRENT_LIST_DIR}/common/uicr/sysbuild.cmake)
endif()

if(SB_CONFIG_SOC_NRF71_GENERATE_UICR)
include(${CMAKE_CURRENT_LIST_DIR}/nrf71/uicr/sysbuild.cmake)
endif()
Loading