Skip to content

Commit 134b701

Browse files
committed
Add wolfBoot FIT support for loading bitstream
1 parent 8fe46bb commit 134b701

17 files changed

Lines changed: 765 additions & 6 deletions

File tree

arch.mk

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ ifeq ($(ARCH),AARCH64)
7474
# Support detection and skip of U-Boot legacy header */
7575
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
7676
CFLAGS+=-DWOLFBOOT_DUALBOOT
77+
# FPGA bitstream-from-FIT is supported via the PMU firmware
78+
# (PM_FPGA_LOAD). Enable with FPGA_BITSTREAM=1 (opt-in).
7779

7880
ifeq ($(HW_SHA3),1)
7981
# Use HAL for hash (see zynqmp.c)
@@ -90,6 +92,9 @@ ifeq ($(ARCH),AARCH64)
9092
CFLAGS+=-DWOLFBOOT_DUALBOOT
9193
# Support detection and skip of U-Boot legacy header
9294
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
95+
# NOTE: FPGA_BITSTREAM is stubbed on Versal (PL config is a PDI loaded
96+
# by the PLM via XilLoader Load-PDI IPI - not yet implemented). Leave
97+
# FPGA_BITSTREAM off until that path lands.
9398
# PLM owns RVBAR on Versal in JTAG boot; skip RVBAR writes
9499
CFLAGS+=-DSKIP_RVBAR=1
95100
# Disable SDMA for multi-block transfers - use PIO instead.
@@ -350,6 +355,8 @@ ifeq ($(ARCH),ARM)
350355
# positive probability from ~2^-32 to ~2^-64, matching what U-Boot's
351356
# own mkimage/bootm does.
352357
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
358+
# FPGA bitstream-from-FIT is supported via the DevC/PCAP DMA engine
359+
# (full bitstream). Enable with FPGA_BITSTREAM=1 (opt-in).
353360
endif
354361

355362
ifeq ($(TARGET),va416x0)

docs/Targets.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4070,6 +4070,58 @@ FDT: Set chosen (...), linux,initrd-start=1073741824
40704070
FDT: Set chosen (...), linux,initrd-end=...
40714071
```
40724072
4073+
**FPGA bitstream from FIT**
4074+
4075+
wolfBoot can program the PL (FPGA fabric) from an `fpga` sub-image carried in the same signed FIT, using the standard U-Boot convention: a sub-image with `type = "fpga"`, referenced from the configuration node via an optional `fpga = "<node>"` property, with a `compatible` string naming the load method. The PL is programmed before the kernel/DTB are loaded so PL-dependent clocks and peripherals come up first. The outer wolfBoot signature authenticates the whole FIT (bitstream included), so no per-image hashing is required.
4076+
4077+
Enable with `FPGA_BITSTREAM=1` (off by default). A failed PL load is fatal (`wolfBoot_panic`) unless `FPGA_NONFATAL=1` is also set, which downgrades it to a logged warning that continues the boot.
4078+
4079+
```sh
4080+
cp config/examples/zynqmp.config .config
4081+
make FPGA_BITSTREAM=1
4082+
```
4083+
4084+
Per-target support:
4085+
- **ZynqMP** (`TARGET=zynq`): supported. The bitstream (a bootgen `.bin`, not `.bit`) is staged to its `load` address in DDR and handed to the PMU firmware via the `PM_FPGA_LOAD` EEMI call (xilfpga over the CSU DMA / PCAP).
4086+
- **Zynq-7000** (`TARGET=zynq7000`): supported (full bitstream). Programmed directly through the DevC/PCAP DMA engine (UG585 ch.6). Partial reconfiguration is not yet implemented.
4087+
- **Versal** (`TARGET=versal`): not yet implemented - Versal programs the PL with a PDI loaded by the PLM (XilLoader Load-PDI IPI), not a raw bitstream. `hal_fpga_load` is a stub; leave `FPGA_BITSTREAM` off on Versal until that path lands.
4088+
4089+
The `compatible` string selects full vs partial: any value containing `partial` requests partial reconfiguration, otherwise a full bitstream is loaded. Typical full-bitstream values are `u-boot,zynqmp-fpga-ddrauth`, `u-boot,zynqmp-fpga-enc`, or `u-boot,fpga-legacy`.
4090+
4091+
Example FIT layout:
4092+
4093+
```dts
4094+
images {
4095+
kernel-1 { ... };
4096+
fdt-1 { ... };
4097+
fpga-1 {
4098+
description = "FPGA bitstream";
4099+
data = /incbin/("system.bit.bin"); /* bootgen .bin */
4100+
type = "fpga";
4101+
arch = "arm64"; /* "arm" for zynq7000 */
4102+
compression = "none";
4103+
load = <0x10000000>; /* DDR staging address */
4104+
compatible = "u-boot,zynqmp-fpga-ddrauth";
4105+
hash-1 { algo = "sha256"; };
4106+
};
4107+
};
4108+
configurations {
4109+
default = "conf-zcu102";
4110+
conf-zcu102 {
4111+
kernel = "kernel-1";
4112+
fdt = "fdt-1";
4113+
fpga = "fpga-1";
4114+
};
4115+
};
4116+
```
4117+
4118+
Successful programming prints (ZynqMP):
4119+
```
4120+
FIT: programming FPGA 'fpga-1' (N bytes, full)
4121+
FPGA status: 0x...
4122+
FIT: FPGA programmed
4123+
```
4124+
40734125
40744126
## Xilinx Zynq-7000 (ZC702)
40754127

hal/versal.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,25 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
13541354
return -1;
13551355
}
13561356

1357+
#ifdef WOLFBOOT_FPGA_BITSTREAM
1358+
/* Versal programs the PL with a PDI (Programmable Device Image), not a raw
1359+
* bitstream. Runtime PL configuration is done by the PLM via the XilLoader
1360+
* "Load PDI" command (XLOADER_CMD_ID_LOAD_PDI), reached by building an IPI
1361+
* request to the PLM channel with the DDR PDI address/size and polling the
1362+
* PLM response. That path is not yet implemented; until it lands, leave
1363+
* FPGA_BITSTREAM disabled for Versal (the FIT loader treats a failed load
1364+
* as fatal unless FPGA_NONFATAL is set). */
1365+
int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
1366+
{
1367+
(void)flags;
1368+
(void)addr;
1369+
(void)size;
1370+
wolfBoot_printf("Versal FPGA/PDI load not implemented "
1371+
"(needs PLM XilLoader Load-PDI IPI)\n");
1372+
return -1;
1373+
}
1374+
#endif /* WOLFBOOT_FPGA_BITSTREAM */
1375+
13571376

13581377
/* ============================================================================
13591378
* External Flash Interface

hal/versal.its

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,35 @@
2929
algo = "sha256";
3030
};
3131
};
32+
/* FPGA bitstream sub-image (requires FPGA_BITSTREAM=1). Add the
33+
* "fpga = ..." reference to the configuration node below. The
34+
* bitstream must be a bootgen .bin staged to its load address.
35+
* NOTE: Versal PL config is a PDI loaded by the PLM (XilLoader
36+
* Load-PDI IPI) - the wolfBoot Versal hal_fpga_load is currently
37+
* a stub, so leave this disabled on Versal. On ZynqMP/Zynq-7000
38+
* this is the working convention.
39+
*
40+
* fpga-1 {
41+
* description = "FPGA bitstream";
42+
* data = /incbin/("../system.bit.bin");
43+
* type = "fpga";
44+
* arch = "arm64";
45+
* compression = "none";
46+
* load = <0x10000000>;
47+
* compatible = "u-boot,zynqmp-fpga-ddrauth";
48+
* hash-1 {
49+
* algo = "sha256";
50+
* };
51+
* };
52+
*/
3253
};
3354
configurations {
3455
default = "conf1";
3556
conf1 {
3657
description = "Linux kernel and FDT blob";
3758
kernel = "kernel-1";
3859
fdt = "fdt-1";
60+
/* fpga = "fpga-1"; */
3961
hash-1 {
4062
algo = "sha256";
4163
};

hal/zynq.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ static void smc_call(struct pt_regs *args)
197197
#define PM_MMIO_WRITE 0x13
198198
#define PM_MMIO_READ 0x14
199199

200+
/* FPGA / PL programming (xilfpga via PMU firmware / TF-A) */
201+
#define PM_FPGA_LOAD 0x16 /* 22 */
202+
#define PM_FPGA_GET_STATUS 0x17 /* 23 */
203+
/* pm_fpga_load flags (bit 0 selects full vs partial bitstream) */
204+
#define XFPGA_FULLBIT_EN 0x0
205+
#define XFPGA_PARTIAL_EN 0x1
206+
200207
/* AES */
201208
/* requires PMU built with -DENABLE_SECURE_VAL=1 */
202209
#define PM_SECURE_AES 0x2F
@@ -1713,6 +1720,43 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
17131720
return 0;
17141721
}
17151722

1723+
#ifdef WOLFBOOT_FPGA_BITSTREAM
1724+
/* Program the PL by handing the bitstream to the PMU firmware (xilfpga)
1725+
* via the PM_FPGA_LOAD EEMI call. The bitstream must be a bootgen .bin
1726+
* resident in DDR; it is flushed from the D-cache so the CSU DMA sees
1727+
* the committed bytes. */
1728+
int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
1729+
{
1730+
uint32_t ret_payload[PM_ARGS_CNT];
1731+
uint32_t pmflags;
1732+
uint32_t words;
1733+
1734+
/* PMU firmware expects the size as a count of 32-bit words. */
1735+
words = (uint32_t)((size + 3U) / 4U);
1736+
pmflags = (flags == HAL_FPGA_PARTIAL) ? XFPGA_PARTIAL_EN : XFPGA_FULLBIT_EN;
1737+
1738+
/* Ensure the bitstream is committed to DDR before the CSU DMA reads it. */
1739+
flush_dcache_range((unsigned long)addr, (unsigned long)(addr + size));
1740+
1741+
memset(ret_payload, 0, sizeof(ret_payload));
1742+
/* arg0=addr_low, arg1=addr_high, arg2=size(words), arg3=flags */
1743+
pmu_request(PM_FPGA_LOAD,
1744+
(uint32_t)(addr & 0xFFFFFFFF), (uint32_t)((uint64_t)addr >> 32),
1745+
words, pmflags, ret_payload);
1746+
if (ret_payload[0] != 0) {
1747+
wolfBoot_printf("PM_FPGA_LOAD failed: %u\n", ret_payload[0]);
1748+
return -1;
1749+
}
1750+
1751+
/* Confirm the PL reports configured (PCAP status). */
1752+
memset(ret_payload, 0, sizeof(ret_payload));
1753+
pmu_request(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload);
1754+
wolfBoot_printf("FPGA status: 0x%x\n", ret_payload[1]);
1755+
1756+
return 0;
1757+
}
1758+
#endif /* WOLFBOOT_FPGA_BITSTREAM */
1759+
17161760
/* Xilinx Write uses SPI mode and Page Program 0x02 */
17171761
/* Issues using write with QSPI mode */
17181762
int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, int len)

hal/zynq7000.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,114 @@ uint64_t hal_get_timer_us(void)
871871
return (count * 1000000ULL) / (uint64_t)Z7_GTIMER_FREQ_HZ;
872872
}
873873

874+
#ifdef WOLFBOOT_FPGA_BITSTREAM
875+
/* PL programming timeout (microseconds). */
876+
#ifndef Z7_FPGA_TIMEOUT_US
877+
#define Z7_FPGA_TIMEOUT_US 1000000ULL /* 1 second */
878+
#endif
879+
880+
/* Clean the D-cache over [start, start+len) by MVA so the DevC DMA sees
881+
* the committed bitstream bytes (DCCMVAC, L1 line = 32B). Mirrors the
882+
* SDMA coherency path. */
883+
static void z7_dcache_clean_range(uintptr_t start, uint32_t len)
884+
{
885+
uintptr_t addr;
886+
uintptr_t end = (start + len + 31U) & ~31U;
887+
start &= ~31U;
888+
for (addr = start; addr < end; addr += 32U) {
889+
__asm__ volatile("mcr p15, 0, %0, c7, c10, 1" : : "r"(addr) : "memory");
890+
}
891+
__asm__ volatile("dsb sy" : : : "memory");
892+
}
893+
894+
/* Program the PL from a bootgen .bin bitstream resident in DDR using the
895+
* DevC PCAP DMA engine (UG585 ch.6 / Xilinx XDcfg full-bitstream flow).
896+
* Only the full-bitstream path is implemented; partial reconfiguration
897+
* returns an error. */
898+
int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
899+
{
900+
uint32_t sts;
901+
uint32_t words = (uint32_t)((size + 3U) / 4U);
902+
uint64_t t0;
903+
904+
if (flags == HAL_FPGA_PARTIAL) {
905+
/* Partial reconfig leaves PROG_B asserted and routes via PCAP_PR;
906+
* not implemented in this initial full-bitstream path. */
907+
wolfBoot_printf("Z7 FPGA: partial reconfig not implemented\n");
908+
return -1;
909+
}
910+
if (addr > 0xFFFFFFFFU || size == 0) {
911+
return -1;
912+
}
913+
914+
/* 1. Unlock DevC and select PCAP (not ICAP). */
915+
Z7_DEVC_UNLOCK = Z7_DEVC_UNLOCK_KEY;
916+
Z7_DEVC_CTRL |= (Z7_DEVC_CTRL_PCAP_MODE | Z7_DEVC_CTRL_PCAP_PR);
917+
/* Disable internal PCAP loopback. */
918+
Z7_DEVC_MCTRL &= ~Z7_DEVC_MCTRL_PCAP_LPBK;
919+
920+
/* 2. Clear sticky interrupts. */
921+
Z7_DEVC_INT_STS = Z7_DEVC_INT_ALL;
922+
923+
/* 3. Pulse PROG_B low then high to clear the PL (full bitstream). */
924+
Z7_DEVC_CTRL &= ~Z7_DEVC_CTRL_PCFG_PROG_B;
925+
t0 = hal_get_timer_us();
926+
while (Z7_DEVC_STATUS & Z7_DEVC_STATUS_PCFG_INIT) {
927+
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
928+
wolfBoot_printf("Z7 FPGA: timeout waiting PCFG_INIT clear\n");
929+
return -1;
930+
}
931+
}
932+
Z7_DEVC_CTRL |= Z7_DEVC_CTRL_PCFG_PROG_B;
933+
t0 = hal_get_timer_us();
934+
while (!(Z7_DEVC_STATUS & Z7_DEVC_STATUS_PCFG_INIT)) {
935+
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
936+
wolfBoot_printf("Z7 FPGA: timeout waiting PCFG_INIT set\n");
937+
return -1;
938+
}
939+
}
940+
941+
/* 4. Clear interrupts and flush the config FIFOs. */
942+
Z7_DEVC_INT_STS = Z7_DEVC_INT_ALL;
943+
Z7_DEVC_MCTRL |= (Z7_DEVC_MCTRL_RFIFO_FLUSH | Z7_DEVC_MCTRL_WFIFO_FLUSH);
944+
945+
/* 5. Make the bitstream coherent in DDR for the DMA. */
946+
z7_dcache_clean_range(addr, (uint32_t)size);
947+
948+
/* 6. Program the DMA: src = DDR bitstream (LSB=1 marks last descriptor),
949+
* dst = PCAP sentinel. Lengths are in 32-bit words. */
950+
Z7_DEVC_DMA_SRC = ((uint32_t)addr) | Z7_DEVC_DMA_LAST;
951+
Z7_DEVC_DMA_DST = Z7_DEVC_DMA_DEST_PCAP;
952+
Z7_DEVC_DMA_SRC_LEN = words;
953+
Z7_DEVC_DMA_DST_LEN = words;
954+
955+
/* 7. Wait for DMA done (and check error bits). */
956+
t0 = hal_get_timer_us();
957+
do {
958+
sts = Z7_DEVC_INT_STS;
959+
if (sts & Z7_DEVC_INT_ERR_MASK) {
960+
wolfBoot_printf("Z7 FPGA: DMA error, INT_STS=0x%x\n", sts);
961+
return -1;
962+
}
963+
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
964+
wolfBoot_printf("Z7 FPGA: timeout waiting DMA done\n");
965+
return -1;
966+
}
967+
} while (!(sts & Z7_DEVC_INT_DMA_DONE));
968+
969+
/* 8. Wait for the PL to report configuration complete. */
970+
t0 = hal_get_timer_us();
971+
while (!(Z7_DEVC_INT_STS & Z7_DEVC_INT_PCFG_DONE)) {
972+
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
973+
wolfBoot_printf("Z7 FPGA: timeout waiting PCFG_DONE\n");
974+
return -1;
975+
}
976+
}
977+
978+
return 0;
979+
}
980+
#endif /* WOLFBOOT_FPGA_BITSTREAM */
981+
874982
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
875983
/* ============================================================================
876984
* SDHCI (SD Card / eMMC) Platform Support

hal/zynq7000.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,51 @@
199199
#define Z7_GTIMER_FREQ_HZ 333333333UL
200200
#endif
201201

202-
/* DevC (Device Configuration: AES + bitstream loader). UG585 ch.6. */
202+
/* DevC (Device Configuration: AES + bitstream loader). UG585 ch.6.
203+
* Register offsets/bits mirror Xilinx xdevcfg_hw.h (XDCFG_*). */
203204
#define Z7_DEVC_BASE 0xF8007000UL
205+
#define Z7_DEVC_CTRL (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x00)))
206+
#define Z7_DEVC_LOCK (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x04)))
207+
#define Z7_DEVC_CFG (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x08)))
208+
#define Z7_DEVC_INT_STS (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x0C)))
209+
#define Z7_DEVC_INT_MASK (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x10)))
210+
#define Z7_DEVC_STATUS (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x14)))
211+
#define Z7_DEVC_DMA_SRC (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x18)))
212+
#define Z7_DEVC_DMA_DST (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x1C)))
213+
#define Z7_DEVC_DMA_SRC_LEN (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x20)))
214+
#define Z7_DEVC_DMA_DST_LEN (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x24)))
215+
#define Z7_DEVC_UNLOCK (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x34)))
216+
#define Z7_DEVC_MCTRL (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x80)))
217+
218+
/* CTRL bits */
219+
#define Z7_DEVC_CTRL_FORCE_RST 0x80000000U /* bit 31 */
220+
#define Z7_DEVC_CTRL_PCFG_PROG_B 0x40000000U /* bit 30 */
221+
#define Z7_DEVC_CTRL_PCAP_PR 0x08000000U /* bit 27: 1=PCAP, 0=ICAP */
222+
#define Z7_DEVC_CTRL_PCAP_MODE 0x04000000U /* bit 26: PCAP enable */
223+
#define Z7_DEVC_CTRL_QUARTER_RATE 0x02000000U /* bit 25 */
224+
225+
/* INT_STS bits */
226+
#define Z7_DEVC_INT_DMA_DONE 0x00002000U /* bit 13 */
227+
#define Z7_DEVC_INT_D_P_DONE 0x00001000U /* bit 12: DMA+PCAP done */
228+
#define Z7_DEVC_INT_PCFG_DONE 0x00000004U /* bit 2 */
229+
/* DMA / AXI / config error aggregate (UG585 Table 6-x). */
230+
#define Z7_DEVC_INT_ERR_MASK 0x00F0C860U
231+
#define Z7_DEVC_INT_ALL 0xFFFFFFFFU
232+
233+
/* STATUS bits */
234+
#define Z7_DEVC_STATUS_DMA_CMD_FULL 0x80000000U /* bit 31 */
235+
#define Z7_DEVC_STATUS_PCFG_INIT 0x00000010U /* bit 4 */
236+
237+
/* MCTRL bits */
238+
#define Z7_DEVC_MCTRL_PCAP_LPBK 0x00000010U /* internal loopback */
239+
#define Z7_DEVC_MCTRL_RFIFO_FLUSH 0x00000400U /* bit 10 */
240+
#define Z7_DEVC_MCTRL_WFIFO_FLUSH 0x00000200U /* bit 9 */
241+
242+
/* UNLOCK magic and PCAP DMA sentinel (UG585 ch.6 / XDcfg). The DMA
243+
* address LSB set to 1 marks the last (only) descriptor in the chain. */
244+
#define Z7_DEVC_UNLOCK_KEY 0x757BDF0DU
245+
#define Z7_DEVC_DMA_DEST_PCAP 0xFFFFFFFFU
246+
#define Z7_DEVC_DMA_LAST 0x00000001U
204247

205248
/* GIC (PL390 / GIC-400 v1) - per-CPU interface and distributor. */
206249
#define Z7_GIC_CPUIF_BASE 0xF8F00100UL

0 commit comments

Comments
 (0)