Add wolfBoot support for NXP LPC54S018M-EVK

pull/759/head
David Garske 2026-03-20 14:36:53 -07:00 committed by Daniele Lacamera
parent 71b395d292
commit 91145d53bb
9 changed files with 777 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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));

View File

@ -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 \

View File

@ -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; }