Skip to content
Draft
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
7 changes: 7 additions & 0 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ ifeq ($(ARCH),AARCH64)
# Support detection and skip of U-Boot legacy header */
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
CFLAGS+=-DWOLFBOOT_DUALBOOT
# FPGA bitstream-from-FIT is supported via the PMU firmware
# (PM_FPGA_LOAD). Enable with FPGA_BITSTREAM=1 (opt-in).

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

ifeq ($(TARGET),va416x0)
Expand Down
52 changes: 52 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -4070,6 +4070,58 @@ FDT: Set chosen (...), linux,initrd-start=1073741824
FDT: Set chosen (...), linux,initrd-end=...
```

**FPGA bitstream from FIT**

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.

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.

```sh
cp config/examples/zynqmp.config .config
make FPGA_BITSTREAM=1
```

Per-target support:
- **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).
- **Zynq-7000** (`TARGET=zynq7000`): supported (full bitstream). Programmed directly through the DevC/PCAP DMA engine (UG585 ch.6). Partial reconfiguration is not yet implemented.
- **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.

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`.

Example FIT layout:

```dts
images {
kernel-1 { ... };
fdt-1 { ... };
fpga-1 {
description = "FPGA bitstream";
data = /incbin/("system.bit.bin"); /* bootgen .bin */
type = "fpga";
arch = "arm64"; /* "arm" for zynq7000 */
compression = "none";
load = <0x10000000>; /* DDR staging address */
compatible = "u-boot,zynqmp-fpga-ddrauth";
hash-1 { algo = "sha256"; };
};
};
configurations {
default = "conf-zcu102";
conf-zcu102 {
kernel = "kernel-1";
fdt = "fdt-1";
fpga = "fpga-1";
};
};
```

Successful programming prints (ZynqMP):
```
FIT: programming FPGA 'fpga-1' (N bytes, full)
FPGA status: 0x...
FIT: FPGA programmed
```


## Xilinx Zynq-7000 (ZC702)

Expand Down
19 changes: 19 additions & 0 deletions hal/versal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,25 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
return -1;
}

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


/* ============================================================================
* External Flash Interface
Expand Down
22 changes: 22 additions & 0 deletions hal/versal.its
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,35 @@
algo = "sha256";
};
};
/* FPGA bitstream sub-image (requires FPGA_BITSTREAM=1). Add the
* "fpga = ..." reference to the configuration node below. The
* bitstream must be a bootgen .bin staged to its load address.
* NOTE: Versal PL config is a PDI loaded by the PLM (XilLoader
* Load-PDI IPI) - the wolfBoot Versal hal_fpga_load is currently
* a stub, so leave this disabled on Versal. On ZynqMP/Zynq-7000
* this is the working convention.
*
* fpga-1 {
* description = "FPGA bitstream";
* data = /incbin/("../system.bit.bin");
* type = "fpga";
* arch = "arm64";
* compression = "none";
* load = <0x10000000>;
* compatible = "u-boot,zynqmp-fpga-ddrauth";
* hash-1 {
* algo = "sha256";
* };
* };
*/
};
configurations {
default = "conf1";
conf1 {
description = "Linux kernel and FDT blob";
kernel = "kernel-1";
fdt = "fdt-1";
/* fpga = "fpga-1"; */
hash-1 {
algo = "sha256";
};
Expand Down
44 changes: 44 additions & 0 deletions hal/zynq.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,13 @@ static void smc_call(struct pt_regs *args)
#define PM_MMIO_WRITE 0x13
#define PM_MMIO_READ 0x14

/* FPGA / PL programming (xilfpga via PMU firmware / TF-A) */
#define PM_FPGA_LOAD 0x16 /* 22 */
#define PM_FPGA_GET_STATUS 0x17 /* 23 */
/* pm_fpga_load flags (bit 0 selects full vs partial bitstream) */
#define XFPGA_FULLBIT_EN 0x0
#define XFPGA_PARTIAL_EN 0x1

/* AES */
/* requires PMU built with -DENABLE_SECURE_VAL=1 */
#define PM_SECURE_AES 0x2F
Expand Down Expand Up @@ -1713,6 +1720,43 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
return 0;
}

#ifdef WOLFBOOT_FPGA_BITSTREAM
/* Program the PL by handing the bitstream to the PMU firmware (xilfpga)
* via the PM_FPGA_LOAD EEMI call. The bitstream must be a bootgen .bin
* resident in DDR; it is flushed from the D-cache so the CSU DMA sees
* the committed bytes. */
int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
{
uint32_t ret_payload[PM_ARGS_CNT];
uint32_t pmflags;
uint32_t words;

/* PMU firmware expects the size as a count of 32-bit words. */
words = (uint32_t)((size + 3U) / 4U);
pmflags = (flags == HAL_FPGA_PARTIAL) ? XFPGA_PARTIAL_EN : XFPGA_FULLBIT_EN;

/* Ensure the bitstream is committed to DDR before the CSU DMA reads it. */
flush_dcache_range((unsigned long)addr, (unsigned long)(addr + size));

memset(ret_payload, 0, sizeof(ret_payload));
/* arg0=addr_low, arg1=addr_high, arg2=size(words), arg3=flags */
pmu_request(PM_FPGA_LOAD,
(uint32_t)(addr & 0xFFFFFFFF), (uint32_t)((uint64_t)addr >> 32),
words, pmflags, ret_payload);
if (ret_payload[0] != 0) {
wolfBoot_printf("PM_FPGA_LOAD failed: %u\n", ret_payload[0]);
return -1;
}

/* Confirm the PL reports configured (PCAP status). */
memset(ret_payload, 0, sizeof(ret_payload));
pmu_request(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload);
wolfBoot_printf("FPGA status: 0x%x\n", ret_payload[1]);

return 0;
}
#endif /* WOLFBOOT_FPGA_BITSTREAM */

/* Xilinx Write uses SPI mode and Page Program 0x02 */
/* Issues using write with QSPI mode */
int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, int len)
Expand Down
108 changes: 108 additions & 0 deletions hal/zynq7000.c
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,114 @@ uint64_t hal_get_timer_us(void)
return (count * 1000000ULL) / (uint64_t)Z7_GTIMER_FREQ_HZ;
}

#ifdef WOLFBOOT_FPGA_BITSTREAM
/* PL programming timeout (microseconds). */
#ifndef Z7_FPGA_TIMEOUT_US
#define Z7_FPGA_TIMEOUT_US 1000000ULL /* 1 second */
#endif

/* Clean the D-cache over [start, start+len) by MVA so the DevC DMA sees
* the committed bitstream bytes (DCCMVAC, L1 line = 32B). Mirrors the
* SDMA coherency path. */
static void z7_dcache_clean_range(uintptr_t start, uint32_t len)
{
uintptr_t addr;
uintptr_t end = (start + len + 31U) & ~31U;
start &= ~31U;
for (addr = start; addr < end; addr += 32U) {
__asm__ volatile("mcr p15, 0, %0, c7, c10, 1" : : "r"(addr) : "memory");
}
__asm__ volatile("dsb sy" : : : "memory");
}

/* Program the PL from a bootgen .bin bitstream resident in DDR using the
* DevC PCAP DMA engine (UG585 ch.6 / Xilinx XDcfg full-bitstream flow).
* Only the full-bitstream path is implemented; partial reconfiguration
* returns an error. */
int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
{
uint32_t sts;
uint32_t words = (uint32_t)((size + 3U) / 4U);
uint64_t t0;

if (flags == HAL_FPGA_PARTIAL) {
/* Partial reconfig leaves PROG_B asserted and routes via PCAP_PR;
* not implemented in this initial full-bitstream path. */
wolfBoot_printf("Z7 FPGA: partial reconfig not implemented\n");
return -1;
}
if (addr > 0xFFFFFFFFU || size == 0) {
return -1;
}

/* 1. Unlock DevC and select PCAP (not ICAP). */
Z7_DEVC_UNLOCK = Z7_DEVC_UNLOCK_KEY;
Z7_DEVC_CTRL |= (Z7_DEVC_CTRL_PCAP_MODE | Z7_DEVC_CTRL_PCAP_PR);
/* Disable internal PCAP loopback. */
Z7_DEVC_MCTRL &= ~Z7_DEVC_MCTRL_PCAP_LPBK;

/* 2. Clear sticky interrupts. */
Z7_DEVC_INT_STS = Z7_DEVC_INT_ALL;

/* 3. Pulse PROG_B low then high to clear the PL (full bitstream). */
Z7_DEVC_CTRL &= ~Z7_DEVC_CTRL_PCFG_PROG_B;
t0 = hal_get_timer_us();
while (Z7_DEVC_STATUS & Z7_DEVC_STATUS_PCFG_INIT) {
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
wolfBoot_printf("Z7 FPGA: timeout waiting PCFG_INIT clear\n");
return -1;
}
}
Z7_DEVC_CTRL |= Z7_DEVC_CTRL_PCFG_PROG_B;
t0 = hal_get_timer_us();
while (!(Z7_DEVC_STATUS & Z7_DEVC_STATUS_PCFG_INIT)) {
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
wolfBoot_printf("Z7 FPGA: timeout waiting PCFG_INIT set\n");
return -1;
}
}

/* 4. Clear interrupts and flush the config FIFOs. */
Z7_DEVC_INT_STS = Z7_DEVC_INT_ALL;
Z7_DEVC_MCTRL |= (Z7_DEVC_MCTRL_RFIFO_FLUSH | Z7_DEVC_MCTRL_WFIFO_FLUSH);

/* 5. Make the bitstream coherent in DDR for the DMA. */
z7_dcache_clean_range(addr, (uint32_t)size);

/* 6. Program the DMA: src = DDR bitstream (LSB=1 marks last descriptor),
* dst = PCAP sentinel. Lengths are in 32-bit words. */
Z7_DEVC_DMA_SRC = ((uint32_t)addr) | Z7_DEVC_DMA_LAST;
Z7_DEVC_DMA_DST = Z7_DEVC_DMA_DEST_PCAP;
Z7_DEVC_DMA_SRC_LEN = words;
Z7_DEVC_DMA_DST_LEN = words;

/* 7. Wait for DMA done (and check error bits). */
t0 = hal_get_timer_us();
do {
sts = Z7_DEVC_INT_STS;
if (sts & Z7_DEVC_INT_ERR_MASK) {
wolfBoot_printf("Z7 FPGA: DMA error, INT_STS=0x%x\n", sts);
return -1;
}
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
wolfBoot_printf("Z7 FPGA: timeout waiting DMA done\n");
return -1;
}
} while (!(sts & Z7_DEVC_INT_DMA_DONE));

/* 8. Wait for the PL to report configuration complete. */
t0 = hal_get_timer_us();
while (!(Z7_DEVC_INT_STS & Z7_DEVC_INT_PCFG_DONE)) {
if (hal_get_timer_us() - t0 > Z7_FPGA_TIMEOUT_US) {
wolfBoot_printf("Z7 FPGA: timeout waiting PCFG_DONE\n");
return -1;
}
}

return 0;
}
#endif /* WOLFBOOT_FPGA_BITSTREAM */

#if defined(DISK_SDCARD) || defined(DISK_EMMC)
/* ============================================================================
* SDHCI (SD Card / eMMC) Platform Support
Expand Down
45 changes: 44 additions & 1 deletion hal/zynq7000.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,51 @@
#define Z7_GTIMER_FREQ_HZ 333333333UL
#endif

/* DevC (Device Configuration: AES + bitstream loader). UG585 ch.6. */
/* DevC (Device Configuration: AES + bitstream loader). UG585 ch.6.
* Register offsets/bits mirror Xilinx xdevcfg_hw.h (XDCFG_*). */
#define Z7_DEVC_BASE 0xF8007000UL
#define Z7_DEVC_CTRL (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x00)))
#define Z7_DEVC_LOCK (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x04)))
#define Z7_DEVC_CFG (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x08)))
#define Z7_DEVC_INT_STS (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x0C)))
#define Z7_DEVC_INT_MASK (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x10)))
#define Z7_DEVC_STATUS (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x14)))
#define Z7_DEVC_DMA_SRC (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x18)))
#define Z7_DEVC_DMA_DST (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x1C)))
#define Z7_DEVC_DMA_SRC_LEN (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x20)))
#define Z7_DEVC_DMA_DST_LEN (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x24)))
#define Z7_DEVC_UNLOCK (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x34)))
#define Z7_DEVC_MCTRL (*((volatile uint32_t*)(Z7_DEVC_BASE + 0x80)))

/* CTRL bits */
#define Z7_DEVC_CTRL_FORCE_RST 0x80000000U /* bit 31 */
#define Z7_DEVC_CTRL_PCFG_PROG_B 0x40000000U /* bit 30 */
#define Z7_DEVC_CTRL_PCAP_PR 0x08000000U /* bit 27: 1=PCAP, 0=ICAP */
#define Z7_DEVC_CTRL_PCAP_MODE 0x04000000U /* bit 26: PCAP enable */
#define Z7_DEVC_CTRL_QUARTER_RATE 0x02000000U /* bit 25 */

/* INT_STS bits */
#define Z7_DEVC_INT_DMA_DONE 0x00002000U /* bit 13 */
#define Z7_DEVC_INT_D_P_DONE 0x00001000U /* bit 12: DMA+PCAP done */
#define Z7_DEVC_INT_PCFG_DONE 0x00000004U /* bit 2 */
/* DMA / AXI / config error aggregate (UG585 Table 6-x). */
#define Z7_DEVC_INT_ERR_MASK 0x00F0C860U
#define Z7_DEVC_INT_ALL 0xFFFFFFFFU

/* STATUS bits */
#define Z7_DEVC_STATUS_DMA_CMD_FULL 0x80000000U /* bit 31 */
#define Z7_DEVC_STATUS_PCFG_INIT 0x00000010U /* bit 4 */

/* MCTRL bits */
#define Z7_DEVC_MCTRL_PCAP_LPBK 0x00000010U /* internal loopback */
#define Z7_DEVC_MCTRL_RFIFO_FLUSH 0x00000400U /* bit 10 */
#define Z7_DEVC_MCTRL_WFIFO_FLUSH 0x00000200U /* bit 9 */

/* UNLOCK magic and PCAP DMA sentinel (UG585 ch.6 / XDcfg). The DMA
* address LSB set to 1 marks the last (only) descriptor in the chain. */
#define Z7_DEVC_UNLOCK_KEY 0x757BDF0DU
#define Z7_DEVC_DMA_DEST_PCAP 0xFFFFFFFFU
#define Z7_DEVC_DMA_LAST 0x00000001U

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