mirror of https://github.com/wolfSSL/wolfBoot.git
Add wolfBoot support for NXP LPC54S018M-EVK
parent
71b395d292
commit
91145d53bb
4
Makefile
4
Makefile
|
|
@ -334,6 +334,10 @@ wolfboot.efi: wolfboot.elf
|
|||
wolfboot.bin: wolfboot.elf
|
||||
@echo "\t[BIN] $@"
|
||||
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O binary $^ $@
|
||||
ifeq ($(TARGET),nxp_lpc54s018m)
|
||||
@echo "\t[LPC] enhanced boot block"
|
||||
$(Q)python3 -c "import struct,os;f=open('$@','r+b');sz=os.path.getsize('$@');f.seek(0x24);f.write(struct.pack('<2I',0xEDDC94BD,0x160));f.seek(0x160);f.write(struct.pack('<25I',0xFEEDA5A5,3,0x10000000,sz-4,0,0,0,0,0,0xEDDC94BD,0,0,0,0x001640EF,0,0,0x1301001D,0,0,0,0x00000100,0,0,0x04030050,0x14110D09));f.seek(0);d=f.read(28);w=struct.unpack('<7I',d);s=sum(w)&0xFFFFFFFF;ck=(0x100000000-s)&0xFFFFFFFF;f.seek(0x1C);f.write(struct.pack('<I',ck));f.close();print('\tvector checksum: 0x%08X'%ck)"
|
||||
endif
|
||||
@echo
|
||||
@echo "\t[SIZE]"
|
||||
$(Q)$(SIZE) wolfboot.elf
|
||||
|
|
|
|||
5
arch.mk
5
arch.mk
|
|
@ -1189,6 +1189,11 @@ ifeq ($(TARGET),lpc)
|
|||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),nxp_lpc54s018m)
|
||||
ARCH_FLASH_OFFSET=0x10000000
|
||||
# Bare-metal HAL — no NXP SDK dependencies
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),lpc55s69)
|
||||
ifneq ($(TZEN),1)
|
||||
LSCRIPT_IN=hal/$(TARGET)-ns.ld
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
ARCH?=ARM
|
||||
TARGET?=nxp_lpc54s018m
|
||||
SIGN?=ECC256
|
||||
HASH?=SHA256
|
||||
DEBUG?=0
|
||||
#DEBUG_UART?=1
|
||||
VTOR?=1
|
||||
NO_ASM?=0
|
||||
EXT_FLASH?=0
|
||||
SPI_FLASH?=0
|
||||
ALLOW_DOWNGRADE?=0
|
||||
NVM_FLASH_WRITEONCE?=0
|
||||
WOLFBOOT_VERSION?=0
|
||||
V?=0
|
||||
SPMATH?=1
|
||||
RAM_CODE?=1
|
||||
DUALBANK_SWAP?=0
|
||||
PKA?=1
|
||||
WOLFBOOT_PARTITION_SIZE?=0xF0000
|
||||
WOLFBOOT_SECTOR_SIZE?=0x1000
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x10010000
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x10100000
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x101F0000
|
||||
197
docs/Targets.md
197
docs/Targets.md
|
|
@ -21,6 +21,7 @@ This README describes configuration of supported targets.
|
|||
* [NXP iMX-RT](#nxp-imx-rt)
|
||||
* [NXP Kinetis](#nxp-kinetis)
|
||||
* [NXP LPC54xxx](#nxp-lpc54xxx)
|
||||
* [NXP LPC54S018M](#nxp-lpc54s018m)
|
||||
* [NXP LPC55S69](#nxp-lpc55s69)
|
||||
* [NXP LS1028A](#nxp-ls1028a)
|
||||
* [NXP MCXA153](#nxp-mcxa153)
|
||||
|
|
@ -1937,6 +1938,202 @@ arm-none-eabi-gdb wolfboot.elf -ex "target remote localhost:3333"
|
|||
```
|
||||
|
||||
|
||||
## NXP LPC54S018M
|
||||
|
||||
The NXP LPC54S018M is a Cortex-M4 microcontroller running at 180MHz. Unlike the
|
||||
LPC54606 which has internal flash, the LPC54S018M has **no internal NOR flash** —
|
||||
all code executes from on-package SPIFI-mapped QSPI flash (Winbond W25Q32JV, 4MB)
|
||||
at address `0x10000000`.
|
||||
|
||||
This has been tested on the LPC54S018M-EVK board, which includes an on-board
|
||||
Link2 debug probe (CMSIS-DAP / J-Link compatible) and a VCOM UART via Flexcomm0.
|
||||
|
||||
Because flash erase/write operations disable XIP (execute-in-place), all flash
|
||||
programming functions must run from RAM. The configuration uses `RAM_CODE=1` to
|
||||
ensure this.
|
||||
|
||||
### LPC54S018M: Link2 debug probe setup
|
||||
|
||||
The LPC54S018M-EVK has an on-board LPC-Link2 debug probe (LPC4322). The probe
|
||||
firmware determines the debug protocol: CMSIS-DAP or J-Link. J-Link firmware is
|
||||
recommended for use with wolfBoot.
|
||||
|
||||
**Jumper JP5** controls the Link2 boot mode:
|
||||
- **Installed (normal):** Link2 runs from its internal flash (debug probe mode)
|
||||
- **Removed (DFU):** Link2 enters DFU mode for firmware programming
|
||||
|
||||
To program J-Link firmware onto the Link2:
|
||||
|
||||
1. Remove JP5 and power cycle the board. The Link2 enters DFU mode
|
||||
(USB `1fc9:000c`).
|
||||
|
||||
2. Install [NXP LinkServer](https://www.nxp.com/design/design-center/software/development-software/mcuxpresso-software-and-tools-/linkserver-for-microcontrollers:LINKERSERVER)
|
||||
which includes LPCScrypt.
|
||||
|
||||
3. Boot LPCScrypt onto the Link2 (requires sudo or udev rules):
|
||||
|
||||
```sh
|
||||
sudo /usr/local/LinkServer/lpcscrypt/scripts/boot_lpcscrypt
|
||||
```
|
||||
|
||||
4. Identify the LPCScrypt serial port and program J-Link firmware:
|
||||
|
||||
```sh
|
||||
# Find the new ttyACM device created after boot_lpcscrypt
|
||||
ls -lt /dev/ttyACM*
|
||||
|
||||
# Program J-Link firmware (replace /dev/ttyACMx with the correct port)
|
||||
sudo /usr/local/LinkServer/lpcscrypt/bin/lpcscrypt -d /dev/ttyACMx \
|
||||
program /usr/local/LinkServer/lpcscrypt/probe_firmware/LPCLink2/Firmware_JLink_LPC-Link2_20230502.bin BankA
|
||||
```
|
||||
|
||||
5. Re-install JP5 and power cycle the board. The Link2 should now enumerate
|
||||
as a Segger J-Link USB device.
|
||||
|
||||
**Note:** If `uart-monitor` or another tool has the serial port open, you must
|
||||
release it first (e.g., `uart-monitor yield /dev/ttyACMx`) before running
|
||||
lpcscrypt.
|
||||
|
||||
To program CMSIS-DAP firmware instead (for use with pyocd/OpenOCD):
|
||||
|
||||
```sh
|
||||
sudo /usr/local/LinkServer/lpcscrypt/bin/lpcscrypt -d /dev/ttyACMx \
|
||||
program /usr/local/LinkServer/lpcscrypt/probe_firmware/LPCLink2/LPC432x_CMSIS_DAP_V5_460.bin.hdr BankA
|
||||
```
|
||||
|
||||
### LPC54S018M: MCUXpresso SDK setup
|
||||
|
||||
This requires the NXP MCUXpresso SDK. We tested using
|
||||
[mcuxsdk-manifests](https://github.com/nxp-mcuxpresso/mcuxsdk-manifests) and
|
||||
[CMSIS_5](https://github.com/nxp-mcuxpresso/CMSIS_5) placed under "../NXP".
|
||||
|
||||
```sh
|
||||
cd ../NXP
|
||||
|
||||
# Install west
|
||||
python -m venv west-venv
|
||||
source west-venv/bin/activate
|
||||
pip install west
|
||||
|
||||
# Set up the repository
|
||||
west init -m https://github.com/nxp-mcuxpresso/mcuxsdk-manifests.git mcuxpresso-sdk
|
||||
cd mcuxpresso-sdk
|
||||
west update
|
||||
|
||||
deactivate
|
||||
```
|
||||
|
||||
The CMSIS headers are also needed:
|
||||
|
||||
```sh
|
||||
cd ../NXP
|
||||
git clone https://github.com/nxp-mcuxpresso/CMSIS_5.git
|
||||
```
|
||||
|
||||
### LPC54S018M: Flash partition layout
|
||||
|
||||
The 4MB SPIFI flash is partitioned as follows:
|
||||
|
||||
| Region | Address | Size |
|
||||
|--------------|-------------|--------|
|
||||
| wolfBoot | 0x10000000 | 64KB |
|
||||
| Boot (app) | 0x10010000 | 960KB |
|
||||
| Update | 0x10100000 | 960KB |
|
||||
| Swap sector | 0x101F0000 | 4KB |
|
||||
|
||||
The sector size is 4KB, matching the W25Q32JV minimum erase size.
|
||||
|
||||
### LPC54S018M: Configuring and compiling
|
||||
|
||||
Copy the example configuration file and build with make:
|
||||
|
||||
```sh
|
||||
cp config/examples/nxp_lpc54s018m.config .config
|
||||
make
|
||||
```
|
||||
|
||||
This produces `factory.bin` containing wolfBoot + the signed test application.
|
||||
|
||||
### LPC54S018M: Loading the firmware
|
||||
|
||||
The on-board Link2 debugger supports both CMSIS-DAP and J-Link protocols.
|
||||
See [Link2 debug probe setup](#lpc54s018m-link2-debug-probe-setup) for
|
||||
programming the probe firmware.
|
||||
|
||||
**Using JLink** (Link2 with J-Link firmware):
|
||||
|
||||
```
|
||||
JLinkExe -device LPC54S018M -if SWD -speed 4000
|
||||
loadbin factory.bin 0x10000000
|
||||
r
|
||||
g
|
||||
```
|
||||
|
||||
**Using pyocd** (Link2 with CMSIS-DAP firmware):
|
||||
|
||||
```sh
|
||||
pyocd pack install LPC54S018J4MET180
|
||||
pyocd flash -t LPC54S018J4MET180 factory.bin --base-address 0x10000000
|
||||
pyocd reset -t LPC54S018J4MET180
|
||||
```
|
||||
|
||||
**Note:** The LPC54S018M boot ROM validates a vector table checksum at offset
|
||||
0x1C. The build system automatically computes and patches this checksum into
|
||||
`wolfboot.bin`. If the checksum is invalid, the boot ROM will enter ISP mode
|
||||
instead of booting from SPIFI flash.
|
||||
|
||||
### LPC54S018M: Testing firmware update
|
||||
|
||||
1. Build and flash factory.bin (version 1). USR_LED1 (P3.14) lights up.
|
||||
|
||||
2. Sign a version 2 update image and load it to the update partition:
|
||||
|
||||
```sh
|
||||
# Build update image (version 2)
|
||||
make test-app/image_v2_signed.bin
|
||||
```
|
||||
|
||||
```
|
||||
JLinkExe -device LPC54S018M -if SWD -speed 4000
|
||||
loadbin test-app/image_v2_signed.bin 0x10100000
|
||||
r
|
||||
g
|
||||
```
|
||||
|
||||
3. The test application detects the update, triggers a swap via
|
||||
`wolfBoot_update_trigger()`, and resets. After the swap, USR_LED2 (P3.3)
|
||||
lights up indicating version 2 is running.
|
||||
|
||||
4. The application calls `wolfBoot_success()` to confirm the update and
|
||||
prevent rollback.
|
||||
|
||||
### LPC54S018M: LED indicators
|
||||
|
||||
The test application uses three user LEDs (accent LEDs accent active low):
|
||||
|
||||
| LED | GPIO | Meaning |
|
||||
|-----------|--------|----------------------------|
|
||||
| USR_LED1 | P3.14 | Version 1 running |
|
||||
| USR_LED2 | P3.3 | Version 2+ running |
|
||||
| USR_LED3 | P2.2 | Update activity in progress |
|
||||
|
||||
### LPC54S018M: Debugging with JLink
|
||||
|
||||
```
|
||||
JLinkGDBServer -device LPC54S018M -if SWD -speed 4000 -port 3333
|
||||
```
|
||||
|
||||
Then, from another console:
|
||||
|
||||
```
|
||||
arm-none-eabi-gdb wolfboot.elf -ex "target remote localhost:3333"
|
||||
(gdb) add-symbol-file test-app/image.elf 0x10010100
|
||||
```
|
||||
|
||||
Note: The image.elf symbol offset is the boot partition address (0x10010000) plus
|
||||
the wolfBoot image header size (0x100).
|
||||
|
||||
|
||||
## NXP LPC55S69
|
||||
|
||||
The NXP LPC55S69 is a dual-core Cortex-M33 microcontroller. The support has been
|
||||
|
|
|
|||
|
|
@ -0,0 +1,293 @@
|
|||
/* nxp_lpc54s018m.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
|
||||
*
|
||||
* NXP LPC54S018M HAL for wolfBoot
|
||||
*
|
||||
* The LPC54S018M has no internal NOR flash. All code executes from
|
||||
* on-package SPIFI QSPI flash (W25Q32JV, 4MB) mapped at 0x10000000.
|
||||
* Flash operations MUST run from RAM since XIP is disabled during
|
||||
* erase/write.
|
||||
*
|
||||
* This HAL uses bare-metal register access — no NXP SDK dependencies.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <target.h>
|
||||
#include "image.h"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* SPIFI controller registers (base 0x40080000) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define SPIFI_BASE 0x40080000
|
||||
#define SPIFI_CTRL (*(volatile uint32_t *)(SPIFI_BASE + 0x00))
|
||||
#define SPIFI_CMD (*(volatile uint32_t *)(SPIFI_BASE + 0x04))
|
||||
#define SPIFI_ADDR (*(volatile uint32_t *)(SPIFI_BASE + 0x08))
|
||||
#define SPIFI_IDATA (*(volatile uint32_t *)(SPIFI_BASE + 0x0C))
|
||||
#define SPIFI_CLIMIT (*(volatile uint32_t *)(SPIFI_BASE + 0x10))
|
||||
#define SPIFI_DATA (*(volatile uint32_t *)(SPIFI_BASE + 0x14))
|
||||
#define SPIFI_MCMD (*(volatile uint32_t *)(SPIFI_BASE + 0x18))
|
||||
#define SPIFI_STAT (*(volatile uint32_t *)(SPIFI_BASE + 0x1C))
|
||||
|
||||
/* STAT register bits */
|
||||
#define SPIFI_STAT_MCINIT (1 << 0) /* Memory command init done */
|
||||
#define SPIFI_STAT_CMD (1 << 1) /* Command active */
|
||||
#define SPIFI_STAT_RESET (1 << 4) /* Reset in progress */
|
||||
|
||||
/* CMD register field positions */
|
||||
#define SPIFI_CMD_DATALEN(n) ((n) & 0x3FFF)
|
||||
#define SPIFI_CMD_POLL (1 << 14)
|
||||
#define SPIFI_CMD_DOUT (1 << 15) /* 1=output, 0=input */
|
||||
#define SPIFI_CMD_INTLEN(n) (((n) & 7) << 16)
|
||||
#define SPIFI_CMD_FIELDFORM(n) (((n) & 3) << 19)
|
||||
#define SPIFI_CMD_FRAMEFORM(n) (((n) & 7) << 21)
|
||||
#define SPIFI_CMD_OPCODE(n) (((n) & 0xFF) << 24)
|
||||
|
||||
/* Frame/field format values */
|
||||
#define FRAMEFORM_OPCODE_ONLY 1
|
||||
#define FRAMEFORM_OPCODE_3ADDR 4
|
||||
#define FIELDFORM_ALL_SERIAL 0
|
||||
#define FIELDFORM_DATA_QUAD 2
|
||||
|
||||
/* W25Q32JV flash commands */
|
||||
#define W25Q_CMD_WRITE_ENABLE 0x06
|
||||
#define W25Q_CMD_READ_STATUS1 0x05
|
||||
#define W25Q_CMD_PAGE_PROGRAM 0x02
|
||||
#define W25Q_CMD_SECTOR_ERASE 0x20 /* 4KB sector erase */
|
||||
#define W25Q_CMD_FAST_READ_QUAD 0x6B /* Quad output fast read */
|
||||
|
||||
/* W25Q status register bits */
|
||||
#define W25Q_STATUS_BUSY 0x01
|
||||
|
||||
/* Flash geometry */
|
||||
#define FLASH_PAGE_SIZE 0x100 /* 256 bytes */
|
||||
#define SPIFI_FLASH_BASE 0x10000000
|
||||
|
||||
static uint8_t flash_page_cache[FLASH_PAGE_SIZE];
|
||||
|
||||
/* Pre-computed SPIFI CMD register values for each flash operation */
|
||||
#define CMD_WRITE_ENABLE \
|
||||
(SPIFI_CMD_DOUT | SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_ONLY) | \
|
||||
SPIFI_CMD_OPCODE(W25Q_CMD_WRITE_ENABLE))
|
||||
|
||||
#define CMD_READ_STATUS \
|
||||
(SPIFI_CMD_DATALEN(1) | SPIFI_CMD_POLL | \
|
||||
SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_ONLY) | \
|
||||
SPIFI_CMD_OPCODE(W25Q_CMD_READ_STATUS1))
|
||||
|
||||
#define CMD_SECTOR_ERASE \
|
||||
(SPIFI_CMD_DOUT | SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \
|
||||
SPIFI_CMD_OPCODE(W25Q_CMD_SECTOR_ERASE))
|
||||
|
||||
#define CMD_PAGE_PROGRAM \
|
||||
(SPIFI_CMD_DATALEN(FLASH_PAGE_SIZE) | SPIFI_CMD_DOUT | \
|
||||
SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \
|
||||
SPIFI_CMD_OPCODE(W25Q_CMD_PAGE_PROGRAM))
|
||||
|
||||
/* Memory-mode command: quad output fast read with 1 intermediate (dummy) byte */
|
||||
#define MCMD_READ_QUAD \
|
||||
(SPIFI_CMD_INTLEN(1) | SPIFI_CMD_FIELDFORM(FIELDFORM_DATA_QUAD) | \
|
||||
SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \
|
||||
SPIFI_CMD_OPCODE(W25Q_CMD_FAST_READ_QUAD))
|
||||
|
||||
#ifdef NVM_FLASH_WRITEONCE
|
||||
# error "wolfBoot LPC54S018M HAL: WRITEONCE not supported on SPIFI flash."
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Boot-time initialization (runs from flash / XIP) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#ifdef __WOLFBOOT
|
||||
|
||||
/* Assert hook (in case any remaining SDK code uses assert) */
|
||||
void __assert_func(const char *a, int b, const char *c, const char *d)
|
||||
{
|
||||
(void)a; (void)b; (void)c; (void)d;
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
void hal_init(void)
|
||||
{
|
||||
/* The boot ROM has already configured basic clocks and SPIFI for XIP.
|
||||
* We must NOT reconfigure clocks or SPIFI from flash (XIP) because
|
||||
* changing the clock source or SPIFI controller while executing from
|
||||
* SPIFI flash will cause an instruction fetch fault.
|
||||
*
|
||||
* Clock and SPIFI reconfiguration can only be done from RAM functions.
|
||||
* The flash erase/write paths (all RAMFUNCTION) handle SPIFI mode
|
||||
* switching as needed.
|
||||
*/
|
||||
}
|
||||
|
||||
void hal_prepare_boot(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* __WOLFBOOT */
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* SPIFI flash helper functions — all MUST run from RAM */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Issue a SPIFI command. Exits memory mode if active, waits for ready,
|
||||
* then writes CMD register. Entirely register-based — no SDK calls.
|
||||
*/
|
||||
static void RAMFUNCTION spifi_set_cmd(uint32_t cmd_val)
|
||||
{
|
||||
/* If in memory mode (MCINIT set), reset to exit */
|
||||
if (SPIFI_STAT & SPIFI_STAT_MCINIT) {
|
||||
SPIFI_STAT = SPIFI_STAT_RESET;
|
||||
while (SPIFI_STAT & SPIFI_STAT_RESET)
|
||||
;
|
||||
}
|
||||
|
||||
/* Wait for any active command to complete */
|
||||
while (SPIFI_STAT & SPIFI_STAT_CMD)
|
||||
;
|
||||
|
||||
SPIFI_CMD = cmd_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enter memory-mapped (XIP) mode using quad output fast read.
|
||||
*/
|
||||
static void RAMFUNCTION spifi_enter_memmode(void)
|
||||
{
|
||||
/* Wait for any active command */
|
||||
while (SPIFI_STAT & SPIFI_STAT_CMD)
|
||||
;
|
||||
|
||||
SPIFI_MCMD = MCMD_READ_QUAD;
|
||||
|
||||
/* Wait for memory mode to initialize */
|
||||
while (!(SPIFI_STAT & SPIFI_STAT_MCINIT))
|
||||
;
|
||||
}
|
||||
|
||||
static void RAMFUNCTION spifi_write_enable(void)
|
||||
{
|
||||
spifi_set_cmd(CMD_WRITE_ENABLE);
|
||||
}
|
||||
|
||||
static void RAMFUNCTION spifi_wait_busy(void)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* Issue read-status command in poll mode */
|
||||
spifi_set_cmd(CMD_READ_STATUS);
|
||||
do {
|
||||
status = *(volatile uint8_t *)&SPIFI_DATA;
|
||||
} while (status & W25Q_STATUS_BUSY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flash write — 256-byte page program via SPIFI
|
||||
*
|
||||
* Handles unaligned writes by decomposing into page-aligned operations.
|
||||
* All flash data goes through a RAM page cache to ensure proper alignment.
|
||||
*/
|
||||
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
||||
{
|
||||
int idx = 0;
|
||||
uint32_t page_address;
|
||||
uint32_t offset;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
while (idx < len) {
|
||||
page_address = ((address + idx) / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE;
|
||||
if ((address + idx) > page_address)
|
||||
offset = (address + idx) - page_address;
|
||||
else
|
||||
offset = 0;
|
||||
size = FLASH_PAGE_SIZE - offset;
|
||||
if (size > (len - idx))
|
||||
size = len - idx;
|
||||
if (size > 0) {
|
||||
/* Read current page content (flash is memory-mapped) */
|
||||
memcpy(flash_page_cache, (void *)(uintptr_t)page_address,
|
||||
FLASH_PAGE_SIZE);
|
||||
memcpy(flash_page_cache + offset, data + idx, size);
|
||||
|
||||
/* Write enable */
|
||||
spifi_write_enable();
|
||||
|
||||
/* Set address and issue page program command */
|
||||
SPIFI_ADDR = page_address - SPIFI_FLASH_BASE;
|
||||
spifi_set_cmd(CMD_PAGE_PROGRAM);
|
||||
|
||||
/* Write page data as 32-bit words */
|
||||
for (i = 0; i < FLASH_PAGE_SIZE; i += 4) {
|
||||
uint32_t word;
|
||||
memcpy(&word, &flash_page_cache[i], 4);
|
||||
SPIFI_DATA = word;
|
||||
}
|
||||
|
||||
/* Wait for program to complete */
|
||||
spifi_wait_busy();
|
||||
|
||||
/* Re-enter memory mode */
|
||||
spifi_enter_memmode();
|
||||
}
|
||||
idx += size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Flash erase — 4KB sector erase via SPIFI
|
||||
*
|
||||
* Address must be aligned to WOLFBOOT_SECTOR_SIZE (4KB).
|
||||
* Length must be a multiple of WOLFBOOT_SECTOR_SIZE.
|
||||
*/
|
||||
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
||||
{
|
||||
uint32_t end = address + len;
|
||||
|
||||
while (address < end) {
|
||||
/* Write enable before each sector erase */
|
||||
spifi_write_enable();
|
||||
|
||||
/* Set address and issue sector erase command */
|
||||
SPIFI_ADDR = address - SPIFI_FLASH_BASE;
|
||||
spifi_set_cmd(CMD_SECTOR_ERASE);
|
||||
|
||||
/* Wait for erase to complete */
|
||||
spifi_wait_busy();
|
||||
|
||||
address += WOLFBOOT_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Re-enter memory mode */
|
||||
spifi_enter_memmode();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x10000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 160K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
KEEP(*(.isr_vector))
|
||||
. = ALIGN(0x400);
|
||||
*(.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);
|
||||
KEEP(*(.ramcode))
|
||||
. = 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);
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
|
||||
}
|
||||
|
||||
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 = _end);
|
||||
PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM));
|
||||
|
|
@ -699,6 +699,16 @@ ifeq ($(TARGET),lpc55s69)
|
|||
LDFLAGS+=-Wl,--no-warn-rwx-segments
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),nxp_lpc54s018m)
|
||||
LSCRIPT_TEMPLATE=ARM-nxp_lpc54s018m.ld
|
||||
# Enable RAMFUNCTION for test app (flash ops must run from RAM)
|
||||
CFLAGS+=-DRAM_CODE -D__WOLFBOOT
|
||||
ifeq (,$(findstring nosys.specs,$(LDFLAGS)))
|
||||
LDFLAGS+=--specs=nosys.specs
|
||||
endif
|
||||
LDFLAGS+=-Wl,--no-warn-rwx-segments
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),imx_rt)
|
||||
LDFLAGS+=\
|
||||
-mcpu=cortex-m7 -Wall --specs=nosys.specs -fno-common -ffunction-sections -fdata-sections \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
/* app_nxp_lpc54s018m.c
|
||||
*
|
||||
* Test application for LPC54S018M-EVK
|
||||
*
|
||||
* 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 "target.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
|
||||
/* LPC54S018M-EVK GPIO register definitions */
|
||||
/* GPIO base: 0x4008C000 */
|
||||
#define GPIO_BASE 0x4008C000
|
||||
#define GPIO_DIR(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2000 + (port) * 4))
|
||||
#define GPIO_SET(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2200 + (port) * 4))
|
||||
#define GPIO_CLR(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2280 + (port) * 4))
|
||||
|
||||
/* SYSCON base for clock gating */
|
||||
#define SYSCON_BASE 0x40000000
|
||||
#define SYSCON_AHBCLKCTRL0 (*(volatile uint32_t *)(SYSCON_BASE + 0x200))
|
||||
|
||||
/* AHB clock control bits for GPIO ports (AHBCLKCTRL[0]) */
|
||||
#define AHBCLKCTRL0_GPIO2 (1UL << 16)
|
||||
#define AHBCLKCTRL0_GPIO3 (1UL << 17)
|
||||
|
||||
/* LPC54S018M-EVK User LEDs (accent LEDs active low) */
|
||||
#define LED1_PORT 3
|
||||
#define LED1_PIN 14 /* USR_LED1: P3.14 */
|
||||
#define LED2_PORT 3
|
||||
#define LED2_PIN 3 /* USR_LED2: P3.3 */
|
||||
#define LED3_PORT 2
|
||||
#define LED3_PIN 2 /* USR_LED3: P2.2 */
|
||||
|
||||
static void leds_init(void)
|
||||
{
|
||||
/* Enable GPIO port 2 and 3 clocks */
|
||||
SYSCON_AHBCLKCTRL0 |= AHBCLKCTRL0_GPIO2 | AHBCLKCTRL0_GPIO3;
|
||||
|
||||
/* Set LED pins as output, initially off (high = off for active-low LEDs) */
|
||||
GPIO_SET(LED1_PORT) = (1UL << LED1_PIN);
|
||||
GPIO_DIR(LED1_PORT) |= (1UL << LED1_PIN);
|
||||
|
||||
GPIO_SET(LED2_PORT) = (1UL << LED2_PIN);
|
||||
GPIO_DIR(LED2_PORT) |= (1UL << LED2_PIN);
|
||||
|
||||
GPIO_SET(LED3_PORT) = (1UL << LED3_PIN);
|
||||
GPIO_DIR(LED3_PORT) |= (1UL << LED3_PIN);
|
||||
}
|
||||
|
||||
static void led_on(int port, int pin)
|
||||
{
|
||||
GPIO_CLR(port) = (1UL << pin); /* Active low */
|
||||
}
|
||||
|
||||
static void led_off(int port, int pin)
|
||||
{
|
||||
GPIO_SET(port) = (1UL << pin);
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
uint32_t boot_ver, update_ver;
|
||||
uint8_t boot_state, update_state;
|
||||
|
||||
/* Clock and SPIFI already initialized by wolfBoot before jump to app */
|
||||
leds_init();
|
||||
|
||||
boot_ver = wolfBoot_current_firmware_version();
|
||||
update_ver = wolfBoot_update_firmware_version();
|
||||
if (wolfBoot_get_partition_state(PART_BOOT, &boot_state) != 0)
|
||||
boot_state = IMG_STATE_NEW;
|
||||
if (wolfBoot_get_partition_state(PART_UPDATE, &update_state) != 0)
|
||||
update_state = IMG_STATE_NEW;
|
||||
|
||||
/* Show LED indicator first (before flash writes which may crash if
|
||||
* RAMFUNCTION isn't working — e.g. SPIFI XIP conflict) */
|
||||
if (boot_ver == 1) {
|
||||
led_on(LED1_PORT, LED1_PIN);
|
||||
}
|
||||
else {
|
||||
led_on(LED2_PORT, LED2_PIN);
|
||||
}
|
||||
|
||||
/* Confirm boot if state is TESTING or NEW */
|
||||
if (boot_ver != 0 &&
|
||||
(boot_state == IMG_STATE_TESTING || boot_state == IMG_STATE_NEW))
|
||||
{
|
||||
wolfBoot_success();
|
||||
}
|
||||
|
||||
if (boot_ver == 1 && update_ver != 0) {
|
||||
/* Update available: LED3 on to indicate update activity */
|
||||
led_on(LED3_PORT, LED3_PIN);
|
||||
wolfBoot_update_trigger();
|
||||
}
|
||||
|
||||
while (1) {
|
||||
__asm__ volatile ("wfi");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#include "sys/stat.h"
|
||||
int _getpid(void) { return 1; }
|
||||
int _kill(int pid, int sig) { (void)pid; (void)sig; return -1; }
|
||||
void _exit(int status) { _kill(status, -1); while (1) {} }
|
||||
int _read(int file, char *ptr, int len)
|
||||
{ (void)file; (void)ptr; (void)len; return -1; }
|
||||
int _write(int file, char *ptr, int len)
|
||||
{ (void)file; (void)ptr; return len; }
|
||||
int _close(int file) { (void)file; return -1; }
|
||||
int _isatty(int file) { (void)file; return 1; }
|
||||
int _lseek(int file, int ptr, int dir)
|
||||
{ (void)file; (void)ptr; (void)dir; return 0; }
|
||||
int _fstat(int file, struct stat *st)
|
||||
{ (void)file; st->st_mode = S_IFCHR; return 0; }
|
||||
Loading…
Reference in New Issue