Skip to content

Commit a052f91

Browse files
committed
Fixes for QSPI flashing tool and getting test-app booting on E51 core.
1 parent 7c966b8 commit a052f91

File tree

7 files changed

+299
-65
lines changed

7 files changed

+299
-65
lines changed

config/examples/polarfire_mpfs250_m_qspi.config

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#
66
# Boot flow:
77
# 1. eNVM (0x20220100) -> L2_SCRATCH (0x0A000000) - wolfBoot starts
8-
# 2. Load signed app image from SC QSPI flash to LIM (0x08000000)
8+
# 2. Load signed app image from SC QSPI flash to L2_SCRATCH (0x0A010200)
99
# 3. Verify signature and boot
1010
#
1111
# Flash using mpfsBootmodeProgrammer (bootmode 1):
@@ -70,10 +70,13 @@ DISK_EMMC?=0
7070
# Stack grows down from end of L2_SCRATCH
7171
WOLFBOOT_ORIGIN?=0x0A000000
7272

73-
# Load application to LIM (Loosely Integrated Memory, 2MB)
73+
# Load application to L2 Scratchpad (above wolfBoot code, below stack)
74+
# wolfBoot occupies ~40KB at 0x0A000000, stack is 64KB at top of 256KB.
7475
# Note: update_ram places header at (LOAD_ADDRESS - IMAGE_HEADER_SIZE),
75-
# so offset by header size to keep everything within LIM (starts at 0x08000000)
76-
WOLFBOOT_LOAD_ADDRESS?=0x08000200
76+
# so offset by header size to keep header aligned.
77+
# IMPORTANT: Strip debug symbols from test-app ELF before signing to keep
78+
# the image small enough to fit in L2 Scratchpad (~150KB available).
79+
WOLFBOOT_LOAD_ADDRESS?=0x0A010200
7780

7881
# Flash geometry (64 KB sector to match QSPI flash)
7982
WOLFBOOT_SECTOR_SIZE?=0x10000

docs/Targets.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -907,12 +907,13 @@ Notes:
907907
### PolarFire SoC M-Mode (bare-metal eNVM boot)
908908
909909
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.
910+
application is loaded from SC QSPI flash into L2 Scratchpad (on-chip RAM). This is the simplest
911+
bring-up path.
911912
912913
**Boot flow:**
913914
1. CPU starts at eNVM reset vector (`0x20220100`)
914915
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+
3. wolfBoot reads the signed image from QSPI flash into L2 Scratchpad (`0x0A010200`)
916917
4. Signature is verified, then execution jumps to the application
917918
918919
**Build:**
@@ -935,7 +936,7 @@ make test-app/image_v1_signed.bin
935936
**Flash the signed application to QSPI** using the UART programmer (requires `EXT_FLASH=1` and
936937
`UART_QSPI_PROGRAM=1` in `.config`, and `pyserial` installed):
937938
```sh
938-
python3 tools/scripts/mpfs_qspi_prog.py /dev/ttyUSB1 \
939+
python3 tools/scripts/mpfs_qspi_prog.py /dev/ttyUSB0 \
939940
test-app/image_v1_signed.bin 0x20000
940941
```
941942
@@ -966,7 +967,17 @@ Booting at 0x...
966967
wolfBoot uses a calibrated busy-loop for all delays (`udelay()` in `hal/mpfs250.c`).
967968
- `UART_QSPI_PROGRAM=1` adds a 3-second boot pause every time. Set to `0` once the flash
968969
contents are stable.
969-
- The config uses `WOLFBOOT_LOAD_ADDRESS=0x08000200` to keep the image header within LIM.
970+
- The config uses `WOLFBOOT_LOAD_ADDRESS=0x0A010200` to place the application in L2 Scratchpad
971+
above wolfBoot code (~64KB at `0x0A000000`), with the stack at the top of the 256KB region.
972+
- **LIM instruction fetch limitation:** The on-chip LIM (`0x08000000`, 2MB) is backed by L2
973+
cache ways. When `L2_WAY_ENABLE` is set to `0x0B` (all cache ways 0-7 active for caching),
974+
no ways remain for LIM backing SRAM. Data reads from LIM work through the L2 cache, but
975+
instruction fetch silently hangs — the CPU stalls with no trap generated. For this reason the
976+
application is loaded into L2 Scratchpad (`0x0A000000`), which is always accessible regardless
977+
of `L2_WAY_ENABLE`. To use LIM, reduce `L2_WAY_ENABLE` to free cache ways for LIM backing.
978+
- **Strip debug symbols** before signing the test-app ELF. The debug build is ~150KB but the
979+
stripped ELF is ~5KB. L2 Scratchpad has ~150KB available between wolfBoot code and the stack:
980+
`riscv64-unknown-elf-strip --strip-debug test-app/image.elf`
970981
971982
### PolarFire testing
972983

hal/mpfs250.c

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,13 @@ static void mpfs_config_l2_cache(void)
116116
}
117117

118118
/* Microsecond delay using busy loop.
119-
* MTIME counter is not running in bare-metal M-mode (no HSS),
120-
* so use a calibrated loop at ~40 MHz reset clock. */
121-
static void udelay(uint32_t us)
119+
* MTIME counter is not running in bare-metal M-mode (no HSS), so use a
120+
* calibrated loop. E51 runs at ~40 MHz on reset; with volatile load/store
121+
* overhead each iteration is ~10 cycles → 4 iterations per microsecond.
122+
* noinline prevents the compiler from collapsing the loop at call sites. */
123+
static __attribute__((noinline)) void udelay(uint32_t us)
122124
{
123125
volatile uint32_t i;
124-
/* ~10 cycles per iteration at -O2, 40 MHz = 4 iterations per us */
125126
for (i = 0; i < us * 4; i++)
126127
;
127128
}
@@ -1644,47 +1645,4 @@ void uart_write_hart(unsigned long hartid, const char* buf, unsigned int sz)
16441645
}
16451646
}
16461647

1647-
/**
1648-
* uart_printf_hart - Simple printf to a specific hart's UART
1649-
* Only supports %d, %x, %s, %lu formats for minimal footprint
1650-
*/
1651-
static void uart_printf_hart(unsigned long hartid, const char* fmt, ...)
1652-
{
1653-
char buf[128];
1654-
int len = 0;
1655-
const char* p = fmt;
1656-
1657-
/* Very simple printf implementation */
1658-
while (*p && len < (int)sizeof(buf) - 1) {
1659-
if (*p == '%') {
1660-
p++;
1661-
if (*p == 'l' && *(p+1) == 'u') {
1662-
/* %lu - unsigned long */
1663-
p += 2;
1664-
/* Skip for now - just print placeholder */
1665-
buf[len++] = '[';
1666-
buf[len++] = 'N';
1667-
buf[len++] = ']';
1668-
} else if (*p == 'd') {
1669-
p++;
1670-
buf[len++] = '[';
1671-
buf[len++] = 'N';
1672-
buf[len++] = ']';
1673-
} else if (*p == 's') {
1674-
p++;
1675-
buf[len++] = '[';
1676-
buf[len++] = 'S';
1677-
buf[len++] = ']';
1678-
} else {
1679-
buf[len++] = '%';
1680-
buf[len++] = *p++;
1681-
}
1682-
} else {
1683-
buf[len++] = *p++;
1684-
}
1685-
}
1686-
buf[len] = '\0';
1687-
1688-
uart_write_hart(hartid, buf, len);
1689-
}
16901648
#endif /* WOLFBOOT_RISCV_MMODE */

src/boot_riscv.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,15 @@ unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc,
175175
last_epc = epc;
176176
last_tval = tval;
177177

178+
#ifdef DEBUG_BOOT
179+
/* Debug: print trap info for synchronous exceptions (not interrupts) */
180+
if (!(cause & MCAUSE_INT)) {
181+
wolfBoot_printf("TRAP: cause=%lx epc=%lx tval=%lx\n", cause, epc,
182+
tval);
183+
while (1) ; /* halt to prevent infinite trap-mret loop */
184+
}
185+
#endif
186+
178187
#ifdef PLIC_BASE
179188
/* Check if this is an interrupt (MSB set) */
180189
if (cause & MCAUSE_INT) {
@@ -463,7 +472,8 @@ void do_boot(const uint32_t *app_offset)
463472
* Use this for test-apps; define WOLFBOOT_MMODE_SMODE_BOOT for Linux.
464473
*/
465474
wolfBoot_printf("M-mode direct jump to 0x%lx\n", (unsigned long)app_offset);
466-
/* Diagnostics before jump */
475+
#ifdef DEBUG_BOOT
476+
/* Verify UART TX is idle and check first two words of the image */
467477
{
468478
volatile uint8_t lsr = MMUART_LSR(DEBUG_UART_BASE);
469479
uint32_t *p = (uint32_t*)app_offset;
@@ -472,13 +482,23 @@ void do_boot(const uint32_t *app_offset)
472482
wolfBoot_printf("App[0]=0x%lx [1]=0x%lx\n",
473483
(unsigned long)p[0], (unsigned long)p[1]);
474484
}
475-
/* Extended drain: allow all diagnostic output to finish (~10ms at 40MHz) */
485+
/* Drain UART TX FIFO before jumping.
486+
* 400000 iters ≈ 10 ms at 40 MHz E51 reset clock (~10 cycles/iter). */
476487
{
477488
volatile int i;
478489
for (i = 0; i < 400000; i++) {}
479490
}
491+
#endif /* DEBUG_BOOT */
480492
(void)hartid;
481493
(void)dts_addr;
494+
/* Synchronize I-cache/pipeline after ELF loading wrote new instructions.
495+
* fence ensures all stores are committed; fence.i ensures the instruction
496+
* fetch unit sees the new code. Required even on E51 (no I-cache) because
497+
* the store buffer may not have drained.
498+
* Note: -march=rv64imac does not define __riscv_zifencei, so
499+
* riscv_icache_sync() in elf.c is a no-op. We emit fence.i manually. */
500+
asm volatile("fence" ::: "memory");
501+
asm volatile(".word 0x0000100f" ::: "memory"); /* fence.i */
482502
asm volatile("jr %0" : : "r"(app_offset));
483503
__builtin_unreachable();
484504
#endif /* WOLFBOOT_MMODE_SMODE_BOOT */

src/sdhci.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,14 +1466,15 @@ int sdhci_init(void)
14661466
status = sdhci_set_timeout(SDHCI_DATA_TIMEOUT_US);
14671467
}
14681468

1469-
wolfBoot_printf("sdhci_init: %s status: %d\n",
1470-
#ifdef DISK_EMMC
1471-
"eMMC"
1472-
#else
1473-
"SD"
1474-
#endif
1475-
, status
1476-
);
1469+
{
1470+
const char *card_type;
1471+
#ifdef DISK_EMMC
1472+
card_type = "eMMC";
1473+
#else
1474+
card_type = "SD";
1475+
#endif
1476+
wolfBoot_printf("sdhci_init: %s status: %d\n", card_type, status);
1477+
}
14771478

14781479
return status;
14791480
}

test-app/startup_riscv.c

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

45+
/* Direct UART diagnostic: write "!\r\n" to confirm test-app is running.
46+
* MPFS MMUART: THR at offset 0x100, LSR at offset 0x14, THRE = bit 5. */
47+
asm volatile(
48+
"li a0, 0x20000000\n" /* UART0 base */
49+
/* write '!' */
50+
"1: lbu a1, 0x14(a0)\n" /* read LSR */
51+
"andi a1, a1, 0x20\n" /* check THRE (bit 5) */
52+
"beqz a1, 1b\n"
53+
"li a2, 0x21\n" /* '!' */
54+
"sb a2, 0x100(a0)\n" /* write to THR */
55+
/* write '\r' */
56+
"2: lbu a1, 0x14(a0)\n"
57+
"andi a1, a1, 0x20\n"
58+
"beqz a1, 2b\n"
59+
"li a2, 0x0d\n" /* '\r' */
60+
"sb a2, 0x100(a0)\n"
61+
/* write '\n' */
62+
"3: lbu a1, 0x14(a0)\n"
63+
"andi a1, a1, 0x20\n"
64+
"beqz a1, 3b\n"
65+
"li a2, 0x0a\n" /* '\n' */
66+
"sb a2, 0x100(a0)\n"
67+
::: "a0", "a1", "a2"
68+
);
69+
4570
/* Set up M-mode vectored interrupt table.
4671
* wolfBoot M-mode does a direct jr (no enter_smode), so payload runs in M-mode.
4772
* Use mtvec. The +1 sets MODE=1 (vectored). */

0 commit comments

Comments
 (0)