Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,15 @@ jobs:
with:
arch: riscv64
config-file: ./config/examples/polarfire_mpfs250_qspi.config
# M-mode + LPDDR4 build: point LIBERO_FPGA_CONFIG_DIR at the in-tree
# CI stub so MPFS_DDR_INIT actually compiles in CI (the upstream config
# leaves the var empty so real boards must override it explicitly).
microchip_mpfs250_m_test:
uses: ./.github/workflows/test-build-riscv.yml
with:
arch: riscv64
config-file: ./config/examples/polarfire_mpfs250_m.config
Comment thread
dgarske marked this conversation as resolved.
make-args: LIBERO_FPGA_CONFIG_DIR=tools/ci/mpfs_libero_stub
microchip_mpfs250_m_qspi_test:
uses: ./.github/workflows/test-build-riscv.yml
with:
Expand Down
18 changes: 18 additions & 0 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -742,8 +742,26 @@ ifeq ($(ARCH),RISCV64)
ifeq ($(RISCV_MMODE),1)
# Machine Mode: Running directly from eNVM/L2 SRAM
CFLAGS+=-DWOLFBOOT_RISCV_MMODE -DWOLFBOOT_DUALBOOT
# Minimal SBI runtime: services S-mode ecalls / timer / IPI when booting
# an S-mode OS (Linux). Compiles to nothing unless WOLFBOOT_MMODE_SMODE_BOOT
# is also defined (see src/riscv_sbi.c).
OBJS+=src/riscv_sbi.o
# Use M-mode specific linker script
LSCRIPT_IN:=hal/$(TARGET)-m.ld
# MPFS DDR init pulls LIBERO_SETTING_* values from a Libero/HSS-generated
# fpga_design_config.h. Setting LIBERO_FPGA_CONFIG_DIR enables DDR init
# and adds the directory to the include search path.
ifneq ($(LIBERO_FPGA_CONFIG_DIR),)
CFLAGS+=-DMPFS_DDR_INIT -I$(LIBERO_FPGA_CONFIG_DIR)
# FIT/FDT boot: the E51 M-mode DDR boot loads a signed Yocto fitImage
# (kernel + dtb) from SD and hands the dtb to S-mode Linux, so enable
# the FIT parser (fit_find_images/fit_load_image in src/fdt.c). The
# U54 S-mode build enables this in the RISCV_MMODE=0 branch below; the
# E51 M-mode DDR build needs it here too (it is not full MMU, so the
# do_boot dtb hand-off is gated on MMU || WOLFBOOT_FDT).
CFLAGS+=-DWOLFBOOT_FDT
OBJS+=src/fdt.o
endif
else
# Supervisor Mode: Running under HSS
CFLAGS+=-DWOLFBOOT_DUALBOOT
Expand Down
161 changes: 161 additions & 0 deletions config/examples/polarfire_mpfs250_m.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# PolarFire SoC MPFS250T M-Mode (Machine Mode) with LPDDR4 + SD card
#
# Standalone wolfBoot replacing HSS:
# 1. eNVM (0x20220100) -> L2_SCRATCH (0x0A000000) - wolfBoot starts
# 2. M-mode init: PLLs, DDR controller, LPDDR4 training (Video Kit)
# 3. Load signed Linux kernel/DTB from SD card to DDR (0x8E000000 / 0x8A000000)
# 4. Verify ECC384/SHA384 signature
# 5. Drop to S-mode and jump to kernel
#
# Flash via mpfsBootmodeProgrammer (bootmode 1):
# java -jar mpfsBootmodeProgrammer.jar --bootmode 1 --die MPFS250T \
# --package FCG1152 --workdir $PWD wolfboot.elf

ARCH?=RISCV64
TARGET?=mpfs250
SIGN?=ECC384
HASH?=SHA384
IMAGE_HEADER_SIZE=512
WOLFBOOT_VERSION?=1
ARMORED?=0
DEBUG?=0
DEBUG_SYMBOLS?=1
DEBUG_UART?=1
VTOR?=1
EXT_FLASH?=0
SPI_FLASH?=0
NO_XIP?=1
NVM_FLASH_WRITEONCE?=0
UART_FLASH?=0
V?=0
NO_MPU?=1
RAM_CODE?=0
SPMATH?=0
SPMATHALL?=1
DUALBANK_SWAP?=0
PKA?=0
ENCRYPT=0
WOLFTPM?=0
ELF?=1
#DEBUG_ELF?=1

OPTIMIZATION_LEVEL=1

# M-Mode configuration: runs on E51 from L2 SRAM
RISCV_MMODE?=1

# Stack size per hart (L2 SRAM constraints)
CFLAGS_EXTRA+=-DSTACK_SIZE_PER_HART=4096

# E51 core lacks RISC-V crypto extensions (Zknh), use portable C
# RISC-V SHA256/384/512 assembly (wolfcrypt port/riscv) enabled by default.
# Verified on the MPFS250 E51 (rv64imac); requires wolfSSL with the RISC-V
# unaligned-access fix (PR #10530, present in the lib/wolfssl submodule).
# The E51 handles unaligned access so the default (faster) asm path is used;
# add -DWOLFSSL_RISCV_ASM_NO_UNALIGNED for RISC-V cores that trap on it.
NO_ASM?=0

# Enable LPDDR4 init in hal_init() by pointing at the Libero/HSS-generated
# fpga_design_config directory for this board. The directory must contain
# fpga_design_config.h and its sub-headers (memory_map/, ddr/, clocks/, ...).
# Typical sources:
# - HSS Video Kit build:
# <hss>/build/boards/mpfs-video-kit/fpga_design_config
# - Libero MSS Configurator export for the design.
# The -I path is added and -DMPFS_DDR_INIT is set automatically when this is
# non-empty (see arch.mk). Override on the command line for one-off builds:
# make LIBERO_FPGA_CONFIG_DIR=/path/to/fpga_design_config
LIBERO_FPGA_CONFIG_DIR?=

# Boot Linux: drop to S-mode after wolfBoot verifies kernel
CFLAGS_EXTRA+=-DWOLFBOOT_MMODE_SMODE_BOOT

# SD card storage for kernel image (no QSPI flash)
DISK_SDCARD?=1
DISK_EMMC?=0

# wolfBoot in L2 SRAM (256KB available)
WOLFBOOT_ORIGIN?=0x0A000000

# 4KB sector size (SD card flow is partition-based, not flash-erase-based)
WOLFBOOT_SECTOR_SIZE?=0x1000

# Scratch address where the signed FIT image is staged before signature
# verification + FIT parse. Placed early in DDR (32 MB into 2 GB) so we
# stay within the fully-trained region near 0x80000000 - the LPDDR4 TIP
# completes BCLK_SCLK only (train_stat=0x1) on this Video Kit and higher
# DDR addresses (e.g. 0x8E000000) have shown intermittent write
# corruption during long disk loads.
# After the FIT is parsed:
# kernel is copied to 0x80200000 (FIT-internal "load")
# DTB is copied to WOLFBOOT_LOAD_DTS_ADDRESS (0x8A000000)
# Layout:
# 0x80200000 - 0x814FFFFF : kernel (~19 MB after parse)
# 0x82000000 - 0x832FFFFF : FIT scratch (~19 MB - overwritten on next boot)
# 0x8A000000 - 0x8A004FFF : DTB
WOLFBOOT_LOAD_ADDRESS?=0x82000000

# DTB load address in DDR
WOLFBOOT_LOAD_DTS_ADDRESS?=0x8A000000

# Use update_disk loader (partition A/B numbering instead of flash addresses).
# BOOT_PART_A / BOOT_PART_B are 0-indexed GPT entry numbers. GPT partitions
# in our SD card layout (see tools/scripts/program-sdcard.sh):
# index 0 (parted "boot" 1 MiB - 33 MiB) -> active boot FIT
# index 1 (parted "update" 33 MiB - 65 MiB) -> inactive/update slot
# index 2 (parted "rootfs" 65 MiB - end) -> Linux rootfs
WOLFBOOT_NO_PARTITIONS=1
CFLAGS_EXTRA+=-DBOOT_PART_A=0
CFLAGS_EXTRA+=-DBOOT_PART_B=1

# Speed up disk partition read (512KB chunks - max DMA size)
CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000

# Disable SDMA on the Cadence SD4HC. SDMA hangs silently at first
# multi-block read on the Video Kit (Cadence boundary-cross bug).
# Use PIO single-block reads instead.
CFLAGS_EXTRA+=-DSDHCI_SDMA_DISABLED

# Force single-block (CMD17) reads. Multi-block PIO suffers a BRR
# race on Arasan/Cadence-family controllers; single-block avoids it.
CFLAGS_EXTRA+=-DSDHCI_FORCE_SINGLE_BLOCK_READ

# Disk-load via PDMA staging. On this board, CPU AXI writes to DDR
# (cached or non-cached) do NOT reliably land at the address that
# subsequent cached reads will fetch from -- empirical alias probe
# showed CPU writes via the 0xC0000000 non-cached window are silently
# dropped, and cached PIO writes appear to allocate L2 lines that are
# never written back to DDR before the integrity-check read.
#
# Workaround: SDHCI PIO into a small L2 Scratch staging buffer, then
# mpfs_pdma_memcpy() copies the block into DDR via the PDMA master.
# PDMA-via-non-cached is the only AXI write path verified to land in
# DDR (the same path used by mpfs_clear_bootup_cache_ways pre-fill).
CFLAGS_EXTRA+=-DSDHCI_BLOCK_VIA_PDMA

# Video Kit routes the SD slot's Card Detect (CD#) signal through the FPGA
# fabric rather than MSSIO, so the SDHCI controller's hardware CI/CDPL
# detection always reads 'no card' in M-mode (no fabric configuration).
# Force the SD bring-up code to assume a card is present.
CFLAGS_EXTRA+=-DSDHCI_FORCE_CARD_DETECT

# Optional encryption (kernel signed+encrypted with AES-256)
#CUSTOM_ENCRYPT_KEY=1
#ENCRYPT=1
#ENCRYPT_WITH_AES256=1
#OBJS_EXTRA=src/my_custom_encrypt_key.o

# Used by test-application/ELF wrapper
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80200000
WOLFBOOT_PARTITION_SIZE=0x4000000

# Debug options (useful for initial M-mode + DDR bring-up)
CFLAGS_EXTRA+=-DDEBUG_BOOT
#CFLAGS_EXTRA+=-DDEBUG_SDHCI
#CFLAGS_EXTRA+=-DDEBUG_DISK
#CFLAGS_EXTRA+=-DDISK_TEST
# Verbose register-level DDR tracing (development aid). Historically the
# DBG_DDR printf delays appeared load-bearing for TIP training; that was
# resolved by the auto-init-disable training reorder and the WRCALIB
# all-4-lane accept gate with retry, so this is safe to leave disabled.
#CFLAGS_EXTRA+=-DDEBUG_DDR
106 changes: 89 additions & 17 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ The PolarFire SoC is a 64-bit RISC-V SoC featuring a five-core CPU cluster (1×

### Supported Boot Configurations

Five ready-to-use config templates cover all supported boot mode / storage / memory combinations:
Six ready-to-use config templates cover all supported boot mode / storage / memory combinations:

| Configuration | Config File | Boot Mode | Storage | Memory | HSS |
|---------------|-------------|-----------|---------|--------|-----|
Expand All @@ -887,21 +887,51 @@ Five ready-to-use config templates cover all supported boot mode / storage / mem
| **QSPI (S-mode)** | `polarfire_mpfs250_qspi.config` | S-mode (U54 via HSS) | MSS or SC QSPI | DDR | Yes |
| **QSPI + L2-LIM** | `polarfire_mpfs250_hss_l2lim.config` | S-mode (U54 via HSS) | SC QSPI | L2-LIM (no DDR) | Yes |
| **M-Mode (no HSS)** | `polarfire_mpfs250_m_qspi.config` | M-mode (E51, no HSS) | SC QSPI | L2 Scratchpad | No |
| **M-Mode + DDR** | `polarfire_mpfs250_m.config` | M-mode (E51, no HSS) | SD Card | LPDDR4 (DDR) | No |

The **M-Mode + DDR** configuration brings up the LPDDR4 controller from
the E51 in M-mode (no HSS), then loads a signed FIT image from SD card,
verifies it (SHA384 + ECC384) and hands off to a U54 hart in S-mode.
wolfBoot includes a minimal SBI runtime (`src/riscv_sbi.c`) so the
hand-off target can be a Linux kernel: tested booting 4-CPU SMP Yocto
Linux to a login prompt in ~40 s from power-on on the MPFS250T Video
Kit. Because all
LIBERO_SETTING_\* values are board-specific, this build pulls them from
a Libero/HSS-generated `fpga_design_config.h` pointed at by the
`LIBERO_FPGA_CONFIG_DIR` makefile variable - typical sources are an
HSS Video Kit build at
`<hss>/build/boards/mpfs-video-kit/fpga_design_config` or a Libero MSS
Configurator export. Setting `LIBERO_FPGA_CONFIG_DIR` automatically
defines `MPFS_DDR_INIT` and adds the directory to the include path
(see `arch.mk`); when unset, the DDR HAL is excluded and the build
still produces a working M-mode wolfBoot without DDR. Add
`-DDEBUG_DDR` to `CFLAGS_EXTRA` for verbose register-level traces
during bring-up.

A compile-only stub `tools/ci/mpfs_libero_stub/fpga_design_config.h`
exists for GitHub Actions: every `LIBERO_SETTING_*` symbol referenced
by the HAL is defined to `0` so the M-Mode + DDR build path stays
under continuous integration. The stub is **not** runnable - real
boards must point `LIBERO_FPGA_CONFIG_DIR` at the actual Libero / HSS
output.

Key build settings that differ between configurations:

| Setting | SDCard | eMMC | QSPI | L2-LIM | M-Mode |
|---------|--------|------|------|--------|--------|
| `WOLFBOOT_ORIGIN` | `0x80000000` | `0x80000000` | `0x80000000` | `0x08040000` | `0x0A000000` |
| `WOLFBOOT_LOAD_ADDRESS` | `0x8E000000` | `0x8E000000` | `0x8E000000` | `0x08060000` | `0x0A010200` |
| `EXT_FLASH` | 0 | 0 | 1 | 1 | 1 |
| `DISK_SDCARD` | 1 | 0 | 0 | 0 | 0 |
| `DISK_EMMC` | 0 | 1 | 0 | 0 | 0 |
| `MPFS_L2LIM` | – | – | – | 1 | – |
| `RISCV_MMODE` | – | – | – | – | 1 |
| Linker script | `mpfs250.ld` | `mpfs250.ld` | `mpfs250.ld` | `mpfs250-hss.ld` | `mpfs250-m.ld` |
| HSS YAML | `mpfs.yaml` | `mpfs.yaml` | `mpfs.yaml` | `mpfs-l2lim.yaml` | N/A |
| `ELF` output | 1 | 1 | 1 | 0 (raw .bin) | 1 |
| Setting | SDCard | eMMC | QSPI | L2-LIM | M-Mode | M-Mode + DDR |
|---------|--------|------|------|--------|--------|--------------|
| `WOLFBOOT_ORIGIN` | `0x80000000` | `0x80000000` | `0x80000000` | `0x08040000` | `0x0A000000` | `0x0A000000` |
| `WOLFBOOT_LOAD_ADDRESS` | `0x8E000000` | `0x8E000000` | `0x8E000000` | `0x08060000` | `0x0A010200` | `0x82000000` |
| `WOLFBOOT_LOAD_DTS_ADDRESS` | `0x8A000000` | `0x8A000000` | `0x8A000000` | – | – | `0x8A000000` |
| `EXT_FLASH` | 0 | 0 | 1 | 1 | 1 | 0 |
| `DISK_SDCARD` | 1 | 0 | 0 | 0 | 0 | 1 |
| `DISK_EMMC` | 0 | 1 | 0 | 0 | 0 | 0 |
| `MPFS_L2LIM` | – | – | – | 1 | – | – |
| `RISCV_MMODE` | – | – | – | – | 1 | 1 |
| `LIBERO_FPGA_CONFIG_DIR` | – | – | – | – | – | required |
| `WOLFBOOT_MMODE_SMODE_BOOT` | – | – | – | – | – | 1 |
| Linker script | `mpfs250.ld` | `mpfs250.ld` | `mpfs250.ld` | `mpfs250-hss.ld` | `mpfs250-m.ld` | `mpfs250-m.ld` |
| HSS YAML | `mpfs.yaml` | `mpfs.yaml` | `mpfs.yaml` | `mpfs-l2lim.yaml` | N/A | N/A |
| `ELF` output | 1 | 1 | 1 | 0 (raw .bin) | 1 | 1 |

> **Note:** All configurations require `NO_ASM=1` because the MPFS250 U54/E51 cores lack RISC-V
> crypto extensions (Zknh); wolfBoot uses portable C implementations for all cryptographic operations.
Expand Down Expand Up @@ -1109,8 +1139,7 @@ or DDR required. This is the simplest bring-up path.
* Executes from L2 Scratchpad SRAM (256 KB at `0x0A000000`)
* Loads signed application from SC QSPI flash to L2 Scratchpad (`0x0A010200`)
* No HSS or DDR required — boots entirely from on-chip memory
* Wakes and manages secondary U54 harts via IPI
* Per-hart UART output (each hart uses its own MMUART)
* Parks and releases secondary U54 harts via CLINT IPI
* ECC384 + SHA384 signature verification

**Relevant files:**
Expand Down Expand Up @@ -1215,8 +1244,51 @@ Booting at 0x...
- **Strip debug symbols** before signing the test-app ELF. The debug build is ~150 KB but the
stripped ELF is ~5 KB. L2 Scratchpad has ~150 KB available between wolfBoot code and the stack:
`riscv64-unknown-elf-strip --strip-debug test-app/image.elf`
- **DDR support:** DDR initialization is available on the `polarfire_ddr` branch for use cases
that require loading larger applications to DDR memory.
- **DDR support:** software LPDDR4 initialization is included via the **M-Mode + DDR**
configuration (`polarfire_mpfs250_m.config`, requires `LIBERO_FPGA_CONFIG_DIR`) for use cases
that require loading larger images (e.g. a Linux FIT) to DDR memory. See the next section.

### PolarFire SoC M-Mode + DDR: booting Linux (wolfSBI)

The **M-Mode + DDR** configuration (`config/examples/polarfire_mpfs250_m.config`) replaces both
HSS and OpenSBI: wolfBoot performs the LPDDR4 init/training on the E51, loads and verifies a
signed Yocto FIT image (kernel + dtb) from SD card into DDR, applies device-tree fixups, releases
U54 hart 1 into S-mode at the kernel entry, and then remains resident as a minimal M-mode SBI
runtime. Validated on the MPFS250T Video Kit: 4-CPU SMP Yocto Linux to login in ~40 s from
power-on.

**wolfSBI runtime** (`src/riscv_sbi.c`, generic RISC-V with HAL hooks; enabled by
`WOLFBOOT_MMODE_SMODE_BOOT`):
* SBI v0.2 extensions: BASE, TIME (per-hart `mtimecmp`, MTIP-to-STIP injection), IPI (SSIP
injection via CLINT MSIP), RFENCE (remote `fence.i` / `sfence.vma` with completion wait),
HSM (`hart_start`/`hart_stop`/`hart_status` backed by per-hart start mailboxes), DBCN and the
legacy console putchar (shared with the wolfBoot UART), SRST.
* `rdtime` emulation: the U54/E51 have no `time` CSR, so reads trap as illegal instruction from
S/U-mode and are emulated from CLINT MTIME (enabled via SYSREG `RTC_CLOCK_CR`, 1 MHz to match
the device tree `timebase-frequency`).
* Misaligned load/store emulation (not delegatable on these harts) via MPRV byte accesses,
including compressed forms; the kernel relies on this for unaligned copy tails.
* Per-hart M-mode trap stacks live in the `hss-buffer` reserved (nomap) DDR region; cross-hart
state (HSM mailboxes, IPI flags, the hart-release gate flag) lives in the E51 DTIM at
`0x01000000`, which is uncached and coherent for all harts. Cacheable L2-scratchpad memory
must not be used for cross-hart signalling (stores can be lost on dirty-line eviction).

**Device-tree fixups** applied to the loaded dtb (`hal/mpfs250.c`): bootargs/root device,
MAC addresses from the device serial number, and all five MSS watchdog nodes are disabled.

**Watchdog policy:** the MSS watchdogs always count and reset the chip on timeout (they cannot
be disabled in hardware, and `CONTROL=0` does not prevent the reset). After hand-off the parked
E51 acts as a monitor and refreshes all five watchdogs; the OS watchdog driver is disabled via
the dtb fixup so the two never conflict.

**Hand-off / SMP flow:** secondary harts park in eNVM until the E51 signals image-copy
completion (DTIM gate flag), then park in a WFI loop. The boot hart is released with a staged
mailbox {entry, dtb} plus MSIP; Linux brings up the remaining harts through SBI HSM
`hart_start`, which uses the same mailbox + MSIP path. The release path must stay fast
(no UART access): the kernel allows roughly one second for a started hart to come online.

**DEBUG_DDR note:** DDR training no longer depends on `DEBUG_DDR` console timing; the flag is
purely diagnostic and off by default.

### PolarFire testing

Expand Down
22 changes: 17 additions & 5 deletions hal/mpfs250-m.ld
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ MEMORY
* This offset is added by mpfsBootmodeProgrammer (bootmode 1) */
FLASH_ENVM (rx) : ORIGIN = 0x20220100, LENGTH = 128k - 0x100

/* L2 Scratchpad SRAM - 256KB available
* Used for code execution, data, and stack in M-mode
/* L2 Scratchpad SRAM - 256 KB used (2 of 4 scratchpad ways).
* Attempted 512 KB (all 4 ways) to match HSS layout, but ways 8-9
* are not initialized by the bootmode programmer -- stack/HLS
* placed there hit a trap immediately after DDR init. Reverted
* until we add explicit scratchpad init for ways 8-9.
* Address range: 0x0A000000 - 0x0A03FFFF */
L2_SCRATCH (rwx) : ORIGIN = @WOLFBOOT_ORIGIN@, LENGTH = 256k
}
Expand Down Expand Up @@ -119,9 +122,12 @@ PROVIDE(_start_heap = _end);
*
* Total stack area: STACK_SIZE + 4 * STACK_SIZE_PER_HART
*/
/* M-mode: only E51 (hart 0) runs; secondary harts park in eNVM WFI loop.
* Set to 0 so no L2 Scratch is wasted on phantom secondary stacks. */
PROVIDE(STACK_SIZE_PER_HART = 0);
/* Per-hart stacks for the secondary (U54) park/wake path. MUST match the
* STACK_SIZE_PER_HART value in CFLAGS (the wake asm computes sp with the
* C macro while this symbol places the region): the historical 0-vs-8192
* mismatch placed the woken harts' stacks INSIDE the E51 stack region,
* smashing the monitor when SBI HSM started the secondary harts. */
PROVIDE(STACK_SIZE_PER_HART = 4096);

/* End of L2 scratchpad */
PROVIDE(_l2_scratch_end = ORIGIN(L2_SCRATCH) + LENGTH(L2_SCRATCH));
Expand Down Expand Up @@ -151,3 +157,9 @@ PROVIDE(_text_size = _end_text - _start_text_sram);
* Image header is loaded at (WOLFBOOT_LOAD_ADDRESS - IMAGE_HEADER_SIZE). */
ASSERT(_end <= @WOLFBOOT_LOAD_ADDRESS@ - @IMAGE_HEADER_SIZE@,
"ERROR: wolfBoot binary overlaps image load area! Increase WOLFBOOT_LOAD_ADDRESS")

/* Build-time safety: keep at least 4 KB between the end of code/data/bss
* (_end) and the main hart stack bottom so code growth cannot silently
* reach the stack region at the top of L2 Scratch. */
ASSERT(_end <= _main_hart_stack_bottom - 0x1000,
"ERROR: wolfBoot L2 image too close to stack (need 4 KB headroom)")
Loading
Loading