Skip to content

Commit 943d4fd

Browse files
aidangarskedanielinux
authored andcommitted
Fix NXP LPC54S018M SPIFI flash operations and complete boot+update cycle
The LPC54S018M HAL had three critical SPIFI controller issues preventing flash write/erase operations from working, which blocked wolfBoot_success(), wolfBoot_update_trigger(), and the firmware swap: 1. Wrong memory-mode read command: Used opcode 0x6B (Quad Output, serial address) but the boot ROM configures 0xEB (Quad I/O, quad address) with MCMD=0xEB930000. The mismatch caused garbled address bits when re-entering XIP after flash operations, crashing on instruction fetch. 2. SPIFI POLL mode not waiting: The boot ROM leaves CLIMIT[7:0]=0x00 which makes the hardware POLL comparison always succeed immediately. Set IDATA=0x00 and CLIMIT[7:0]=0x01 to properly wait for flash BUSY to clear. 3. SPIFI reset clears CTRL/CLIMIT: The reset used to exit memory mode clears the boot ROM's timing config (CTRL=0x600F03E8) and cache limit (CLIMIT=0x08000000). Save and restore both registers around every reset. Additional changes: - Add RAMFUNCTION memcpy (src/string.o) to test app for SPIFI XIP safety - Add bare-metal UART driver for Flexcomm0 (non-blocking, skips if FC0 doesn't respond — observed on some LPC54S018M-EVK boards) - Add DSB+ISB barriers after entering memory mode for pipeline coherency - Enable DEBUG_UART in example config - Update test app to follow LPC55S69 port patterns with LED indicators for boot version and update status Tested: cold boot, ECC256 signature verify, wolfBoot_success(), wolfBoot_update_trigger(), and full v1->v2 firmware swap all working. Swap takes ~60 seconds for the 960KB partition (240 sector operations).
1 parent 91145d5 commit 943d4fd

4 files changed

Lines changed: 291 additions & 23 deletions

File tree

config/examples/nxp_lpc54s018m.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ TARGET?=nxp_lpc54s018m
33
SIGN?=ECC256
44
HASH?=SHA256
55
DEBUG?=0
6-
#DEBUG_UART?=1
6+
DEBUG_UART?=1
77
VTOR?=1
88
NO_ASM?=0
99
EXT_FLASH?=0

hal/nxp_lpc54s018m.c

Lines changed: 229 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
#define W25Q_CMD_READ_STATUS1 0x05
7272
#define W25Q_CMD_PAGE_PROGRAM 0x02
7373
#define W25Q_CMD_SECTOR_ERASE 0x20 /* 4KB sector erase */
74-
#define W25Q_CMD_FAST_READ_QUAD 0x6B /* Quad output fast read */
74+
#define W25Q_CMD_FAST_READ_QUAD_IO 0xEB /* Quad I/O fast read */
7575

7676
/* W25Q status register bits */
7777
#define W25Q_STATUS_BUSY 0x01
@@ -101,16 +101,167 @@ static uint8_t flash_page_cache[FLASH_PAGE_SIZE];
101101
SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \
102102
SPIFI_CMD_OPCODE(W25Q_CMD_PAGE_PROGRAM))
103103

104-
/* Memory-mode command: quad output fast read with 1 intermediate (dummy) byte */
104+
/* Memory-mode command: Quad I/O fast read (0xEB) — must match boot ROM config.
105+
* Boot ROM MCMD = 0xEB930000:
106+
* opcode 0xEB, FRAMEFORM=4 (opcode+3addr), FIELDFORM=2 (addr+data quad),
107+
* INTLEN=3 (3 intermediate/dummy bytes in quad mode) */
105108
#define MCMD_READ_QUAD \
106-
(SPIFI_CMD_INTLEN(1) | SPIFI_CMD_FIELDFORM(FIELDFORM_DATA_QUAD) | \
109+
(SPIFI_CMD_INTLEN(3) | SPIFI_CMD_FIELDFORM(FIELDFORM_DATA_QUAD) | \
107110
SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \
108-
SPIFI_CMD_OPCODE(W25Q_CMD_FAST_READ_QUAD))
111+
SPIFI_CMD_OPCODE(W25Q_CMD_FAST_READ_QUAD_IO))
109112

110113
#ifdef NVM_FLASH_WRITEONCE
111114
# error "wolfBoot LPC54S018M HAL: WRITEONCE not supported on SPIFI flash."
112115
#endif
113116

117+
/* -------------------------------------------------------------------------- */
118+
/* UART via Flexcomm0 (bare-metal, no SDK) */
119+
/* -------------------------------------------------------------------------- */
120+
#ifdef DEBUG_UART
121+
122+
/* SYSCON registers for clock gating and peripheral reset */
123+
#define SYSCON_BASE 0x40000000
124+
#define SYSCON_AHBCLKCTRL0 (*(volatile uint32_t *)(SYSCON_BASE + 0x200))
125+
#define SYSCON_AHBCLKCTRL1 (*(volatile uint32_t *)(SYSCON_BASE + 0x204))
126+
#define SYSCON_PRESETCTRL1 (*(volatile uint32_t *)(SYSCON_BASE + 0x104))
127+
#define SYSCON_FCLKSEL0 (*(volatile uint32_t *)(SYSCON_BASE + 0x2B0))
128+
129+
#define AHBCLKCTRL0_IOCON (1UL << 13)
130+
#define AHBCLKCTRL1_FC0 (1UL << 11)
131+
#define PRESETCTRL1_FC0 (1UL << 11)
132+
133+
/* IOCON pin mux registers */
134+
#define IOCON_BASE 0x40001000
135+
#define IOCON_PIO0_29 (*(volatile uint32_t *)(IOCON_BASE + 0x074))
136+
#define IOCON_PIO0_30 (*(volatile uint32_t *)(IOCON_BASE + 0x078))
137+
#define IOCON_FUNC1 1U
138+
#define IOCON_DIGITAL_EN (1U << 8)
139+
140+
/* Flexcomm0 USART registers */
141+
#define FC0_BASE 0x40086000
142+
#define FC0_CFG (*(volatile uint32_t *)(FC0_BASE + 0x000))
143+
#define FC0_BRG (*(volatile uint32_t *)(FC0_BASE + 0x020))
144+
#define FC0_OSR (*(volatile uint32_t *)(FC0_BASE + 0x028))
145+
#define FC0_FIFOCFG (*(volatile uint32_t *)(FC0_BASE + 0xE00))
146+
#define FC0_FIFOSTAT (*(volatile uint32_t *)(FC0_BASE + 0xE04))
147+
#define FC0_FIFOWR (*(volatile uint32_t *)(FC0_BASE + 0xE20))
148+
#define FC0_PSELID (*(volatile uint32_t *)(FC0_BASE + 0xFF8))
149+
150+
/* USART CFG bits */
151+
#define USART_CFG_ENABLE (1U << 0)
152+
#define USART_CFG_DATALEN8 (1U << 2) /* 8-bit data */
153+
154+
/* FIFO bits */
155+
#define FIFOCFG_ENABLETX (1U << 0)
156+
#define FIFOCFG_ENABLERX (1U << 1)
157+
#define FIFOCFG_EMPTYTX (1U << 16)
158+
#define FIFOCFG_EMPTYRX (1U << 17)
159+
#define FIFOSTAT_TXEMPTY (1U << 3)
160+
#define FIFOSTAT_TXNOTFULL (1U << 4)
161+
162+
/* Baud rate: FRO 12 MHz / (13 * 8) = 115384 (0.16% error from 115200) */
163+
#define UART_OSR_VAL 12 /* oversampling = OSR + 1 = 13 */
164+
#define UART_BRG_VAL 7 /* divisor = BRG + 1 = 8 */
165+
166+
/* Timeout for UART FIFO polling */
167+
#define UART_TX_TIMEOUT 100000
168+
169+
/* SYSCON SET/CLR registers for atomic bit manipulation */
170+
#define SYSCON_PRESETCTRLSET1 (*(volatile uint32_t *)(SYSCON_BASE + 0x124))
171+
#define SYSCON_PRESETCTRLCLR1 (*(volatile uint32_t *)(SYSCON_BASE + 0x144))
172+
#define SYSCON_AHBCLKCTRLSET1 (*(volatile uint32_t *)(SYSCON_BASE + 0x224))
173+
174+
static int uart_ready;
175+
176+
void uart_init(void)
177+
{
178+
volatile int i;
179+
180+
uart_ready = 0;
181+
182+
/* Enable IOCON clock */
183+
SYSCON_AHBCLKCTRL0 |= AHBCLKCTRL0_IOCON;
184+
185+
/* Pin mux: P0_29 = FC0_RXD, P0_30 = FC0_TXD (function 1, digital) */
186+
IOCON_PIO0_29 = IOCON_FUNC1 | IOCON_DIGITAL_EN;
187+
IOCON_PIO0_30 = IOCON_FUNC1 | IOCON_DIGITAL_EN;
188+
189+
/* Select FRO 12 MHz as Flexcomm0 clock source */
190+
SYSCON_FCLKSEL0 = 0;
191+
192+
/* Enable Flexcomm0 clock (use atomic SET register) */
193+
SYSCON_AHBCLKCTRLSET1 = AHBCLKCTRL1_FC0;
194+
195+
/* Reset Flexcomm0 using atomic CLR/SET registers (NXP SDK pattern) */
196+
SYSCON_PRESETCTRLCLR1 = PRESETCTRL1_FC0; /* Assert reset */
197+
while (SYSCON_PRESETCTRL1 & PRESETCTRL1_FC0) /* Wait for assert */
198+
;
199+
SYSCON_PRESETCTRLSET1 = PRESETCTRL1_FC0; /* Deassert reset */
200+
while (!(SYSCON_PRESETCTRL1 & PRESETCTRL1_FC0)) /* Wait for deassert */
201+
;
202+
203+
/* Small delay after reset deassertion for peripheral to stabilize */
204+
for (i = 0; i < 100; i++)
205+
;
206+
207+
/* Select USART mode */
208+
FC0_PSELID = 1;
209+
210+
/* Verify Flexcomm0 is accessible — if PSELID reads 0, peripheral is
211+
* not responding (observed on some LPC54S018M boards). Skip UART. */
212+
if ((FC0_PSELID & 0x71) == 0) {
213+
return;
214+
}
215+
216+
/* Configure 8N1 (disabled initially) */
217+
FC0_CFG = USART_CFG_DATALEN8;
218+
219+
/* Set baud rate */
220+
FC0_OSR = UART_OSR_VAL;
221+
FC0_BRG = UART_BRG_VAL;
222+
223+
/* Enable and flush FIFOs */
224+
FC0_FIFOCFG = FIFOCFG_ENABLETX | FIFOCFG_ENABLERX |
225+
FIFOCFG_EMPTYTX | FIFOCFG_EMPTYRX;
226+
227+
/* Enable USART */
228+
FC0_CFG |= USART_CFG_ENABLE;
229+
230+
uart_ready = 1;
231+
}
232+
233+
void uart_write(const char *buf, unsigned int sz)
234+
{
235+
unsigned int i;
236+
uint32_t timeout;
237+
238+
if (!uart_ready)
239+
return;
240+
241+
for (i = 0; i < sz; i++) {
242+
if (buf[i] == '\n') {
243+
timeout = UART_TX_TIMEOUT;
244+
while (!(FC0_FIFOSTAT & FIFOSTAT_TXNOTFULL) && --timeout)
245+
;
246+
if (timeout == 0)
247+
return;
248+
FC0_FIFOWR = '\r';
249+
}
250+
timeout = UART_TX_TIMEOUT;
251+
while (!(FC0_FIFOSTAT & FIFOSTAT_TXNOTFULL) && --timeout)
252+
;
253+
if (timeout == 0)
254+
return;
255+
FC0_FIFOWR = (uint32_t)buf[i];
256+
}
257+
/* Wait for transmit to complete */
258+
timeout = UART_TX_TIMEOUT;
259+
while (!(FC0_FIFOSTAT & FIFOSTAT_TXEMPTY) && --timeout)
260+
;
261+
}
262+
263+
#endif /* DEBUG_UART */
264+
114265
/* -------------------------------------------------------------------------- */
115266
/* Boot-time initialization (runs from flash / XIP) */
116267
/* -------------------------------------------------------------------------- */
@@ -136,6 +287,10 @@ void hal_init(void)
136287
* The flash erase/write paths (all RAMFUNCTION) handle SPIFI mode
137288
* switching as needed.
138289
*/
290+
#ifdef DEBUG_UART
291+
uart_init();
292+
uart_write("wolfBoot HAL init\n", 18);
293+
#endif
139294
}
140295

141296
void hal_prepare_boot(void)
@@ -154,11 +309,17 @@ void hal_prepare_boot(void)
154309
*/
155310
static void RAMFUNCTION spifi_set_cmd(uint32_t cmd_val)
156311
{
157-
/* If in memory mode (MCINIT set), reset to exit */
312+
/* If in memory mode (MCINIT set), reset to exit.
313+
* The SPIFI reset clears CTRL and CLIMIT — save and restore
314+
* the boot ROM's configuration. */
158315
if (SPIFI_STAT & SPIFI_STAT_MCINIT) {
316+
uint32_t ctrl = SPIFI_CTRL;
317+
uint32_t climit = SPIFI_CLIMIT;
159318
SPIFI_STAT = SPIFI_STAT_RESET;
160319
while (SPIFI_STAT & SPIFI_STAT_RESET)
161320
;
321+
SPIFI_CTRL = ctrl;
322+
SPIFI_CLIMIT = climit;
162323
}
163324

164325
/* Wait for any active command to complete */
@@ -173,15 +334,29 @@ static void RAMFUNCTION spifi_set_cmd(uint32_t cmd_val)
173334
*/
174335
static void RAMFUNCTION spifi_enter_memmode(void)
175336
{
176-
/* Wait for any active command */
337+
uint32_t ctrl = SPIFI_CTRL;
338+
uint32_t climit = SPIFI_CLIMIT;
339+
340+
/* Wait for any active command to complete */
177341
while (SPIFI_STAT & SPIFI_STAT_CMD)
178342
;
179343

344+
/* Reset to clear stale command/POLL state, restore config, enter
345+
* memory mode. */
346+
SPIFI_STAT = SPIFI_STAT_RESET;
347+
while (SPIFI_STAT & SPIFI_STAT_RESET)
348+
;
349+
SPIFI_CTRL = ctrl;
350+
SPIFI_CLIMIT = climit;
351+
180352
SPIFI_MCMD = MCMD_READ_QUAD;
181353

182354
/* Wait for memory mode to initialize */
183355
while (!(SPIFI_STAT & SPIFI_STAT_MCINIT))
184356
;
357+
358+
__asm__ volatile ("dsb");
359+
__asm__ volatile ("isb");
185360
}
186361

187362
static void RAMFUNCTION spifi_write_enable(void)
@@ -191,13 +366,55 @@ static void RAMFUNCTION spifi_write_enable(void)
191366

192367
static void RAMFUNCTION spifi_wait_busy(void)
193368
{
194-
uint8_t status;
369+
/* Use SPIFI POLL mode with properly configured IDATA/CLIMIT.
370+
*
371+
* The boot ROM leaves CLIMIT[7:0]=0x00 which makes the POLL comparison
372+
* always succeed immediately. We must set CLIMIT[7:0] to mask the BUSY
373+
* bit and IDATA[7:0] to the expected value (0 = not busy).
374+
*
375+
* CLIMIT also serves as the cache limit register (upper bits), so we
376+
* preserve those bits and only modify the lower byte used for POLL mask.
377+
*/
378+
uint32_t saved_climit = SPIFI_CLIMIT;
379+
380+
SPIFI_IDATA = 0x00; /* expect BUSY=0 */
381+
SPIFI_CLIMIT = (saved_climit & 0xFFFFFF00) | W25Q_STATUS_BUSY; /* mask bit 0 */
382+
383+
spifi_set_cmd(CMD_READ_STATUS); /* POLL mode command */
384+
385+
/* SPIFI hardware polls flash status internally.
386+
* CMD bit clears when (status & mask) == (IDATA & mask). */
387+
while (SPIFI_STAT & SPIFI_STAT_CMD)
388+
;
389+
390+
SPIFI_CLIMIT = saved_climit; /* restore cache limit */
391+
}
392+
393+
/*
394+
* Minimal SPIFI mode-switch test: exit memory mode, immediately re-enter.
395+
* Used to verify the basic exit/enter cycle works before testing flash ops.
396+
*/
397+
void RAMFUNCTION spifi_test_mode_switch(void)
398+
{
399+
uint32_t ctrl = SPIFI_CTRL;
400+
uint32_t climit = SPIFI_CLIMIT;
401+
402+
/* Exit memory mode */
403+
SPIFI_STAT = SPIFI_STAT_RESET;
404+
while (SPIFI_STAT & SPIFI_STAT_RESET)
405+
;
406+
407+
/* Restore all config */
408+
SPIFI_CTRL = ctrl;
409+
SPIFI_CLIMIT = climit;
410+
411+
/* Re-enter memory mode */
412+
SPIFI_MCMD = MCMD_READ_QUAD;
413+
while (!(SPIFI_STAT & SPIFI_STAT_MCINIT))
414+
;
195415

196-
/* Issue read-status command in poll mode */
197-
spifi_set_cmd(CMD_READ_STATUS);
198-
do {
199-
status = *(volatile uint8_t *)&SPIFI_DATA;
200-
} while (status & W25Q_STATUS_BUSY);
416+
__asm__ volatile ("dsb");
417+
__asm__ volatile ("isb");
201418
}
202419

203420
/*

test-app/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,10 @@ ifeq ($(TARGET),nxp_lpc54s018m)
703703
LSCRIPT_TEMPLATE=ARM-nxp_lpc54s018m.ld
704704
# Enable RAMFUNCTION for test app (flash ops must run from RAM)
705705
CFLAGS+=-DRAM_CODE -D__WOLFBOOT
706+
# SPIFI XIP: memcpy/memset must be in RAM for use during flash operations
707+
ifeq ($(DEBUG_UART),)
708+
APP_OBJS+=../src/string.o
709+
endif
706710
ifeq (,$(findstring nosys.specs,$(LDFLAGS)))
707711
LDFLAGS+=--specs=nosys.specs
708712
endif

0 commit comments

Comments
 (0)