Skip to content

Support seconday image #94317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 2 additions & 2 deletions boards/nordic/nrf54h20dk/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
config HW_STACK_PROTECTION
default ARCH_HAS_STACK_PROTECTION

if BOARD_NRF54H20DK_NRF54H20_CPUAPP
if BOARD_NRF54H20DK_NRF54H20_CPUAPP || BOARD_NRF54H20DK_NRF54H20_CPUAPP_SECONDARY

config BT_HCI_IPC
default y if BT
Expand All @@ -31,7 +31,7 @@ config FLASH_LOAD_SIZE

endif # !USE_DT_CODE_PARTITION

endif # BOARD_NRF54H20DK_NRF54H20_CPUAPP
endif # BOARD_NRF54H20DK_NRF54H20_CPUAPP || BOARD_NRF54H20DK_NRF54H20_CPUAPP_SECONDARY

if BOARD_NRF54H20DK_NRF54H20_CPURAD

Expand Down
2 changes: 1 addition & 1 deletion boards/nordic/nrf54h20dk/Kconfig.nrf54h20dk
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

config BOARD_NRF54H20DK
select SOC_NRF54H20_CPUAPP if BOARD_NRF54H20DK_NRF54H20_CPUAPP
select SOC_NRF54H20_CPUAPP if BOARD_NRF54H20DK_NRF54H20_CPUAPP || BOARD_NRF54H20DK_NRF54H20_CPUAPP_SECONDARY
select SOC_NRF54H20_CPURAD if BOARD_NRF54H20DK_NRF54H20_CPURAD
select SOC_NRF54H20_CPUPPR if (BOARD_NRF54H20DK_NRF54H20_CPUPPR || \
BOARD_NRF54H20DK_NRF54H20_CPUPPR_XIP)
Expand Down
2 changes: 2 additions & 0 deletions boards/nordic/nrf54h20dk/board.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ board:
socs:
- name: nrf54h20
variants:
- name: secondary
cpucluster: cpuapp
- name: xip
cpucluster: cpuppr
- name: xip
Expand Down
8 changes: 8 additions & 0 deletions boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,13 @@
periphconf_partition: partition@1ae000 {
reg = <0x1ae000 DT_SIZE_K(8)>;
};

cpuapp_secondary_partition: partition@1b0000 {
reg = <0x1b0000 DT_SIZE_K(312)>;
};

secondary_periphconf_partition: partition@1fe000 {
reg = <0x1fe000 DT_SIZE_K(8)>;
};
};
};
13 changes: 13 additions & 0 deletions boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp_secondary.dts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "nrf54h20dk_nrf54h20_cpuapp.dts"

/ {
chosen {
zephyr,code-partition = &cpuapp_secondary_partition;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

identifier: nrf54h20dk/nrf54h20/cpuapp/secondary
name: nRF54H20-DK-nRF54H20-Application-Secondary (revision 0.9.0)
type: mcu
arch: arm
toolchain:
- gnuarmemb
- zephyr
sysbuild: true
ram: 760
flash: 312
supported:
- adc
- can
- counter
- gpio
- i2c
- i2s
- pwm
- retained_mem
- spi
- watchdog
- usbd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

# Use devicetree code partition instead of fallback boot partition
CONFIG_USE_DT_CODE_PARTITION=y
201 changes: 162 additions & 39 deletions soc/nordic/common/uicr/gen_uicr.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""
Copyright (c) 2025 Nordic Semiconductor ASA
SPDX-License-Identifier: Apache-2.0
Expand All @@ -13,6 +13,7 @@
import sys
from collections import defaultdict
from itertools import groupby
from pprint import pprint

from elftools.elf.elffile import ELFFile
from intelhex import IntelHex
Expand All @@ -25,11 +26,16 @@
UICR_NODELABEL = "uicr"
# Nodelabel of the PERIPHCONF devicetree node, used to extract its location from the devicetree.
PERIPHCONF_NODELABEL = "periphconf_partition"
# Nodelabel of the SECONDARY PERIPHCONF devicetree node, used to extract its location from the devicetree.

Check failure on line 29 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (E501) see https://docs.astral.sh/ruff/rules/line-too-long

soc/nordic/common/uicr/gen_uicr.py:29 Line too long (106 > 100)
SECONDARY_PERIPHCONF_NODELABEL = "secondary_periphconf_partition"

# Common values for representing enabled/disabled in the UICR format.
ENABLED_VALUE = 0xFFFF_FFFF
DISABLED_VALUE = 0xBD23_28A8

# Enum values for representing PROCESSOR in the UICR format.
PROCESSOR_APPLICATION = 0xBD23_28A8
PROCESSOR_RADIO = 0x1730_C77F

class ScriptError(RuntimeError): ...

Expand Down Expand Up @@ -62,17 +68,6 @@
("SIZE4KB", c.c_uint32),
]


class Recovery(c.LittleEndianStructure):
_pack_ = 1
_fields_ = [
("ENABLE", c.c_uint32),
("PROCESSOR", c.c_uint32),
("INITSVTOR", c.c_uint32),
("SIZE4KB", c.c_uint32),
]


class Its(c.LittleEndianStructure):
_pack_ = 1
_fields_ = [
Expand Down Expand Up @@ -101,6 +96,28 @@
]


class SecondaryTrigger(c.LittleEndianStructure):
_pack_ = 1
_fields_ = [
("ENABLE", c.c_uint32),
("RESETREAS", c.c_uint32),
]


class Secondary(c.LittleEndianStructure):
_pack_ = 1
_fields_ = [
("ENABLE", c.c_uint32),
("PROCESSOR", c.c_uint32),
("SECONDARY_TRIGGER", SecondaryTrigger),
("ADDRESS", c.c_uint32),
("SIZE4KB", c.c_uint32),
("PROTECTEDMEM", Protectedmem),
("PERIPHCONF", Periphconf),
("MPCCONF", Mpcconf),
]


class Uicr(c.LittleEndianStructure):
_pack_ = 1
_fields_ = [
Expand All @@ -111,11 +128,13 @@
("APPROTECT", Approtect),
("ERASEPROTECT", c.c_uint32),
("PROTECTEDMEM", Protectedmem),
("RECOVERY", Recovery),
("RESERVED2", c.c_uint32 * 4),
("ITS", Its),
("RESERVED2", c.c_uint32 * 7),
("RESERVED3", c.c_uint32 * 7),
("PERIPHCONF", Periphconf),
("MPCCONF", Mpcconf),
("SECONDARY", Secondary),
("RESERVED4", c.c_uint32 * 2),
]


Expand All @@ -135,12 +154,22 @@
type=argparse.FileType("r"),
help="Path to the .config file from the application build",
)
parser.add_argument(
"--in-secondary-config",
type=argparse.FileType("r"),
help="Path to the .config file from the secondary application build",
)
parser.add_argument(
"--in-edt-pickle",
required=True,
type=argparse.FileType("rb"),
help="Path to the edt.pickle file from the application build",
)
parser.add_argument(
"--in-secondary-edt-pickle",
type=argparse.FileType("rb"),
help="Path to the edt.pickle file from the secondary application build",
)
parser.add_argument(
"--in-periphconf-elf",
dest="in_periphconf_elfs",
Expand All @@ -153,6 +182,19 @@
"by ascending address and cleared of duplicate entries."
),
)
parser.add_argument(
"--in-secondary-periphconf-elf",
dest="in_secondary_periphconf_elfs",
default=[],
action="append",
type=argparse.FileType("rb"),
help=(
"Path to an ELF file to extract PERIPHCONF data from. Can be provided multiple times. "
"The PERIPHCONF data from each ELF file is combined in a single list which is sorted "
"by ascending address and cleared of duplicate entries. This is used for the secondary "
"application."
),
)
parser.add_argument(
"--out-uicr-hex",
required=True,
Expand All @@ -165,37 +207,55 @@
type=argparse.FileType("w", encoding="utf-8"),
help="Path to write the generated PERIPHCONF HEX file to",
)
parser.add_argument(
"--out-secondary-periphconf-hex",
default=None,
type=argparse.FileType("w", encoding="utf-8"),
help="Path to write the generated secondary PERIPHCONF HEX file to",
)
parser.add_argument(
"--verbose",
action="store_true",
help="Enable verbose output including UICR structure contents",
)
args = parser.parse_args()

try:
init_values = DISABLED_VALUE.to_bytes(4, "little") * (c.sizeof(Uicr) // 4)
uicr = Uicr.from_buffer_copy(init_values)

kconfig_str = args.in_config.read()
kconfig = parse_kconfig(kconfig_str)

kconfig = parse_kconfig(args.in_config.read())
edt = pickle.load(args.in_edt_pickle)

try:
periphconf_partition = edt.label2node[PERIPHCONF_NODELABEL]
except LookupError as e:
raise ScriptError(
"Failed to find a PERIPHCONF partition in the devicetree. "
f"Expected a DT node with label '{PERIPHCONF_NODELABEL}'."
) from e
handle_periphconf(
uicr=uicr, edt=edt, kconfig=kconfig, is_primary=True, in_periphconf_elfs=args.in_periphconf_elfs, out_periphconf_hex=args.out_periphconf_hex

Check failure on line 231 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (E501) see https://docs.astral.sh/ruff/rules/line-too-long

soc/nordic/common/uicr/gen_uicr.py:231 Line too long (152 > 100)
)

if args.in_secondary_edt_pickle:
if not args.in_secondary_config:
raise ScriptError(
"Secondary configuration file is required when secondary edt.pickle is provided."

Check failure on line 237 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (E501) see https://docs.astral.sh/ruff/rules/line-too-long

soc/nordic/common/uicr/gen_uicr.py:237 Line too long (101 > 100)
)

flash_base_address = periphconf_partition.flash_controller.regs[0].addr
periphconf_address = flash_base_address + periphconf_partition.regs[0].addr
periphconf_size = periphconf_partition.regs[0].size
secondary_edt = pickle.load(args.in_secondary_edt_pickle)
secondary_kconfig = parse_kconfig(args.in_secondary_config.read())
handle_periphconf(
uicr=uicr,
edt=secondary_edt,
kconfig=secondary_kconfig,
is_primary=False,
in_periphconf_elfs=args.in_secondary_periphconf_elfs,
out_periphconf_hex=args.out_secondary_periphconf_hex,
)

periphconf_combined = extract_and_combine_periphconfs(args.in_periphconf_elfs)
padding_len = periphconf_size - len(periphconf_combined)
periphconf_final = periphconf_combined + bytes([0xFF for _ in range(padding_len)])
uicr.SECONDARY.ENABLE = ENABLED_VALUE
uicr.SECONDARY.PROCESSOR = PROCESSOR_APPLICATION

# TODO - This should be zephyr chosen code partition
code_partition = edt.label2node["cpuapp_secondary_partition"]
flash_base_address = code_partition.flash_controller.regs[0].addr
uicr.SECONDARY.ADDRESS = flash_base_address + code_partition.regs[0].addr

if kconfig.get("CONFIG_NRF_HALTIUM_UICR_PERIPHCONF") == "y":
uicr.PERIPHCONF.ENABLE = ENABLED_VALUE
uicr.PERIPHCONF.ADDRESS = periphconf_address
uicr.PERIPHCONF.MAXCOUNT = math.floor(periphconf_size / 8)

try:
uicr_node = edt.label2node[UICR_NODELABEL]
Expand All @@ -208,18 +268,50 @@
uicr_hex = IntelHex()
uicr_hex.frombytes(bytes(uicr), offset=uicr_node.regs[0].addr)

if args.verbose:
pretty_print_uicr(uicr)
uicr_hex.write_hex_file(args.out_uicr_hex)

if args.out_periphconf_hex is not None:
periphconf_hex = IntelHex()
periphconf_hex.frombytes(periphconf_final, offset=periphconf_address)
periphconf_hex.write_hex_file(args.out_periphconf_hex)

except ScriptError as e:
print(f"Error: {e!s}")
sys.exit(1)


def handle_periphconf(uicr, edt, kconfig, is_primary, in_periphconf_elfs, out_periphconf_hex):
try:
if is_primary:
periphconf_partition = edt.label2node[PERIPHCONF_NODELABEL]
else:
periphconf_partition = edt.label2node[SECONDARY_PERIPHCONF_NODELABEL]
except LookupError as e:
raise ScriptError(
"Failed to find a PERIPHCONF partition in the devicetree. "
f"Expected a DT node with label '{PERIPHCONF_NODELABEL}'."
) from e

flash_base_address = periphconf_partition.flash_controller.regs[0].addr
periphconf_address = flash_base_address + periphconf_partition.regs[0].addr
periphconf_size = periphconf_partition.regs[0].size

periphconf_combined = extract_and_combine_periphconfs(in_periphconf_elfs)
padding_len = periphconf_size - len(periphconf_combined)
periphconf_final = periphconf_combined + bytes([0xFF for _ in range(padding_len)])

if kconfig.get("CONFIG_NRF_HALTIUM_UICR_PERIPHCONF") == "y":
if is_primary:
uicr.PERIPHCONF.ENABLE = ENABLED_VALUE
uicr.PERIPHCONF.ADDRESS = periphconf_address
uicr.PERIPHCONF.MAXCOUNT = math.floor(periphconf_size / 8)
else:
uicr.SECONDARY.PERIPHCONF.ENABLE = ENABLED_VALUE
uicr.SECONDARY.PERIPHCONF.ADDRESS = periphconf_address
uicr.SECONDARY.PERIPHCONF.MAXCOUNT = math.floor(periphconf_size / 8)

if out_periphconf_hex is not None:
periphconf_hex = IntelHex()
periphconf_hex.frombytes(periphconf_final, offset=periphconf_address)
periphconf_hex.write_hex_file(out_periphconf_hex)


def extract_and_combine_periphconfs(elf_files: list[argparse.FileType]) -> bytes:
combined_periphconf = []

Expand Down Expand Up @@ -266,5 +358,36 @@
return result


def pretty_print_uicr(uicr: Uicr) -> None:
"""Pretty print the UICR structure contents."""
print("=" * 60)
print("UICR STRUCTURE CONTENTS")
print("=" * 60)

Check warning on line 366 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

C0303

soc/nordic/common/uicr/gen_uicr.py:366 Trailing whitespace (trailing-whitespace)

Check failure on line 366 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (W293) see https://docs.astral.sh/ruff/rules/blank-line-with-whitespace

soc/nordic/common/uicr/gen_uicr.py:366 Blank line contains whitespace

Check failure on line 366 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

TRAILING_WHITESPACE

soc/nordic/common/uicr/gen_uicr.py:366 trailing whitespace
def struct_to_dict(struct):
"""Convert ctypes structure to dict recursively."""
result = {}
for field_name, _ in struct._fields_:
value = getattr(struct, field_name)
if hasattr(value, '_fields_'): # Nested structure
result[field_name] = struct_to_dict(value)
elif hasattr(value, '__getitem__') and hasattr(value, '__len__'): # Array
try:
result[field_name] = [f"0x{v:08X}" if isinstance(v, int) and v > 255 else v for v in value]

Check failure on line 376 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (E501) see https://docs.astral.sh/ruff/rules/line-too-long

soc/nordic/common/uicr/gen_uicr.py:376 Line too long (111 > 100)
except:

Check warning on line 377 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

W0702

soc/nordic/common/uicr/gen_uicr.py:377 No exception type(s) specified (bare-except)

Check failure on line 377 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (E722) see https://docs.astral.sh/ruff/rules/bare-except

soc/nordic/common/uicr/gen_uicr.py:377 Do not use bare `except`
result[field_name] = str(value)
else:
# Format large integers as hex
if isinstance(value, int) and value > 255:
result[field_name] = f"0x{value:08X}"
else:
result[field_name] = value
return result

Check warning on line 386 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

C0303

soc/nordic/common/uicr/gen_uicr.py:386 Trailing whitespace (trailing-whitespace)

Check failure on line 386 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (W293) see https://docs.astral.sh/ruff/rules/blank-line-with-whitespace

soc/nordic/common/uicr/gen_uicr.py:386 Blank line contains whitespace

Check failure on line 386 in soc/nordic/common/uicr/gen_uicr.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

TRAILING_WHITESPACE

soc/nordic/common/uicr/gen_uicr.py:386 trailing whitespace
uicr_dict = struct_to_dict(uicr)
pprint(uicr_dict, width=100, depth=None)
print("=" * 60)


if __name__ == "__main__":
main()
Loading