Skip to content

Commit 63adfa7

Browse files
committed
Add flashing tool support (enabled with UART_QSPI_PROGRAM=1)
1 parent edc8708 commit 63adfa7

File tree

10 files changed

+241
-21
lines changed

10 files changed

+241
-21
lines changed

.gdbinit

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
file wolfboot.elf
22
tar rem:3333
3-
add-symbol-file test-app/image.elf
3+
add-symbol-file ../hart-software-services/build/hss-l2scratch.elf
44
set pagination off
55
foc c
66

7-
7+
set $target_riscv=1
8+
set mem inaccessible-by-default off
9+
set architecture riscv:rv64

arch.mk

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,13 @@ ifeq ($(DEBUG_UART),1)
16371637
endif
16381638
endif
16391639

1640+
# UART QSPI programmer (PolarFire SoC MPFS): receive binary over UART and
1641+
# write it directly to QSPI flash. Requires EXT_FLASH=1 and DEBUG_UART=1.
1642+
# Use tools/scripts/mpfs_qspi_prog.py on the host side.
1643+
ifeq ($(UART_QSPI_PROGRAM),1)
1644+
CFLAGS+=-DUART_QSPI_PROGRAM
1645+
endif
1646+
16401647
ifeq ($(NXP_CUSTOM_DCD),1)
16411648
CFLAGS+=-DNXP_CUSTOM_DCD
16421649
OBJS+=$(NXP_CUSTOM_DCD_OBJS)

config/examples/polarfire_mpfs250_m_qspi.config

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,10 @@ CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096
100100
# Optional QSPI flash test (erase/write/read on update partition)
101101
# Uncomment to run test during hal_init()
102102
#CFLAGS_EXTRA+=-DTEST_EXT_FLASH
103+
104+
# UART QSPI programmer (disabled by default)
105+
# When enabled, wolfBoot prompts on UART at startup to receive a signed firmware
106+
# image and write it to QSPI flash -- no Libero/JTAG tool required for updates.
107+
# Use: python3 tools/scripts/mpfs_qspi_prog.py <port> <image.bin> [qspi_offset]
108+
# Requires EXT_FLASH=1 (already set) and DEBUG_UART=1.
109+
UART_QSPI_PROGRAM?=0

docs/Targets.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,70 @@ Notes:
904904
- The MSS QSPI path expects external flash on the MSS QSPI pins; the SC QSPI path is for
905905
fabric-connected flash (design flash) accessed via the System Controller's QSPI instance.
906906
907+
### PolarFire SoC M-Mode (bare-metal eNVM boot)
908+
909+
In M-Mode wolfBoot runs directly on the E51 monitor core from eNVM — no HSS required. The signed
910+
application is loaded from SC QSPI flash into LIM (on-chip RAM). This is the simplest bring-up path.
911+
912+
**Boot flow:**
913+
1. CPU starts at eNVM reset vector (`0x20220100`)
914+
2. Startup code copies wolfBoot to L2 Scratchpad (`0x0A000000`) and jumps there
915+
3. wolfBoot reads the signed image from QSPI flash into LIM (`0x08000000`)
916+
4. Signature is verified, then execution jumps to the application
917+
918+
**Build:**
919+
```sh
920+
cp config/examples/polarfire_mpfs250_m_qspi.config .config
921+
make clean && make wolfboot.elf
922+
```
923+
924+
**Flash wolfBoot to eNVM** (requires SmartDesign / SmartFusion2 Libero SoC install):
925+
```sh
926+
java -jar $SC_INSTALL_DIR/extras/mpfs/mpfsBootmodeProgrammer.jar \
927+
--bootmode 1 --die MPFS250T --package FCG1152 --workdir $PWD wolfboot.elf
928+
```
929+
930+
**Build and sign the test application:**
931+
```sh
932+
make test-app/image_v1_signed.bin
933+
```
934+
935+
**Flash the signed application to QSPI** using the UART programmer (requires `EXT_FLASH=1` and
936+
`UART_QSPI_PROGRAM=1` in `.config`, and `pyserial` installed):
937+
```sh
938+
python3 tools/scripts/mpfs_qspi_prog.py /dev/ttyUSB1 \
939+
test-app/image_v1_signed.bin 0x20000
940+
```
941+
942+
The script:
943+
1. Waits for wolfBoot to print the `QSPI-PROG: Press 'P'` prompt (power-cycle the board)
944+
2. Sends `P` to enter programming mode
945+
3. Transfers the binary in 256-byte ACK-driven chunks
946+
4. wolfBoot erases, writes, and then continues booting the new image
947+
948+
Use `0x20000` for the boot partition and `0x2000000` for the update partition.
949+
950+
**Expected serial output on successful boot:**
951+
```
952+
wolfBoot Version: 2.7.0 (...)
953+
Running on E51 (hart 0) in M-mode
954+
QSPI: Using SC QSPI Controller (0x37020100)
955+
QSPI: Flash ID = 0x20 0xBA 0x21
956+
QSPI-PROG: Press 'P' within 3s to program flash
957+
QSPI-PROG: No trigger (got 0x00 ...), booting
958+
Versions: Boot 1, Update 0
959+
...
960+
Firmware Valid
961+
Booting at 0x...
962+
```
963+
964+
**Notes:**
965+
- The E51 is `rv64imac`; the `rdtime` CSR instruction is not available in bare-metal M-mode.
966+
wolfBoot uses a calibrated busy-loop for all delays (`udelay()` in `hal/mpfs250.c`).
967+
- `UART_QSPI_PROGRAM=1` adds a 3-second boot pause every time. Set to `0` once the flash
968+
contents are stable.
969+
- The config uses `WOLFBOOT_LOAD_ADDRESS=0x08000200` to keep the image header within LIM.
970+
907971
### PolarFire testing
908972
909973
This section describes how to build the test-application, create a custom uSD with required partitions and copying signed test-application to uSD partitions.

hal/mpfs250.c

Lines changed: 132 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ void secondary_hart_entry(unsigned long hartid, HLS_DATA* hls)
246246
#if defined(EXT_FLASH) && defined(TEST_EXT_FLASH) && defined(__WOLFBOOT)
247247
static int test_ext_flash(void);
248248
#endif
249+
#if defined(EXT_FLASH) && defined(UART_QSPI_PROGRAM) && defined(__WOLFBOOT)
250+
static void qspi_uart_program(void);
251+
#endif
249252

250253
void hal_init(void)
251254
{
@@ -279,12 +282,14 @@ void hal_init(void)
279282
#ifdef EXT_FLASH
280283
if (qspi_init() != 0) {
281284
wolfBoot_printf("QSPI: Init failed\n");
282-
}
285+
} else {
283286
#if defined(TEST_EXT_FLASH) && defined(__WOLFBOOT)
284-
else {
285287
test_ext_flash();
286-
}
287288
#endif
289+
#if defined(UART_QSPI_PROGRAM) && defined(__WOLFBOOT)
290+
qspi_uart_program();
291+
#endif
292+
}
288293
#endif /* EXT_FLASH */
289294
}
290295

@@ -998,6 +1003,130 @@ int ext_flash_erase(uintptr_t address, int len)
9981003
return total;
9991004
}
10001005

1006+
/* ============================================================================
1007+
* UART QSPI Programmer
1008+
*
1009+
* Allows programming the QSPI flash over the debug UART without a JTAG/Libero
1010+
* tool. Enabled at build time with UART_QSPI_PROGRAM=1 in the .config.
1011+
*
1012+
* Protocol (after wolfBoot prints the "QSPI-PROG" prompt):
1013+
* 1. Host sends 'P' within the timeout window to enter programming mode
1014+
* 2. wolfBoot sends "READY\r\n"
1015+
* 3. Host sends [4-byte LE QSPI address][4-byte LE data length]
1016+
* 4. wolfBoot erases required sectors, sends "ERASED\r\n"
1017+
* 5. For each 256-byte chunk:
1018+
* wolfBoot sends ACK byte (0x06) -> host sends chunk -> wolfBoot writes
1019+
* 6. wolfBoot sends "DONE\r\n" and continues normal boot
1020+
*
1021+
* Host side: tools/scripts/mpfs_qspi_prog.py
1022+
* ============================================================================ */
1023+
#if defined(UART_QSPI_PROGRAM) && defined(__WOLFBOOT)
1024+
1025+
#define QSPI_PROG_CHUNK 256
1026+
#define QSPI_PROG_ACK 0x06
1027+
1028+
static uint8_t uart_qspi_rx(void)
1029+
{
1030+
while (!(MMUART_LSR(DEBUG_UART_BASE) & MSS_UART_DR))
1031+
;
1032+
return MMUART_RBR(DEBUG_UART_BASE);
1033+
}
1034+
1035+
static void uart_qspi_tx(uint8_t c)
1036+
{
1037+
while (!(MMUART_LSR(DEBUG_UART_BASE) & MSS_UART_THRE))
1038+
;
1039+
MMUART_THR(DEBUG_UART_BASE) = c;
1040+
}
1041+
1042+
static void uart_qspi_puts(const char *s)
1043+
{
1044+
while (*s)
1045+
uart_qspi_tx((uint8_t)*s++);
1046+
}
1047+
1048+
static void qspi_uart_program(void)
1049+
{
1050+
uint8_t ch = 0;
1051+
uint32_t addr, size, n_sectors, written, t;
1052+
uint8_t chunk[QSPI_PROG_CHUNK];
1053+
1054+
wolfBoot_printf("QSPI-PROG: Press 'P' within 3s to program flash\r\n");
1055+
1056+
/* Drain any stale RX bytes before opening the window */
1057+
while (MMUART_LSR(DEBUG_UART_BASE) & MSS_UART_DR)
1058+
(void)MMUART_RBR(DEBUG_UART_BASE);
1059+
1060+
/* Wait up to 3s: 3000 iterations of 1ms each */
1061+
for (t = 0; t < 3000U; t++) {
1062+
udelay(1000);
1063+
if (MMUART_LSR(DEBUG_UART_BASE) & MSS_UART_DR) {
1064+
ch = MMUART_RBR(DEBUG_UART_BASE);
1065+
break;
1066+
}
1067+
}
1068+
1069+
if (ch != 'P' && ch != 'p') {
1070+
wolfBoot_printf("QSPI-PROG: No trigger (got 0x%02x LSR=0x%02x), booting\r\n",
1071+
(unsigned)ch,
1072+
(unsigned)MMUART_LSR(DEBUG_UART_BASE));
1073+
return;
1074+
}
1075+
1076+
wolfBoot_printf("QSPI-PROG: Entering programmer mode\r\n");
1077+
uart_qspi_puts("READY\r\n");
1078+
1079+
/* Receive destination address then data length (4 bytes LE each) */
1080+
addr = 0;
1081+
for (int i = 0; i < 4; i++)
1082+
addr |= ((uint32_t)uart_qspi_rx() << (i * 8));
1083+
size = 0;
1084+
for (int i = 0; i < 4; i++)
1085+
size |= ((uint32_t)uart_qspi_rx() << (i * 8));
1086+
1087+
wolfBoot_printf("QSPI-PROG: addr=0x%x size=%u bytes\r\n", addr, size);
1088+
1089+
if (size == 0 || size > 0x200000U) {
1090+
wolfBoot_printf("QSPI-PROG: Invalid size, aborting\r\n");
1091+
return;
1092+
}
1093+
1094+
/* Erase all required sectors (FLASH_SECTOR_SIZE = 64 KB) */
1095+
n_sectors = (size + FLASH_SECTOR_SIZE - 1) / FLASH_SECTOR_SIZE;
1096+
wolfBoot_printf("QSPI-PROG: Erasing %u sector(s) at 0x%x...\r\n",
1097+
n_sectors, addr);
1098+
ext_flash_unlock();
1099+
for (uint32_t s = 0; s < n_sectors; s++)
1100+
ext_flash_erase(addr + s * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE);
1101+
1102+
/* "ERASED\r\n" must be the last bytes before the first ACK (0x06).
1103+
* Do not insert any wolfBoot_printf between here and the transfer loop. */
1104+
uart_qspi_puts("ERASED\r\n");
1105+
1106+
/* Chunk transfer: wolfBoot requests each 256-byte block with ACK 0x06 */
1107+
written = 0;
1108+
while (written < size) {
1109+
uint32_t chunk_len = size - written;
1110+
if (chunk_len > QSPI_PROG_CHUNK)
1111+
chunk_len = QSPI_PROG_CHUNK;
1112+
1113+
uart_qspi_tx(QSPI_PROG_ACK); /* request next chunk */
1114+
1115+
for (uint32_t i = 0; i < chunk_len; i++)
1116+
chunk[i] = uart_qspi_rx();
1117+
1118+
ext_flash_write(addr + written, chunk, (int)chunk_len);
1119+
written += chunk_len;
1120+
}
1121+
ext_flash_lock();
1122+
1123+
wolfBoot_printf("QSPI-PROG: Wrote %u bytes to 0x%x\r\n", written, addr);
1124+
uart_qspi_puts("DONE\r\n");
1125+
wolfBoot_printf("QSPI-PROG: Done, continuing boot\r\n");
1126+
}
1127+
1128+
#endif /* UART_QSPI_PROGRAM */
1129+
10011130
/* Test for external QSPI flash erase/write/read */
10021131
#ifdef TEST_EXT_FLASH
10031132

hal/mpfs250.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ static const unsigned long MSS_UART_BASE_ADDR[] = {
151151
#define MSS_UART_ONE_STOP_BIT ((uint8_t)0x00)
152152

153153
/* LSR (Line Status Register) */
154+
#define MSS_UART_DR ((uint8_t)0x01) /* Data ready (receive buffer full) */
154155
#define MSS_UART_THRE ((uint8_t)0x20) /* Transmitter holding register empty */
155156
#define MSS_UART_TEMT ((uint8_t)0x40) /* Transmit empty */
156157

src/boot_riscv.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,10 +463,19 @@ void do_boot(const uint32_t *app_offset)
463463
* Use this for test-apps; define WOLFBOOT_MMODE_SMODE_BOOT for Linux.
464464
*/
465465
wolfBoot_printf("M-mode direct jump to 0x%lx\n", (unsigned long)app_offset);
466-
/* Drain UART before jumping */
466+
/* Diagnostics before jump */
467+
{
468+
volatile uint8_t lsr = MMUART_LSR(DEBUG_UART_BASE);
469+
uint32_t *p = (uint32_t*)app_offset;
470+
wolfBoot_printf("Pre-jump: LSR=0x%x THRE=%d\n",
471+
(unsigned)lsr, (lsr & MSS_UART_THRE) ? 1 : 0);
472+
wolfBoot_printf("App[0]=0x%lx [1]=0x%lx\n",
473+
(unsigned long)p[0], (unsigned long)p[1]);
474+
}
475+
/* Extended drain: allow all diagnostic output to finish (~10ms at 40MHz) */
467476
{
468477
volatile int i;
469-
for (i = 0; i < 100000; i++) {}
478+
for (i = 0; i < 400000; i++) {}
470479
}
471480
(void)hartid;
472481
(void)dts_addr;

test-app/app_mpfs250.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@
3636

3737
void main(void)
3838
{
39-
/* wolfBoot already initialized L2 cache, UART clocks, and QSPI.
40-
* Just re-init UART registers (clocks already on). Do NOT call
41-
* hal_init() - it would try to write to _main_hart_hls=0 (NULL). */
42-
uart_init();
39+
/* wolfBoot fully configured UART0 before jumping here.
40+
* Calling uart_init() again clears the TX FIFO (FCR write) while wolfBoot's
41+
* last output may still be draining, which can leave THRE stuck at 0.
42+
* Calling hal_init() writes to _main_hart_hls=0 (NULL ptr crash).
43+
* So use wolfBoot_printf directly — UART0 is already ready. */
4344

4445
wolfBoot_printf("========================\r\n");
4546
wolfBoot_printf("PolarFire SoC MPFS250 wolfBoot demo Application\r\n");

test-app/startup_riscv.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ void __attribute__((naked,section(".init"))) _reset(void) {
4242
asm volatile("la gp, _global_pointer");
4343
asm volatile("la sp, _end_stack");
4444

45-
/* Set up vectored interrupt, with IV starting at offset 0x100.
46-
* Always use stvec: wolfBoot M-mode uses enter_smode() to transition
47-
* the payload to S-mode, so stvec is correct in all cases. */
48-
asm volatile("csrw stvec, %0":: "r"((uint8_t *)(&_start_vector) + 1));
45+
/* Set up M-mode vectored interrupt table.
46+
* wolfBoot M-mode does a direct jr (no enter_smode), so payload runs in M-mode.
47+
* Use mtvec. The +1 sets MODE=1 (vectored). */
48+
asm volatile("csrw mtvec, %0":: "r"((uint8_t *)(&_start_vector) + 1));
4949

5050
src = (uint32_t *) &_stored_data;
5151
dst = (uint32_t *) &_start_data;
@@ -77,8 +77,8 @@ void do_boot(const uint32_t *app_offset)
7777
static uint32_t synctrap_cause = 0;
7878
void __attribute__((naked)) isr_synctrap(void)
7979
{
80-
/* Always use scause: payload always runs in S-mode under wolfBoot */
81-
asm volatile("csrr %0, scause" : "=r"(synctrap_cause));
80+
/* Use mcause: payload runs in M-mode (wolfBoot does M-mode direct jump) */
81+
asm volatile("csrr %0, mcause" : "=r"(synctrap_cause));
8282
}
8383

8484
void isr_empty(void)

test-app/vector_riscv.S

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@
6262
ld x30, 112(sp)
6363
ld x31, 120(sp)
6464
addi sp, sp, 128
65-
/* Always sret: payload always runs in S-mode under wolfBoot */
66-
sret
65+
/* mret: payload runs in M-mode (wolfBoot does M-mode direct jump) */
66+
mret
6767
.endm
6868

6969
#else /* __riscv_xlen == 32 */
@@ -107,8 +107,8 @@
107107
lw x30, 56(sp)
108108
lw x31, 60(sp)
109109
addi sp, sp, 64
110-
/* Always sret: payload always runs in S-mode under wolfBoot */
111-
sret
110+
/* mret: payload runs in M-mode (wolfBoot does M-mode direct jump) */
111+
mret
112112
.endm
113113

114114
#endif /* __riscv_xlen */

0 commit comments

Comments
 (0)