Nordic nrf54l port (with and without TrustZone)

pull/714/head
Mattia Moffa 2026-03-09 18:14:24 +01:00 committed by Daniele Lacamera
parent b456d0eabb
commit 0656ff4afa
15 changed files with 1936 additions and 0 deletions

View File

@ -395,6 +395,7 @@ if(NOT DEFINED ARM_TARGETS)
nrf52840
nrf5340
nrf5340_net
nrf54l
rp2350
sama5d3
same51

View File

@ -867,6 +867,12 @@ ifeq ($(TARGET),nrf5340)
endif
endif
ifeq ($(TARGET),nrf54l)
ifneq ($(TZEN), 1)
LSCRIPT_IN=hal/$(TARGET)-ns.ld
endif
endif
ifeq ($(TARGET),nrf5340_net)
# Net core doesn't support DSP and FP
CFLAGS+=-mcpu=cortex-m33+nodsp+nofp

View File

@ -0,0 +1,65 @@
ARCH?=ARM
TZEN?=1
TARGET?=nrf54l
SIGN?=ECC384
HASH?=SHA384
WOLFBOOT_VERSION?=1
VTOR?=1
CORTEX_M0?=0
CORTEX_M33?=1
NO_ASM?=0
NO_MPU=1
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
DELTA_UPDATES?=1
SPMATH?=1
RAM_CODE?=1
DUALBANK_SWAP?=0
FLAGS_HOME=0
DISABLE_BACKUP=0
EXT_FLASH?=0
SPI_FLASH?=0
QSPI_FLASH?=0
UART_FLASH?=0
WOLFCRYPT_TZ?=1
WOLFCRYPT_TZ_PKCS11?=1
# 4096 sector size (the RRAM doesn't have an intrinsic page size)
WOLFBOOT_SECTOR_SIZE?=0x1000
# Flash layout
#
# 0x00000000 - 0x0004EFFF wolfBoot (316 KB) secure
# 0x0004F000 - 0x00064FFF Keyvault (88 KB) secure
# 0x00065000 - 0x00065FFF NSC region (4 KB) non-secure callable
# 0x00066000 - 0x000F0FFF Boot partition (556 KB) non-secure
# 0x000F1000 - 0x0017BFFF Update partition (556 KB) secure
# 0x0017C000 - 0x0017CFFF Swap area (4 KB) secure
#
# The update partition is meant to be written to via wolfBoot's NSC veneers
WOLFBOOT_KEYVAULT_ADDRESS?=0x4F000
WOLFBOOT_KEYVAULT_SIZE?=0x16000
WOLFBOOT_NSC_ADDRESS?=0x65000
WOLFBOOT_NSC_SIZE?=0x1000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x66000
WOLFBOOT_PARTITION_SIZE?=0x8B000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0xF1000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x17C000
V?=0
DEBUG?=0
DEBUG_UART?=1
USE_GCC=1
OPTIMIZATION_LEVEL=2
# Use larger block size for swapping sectors (performance improvement)
CFLAGS_EXTRA+=-DFLASHBUFFER_SIZE=0x1000
#CFLAGS_EXTRA+=-DDEBUG_FLASH

View File

@ -0,0 +1,60 @@
ARCH?=ARM
TZEN?=0
TARGET?=nrf54l
SIGN?=ECC384
HASH?=SHA384
WOLFBOOT_VERSION?=1
VTOR?=1
CORTEX_M0?=0
CORTEX_M33?=1
NO_ASM?=0
NO_MPU=1
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
DELTA_UPDATES?=1
SPMATH?=1
RAM_CODE?=1
DUALBANK_SWAP?=0
FLAGS_HOME=0
DISABLE_BACKUP=0
EXT_FLASH?=0
SPI_FLASH?=0
QSPI_FLASH?=0
UART_FLASH?=0
# 4096 sector size (the RRAM doesn't have an intrinsic page size)
WOLFBOOT_SECTOR_SIZE?=0x1000
# Reserve the first 64KB of internal flash for wolfBoot itself
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x10000
# Application partition spans the remainder of the 1524K internal flash
# (1524K - 64K - 4K) / 2 = 728K = 0xB6000
WOLFBOOT_PARTITION_SIZE?=0xB6000
# Flash offset for application update image
# (64K + 728K) = 792K = 0xC6000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0xC6000
# Flash offset for swap area
# (1524K - 4K) = 1520K = 0x17C000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x17C000
V?=0
DEBUG?=0
DEBUG_UART?=1
USE_GCC=1
OPTIMIZATION_LEVEL=2
# Use larger block size for swapping sectors (performance improvement)
CFLAGS_EXTRA+=-DFLASHBUFFER_SIZE=0x1000
# SPI flash hookup for the DK radio shield
#CFLAGS_EXTRA+=-DSPI_CS_PORT=0 -DSPI_CS_PIN=25
#CFLAGS_EXTRA+=-DSPI_SCK_PORT=0 -DSPI_SCK_PIN=29
#CFLAGS_EXTRA+=-DSPI_MOSI_PORT=0 -DSPI_MOSI_PIN=28
#CFLAGS_EXTRA+=-DSPI_MISO_PORT=0 -DSPI_MISO_PIN=27
#CFLAGS_EXTRA+=-DDEBUG_FLASH

View File

@ -17,6 +17,7 @@ This README describes configuration of supported targets.
* [Microchip SAME51](#microchip-same51)
* [Nordic nRF52840](#nordic-nrf52840)
* [Nordic nRF5340](#nordic-nrf5340)
* [Nordic nRF54L15](#nordic-nrf54l15)
* [NXP iMX-RT](#nxp-imx-rt)
* [NXP Kinetis](#nxp-kinetis)
* [NXP LPC54xxx](#nxp-lpc54xxx)
@ -4582,6 +4583,120 @@ c
```
## Nordic nRF54L15
Tested with the Nordic nRF54L15-DK. This device features a 128MHz Arm Cortex-M33 application
processor with TrustZone support, a 128MHz RISC-V coprocessor (VPR) used as a SoftPeripheral,
1524KB of RRAM (Resistive RAM), and 256KB of RAM. wolfBoot runs on the Cortex-M33 only and does
not interact with the RISC-V coprocessor.
Two configurations are available at `config/examples`:
- `nrf54l15.config`: TrustZone disabled; wolfBoot and the application always run in secure mode.
Delta updates are enabled.
- `nrf54l15-wolfcrypt-tz.config`: TrustZone enabled; wolfBoot runs in secure mode and boots the
application as non-secure code. Includes a non-secure callable (NSC) wolfPKCS11 API for
cryptographic operations via wolfCrypt, and a secure keyvault managed by wolfBoot. The update
partition is in secure memory and is intended to be written via wolfBoot's NSC veneers from the
non-secure application. See the "NSC API" section in `docs/API.md`.
### Flash Memory Layout
#### nrf54l15.config
```
0x00000000 - 0x0000FFFF wolfBoot (64 KB)
0x00010000 - 0x000C5FFF Boot partition (728 KB)
0x000C6000 - 0x0017BFFF Update partition (728 KB)
0x0017C000 - 0x0017CFFF Swap area (4 KB)
```
#### nrf54l15-wolfcrypt-tz.config
```
0x00000000 - 0x0004EFFF wolfBoot (316 KB) secure
0x0004F000 - 0x00064FFF Keyvault (88 KB) secure
0x00065000 - 0x00065FFF NSC region (4 KB) non-secure callable
0x00066000 - 0x000F0FFF Boot partition (556 KB) non-secure
0x000F1000 - 0x0017BFFF Update partition (556 KB) secure
0x0017C000 - 0x0017CFFF Swap area (4 KB) secure
```
### UART
Debug output is available on UART20, connected to the J-Link VCOM port (TX=P1.4, RX=P1.5).
A secondary UART (UART30, TX=P0.0, RX=P0.1) is reserved for the `UART_FLASH` feature.
### Building
```sh
cp config/examples/nrf54l15.config .config
make clean
make
```
Or, for the TrustZone + wolfCrypt variant:
```sh
cp config/examples/nrf54l15-wolfcrypt-tz.config .config
make clean
make
```
### Flashing
Flash the factory image using JLink:
```
JLinkExe -device nRF54L15_xxAA -if SWD -speed 4000 -autoconnect 1
loadbin factory.bin 0x0
rnh
```
### Testing an Update
Sign the test application as version 2, then write the update trigger magic (`pBOOT`)
at the end of the partition.
#### nrf54l15.config (partition size 0xB6000)
```sh
tools/keytools/sign --ecc384 --sha384 test-app/image.bin wolfboot_signing_private_key.der 2
echo -n "pBOOT" > trigger_magic.bin
./tools/bin-assemble/bin-assemble \
update.bin \
0x0 test-app/image_v2_signed.bin \
0xB5FFB trigger_magic.bin
```
Flash the assembled image to the update partition:
```
JLinkExe -device nRF54L15_xxAA -if SWD -speed 4000 -autoconnect 1
loadbin update.bin 0xC6000
rnh
```
#### nrf54l15-wolfcrypt-tz.config (partition size 0x8B000)
```sh
tools/keytools/sign --ecc384 --sha384 test-app/image.bin wolfboot_signing_private_key.der 2
echo -n "pBOOT" > trigger_magic.bin
./tools/bin-assemble/bin-assemble \
update.bin \
0x0 test-app/image_v2_signed.bin \
0x8AFFB trigger_magic.bin
```
Flash the assembled image to the update partition:
```
JLinkExe -device nRF54L15_xxAA -if SWD -speed 4000 -autoconnect 1
loadbin update.bin 0xF1000
rnh
```
## Simulated
You can create a simulated target that uses files to mimic an internal and

52
hal/nrf54l-ns.ld 100644
View File

@ -0,0 +1,52 @@
MEMORY
{
FLASH (rx) : ORIGIN = @ARCH_FLASH_OFFSET@, LENGTH = @BOOTLOADER_PARTITION_SIZE@
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
}
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.isr_vector))
*(.boot*)
*(.text*)
*(.rodata*)
*(.init*)
*(.fini*)
. = ALIGN(4);
_end_text = .;
} > FLASH
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > FLASH
_stored_data = .;
.data : AT (_stored_data)
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
_end_data = .;
} > RAM
.bss (NOLOAD) :
{
_start_bss = .;
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
__bss_end__ = .;
_end = .;
} > RAM
. = ALIGN(4);
}
END_STACK = ORIGIN(RAM) + LENGTH(RAM);

611
hal/nrf54l.c 100644
View File

@ -0,0 +1,611 @@
/* nrf54l.c
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifdef TARGET_nrf54l
#include <stdint.h>
#include <string.h>
#include "hal.h"
#include "image.h"
#include "nrf54l.h"
#include "printf.h"
#include "wolfboot/wolfboot.h"
#ifdef TZEN
#include "hal/armv8m_tz.h"
#endif
#ifndef DEBUG_UART
#define DEBUG_UART 1
#endif
/* UART */
#ifdef DEBUG_UART
#define UART_WRITE_BUF_SIZE 128
static void uart_init_device(int device, uint32_t bitrate, uint8_t data, char parity, uint8_t stop)
{
int port = UART_PORT_NUM(device);
int pinTx = UART_PIN_NUM_TX(device);
int pinRx = UART_PIN_NUM_RX(device);
UART_ENABLE(device) = UART_ENABLE_ENABLE_Disabled;
/* Pre-drive TX high (IDLE) before configuring as output to avoid a low
* glitch that would look like start bits to the receiver. */
GPIO_OUTSET(port) = (1U << pinTx);
/* Configure TX pin */
GPIO_PIN_CNF(port, pinTx) = (GPIO_CNF_OUT | GPIO_CNF_STD_DRIVE_0 | GPIO_CNF_MCUSEL(0));
/* Configure RX pin */
GPIO_PIN_CNF(port, pinRx) = (GPIO_CNF_IN | GPIO_CNF_STD_DRIVE_0 | GPIO_CNF_MCUSEL(0));
UART_PSEL_TXD(device) = ((pinTx << UART_PSEL_TXD_PIN_Pos) & UART_PSEL_TXD_PIN_Msk) |
((port << UART_PSEL_TXD_PORT_Pos) & UART_PSEL_TXD_PORT_Msk);
UART_PSEL_RXD(device) = ((pinRx << UART_PSEL_RXD_PIN_Pos) & UART_PSEL_RXD_PIN_Msk) |
((port << UART_PSEL_RXD_PORT_Pos) & UART_PSEL_RXD_PORT_Msk);
UART_PSEL_CTS(device) = UART_PSEL_CTS_CONNECT_Disconnected;
UART_PSEL_RTS(device) = UART_PSEL_RTS_CONNECT_Disconnected;
UART_BAUDRATE(device) = UART_BAUDRATE_BAUDRATE_Baud115200;
UART_CONFIG(device) = UART_CONFIG_8N1; /* 8N1, no HW flow control */
UART_ENABLE(device) = UART_ENABLE_ENABLE_Enabled;
}
void uart_write_raw(int device, const char* buffer, unsigned int sz)
{
/* EasyDMA requires a RAM buffer */
static uint8_t uartTxBuf[UART_WRITE_BUF_SIZE];
while (sz > 0) {
/*
* loop until all bytes written,
* but only write UART_WRITE_BUF_SIZE max chars at once
*/
unsigned int xfer = sz;
if (xfer > sizeof(uartTxBuf))
xfer = sizeof(uartTxBuf);
memcpy(uartTxBuf, buffer, xfer);
UART_EVENTS_DMA_TX_END(device) = 0;
UART_EVENTS_DMA_TX_BUSERROR(device) = 0;
UART_DMA_TX_PTR(device) = (uint32_t)uartTxBuf;
UART_DMA_TX_MAXCNT(device) = xfer;
UART_TASKS_DMA_TX_START(device) = UART_TASKS_DMA_TX_START_START_Trigger;
while ((UART_EVENTS_DMA_TX_END(device) == 0) &&
(UART_EVENTS_DMA_TX_BUSERROR(device) == 0))
;
sz -= xfer;
buffer += xfer;
}
}
void uart_write_device(int device, const char* buf, unsigned int sz)
{
static char buffer[UART_WRITE_BUF_SIZE];
int bufsz = 0;
for(int i=0; i<(int)sz && bufsz < UART_WRITE_BUF_SIZE; i++)
{
char ch = (char) buf[i];
if(ch == '\r')
continue;
if(ch == '\n')
buffer[bufsz++] = '\r';
buffer[bufsz++] = ch;
}
uart_write_raw(device, buffer, bufsz);
}
void uart_write(const char* buf, unsigned int sz)
{
uart_write_device(DEVICE_MONITOR, buf, sz);
}
#endif /* DEBUG_UART */
#if (defined DEBUG_UART || UART_FLASH)
#define UART_RX_TIMEOUT 1000000UL
int uart_read(int device, uint8_t* buf, unsigned int sz)
{
if ((buf == NULL) || (sz == 0))
return -1;
UART_EVENTS_DMA_RX_END(device) = 0;
UART_EVENTS_DMA_RX_BUSERROR(device) = 0;
UART_DMA_RX_PTR(device) = (uint32_t)buf;
UART_DMA_RX_MAXCNT(device) = sz;
UART_TASKS_DMA_RX_START(device) = UART_TASKS_DMA_RX_START_START_Trigger;
for (uint32_t guard = 0; UART_EVENTS_DMA_RX_END(device) == 0; guard++) {
if (UART_EVENTS_DMA_RX_BUSERROR(device) != 0) {
UART_TASKS_DMA_RX_STOP(device) = UART_TASKS_DMA_RX_STOP_STOP_Trigger;
return -1;
}
if (guard > UART_RX_TIMEOUT) {
UART_TASKS_DMA_RX_STOP(device) = UART_TASKS_DMA_RX_STOP_STOP_Trigger;
return 0;
}
}
UART_TASKS_DMA_RX_STOP(device) = UART_TASKS_DMA_RX_STOP_STOP_Trigger;
return (int)UART_DMA_RX_AMOUNT(device);
}
#endif /* DEBUG_UART || UART_FLASH */
static void RAMFUNCTION flash_wait_ready(void)
{
while ((RRAMC_READY & RRAMC_READY_READY_Msk) == 0U)
;
}
static void RAMFUNCTION flash_wait_ready_next(void)
{
while ((RRAMC_READYNEXT & RRAMC_READYNEXT_READYNEXT_Msk) == 0U)
;
}
static void RAMFUNCTION flash_wait_buf_empty(void)
{
while ((RRAMC_BUFSTATUS_WRITEBUFEMPTY &
RRAMC_BUFSTATUS_WRITEBUFEMPTY_EMPTY_Msk) == 0U)
;
}
static void RAMFUNCTION flash_commit_writebuf(void)
{
if ((RRAMC_BUFSTATUS_WRITEBUFEMPTY &
RRAMC_BUFSTATUS_WRITEBUFEMPTY_EMPTY_Msk) == 0U) {
RRAMC_TASKS_COMMITWRITEBUF =
RRAMC_TASKS_COMMITWRITEBUF_TASKS_COMMITWRITEBUF_Trigger;
flash_wait_ready();
flash_wait_buf_empty();
}
}
static void RAMFUNCTION flash_write_enable(int enable)
{
uint32_t cfg = RRAMC_CONFIG;
if (enable != 0)
cfg |= RRAMC_CONFIG_WEN_Msk;
else
cfg &= ~RRAMC_CONFIG_WEN_Msk;
RRAMC_CONFIG = cfg;
flash_wait_ready();
}
static int RAMFUNCTION flash_program_range(uint32_t address,
const uint8_t *data, int len)
{
int i = 0;
while (i < len) {
flash_wait_ready_next();
if ((((address + i) & 0x3U) == 0U) &&
((((uintptr_t)(data + i)) & 0x3U) == 0U) &&
(len - i) >= 4) {
const uint32_t *src = (const uint32_t *)(data + i);
volatile uint32_t *dst = (volatile uint32_t *)(address + i);
*dst = *src;
i += 4;
}
else {
uint32_t word;
volatile uint32_t *dst =
(volatile uint32_t *)((address + i) & ~0x3U);
int offset = (int)((address + i) & 0x3U);
word = *dst;
((uint8_t *)&word)[offset] = data[i];
*dst = word;
i++;
}
}
return 0;
}
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
flash_write_enable(1);
flash_program_range(address, data, len);
flash_commit_writebuf();
flash_write_enable(0);
return 0;
}
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
uint32_t end = address + (uint32_t)len;
uint8_t blank[64];
memset(blank, 0xFF, sizeof(blank));
flash_write_enable(1);
while (address < end) {
int chunk = (int)(end - address);
if (chunk > (int)sizeof(blank))
chunk = (int)sizeof(blank);
flash_program_range(address, blank, chunk);
address += (uint32_t)chunk;
}
flash_commit_writebuf();
flash_write_enable(0);
return 0;
}
void RAMFUNCTION hal_flash_unlock(void)
{
flash_write_enable(1);
}
void RAMFUNCTION hal_flash_lock(void)
{
flash_write_enable(0);
}
#if (UART_FLASH)
int uart_tx(const uint8_t c)
{
uart_write((const char *)&c, 1);
return 0;
}
int uart_rx(uint8_t *c)
{
return uart_read(DEVICE_DOWNLOAD, c, 1);
}
int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop)
{
uart_init_device(DEVICE_DOWNLOAD, bitrate, data, parity, stop);
return 0;
}
#else
void uart_init(void)
{
uart_init_device(DEVICE_DOWNLOAD, 115200, 8, 'N', 1);
}
#endif
static uintptr_t ext_flash_addr_calc(uintptr_t address)
{
/* offset external flash addresses by the update partition address */
address -= WOLFBOOT_PARTITION_UPDATE_ADDRESS;
return address;
}
int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
{
#ifdef DEBUG_FLASH
uintptr_t addr = ext_flash_addr_calc(address);
wolfBoot_printf("Ext Write: Len %d, Addr 0x%x (off 0x%x) -> 0x%x\n",
len, address, addr, data);
#endif
return 0;
}
int ext_flash_read(uintptr_t address, uint8_t *data, int len)
{
#ifdef DEBUG_FLASH
uintptr_t addr = ext_flash_addr_calc(address);
wolfBoot_printf("Ext Read: Len %d, Addr 0x%x (off 0x%x) -> %p\n",
len, address, addr, data);
#endif
memset(data, FLASH_BYTE_ERASED, len);
return len;
}
int ext_flash_erase(uintptr_t address, int len)
{
#ifdef DEBUG_FLASH
uintptr_t addr = ext_flash_addr_calc(address);
wolfBoot_printf("Ext Erase: Len %d, Addr 0x%x (off 0x%x)\n",
len, address, addr);
#endif
return 0;
}
void ext_flash_lock(void)
{
/* no op */
}
void ext_flash_unlock(void)
{
/* no op */
}
static void high_freq_clock_init(void)
{
/* Start the HFXO and wait until it is running */
CLOCK_EVENTS_XOSTARTED = 0;
CLOCK_TASKS_XOSTART = CLOCK_TASKS_XOSTART_TASKS_XOSTART_Trigger;
while ((CLOCK_EVENTS_XOSTARTED == 0) ||
((CLOCK_XO_STAT & CLOCK_XO_STAT_STATE_Msk) ==
(CLOCK_XO_STAT_STATE_NotRunning << CLOCK_XO_STAT_STATE_Pos))) {
/* wait */
}
}
static void low_freq_clock_init(void)
{
/* Configure the 32.768 kHz crystal load caps using factory trim when present */
uint32_t intcap = OSCILLATORS_XOSC32KI_INTCAP_ResetValue &
OSCILLATORS_XOSC32KI_INTCAP_VAL_Msk;
if (FICR_XOSC32KTRIM != FICR_XOSC32KTRIM_ResetValue) {
uint32_t trim = (FICR_XOSC32KTRIM & FICR_XOSC32KTRIM_OFFSET_Msk) >>
FICR_XOSC32KTRIM_OFFSET_Pos;
intcap = trim & (OSCILLATORS_XOSC32KI_INTCAP_VAL_Msk >>
OSCILLATORS_XOSC32KI_INTCAP_VAL_Pos);
}
OSCILLATORS_XOSC32KI_INTCAP =
(intcap << OSCILLATORS_XOSC32KI_INTCAP_VAL_Pos) &
OSCILLATORS_XOSC32KI_INTCAP_VAL_Msk;
/* Start the LFCLK from the external LFXO and wait until it is running */
CLOCK_EVENTS_LFCLKSTARTED = 0;
CLOCK_LFCLK_SRC = CLOCK_LFCLK_SRC_SRC_LFXO;
CLOCK_TASKS_LFCLKSTART = CLOCK_TASKS_LFCLKSTART_TASKS_LFCLKSTART_Trigger;
while ((CLOCK_EVENTS_LFCLKSTARTED == 0) ||
((CLOCK_LFCLK_STAT & CLOCK_LFCLK_STAT_SRC_Msk) !=
(CLOCK_LFCLK_STAT_SRC_LFXO << CLOCK_LFCLK_STAT_SRC_Pos)) ||
((CLOCK_LFCLK_STAT & CLOCK_LFCLK_STAT_STATE_Msk) ==
(CLOCK_LFCLK_STAT_STATE_NotRunning << CLOCK_LFCLK_STAT_STATE_Pos))) {
/* wait */
}
}
static void clock_init(void)
{
high_freq_clock_init();
low_freq_clock_init();
}
static void clock_deinit(void)
{
}
static void hal_handle_approtect(void)
{
#ifdef DEBUG_SYMBOLS
/* APPROTECT re-enables on every reset unless firmware
* explicitly opens the TAMPC signals. */
volatile uint32_t *regs[] = {
&TAMPC_PROTECT_DOMAIN0_DBGEN_CTRL,
&TAMPC_PROTECT_DOMAIN0_NIDEN_CTRL,
&TAMPC_PROTECT_DOMAIN0_SPIDEN_CTRL,
&TAMPC_PROTECT_DOMAIN0_SPNIDEN_CTRL,
&TAMPC_PROTECT_AP0_DBGEN_CTRL,
};
unsigned int i;
for (i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) {
if (!(*regs[i] & TAMPC_SIGNAL_LOCK_Msk)) {
*regs[i] = TAMPC_SIGNAL_CLEAR_WRITEPROTECTION;
*regs[i] = TAMPC_SIGNAL_OPEN;
}
}
#endif
}
#if TZ_SECURE()
/* Make a single peripheral non-secure */
static void spu_periph_set_ns(uint32_t periph_s_addr)
{
uint32_t spu_base = SPU_BASE_FOR(periph_s_addr);
uint32_t slave_idx = SPU_SLAVE_IDX(periph_s_addr);
SPU_PERIPH_PERM(spu_base, slave_idx) &= ~SPU_PERIPH_PERM_SECATTR;
}
/* Mark all 32 GPIO pins of a GPIO port as non-secure */
static void spu_gpio_pins_set_ns(uint32_t spu_base, uint32_t gpio_port)
{
uint32_t pin;
for (pin = 0; pin < 32; pin++) {
SPU_FEATURE_GPIO_PIN(spu_base, gpio_port, pin) &=
~SPU_FEATURE_SECATTR;
}
}
/* Mark a flash/RAM region as NonSecure in the MPC.
* start and end must be 4 KB aligned */
static void mpc_region_set_ns(uint32_t region, uint32_t start, uint32_t end)
{
MPC_OVERRIDE_STARTADDR(region) = start;
MPC_OVERRIDE_ENDADDR(region) = end;
/* set READ, WRITE, EXECUTE, don't set SECATTR, i.e. make non-secure */
MPC_OVERRIDE_PERM(region) = MPC_PERM_READ | MPC_PERM_WRITE |
MPC_PERM_EXECUTE;
/* apply all, including SECATTR */
MPC_OVERRIDE_PERMMASK(region) = MPC_PERM_READ | MPC_PERM_WRITE |
MPC_PERM_EXECUTE | MPC_PERM_SECURE;
MPC_OVERRIDE_CONFIG(region) = MPC_CONFIG_ENABLE;
}
static void hal_tz_init(void)
{
/* Memory must be marked as NS via both MPC and SAU. Only marking it via
* SAU will just cause accesses from secure code to be non-secure, and the
* MPC will restrict them because it considers the memory secure. */
/* MPC: NS flash (boot partition only) */
mpc_region_set_ns(0,
WOLFBOOT_PARTITION_BOOT_ADDRESS,
WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE);
/* MPC: NS RAM */
mpc_region_set_ns(1, NS_RAM_BASE, NS_RAM_BASE + NS_RAM_SIZE);
/* SAU: NS flash (boot partition only) */
sau_init_region(0,
WOLFBOOT_PARTITION_BOOT_ADDRESS,
WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE - 1,
0);
/* SAU: NSC (Non-Secure Callable) */
sau_init_region(1,
WOLFBOOT_NSC_ADDRESS,
WOLFBOOT_NSC_ADDRESS + WOLFBOOT_NSC_SIZE - 1,
1);
/* SAU: NS RAM */
sau_init_region(2,
NS_RAM_BASE,
NS_RAM_BASE + NS_RAM_SIZE - 1,
0);
/* Region 3: NS peripherals (covered by SPU, not MPC) */
sau_init_region(3, 0x40000000, 0x4FFFFFFF, 0);
/* Enable SAU and SecureFault */
SAU_CTRL = SAU_INIT_CTRL_ENABLE;
SCB_SHCSR |= SCB_SHCSR_SECUREFAULT_EN;
}
static void periph_unsecure(void)
{
/* UARTE20: UART connected to JLink on nRF54l15-DK */
spu_periph_set_ns(UARTE20_S_BASE);
/* UARTE20 + LED1 GPIO pins */
spu_periph_set_ns(GPIO_P1_S_BASE);
spu_gpio_pins_set_ns(SPU20_BASE, 1);
/* UARTE30 (download UART) */
spu_periph_set_ns(UARTE30_S_BASE);
/* UARTE30 pins */
spu_periph_set_ns(GPIO_P0_S_BASE);
spu_gpio_pins_set_ns(SPU30_BASE, 0);
}
#endif /* TZ_SECURE() */
void hal_init(void)
{
#ifdef DEBUG_UART
const char* bootStr = "wolfBoot HAL Init\n";
#endif
#ifdef __WOLFBOOT
hal_handle_approtect();
clock_init();
#endif
#if TZ_SECURE()
hal_tz_init();
#endif
#ifdef DEBUG_UART
uart_init_device(DEVICE_MONITOR, 115200, 8, 'N', 1);
uart_write(bootStr, strlen(bootStr));
#endif
}
void hal_prepare_boot(void)
{
clock_deinit();
#if TZ_SECURE()
periph_unsecure();
DSB();
ISB();
#endif
}
#ifdef WOLFCRYPT_SECURE_MODE
void hal_trng_init(void)
{
uint32_t state;
CRACEN_ENABLE |= CRACEN_ENABLE_RNG_Msk;
/* Soft-reset the RNGCONTROL block */
CRACENCORE_RNG_CONTROL = CRACENCORE_RNG_CONTROL_SOFTRST_Msk;
/* Configure: ring oscillator clock divider=0, init wait=512, off timer=0 */
CRACENCORE_RNG_CLKDIV = 0;
CRACENCORE_RNG_INITWAITVAL = CRACENCORE_RNG_INITWAITVAL_DEFAULT;
CRACENCORE_RNG_SWOFFTMRVAL = 0;
/* Enable with 4 AES-128 conditioning blocks */
CRACENCORE_RNG_CONTROL = CRACENCORE_RNG_CONTROL_ENABLE_Msk |
(CRACENCORE_RNG_NB128BITBLOCKS_DEFAULT
<< CRACENCORE_RNG_CONTROL_NB128BITBLOCKS_Pos);
/* Wait until FSM leaves RESET/STARTUP */
do {
state = (CRACENCORE_RNG_STATUS & CRACENCORE_RNG_STATUS_STATE_Msk)
>> CRACENCORE_RNG_STATUS_STATE_Pos;
} while (state == CRACENCORE_RNG_STATUS_STATE_RESET ||
state == CRACENCORE_RNG_STATUS_STATE_STARTUP);
}
void hal_trng_fini(void)
{
CRACENCORE_RNG_CONTROL = 0;
CRACEN_ENABLE &= ~CRACEN_ENABLE_RNG_Msk;
}
int hal_trng_get_entropy(unsigned char *out, unsigned int len)
{
unsigned int i = 0;
while (i < len) {
uint32_t word;
unsigned int j;
unsigned int avail;
/* wait until at least one 32-bit word is available */
while ((avail = CRACENCORE_RNG_FIFOLEVEL) == 0) {}
/* read all available words */
while (avail-- > 0 && i < len) {
word = CRACENCORE_RNG_FIFO;
for (j = 0; j < 4 && i < len; j++, i++) {
out[i] = (unsigned char)(word & 0xFF);
word >>= 8;
}
}
}
return 0;
}
#endif /* WOLFCRYPT_SECURE_MODE */
#endif /* TARGET_nrf54l */

589
hal/nrf54l.h 100644
View File

@ -0,0 +1,589 @@
/* nrf54l.h
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef _HAL_NRF54L_H_
#define _HAL_NRF54L_H_
#include <stdint.h>
#include <string.h>
void uart_write_device(int device, const char* buf, unsigned int sz);
#if !defined(TZEN) || !defined(NONSECURE_APP)
/* Use secure addresses */
# define TZ_SECURE() 1
#else
/* Use non-secure addresses */
# define TZ_SECURE() 0
#endif
#define CPU_CLOCK 128000000UL
#define FLASH_BASE_ADDR (0x00000000UL)
#define FLASH_SIZE (1560576UL) /* 1524KB (nRF54L15) */
/* The RRAM doesn't have a page size, this is just for wolfBoot purposes */
#define FLASH_PAGE_SIZE (0x1000UL) /* 4KB granularity */
#define FLASH_END (FLASH_BASE_ADDR + FLASH_SIZE)
/* Assembly helpers */
#define DMB() __asm__ volatile ("dmb")
#define DSB() __asm__ volatile ("dsb")
#define ISB() __asm__ volatile ("isb")
#define NOP() __asm__ volatile ("nop")
/* PSEL Port (bit 5) - used across UART/SPI GPIO muxing */
#define PSEL_PORT(n) (((n) & 0x7) << 5)
#if TZ_SECURE()
#define CLOCK_BASE_DEFAULT (0x5010E000UL)
#define OSCILLATORS_BASE_DEFAULT (0x50120000UL)
#else
#define CLOCK_BASE_DEFAULT (0x4010E000UL)
#define OSCILLATORS_BASE_DEFAULT (0x40120000UL)
#endif
#define CLOCK_BASE CLOCK_BASE_DEFAULT
#define OSCILLATORS_BASE OSCILLATORS_BASE_DEFAULT
#define FICR_BASE (0x00FFC000UL)
/* Clock control registers */
#define CLOCK_TASKS_XOSTART *((volatile uint32_t *)(CLOCK_BASE + 0x000))
#define CLOCK_TASKS_LFCLKSTART *((volatile uint32_t *)(CLOCK_BASE + 0x010))
#define CLOCK_EVENTS_XOSTARTED *((volatile uint32_t *)(CLOCK_BASE + 0x100))
#define CLOCK_EVENTS_LFCLKSTARTED *((volatile uint32_t *)(CLOCK_BASE + 0x108))
#define CLOCK_XO_STAT *((volatile uint32_t *)(CLOCK_BASE + 0x40C))
#define CLOCK_LFCLK_SRC *((volatile uint32_t *)(CLOCK_BASE + 0x440))
#define CLOCK_LFCLK_STAT *((volatile uint32_t *)(CLOCK_BASE + 0x44C))
#define CLOCK_TASKS_XOSTART_TASKS_XOSTART_Trigger 0x1UL
#define CLOCK_TASKS_LFCLKSTART_TASKS_LFCLKSTART_Trigger 0x1UL
#define CLOCK_XO_STAT_STATE_Pos 16UL
#define CLOCK_XO_STAT_STATE_Msk (0x1UL << CLOCK_XO_STAT_STATE_Pos)
#define CLOCK_XO_STAT_STATE_NotRunning 0x0UL
#define CLOCK_XO_STAT_STATE_Running 0x1UL
#define CLOCK_LFCLK_SRC_SRC_Pos 0UL
#define CLOCK_LFCLK_SRC_SRC_Msk (0x3UL << CLOCK_LFCLK_SRC_SRC_Pos)
#define CLOCK_LFCLK_SRC_SRC_LFRC 0x0UL
#define CLOCK_LFCLK_SRC_SRC_LFXO 0x1UL
#define CLOCK_LFCLK_SRC_SRC_LFSYNT 0x2UL
#define CLOCK_LFCLK_STAT_SRC_Pos 0UL
#define CLOCK_LFCLK_STAT_SRC_Msk (0x3UL << CLOCK_LFCLK_STAT_SRC_Pos)
#define CLOCK_LFCLK_STAT_SRC_LFRC 0x0UL
#define CLOCK_LFCLK_STAT_SRC_LFXO 0x1UL
#define CLOCK_LFCLK_STAT_SRC_LFSYNT 0x2UL
#define CLOCK_LFCLK_STAT_STATE_Pos 16UL
#define CLOCK_LFCLK_STAT_STATE_Msk (0x1UL << CLOCK_LFCLK_STAT_STATE_Pos)
#define CLOCK_LFCLK_STAT_STATE_NotRunning 0x0UL
#define CLOCK_LFCLK_STAT_STATE_Running 0x1UL
/* LFCLK source and oscillator trims */
#define OSCILLATORS_XOSC32KI_INTCAP *((volatile uint32_t *)(OSCILLATORS_BASE + 0x904))
#define OSCILLATORS_XOSC32KI_INTCAP_ResetValue 0x00000017UL
#define OSCILLATORS_XOSC32KI_INTCAP_VAL_Pos 0UL
#define OSCILLATORS_XOSC32KI_INTCAP_VAL_Msk (0x1FUL << OSCILLATORS_XOSC32KI_INTCAP_VAL_Pos)
#define FICR_XOSC32KTRIM *((volatile uint32_t *)(FICR_BASE + 0x624))
#define FICR_XOSC32KTRIM_ResetValue 0xFFFFFFFFUL
#define FICR_XOSC32KTRIM_OFFSET_Pos 16UL
#define FICR_XOSC32KTRIM_OFFSET_Msk (0x3FFUL << FICR_XOSC32KTRIM_OFFSET_Pos)
/* RRAM controller */
#define RRAMC_BASE_DEFAULT (0x5004B000UL)
#define RRAMC_BASE RRAMC_BASE_DEFAULT
#define RRAMC_TASKS_COMMITWRITEBUF *((volatile uint32_t *)(RRAMC_BASE + 0x008))
#define RRAMC_READY *((volatile uint32_t *)(RRAMC_BASE + 0x400))
#define RRAMC_READYNEXT *((volatile uint32_t *)(RRAMC_BASE + 0x404))
#define RRAMC_BUFSTATUS_WRITEBUFEMPTY *((volatile uint32_t *)(RRAMC_BASE + 0x418))
#define RRAMC_CONFIG *((volatile uint32_t *)(RRAMC_BASE + 0x500))
#define RRAMC_TASKS_COMMITWRITEBUF_TASKS_COMMITWRITEBUF_Trigger 0x1UL
#define RRAMC_READY_READY_Pos 0UL
#define RRAMC_READY_READY_Msk (0x1UL << RRAMC_READY_READY_Pos)
#define RRAMC_READYNEXT_READYNEXT_Pos 0UL
#define RRAMC_READYNEXT_READYNEXT_Msk (0x1UL << RRAMC_READYNEXT_READYNEXT_Pos)
#define RRAMC_BUFSTATUS_WRITEBUFEMPTY_EMPTY_Pos 0UL
#define RRAMC_BUFSTATUS_WRITEBUFEMPTY_EMPTY_Msk \
(0x1UL << RRAMC_BUFSTATUS_WRITEBUFEMPTY_EMPTY_Pos)
#define RRAMC_CONFIG_WEN_Pos 0UL
#define RRAMC_CONFIG_WEN_Msk (0x1UL << RRAMC_CONFIG_WEN_Pos)
/* TWIM */
#if TZ_SECURE()
#define TWIM20_BASE_DEFAULT (0x500C6000UL)
#else
#define TWIM20_BASE_DEFAULT (0x400C6000UL)
#endif
#define PMIC_TWIM_BASE TWIM20_BASE_DEFAULT
#define TWIM_TASKS_STOP(base) (*((volatile uint32_t *)((base) + 0x004)))
#define TWIM_TASKS_DMA_RX_START(base) (*((volatile uint32_t *)((base) + 0x028)))
#define TWIM_TASKS_DMA_TX_START(base) (*((volatile uint32_t *)((base) + 0x050)))
#define TWIM_EVENTS_STOPPED(base) (*((volatile uint32_t *)((base) + 0x104)))
#define TWIM_EVENTS_ERROR(base) (*((volatile uint32_t *)((base) + 0x114)))
#define TWIM_EVENTS_LASTRX(base) (*((volatile uint32_t *)((base) + 0x134)))
#define TWIM_EVENTS_LASTTX(base) (*((volatile uint32_t *)((base) + 0x138)))
#define TWIM_EVENTS_DMA_RX_END(base) (*((volatile uint32_t *)((base) + 0x14C)))
#define TWIM_EVENTS_DMA_TX_END(base) (*((volatile uint32_t *)((base) + 0x168)))
#define TWIM_SHORTS_REG(base) (*((volatile uint32_t *)((base) + 0x200)))
#define TWIM_ERRORSRC_REG(base) (*((volatile uint32_t *)((base) + 0x4C4)))
#define TWIM_ENABLE_REG(base) (*((volatile uint32_t *)((base) + 0x500)))
#define TWIM_FREQUENCY_REG(base) (*((volatile uint32_t *)((base) + 0x524)))
#define TWIM_ADDRESS_REG(base) (*((volatile uint32_t *)((base) + 0x588)))
#define TWIM_PSEL_SCL_REG(base) (*((volatile uint32_t *)((base) + 0x600)))
#define TWIM_PSEL_SDA_REG(base) (*((volatile uint32_t *)((base) + 0x604)))
#define TWIM_DMA_RX_PTR(base) (*((volatile uint32_t *)((base) + 0x704)))
#define TWIM_DMA_RX_MAXCNT(base) (*((volatile uint32_t *)((base) + 0x708)))
#define TWIM_DMA_RX_TERMINATE(base) (*((volatile uint32_t *)((base) + 0x71C)))
#define TWIM_DMA_TX_PTR(base) (*((volatile uint32_t *)((base) + 0x73C)))
#define TWIM_DMA_TX_MAXCNT(base) (*((volatile uint32_t *)((base) + 0x740)))
#define TWIM_DMA_TX_TERMINATE(base) (*((volatile uint32_t *)((base) + 0x754)))
#define TWIM_TASKS_STOP_TASKS_STOP_Trigger 0x1UL
#define TWIM_TASKS_DMA_RX_START_START_Trigger 0x1UL
#define TWIM_TASKS_DMA_TX_START_START_Trigger 0x1UL
#define TWIM_ENABLE_ENABLE_Disabled 0x0UL
#define TWIM_ENABLE_ENABLE_Enabled 0x6UL
#define TWIM_FREQUENCY_FREQUENCY_K100 0x01980000UL
#define TWIM_DMA_RX_TERMINATEONBUSERROR_ENABLE_Enabled 0x1UL
#define TWIM_DMA_TX_TERMINATEONBUSERROR_ENABLE_Enabled 0x1UL
#define TWIM_SHORTS_LASTTX_DMA_RX_START_Msk (0x1UL << 7)
#define TWIM_SHORTS_LASTTX_STOP_Msk (0x1UL << 9)
#define TWIM_SHORTS_LASTRX_STOP_Msk (0x1UL << 12)
/* GPIO */
#if TZ_SECURE()
#define GPIO_P0_S_BASE (0x5010A000UL)
#define GPIO_P1_S_BASE (0x500D8200UL)
#define GPIO_P2_S_BASE (0x50050400UL)
#else
#define GPIO_P0_NS_BASE (0x4010A000UL)
#define GPIO_P1_NS_BASE (0x400D8200UL)
#define GPIO_P2_NS_BASE (0x40050400UL)
#endif
/* GPIO configuration */
#if TZ_SECURE()
#define GPIO_PORT0_BASE_DEFAULT GPIO_P0_S_BASE
#define GPIO_PORT1_BASE_DEFAULT GPIO_P1_S_BASE
#define GPIO_PORT2_BASE_DEFAULT GPIO_P2_S_BASE
#else
#define GPIO_PORT0_BASE_DEFAULT GPIO_P0_NS_BASE
#define GPIO_PORT1_BASE_DEFAULT GPIO_P1_NS_BASE
#define GPIO_PORT2_BASE_DEFAULT GPIO_P2_NS_BASE
#endif
#define GPIO0_BASE GPIO_PORT0_BASE_DEFAULT
#define GPIO1_BASE GPIO_PORT1_BASE_DEFAULT
#define GPIO2_BASE GPIO_PORT2_BASE_DEFAULT
static inline uintptr_t hal_gpio_port_base(unsigned int port)
{
switch (port) {
case 0:
return (uintptr_t)GPIO0_BASE;
case 1:
return (uintptr_t)GPIO1_BASE;
case 2:
return (uintptr_t)GPIO2_BASE;
default:
return (uintptr_t)GPIO0_BASE;
}
}
#define GPIO_BASE(n) hal_gpio_port_base((unsigned int)(n))
#define GPIO_OUT(n) *((volatile uint32_t *)(GPIO_BASE(n) + 0x000))
#define GPIO_OUTSET(n) *((volatile uint32_t *)(GPIO_BASE(n) + 0x004))
#define GPIO_OUTCLR(n) *((volatile uint32_t *)(GPIO_BASE(n) + 0x008))
#define GPIO_DIRSET(n) *((volatile uint32_t *)(GPIO_BASE(n) + 0x014))
#define GPIO_DIRCLR(n) *((volatile uint32_t *)(GPIO_BASE(n) + 0x018))
#define GPIO_PIN_CNF(n, p) *((volatile uint32_t *)(GPIO_BASE(n) + 0x080 + ((p) * 0x4)))
#define GPIO_CNF_IN 0 // input
#define GPIO_CNF_OUT 1 // output
#define GPIO_CNF_IN_DIS 2 // input, disconnect input buffer
#define GPIO_CNF_OUT_DIS 3 // output, disconnect input buffer
#define GPIO_CNF_PULL_DIS 0
#define GPIO_CNF_PULL_UP (3UL << 2)
#define GPIO_CNF_PULL_DOWN (1UL << 2)
#define GPIO_CNF_STD_DRIVE_0 0
#define GPIO_CNF_HIGH_DRIVE_0 (1UL << 8) // High drive
#define GPIO_CNF_EXTRA_HIGH_DRIVE_0 (3UL << 8) // Extra-High drive
#define GPIO_CNF_STD_DRIVE_1 0
#define GPIO_CNF_HIGH_DRIVE_1 (1UL << 10) // High drive
#define GPIO_CNF_EXTRA_HIGH_DRIVE_1 (3UL << 10) // Extra-High drive
#define GPIO_CNF_SENSE_NONE 0
#define GPIO_CNF_MCUSEL(n) (((n) & 0x7) << 28)
/* UART */
// UART Device P0 P1 P2
// -----------------------------
// NRF_UARTE00 XX (P2 dedicated)
// NRF_UARTE20 XX (XX) (P2 cross-domain only)
// NRF_UARTE21 XX (XX) (P2 cross-domain only)
// NRF_UARTE22 XX
// NRF_UARTE30 XX
#define UARTE20_S_BASE 0x500C6000UL // monitor (secure)
#define UARTE30_S_BASE 0x50104000UL // download (secure)
#define UARTE20_NS_BASE 0x400C6000UL // monitor (non-secure)
#define UARTE30_NS_BASE 0x40104000UL // download (non-secure)
#define DEVICE_MONITOR 1
#define DEVICE_DOWNLOAD 2
// setup for DK board
#define PORT_MONITOR 1
#define PIN_TX_MONITOR 4
#define PIN_RX_MONITOR 5
#if TZ_SECURE()
#define BASE_ADDR_MONITOR UARTE20_S_BASE
#else
#define BASE_ADDR_MONITOR UARTE20_NS_BASE
#endif
// setup for DK board
#define PORT_DOWNLOAD 0
#define PIN_TX_DOWNLOAD 0
#define PIN_RX_DOWNLOAD 1
#if TZ_SECURE()
#define BASE_ADDR_DOWNLOAD UARTE30_S_BASE
#else
#define BASE_ADDR_DOWNLOAD UARTE30_NS_BASE
#endif
static inline uintptr_t hal_uart_port_base(int device)
{
switch (device) {
case DEVICE_DOWNLOAD:
return (uintptr_t) BASE_ADDR_DOWNLOAD;
case DEVICE_MONITOR:
default:
return (uintptr_t) BASE_ADDR_MONITOR;
}
}
static inline int hal_uart_port_num(int device)
{
switch (device) {
case DEVICE_DOWNLOAD:
return (int) PORT_DOWNLOAD;
case DEVICE_MONITOR:
default:
return (int) PORT_MONITOR;
}
}
static inline int hal_uart_pin_num_tx(int device)
{
switch (device) {
case DEVICE_DOWNLOAD:
return (int) PIN_TX_DOWNLOAD;
case DEVICE_MONITOR:
default:
return (int) PIN_TX_MONITOR;
}
}
static inline int hal_uart_pin_num_rx(int device)
{
switch (device) {
case DEVICE_DOWNLOAD:
return (int) PIN_RX_DOWNLOAD;
case DEVICE_MONITOR:
default:
return (int) PIN_RX_MONITOR;
}
}
/* UART Addressing */
#define UART_PORT_NUM(n) hal_uart_port_num((unsigned int)(n))
#define UART_PIN_NUM_TX(n) hal_uart_pin_num_tx((unsigned int)(n))
#define UART_PIN_NUM_RX(n) hal_uart_pin_num_rx((unsigned int)(n))
#define UART_BASE(n) hal_uart_port_base((unsigned int)(n))
#define UART_PSEL_TXD(n) *((volatile uint32_t *)(UART_BASE(n) + 0x604 + 0x000))
#define UART_PSEL_CTS(n) *((volatile uint32_t *)(UART_BASE(n) + 0x604 + 0x004))
#define UART_PSEL_RXD(n) *((volatile uint32_t *)(UART_BASE(n) + 0x604 + 0x008))
#define UART_PSEL_RTS(n) *((volatile uint32_t *)(UART_BASE(n) + 0x604 + 0x00C))
#define UART_ENABLE(n) *((volatile uint32_t *)(UART_BASE(n) + 0x500))
#define UART_BAUDRATE(n) *((volatile uint32_t *)(UART_BASE(n) + 0x524))
#define UART_CONFIG(n) *((volatile uint32_t *)(UART_BASE(n) + 0x56C))
#define UART_DMA_TX_PTR(n) *((volatile uint32_t *)(UART_BASE(n) + 0x700 + 0x038 + 0x004))
#define UART_DMA_TX_MAXCNT(n) *((volatile uint32_t *)(UART_BASE(n) + 0x700 + 0x038 + 0x008))
#define UART_DMA_RX_PTR(n) *((volatile uint32_t *)(UART_BASE(n) + 0x700 + 0x000 + 0x004))
#define UART_DMA_RX_MAXCNT(n) *((volatile uint32_t *)(UART_BASE(n) + 0x700 + 0x000 + 0x008))
#define UART_DMA_RX_AMOUNT(n) *((volatile uint32_t *)(UART_BASE(n) + 0x700 + 0x000 + 0x00C))
#define UART_EVENTS_DMA_TX_END(n) *((volatile uint32_t *)(UART_BASE(n) + 0x14C + 0x01C + 0x000))
#define UART_EVENTS_DMA_TX_BUSERROR(n) *((volatile uint32_t *)(UART_BASE(n) + 0x14C + 0x01C + 0x008))
#define UART_EVENTS_DMA_RX_END(n) *((volatile uint32_t *)(UART_BASE(n) + 0x14C + 0x000 + 0x000))
#define UART_EVENTS_DMA_RX_BUSERROR(n) *((volatile uint32_t *)(UART_BASE(n) + 0x14C + 0x000 + 0x008))
#define UART_TASKS_DMA_TX_START(n) *((volatile uint32_t *)(UART_BASE(n) + 0x028 + 0x028 + 0x000))
#define UART_TASKS_DMA_TX_STOP(n) *((volatile uint32_t *)(UART_BASE(n) + 0x028 + 0x028 + 0x004))
#define UART_TASKS_DMA_RX_START(n) *((volatile uint32_t *)(UART_BASE(n) + 0x028 + 0x000 + 0x000))
#define UART_TASKS_DMA_RX_STOP(n) *((volatile uint32_t *)(UART_BASE(n) + 0x028 + 0x000 + 0x004))
/* UART Settings */
#define UART_ENABLE_ENABLE_Enabled 0x8UL
#define UART_ENABLE_ENABLE_Disabled 0x0UL
#define UART_PSEL_TXD_PIN_Pos 0UL
#define UART_PSEL_TXD_PIN_Msk (0x1FUL << UART_PSEL_TXD_PIN_Pos)
#define UART_PSEL_TXD_PORT_Pos 5UL
#define UART_PSEL_TXD_PORT_Msk (0x7UL << UART_PSEL_TXD_PORT_Pos)
#define UART_PSEL_RXD_PIN_Pos 0UL
#define UART_PSEL_RXD_PIN_Msk (0x1FUL << UART_PSEL_RXD_PIN_Pos)
#define UART_PSEL_RXD_PORT_Pos 0x5UL
#define UART_PSEL_RXD_PORT_Msk (0x7UL << UART_PSEL_RXD_PORT_Pos)
#define UART_PSEL_CTS_CONNECT_Disconnected (0x1UL << 31)
#define UART_PSEL_RTS_CONNECT_Disconnected (0x1UL << 31)
#define UART_CONFIG_FRAMESIZE_Pos 9UL
#define UART_CONFIG_FRAMESIZE_8bit 0x8UL
#define UART_CONFIG_8N1 (UART_CONFIG_FRAMESIZE_8bit << UART_CONFIG_FRAMESIZE_Pos)
#define UART_TASKS_DMA_TX_START_START_Trigger 0x1UL
#define UART_TASKS_DMA_TX_STOP_STOP_Trigger 0x1UL
#define UART_TASKS_DMA_RX_START_START_Trigger 0x1UL
#define UART_TASKS_DMA_RX_STOP_STOP_Trigger 0x1UL
#define UART_BAUDRATE_BAUDRATE_Baud115200 0x01D60000UL
#define BAUD_115200 UART_BAUDRATE_BAUDRATE_Baud115200
/* Nordic PMIC */
#define PMIC_TWIM_PORT 1
#define PMIC_TWIM_SDA_PIN 2
#define PMIC_TWIM_SCL_PIN 3
#define PMIC_TWIM_TIMEOUT 1000000UL
#define PMIC_REG_PAYLOAD_MAX 8U
#define PMIC_I2C_ADDRESS 0x6BU // from pdf p122 (110 1011)
#define LED_PWR_CTRL_PORT 1
#define LED_PWR_CTRL_PIN 13
#define NPM1300_REG_TASK_LDSW2_SET 0x0802U
#define NPM1300_REG_TASK_LDSW2_CLR 0x0803U
#define NPM1300_REG_LDSW2_GPISEL 0x0806U
#define NPM1300_REG_LDSWCONFIG 0x0807U
#define NPM1300_REG_LDSW2LDOSEL 0x0809U
#define NPM1300_REG_GPIOMODE(n) (0x0600U + (uint16_t)(n))
#define NPM1300_REG_GPIOPUEN(n) (0x060AU + (uint16_t)(n))
#define NPM1300_REG_GPIOPDEN(n) (0x060FU + (uint16_t)(n))
/* SPIM */
#if TZ_SECURE()
#define SPIM00_BASE_DEFAULT (0x5004A000UL)
#else
#define SPIM00_BASE_DEFAULT (0x4004A000UL)
#endif
#define SPI_BASE SPIM00_BASE_DEFAULT
#define SPI_TASKS_START (*((volatile uint32_t *)(SPI_BASE + 0x000)))
#define SPI_TASKS_STOP (*((volatile uint32_t *)(SPI_BASE + 0x004)))
#define SPI_EVENTS_STARTED (*((volatile uint32_t *)(SPI_BASE + 0x100)))
#define SPI_EVENTS_STOPPED (*((volatile uint32_t *)(SPI_BASE + 0x104)))
#define SPI_EVENTS_END (*((volatile uint32_t *)(SPI_BASE + 0x108)))
#define SPI_EVENTS_DMA_RX_END (*((volatile uint32_t *)(SPI_BASE + 0x14C + 0x000)))
#define SPI_EVENTS_DMA_RX_READY (*((volatile uint32_t *)(SPI_BASE + 0x14C + 0x004)))
#define SPI_EVENTS_DMA_RX_BUSERROR (*((volatile uint32_t *)(SPI_BASE + 0x14C + 0x008)))
#define SPI_EVENTS_DMA_TX_END (*((volatile uint32_t *)(SPI_BASE + 0x14C + 0x01C)))
#define SPI_EVENTS_DMA_TX_READY (*((volatile uint32_t *)(SPI_BASE + 0x14C + 0x020)))
#define SPI_EVENTS_DMA_TX_BUSERROR (*((volatile uint32_t *)(SPI_BASE + 0x14C + 0x024)))
#define SPI_ENABLE_REG (*((volatile uint32_t *)(SPI_BASE + 0x500)))
#define SPI_PRESCALER_REG (*((volatile uint32_t *)(SPI_BASE + 0x52C)))
#define SPI_CONFIG_REG (*((volatile uint32_t *)(SPI_BASE + 0x554)))
#define SPI_IFTIMING_RXDELAY (*((volatile uint32_t *)(SPI_BASE + 0x5AC)))
#define SPI_IFTIMING_CSNDUR (*((volatile uint32_t *)(SPI_BASE + 0x5B0)))
#define SPI_PSEL_SCK (*((volatile uint32_t *)(SPI_BASE + 0x600)))
#define SPI_PSEL_MOSI (*((volatile uint32_t *)(SPI_BASE + 0x604)))
#define SPI_PSEL_MISO (*((volatile uint32_t *)(SPI_BASE + 0x608)))
#define SPI_PSEL_CSN (*((volatile uint32_t *)(SPI_BASE + 0x610)))
#define SPI_DMA_RX_PTR (*((volatile uint32_t *)(SPI_BASE + 0x704)))
#define SPI_DMA_RX_MAXCNT (*((volatile uint32_t *)(SPI_BASE + 0x708)))
#define SPI_DMA_RX_LIST (*((volatile uint32_t *)(SPI_BASE + 0x714)))
#define SPI_DMA_TX_PTR (*((volatile uint32_t *)(SPI_BASE + 0x73C)))
#define SPI_DMA_TX_MAXCNT (*((volatile uint32_t *)(SPI_BASE + 0x740)))
#define SPI_DMA_TX_LIST (*((volatile uint32_t *)(SPI_BASE + 0x74C)))
#define SPIM_TASKS_START_TASKS_START_Pos 0UL
#define SPIM_TASKS_START_TASKS_START_Msk \
(0x1UL << SPIM_TASKS_START_TASKS_START_Pos)
#define SPIM_TASKS_START_TASKS_START_Trigger 0x1UL
#define SPIM_TASKS_STOP_TASKS_STOP_Pos 0UL
#define SPIM_TASKS_STOP_TASKS_STOP_Msk \
(0x1UL << SPIM_TASKS_STOP_TASKS_STOP_Pos)
#define SPIM_TASKS_STOP_TASKS_STOP_Trigger 0x1UL
#define SPIM_ENABLE_ENABLE_Pos 0UL
#define SPIM_ENABLE_ENABLE_Msk (0xFUL << SPIM_ENABLE_ENABLE_Pos)
#define SPIM_ENABLE_ENABLE_Disabled 0x0UL
#define SPIM_ENABLE_ENABLE_Enabled 0x7UL
#define SPIM_CONFIG_ORDER_Pos 0UL
#define SPIM_CONFIG_ORDER_Msk (0x1UL << SPIM_CONFIG_ORDER_Pos)
#define SPIM_CONFIG_ORDER_MsbFirst 0x0UL
#define SPIM_CONFIG_ORDER_LsbFirst 0x1UL
#define SPIM_CONFIG_CPHA_Pos 1UL
#define SPIM_CONFIG_CPHA_Msk (0x1UL << SPIM_CONFIG_CPHA_Pos)
#define SPIM_CONFIG_CPHA_Leading 0x0UL
#define SPIM_CONFIG_CPHA_Trailing 0x1UL
#define SPIM_CONFIG_CPOL_Pos 2UL
#define SPIM_CONFIG_CPOL_Msk (0x1UL << SPIM_CONFIG_CPOL_Pos)
#define SPIM_CONFIG_CPOL_ActiveHigh 0x0UL
#define SPIM_CONFIG_CPOL_ActiveLow 0x1UL
#define SPI_PRESCALER_DIV 0x08UL
/* SPU - Security Processing Unit
*
* nRF54L has four SPU instances, one per power domain:
* SPU00 (MCU domain) 0x50040000 covers peripherals at 0x5004xxxx-0x5007xxxx
* SPU10 (RADIO domain) 0x50080000 covers peripherals at 0x5008xxxx-0x500Bxxxx
* SPU20 (PERI domain) 0x500C0000 covers peripherals at 0x500Cxxxx-0x500Fxxxx
* SPU30 (LP domain) 0x50100000 covers peripherals at 0x5010xxxx-0x5013xxxx
*/
#define SPU00_BASE (0x50040000UL) /* MCU domain */
#define SPU10_BASE (0x50080000UL) /* RADIO domain */
#define SPU20_BASE (0x500C0000UL) /* PERI domain */
#define SPU30_BASE (0x50100000UL) /* LP domain */
/* Derive which SPU instance governs a peripheral */
#define SPU_BASE_FOR(paddr) (0x50000000UL | ((paddr) & 0x00FC0000UL))
/* Derive the slave index within that SPU */
#define SPU_SLAVE_IDX(paddr) (((paddr) & 0x0003F000UL) >> 12)
#define SPU_PERIPH_PERM(spu, n) \
(*((volatile uint32_t *)((spu) + 0x500UL + (uint32_t)(n) * 4UL)))
#define SPU_FEATURE_GPIO_PIN(spu, g, p) \
(*((volatile uint32_t *)((spu) + 0x800UL + (uint32_t)(g) * 0x80UL + \
(uint32_t)(p) * 4UL)))
#define SPU_PERIPH_PERM_SECATTR (1UL << 4)
#define SPU_FEATURE_SECATTR (1UL << 4)
/* Non-secure RAM layout */
#define NS_RAM_BASE (0x20010000UL)
#define NS_RAM_SIZE (0x00030000UL)
/* MPC00 - Memory Privilege Controller */
#define MPC00_BASE (0x50041000UL)
#define MPC_OVERRIDE_CONFIG(n) \
(*((volatile uint32_t *)(MPC00_BASE + 0x800UL + (uint32_t)(n) * 0x20UL)))
#define MPC_OVERRIDE_STARTADDR(n) \
(*((volatile uint32_t *)(MPC00_BASE + 0x804UL + (uint32_t)(n) * 0x20UL)))
#define MPC_OVERRIDE_ENDADDR(n) \
(*((volatile uint32_t *)(MPC00_BASE + 0x808UL + (uint32_t)(n) * 0x20UL)))
#define MPC_OVERRIDE_PERM(n) \
(*((volatile uint32_t *)(MPC00_BASE + 0x810UL + (uint32_t)(n) * 0x20UL)))
#define MPC_OVERRIDE_PERMMASK(n) \
(*((volatile uint32_t *)(MPC00_BASE + 0x814UL + (uint32_t)(n) * 0x20UL)))
#define MPC_CONFIG_ENABLE (1UL << 9)
#define MPC_PERM_READ (1UL << 0)
#define MPC_PERM_WRITE (1UL << 1)
#define MPC_PERM_EXECUTE (1UL << 2)
#define MPC_PERM_SECURE (1UL << 3) /* 0 = NonSecure, 1 = Secure */
/* TAMPC - Tamper controller (access port protection) */
#define TAMPC_BASE (0x500DC000UL)
#define TAMPC_PROTECT_DOMAIN0_DBGEN_CTRL (*(volatile uint32_t *)(TAMPC_BASE + 0x500))
#define TAMPC_PROTECT_DOMAIN0_NIDEN_CTRL (*(volatile uint32_t *)(TAMPC_BASE + 0x508))
#define TAMPC_PROTECT_DOMAIN0_SPIDEN_CTRL (*(volatile uint32_t *)(TAMPC_BASE + 0x510))
#define TAMPC_PROTECT_DOMAIN0_SPNIDEN_CTRL (*(volatile uint32_t *)(TAMPC_BASE + 0x518))
#define TAMPC_PROTECT_AP0_DBGEN_CTRL (*(volatile uint32_t *)(TAMPC_BASE + 0x700))
/* KEY=0x50FA at bits[31:16], WRITEPROTECTION_Clear=0xF at bits[7:4] */
#define TAMPC_SIGNAL_CLEAR_WRITEPROTECTION (0x50FA00F0UL)
/* KEY=0x50FA at bits[31:16], VALUE=High at bit[0], LOCK=Disabled at bit[1] */
#define TAMPC_SIGNAL_OPEN (0x50FA0001UL)
#define TAMPC_SIGNAL_LOCK_Msk (1UL << 1)
/* CRACEN (Crypto Accelerator) */
#define CRACEN_BASE (0x50048000UL)
#define CRACENCORE_BASE (0x51800000UL)
/* CRACEN.ENABLE register (offset 0x400) */
#define CRACEN_ENABLE (*((volatile uint32_t *)(CRACEN_BASE + 0x400)))
#define CRACEN_ENABLE_RNG_Msk (0x1UL << 1)
#define CRACENCORE_RNGCTRL_BASE (CRACENCORE_BASE + 0x1000UL)
#define CRACENCORE_RNG_CONTROL (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x000)))
#define CRACENCORE_RNG_FIFOLEVEL (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x004)))
#define CRACENCORE_RNG_KEY(n) (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x010 + (n)*4)))
#define CRACENCORE_RNG_STATUS (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x030)))
#define CRACENCORE_RNG_INITWAITVAL (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x034)))
#define CRACENCORE_RNG_SWOFFTMRVAL (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x040)))
#define CRACENCORE_RNG_CLKDIV (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x044)))
#define CRACENCORE_RNG_FIFO (*((volatile uint32_t *)(CRACENCORE_RNGCTRL_BASE + 0x080)))
#define CRACENCORE_RNG_CONTROL_ENABLE_Msk (0x1UL << 0)
#define CRACENCORE_RNG_CONTROL_SOFTRST_Msk (0x1UL << 8)
#define CRACENCORE_RNG_CONTROL_NB128BITBLOCKS_Pos 16
#define CRACENCORE_RNG_CONTROL_NB128BITBLOCKS_Msk (0xFUL << CRACENCORE_RNG_CONTROL_NB128BITBLOCKS_Pos)
#define CRACENCORE_RNG_STATUS_STATE_Pos 1
#define CRACENCORE_RNG_STATUS_STATE_Msk (0x7UL << CRACENCORE_RNG_STATUS_STATE_Pos)
#define CRACENCORE_RNG_STATUS_STATE_RESET 0x0UL
#define CRACENCORE_RNG_STATUS_STATE_STARTUP 0x1UL
/* TRNG configuration values recommended by Nordic */
#define CRACENCORE_RNG_INITWAITVAL_DEFAULT 512UL
#define CRACENCORE_RNG_NB128BITBLOCKS_DEFAULT 4UL
#endif /* _HAL_NRF54L_H_ */

70
hal/nrf54l.ld 100644
View File

@ -0,0 +1,70 @@
MEMORY
{
FLASH (rx) : ORIGIN = @ARCH_FLASH_OFFSET@, LENGTH = @WOLFBOOT_KEYVAULT_ADDRESS@ - @ARCH_FLASH_OFFSET@
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0xC000 /* 48K */
RAM_HEAP (rw) : ORIGIN = 0x2000C000, LENGTH = 0x4000 /* 16K */
RAM_NS (rwx) : ORIGIN = 0x20010000, LENGTH = 192K
FLASH_KEYVAULT (rw) : ORIGIN = @WOLFBOOT_KEYVAULT_ADDRESS@, LENGTH = @WOLFBOOT_KEYVAULT_SIZE@
FLASH_NSC (rx) : ORIGIN = @WOLFBOOT_NSC_ADDRESS@, LENGTH = @WOLFBOOT_NSC_SIZE@
}
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.isr_vector))
*(.boot*)
*(.text*)
*(.rodata*)
*(.init*)
*(.fini*)
. = ALIGN(4);
_end_text = .;
} > FLASH
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > FLASH
.gnu.sgstubs :
{
. += 0x400;
. = ALIGN(4);
*(.gnu.sgstubs*) /* Secure Gateway Stubs */
. = ALIGN(4);
} > FLASH_NSC
_stored_data = .;
.data : AT (_stored_data)
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
_end_data = .;
} > RAM
.bss (NOLOAD) :
{
_start_bss = .;
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
__bss_end__ = .;
_end = .;
} > RAM
. = ALIGN(4);
}
END_STACK = ORIGIN(RAM) + LENGTH(RAM);
_flash_keyvault = ORIGIN(FLASH_KEYVAULT);
_flash_keyvault_size = LENGTH(FLASH_KEYVAULT);
_start_heap = ORIGIN(RAM_HEAP);
_heap_size = LENGTH(RAM_HEAP);

View File

@ -0,0 +1,164 @@
/* spi_drv_nrf54l.c
*
* Driver for the SPI back-end of the SPI_FLASH module.
*
* Pinout: see spi_drv_nrf54l.h
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdint.h>
#include "spi_drv.h"
#ifdef TARGET_nrf54l
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM)
#include "hal/nrf54l.h"
#include "hal/spi/spi_drv_nrf54l.h"
static uint8_t spi_tx_byte;
static volatile uint8_t spi_rx_byte;
static volatile uint8_t spi_rx_ready;
static inline void spim_clear_events(void)
{
SPI_EVENTS_STARTED = 0;
SPI_EVENTS_STOPPED = 0;
SPI_EVENTS_END = 0;
SPI_EVENTS_DMA_RX_END = 0;
SPI_EVENTS_DMA_RX_READY = 0;
SPI_EVENTS_DMA_RX_BUSERROR = 0;
SPI_EVENTS_DMA_TX_END = 0;
SPI_EVENTS_DMA_TX_READY = 0;
SPI_EVENTS_DMA_TX_BUSERROR = 0;
}
void RAMFUNCTION spi_cs_off(uint32_t base, int pin)
{
uint32_t mask = (1U << pin);
GPIO_OUTSET(base) = mask;
}
void RAMFUNCTION spi_cs_on(uint32_t base, int pin)
{
uint32_t mask = (1U << pin);
GPIO_OUTCLR(base) = mask;
}
uint8_t RAMFUNCTION spi_read(void)
{
while (!spi_rx_ready)
;
spi_rx_ready = 0;
return spi_rx_byte;
}
void RAMFUNCTION spi_write(const char byte)
{
spi_tx_byte = (uint8_t)byte;
spi_rx_ready = 0;
spim_clear_events();
SPI_DMA_RX_PTR = (uint32_t)&spi_rx_byte;
SPI_DMA_RX_MAXCNT = 1;
SPI_DMA_RX_LIST = 0;
SPI_DMA_TX_PTR = (uint32_t)&spi_tx_byte;
SPI_DMA_TX_MAXCNT = 1;
SPI_DMA_TX_LIST = 0;
SPI_TASKS_START = SPIM_TASKS_START_TASKS_START_Trigger;
while (SPI_EVENTS_END == 0)
;
SPI_TASKS_STOP = SPIM_TASKS_STOP_TASKS_STOP_Trigger;
while (SPI_EVENTS_STOPPED == 0)
;
SPI_EVENTS_STOPPED = 0;
spi_rx_ready = 1;
}
void spi_init(int polarity, int phase)
{
static int initialized = 0;
if (!initialized) {
initialized++;
GPIO_PIN_CNF(SPI_CS_PORT, SPI_CS_PIN) =
(GPIO_CNF_OUT | GPIO_CNF_HIGH_DRIVE_0);
GPIO_PIN_CNF(SPI_SCK_PORT, SPI_SCK_PIN) =
(GPIO_CNF_OUT | GPIO_CNF_HIGH_DRIVE_0);
GPIO_PIN_CNF(SPI_MOSI_PORT, SPI_MOSI_PIN) =
(GPIO_CNF_OUT | GPIO_CNF_HIGH_DRIVE_0);
GPIO_PIN_CNF(SPI_MISO_PORT, SPI_MISO_PIN) =
(GPIO_CNF_IN | GPIO_CNF_PULL_UP);
GPIO_OUTSET(SPI_CS_PORT) = (1 << SPI_CS_PIN);
GPIO_OUTCLR(SPI_SCK_PORT) = (1 << SPI_SCK_PIN);
GPIO_OUTCLR(SPI_MOSI_PORT) = (1 << SPI_MOSI_PIN);
SPI_ENABLE_REG = SPIM_ENABLE_ENABLE_Disabled;
SPI_PSEL_MISO = (PSEL_PORT(SPI_MISO_PORT) | SPI_MISO_PIN);
SPI_PSEL_MOSI = (PSEL_PORT(SPI_MOSI_PORT) | SPI_MOSI_PIN);
SPI_PSEL_SCK = (PSEL_PORT(SPI_SCK_PORT) | SPI_SCK_PIN);
SPI_PSEL_CSN = 0xFFFFFFFFUL; /* manual CS */
SPI_PRESCALER_REG = SPI_PRESCALER_DIV;
uint32_t cfg = (SPIM_CONFIG_ORDER_MsbFirst << SPIM_CONFIG_ORDER_Pos);
if (phase)
cfg |= (SPIM_CONFIG_CPHA_Trailing << SPIM_CONFIG_CPHA_Pos);
if (polarity)
cfg |= (SPIM_CONFIG_CPOL_ActiveLow << SPIM_CONFIG_CPOL_Pos);
SPI_CONFIG_REG = cfg;
SPI_IFTIMING_RXDELAY = 0;
SPI_IFTIMING_CSNDUR = 2;
SPI_DMA_RX_LIST = 0;
SPI_DMA_TX_LIST = 0;
SPI_ENABLE_REG = SPIM_ENABLE_ENABLE_Enabled;
}
(void)polarity;
(void)phase;
}
void spi_release(void)
{
}
#ifdef WOLFBOOT_TPM
int spi_xfer(int cs, const uint8_t* tx, uint8_t* rx, uint32_t sz, int flags)
{
uint32_t i;
spi_cs_on(SPI_CS_TPM_PIO_BASE, cs);
for (i = 0; i < sz; i++) {
spi_write((const char)tx[i]);
rx[i] = spi_read();
}
if (!(flags & SPI_XFER_FLAG_CONTINUE)) {
spi_cs_off(SPI_CS_TPM_PIO_BASE, cs);
}
return 0;
}
#endif /* WOLFBOOT_TPM */
#endif /* SPI_FLASH || WOLFBOOT_TPM */
#endif /* TARGET_nrf54l */

View File

@ -0,0 +1,55 @@
/* spi_drv_nrf54l.h
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef SPI_DRV_NRF54L_H_INCLUDED
#define SPI_DRV_NRF54L_H_INCLUDED
#include <stdint.h>
#include "hal/nrf54l.h"
/* Default pin-mux matches the nRF54L15-DK */
#ifndef SPI_CS_PORT
#define SPI_CS_PORT 0
#endif
#ifndef SPI_CS_PIN
#define SPI_CS_PIN 25
#endif
#ifndef SPI_SCK_PORT
#define SPI_SCK_PORT 0
#endif
#ifndef SPI_SCK_PIN
#define SPI_SCK_PIN 29
#endif
#ifndef SPI_MOSI_PORT
#define SPI_MOSI_PORT 0
#endif
#ifndef SPI_MOSI_PIN
#define SPI_MOSI_PIN 28
#endif
#ifndef SPI_MISO_PORT
#define SPI_MISO_PORT 0
#endif
#ifndef SPI_MISO_PIN
#define SPI_MISO_PIN 27
#endif
#define SPI_CS_FLASH SPI_CS_PIN
#define SPI_CS_PIO_BASE SPI_CS_PORT
#define SPI_CS_TPM_PIO_BASE SPI_CS_PORT
#endif /* SPI_DRV_NRF54L_H_INCLUDED */

View File

@ -60,6 +60,10 @@
#include "hal/spi/spi_drv_nrf5340.h"
#endif
#if defined(TARGET_nrf54l)
#include "hal/spi/spi_drv_nrf54l.h"
#endif
#if defined(TARGET_nxp_p1021) || defined(TARGET_nxp_t1024) || \
defined(TARGET_nxp_ls1028a)
#include "hal/spi/spi_drv_nxp.h"

View File

@ -0,0 +1,62 @@
/* ARM-nrf54l-ns.ld - NS test-app linker script for the nRF54L15 TZ build.
* The NS application runs from the boot partition in non-secure flash.
* RAM origin must match NS_RAM_BASE in hal/nrf54l.h and the SAU region
* programmed by hal_tz_init() in hal/nrf54l.c. */
MEMORY
{
FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
RAM (rwx) : ORIGIN = 0x20010000, LENGTH = 192K
}
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.isr_vector))
*(.init)
*(.fini)
*(.text*)
KEEP(*(.rodata*))
. = ALIGN(4);
_end_text = .;
} > FLASH
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > FLASH
_stored_data = .;
.data : AT (_stored_data)
{
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
KEEP(*(.ramcode))
. = ALIGN(4);
_end_data = .;
} > RAM
.bss :
{
_start_bss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_end_bss = .;
_end = .;
} > RAM
}
_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@;
_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@;
_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@;
_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@;
PROVIDE(_start_heap = _end);
PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM));

View File

@ -435,6 +435,14 @@ ifeq ($(TARGET),nrf5340)
endif
endif
ifeq ($(TARGET),nrf54l)
ifeq ($(TZEN),1)
LSCRIPT_TEMPLATE=ARM-nrf54l-ns.ld
else
LSCRIPT_TEMPLATE=ARM.ld
endif
endif
ifeq ($(TARGET),nrf5340_net)
CFLAGS+=-mcpu=cortex-m33+nodsp+nofp
LDFLAGS+=-mcpu=cortex-m33+nodsp+nofp

View File

@ -0,0 +1,74 @@
/* app_nrf54l.c
*
* Basic smoke test for the nRF54L15 target.
*/
#include <stdint.h>
#include <string.h>
#include "target.h"
#include "wolfboot/wolfboot.h"
#include "hal/nrf54l.h"
#include "printf.h"
#ifndef TEST_LED_PORT
#define TEST_LED_PORT 1
#endif
#ifndef TEST_LED_PIN
#define TEST_LED_PIN 10
#endif
extern void hal_init(void);
extern void wolfBoot_panic(void);
#ifdef RAM_CODE
#define AIRCR *(volatile uint32_t *)(0xE000ED0C)
#define AIRCR_VKEY (0x05FA << 16)
#define AIRCR_SYSRESETREQ (1 << 2)
#define WEAKFUNCTION __attribute__((weak))
void WEAKFUNCTION RAMFUNCTION arch_reboot(void)
{
AIRCR = AIRCR_SYSRESETREQ | AIRCR_VKEY;
while(1)
;
wolfBoot_panic();
}
#endif
static void led_toggle(void)
{
uint32_t mask = (1U << TEST_LED_PIN);
if (GPIO_OUT(TEST_LED_PORT) & mask)
GPIO_OUTCLR(TEST_LED_PORT) = mask;
else
GPIO_OUTSET(TEST_LED_PORT) = mask;
}
void main(void)
{
uint32_t version = wolfBoot_current_firmware_version();
uint8_t version_bytes[sizeof(version)];
memcpy(version_bytes, &version, sizeof(version));
hal_init();
GPIO_PIN_CNF(TEST_LED_PORT, TEST_LED_PIN) =
(GPIO_CNF_OUT | GPIO_CNF_HIGH_DRIVE_0);
GPIO_OUTCLR(TEST_LED_PORT) = (1U << TEST_LED_PIN);
uart_init();
uart_write("*", 1);
for (int i = (int)(sizeof(version_bytes) - 1); i >= 0; i--) {
uart_write((const char*)&version_bytes[i], 1);
}
for (;;) {
led_toggle();
for (volatile uint32_t n = 0; n < 1000000UL; n++)
NOP();
}
}