Skip to content

Commit 10c4ecd

Browse files
committed
versal: bare-metal wolfIP port for Versal Gen 1 (Cortex-A72 EL3)
VMK180 bring-up reusing ~90% of the ZCU102 port: A72 at EL3, GICv3, PL011 console, Cadence GEM0 + DP83867. Platform comes up from the PLM (boot PDI over JTAG); the GEM reference clock is left to the PLM (Versal CRL is PMC-protected). Poll-driven RX, all-address PHY scan preferring the linked PHY. HW-tested on a VMK180: DHCP + ping + UDP echo.
1 parent a622ce0 commit 10c4ecd

23 files changed

Lines changed: 3361 additions & 0 deletions

src/port/versal/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.o
2+
*.elf
3+
*.bin
4+
BOOT.BIN
5+
.layout_stamp

src/port/versal/Makefile

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Xilinx Versal Gen 1 (VMK180, Cortex-A72) wolfIP bare-metal port
2+
#
3+
# Build: make CROSS_COMPILE=aarch64-none-elf-
4+
#
5+
# Toolchain: ARM GNU aarch64-none-elf-gcc (tested with 14.3.rel1).
6+
#
7+
# UNTESTED ON HARDWARE -- structural scaffold mirroring src/port/zcu102/.
8+
9+
CROSS_COMPILE ?= aarch64-none-elf-
10+
CC := $(CROSS_COMPILE)gcc
11+
OBJCOPY := $(CROSS_COMPILE)objcopy
12+
SIZE := $(CROSS_COMPILE)size
13+
14+
ROOT := ../../..
15+
16+
# Cortex-A72, AArch64, EL3 single-EL bare-metal. No SIMD/FP in the
17+
# wolfIP/driver paths - keep -mgeneral-regs-only to catch any
18+
# accidental FP use and make the ABI deterministic for cert.
19+
CFLAGS := -mcpu=cortex-a72 -mgeneral-regs-only
20+
CFLAGS += -Os -ffreestanding -fno-builtin -fno-common
21+
CFLAGS += -fdata-sections -ffunction-sections
22+
CFLAGS += -g -Wall -Wextra -Werror -Wno-unused-parameter
23+
CFLAGS += -std=gnu99
24+
CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src -I$(ROOT)/src/port
25+
CFLAGS += -DVERSAL -DXILINX_AARCH64
26+
# Append extra defines for investigation builds, e.g.:
27+
# make CFLAGS_EXTRA="-DDEBUG_GIC -DDEBUG_GEM -DDEBUG_PHY"
28+
CFLAGS += $(CFLAGS_EXTRA)
29+
30+
ASFLAGS := -mcpu=cortex-a72
31+
32+
# Layout selector. Default ocm keeps the OCM-only layout that the JTAG
33+
# iteration scripts depend on (everything in OCM @ 0xFFFC0000). Pass
34+
# LAYOUT=ddr to relink for DDR @ 0x10000000 -- which is what wolfBoot
35+
# uses (WOLFBOOT_LOAD_ADDRESS in zynqmp.config also applies to Versal
36+
# when adapted).
37+
LAYOUT ?= ocm
38+
ifeq ($(LAYOUT),ddr)
39+
LDSCRIPT := target_ddr.ld
40+
CFLAGS += -DVERSAL_LAYOUT_DDR
41+
else ifeq ($(LAYOUT),ocm)
42+
LDSCRIPT := target.ld
43+
CFLAGS += -DVERSAL_LAYOUT_OCM
44+
else
45+
$(error LAYOUT must be 'ocm' or 'ddr')
46+
endif
47+
48+
LDFLAGS := -nostdlib -nostartfiles -T $(LDSCRIPT) -Wl,-gc-sections
49+
# Replace newlib's aarch64 memset/memcpy (which use 'dc zva' and may
50+
# hang on a similar Cortex-A72 setup; the safe pattern is to override
51+
# them as we did on ZCU102).
52+
LDFLAGS += -Wl,--wrap=memset -Wl,--wrap=memcpy
53+
54+
LOCAL_C := main.c uart.c mmu.c gic.c gem.c phy_dp83867.c entropy.c
55+
LOCAL_S := startup.S
56+
LOCAL_OBJS := $(LOCAL_C:.c=.o) $(LOCAL_S:.S=.o)
57+
58+
WOLFIP_OBJ := wolfip.o
59+
OBJS := $(LOCAL_OBJS) $(WOLFIP_OBJ)
60+
61+
# Keep 'all' the default goal even though the layout-stamp rules below
62+
# are defined before it.
63+
.DEFAULT_GOAL := all
64+
65+
# A change in LAYOUT must force a full rebuild: OCM objects link against
66+
# 0xFFFC0000 and a DDR build against 0x10000000, so reusing stale objects
67+
# across a layout switch silently produces a wrong image. The stamp
68+
# records the last LAYOUT; its mtime only bumps when LAYOUT actually
69+
# changes, so same-layout incremental builds are unaffected.
70+
LAYOUT_STAMP := .layout_stamp
71+
.PHONY: FORCE
72+
FORCE:
73+
$(LAYOUT_STAMP): FORCE
74+
@if [ "`cat $@ 2>/dev/null`" != "$(LAYOUT)" ]; then \
75+
echo "LAYOUT -> $(LAYOUT) (was `cat $@ 2>/dev/null`); forcing rebuild"; \
76+
echo "$(LAYOUT)" > $@; \
77+
fi
78+
$(OBJS): $(LAYOUT_STAMP)
79+
80+
all: app.elf
81+
@echo "Built: app.elf"
82+
@$(SIZE) app.elf
83+
84+
app.elf: $(OBJS) $(LDSCRIPT)
85+
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) \
86+
-Wl,--start-group -lc -lgcc -Wl,--end-group -o $@
87+
88+
$(WOLFIP_OBJ): $(ROOT)/src/wolfip.c
89+
$(CC) $(CFLAGS) -Wno-zero-length-bounds -Wno-type-limits -c $< -o $@
90+
91+
%.o: %.c
92+
$(CC) $(CFLAGS) -c $< -o $@
93+
94+
%.o: %.S
95+
$(CC) $(ASFLAGS) -c $< -o $@
96+
97+
clean:
98+
rm -f $(OBJS) app.elf BOOT.BIN $(LAYOUT_STAMP)
99+
100+
.PHONY: all clean help
101+
102+
help:
103+
@echo "Versal Gen 1 wolfIP build (scaffold, untested):"
104+
@echo " make - build app.elf (OCM layout)"
105+
@echo " make LAYOUT=ddr - DDR layout for wolfBoot"
106+
@echo " make clean - remove artifacts"
107+
@echo ""
108+
@echo "Override CROSS_COMPILE if your toolchain prefix differs."

src/port/versal/README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# wolfIP port: Xilinx Versal Gen 1 (VMK180)
2+
3+
**STATUS: brought up on a VMK180.** DHCP, ICMP ping and the UDP echo demo all work on real hardware (Cortex-A72 EL3, GEM0 + DP83867). See "Hardware bring-up notes" below for the Versal-specific differences.
4+
5+
## What this port is
6+
7+
Bare-metal wolfIP port for the AMD/Xilinx Versal ACAP Gen 1, demoed on the VMK180 dev board. Cortex-A72 APU 0 at EL3, GCC bare-metal, no Xilinx Standalone BSP, no FreeRTOS. Targets the same deterministic UDP/IPv4 profile as the ZCU102 port for DO-178C DAL-C qualification.
8+
9+
## What differs from ZCU102
10+
11+
| Subsystem | ZCU102 | Versal Gen 1 | Where it lives |
12+
|-----------|--------|--------------|----------------|
13+
| APU core | Cortex-A53 | Cortex-A72 | `Makefile` (-mcpu) |
14+
| Bootloader handoff | FSBL -> EL3 | PLM -> BL31 -> EL3 (or EL2) | `startup.S` |
15+
| GIC | GIC-400 (GICv2) | GIC-600 (GICv3) | `gic.c` rewritten for GICv3 system regs + GICR |
16+
| UART | Cadence | ARM PL011 | `uart.c` rewritten |
17+
| GEM count | 4 (GEM0-3) | 2 (GEM0-1) | `board.h` |
18+
| On-board RJ45 | GEM3 (INTID 95) | GEM0 (INTID 88) | `board.h` |
19+
| GEM IP | Cadence GEM3 | Cadence GEM3 | `gem.c` unchanged (just base addr / INTID) |
20+
| PHY | DP83867 RGMII | DP83867 RGMII (VMK180) | `phy_dp83867.c` unchanged |
21+
| MMU | EL3 ARMv8 | EL3 ARMv8 | `mmu.c` unchanged |
22+
| RNG | memuse entropy | memuse entropy | `entropy.c` unchanged |
23+
24+
The reused 90% (`gem.c`, `phy_dp83867.c`, `mmu.c`, `entropy.c`, `main.c`, `target.ld`, `target_ddr.ld`) is identical to the ZCU102 port; only `board.h`, `uart.c`, `gic.c`, and the startup/Makefile breadcrumbs are Versal-specific.
25+
26+
## Build
27+
28+
```
29+
cd src/port/versal
30+
make CROSS_COMPILE=aarch64-none-elf- # OCM layout (default)
31+
make CROSS_COMPILE=aarch64-none-elf- LAYOUT=ddr # DDR layout for wolfBoot
32+
```
33+
34+
Output: `app.elf`. Size info is printed at the end of the build.
35+
36+
## JTAG boot (VMK180)
37+
38+
The VMK180 must be in **JTAG boot mode** (SW1 mode pins = 0000) and
39+
power-cycled so the BootROM does not auto-boot Linux from SD/QSPI -- a
40+
booted Linux owns GEM0 and runtime-suspends its clock, which stalls the
41+
bare-metal driver. Then:
42+
43+
```
44+
XSDB=/opt/Xilinx/<ver>/Vitis/bin/xsdb \
45+
BOOT_PDI=/path/to/vmk180_boot.pdi \
46+
./jtag/boot.sh
47+
```
48+
49+
`jtag/boot.tcl` does `rst -system`, programs the boot PDI through the PMC
50+
(the PLM brings up DDR/clocks/MIO and de-isolates the A72), then resets
51+
A72 #0 (`-skip-activate-subsystem`, which lands at EL3) and loads
52+
`app.elf`. The PS console is on FT4232 **interface 1**
53+
(`VERSAL_VMK180_UART1`).
54+
55+
## Hardware bring-up notes (what was Versal-specific)
56+
57+
- **GEM RX is poll-driven.** The GICv3 CPU interface did not deliver the
58+
GEM SPI in this EL3 bring-up, so `eth_poll` polls `gem_isr` from the
59+
main loop to drain the RX ring (the IRQ path stays registered but
60+
dormant).
61+
- **GEM clock is owned by the PLM.** The CRL block is PMC/PLM-protected;
62+
a direct APU write to `CRL.GEM0_REF_CTRL` (`0xFF5E0118`, not the ZynqMP
63+
`+0x50`) stalls the bus. The PLM already configures the GEM clock, so
64+
`gem.c` does not touch it. The correct Versal offsets are documented in
65+
`board.h` for reference.
66+
- **Two DP83867 PHYs.** The VMK180 presents more than one PHY on the MDIO
67+
bus; `gem.c` scans all 32 addresses and prefers the one reporting copper
68+
link (the on-board RJ45 PHY answered at addr 1). Make sure the cable is
69+
in the **PS-GEM** RJ45, not the System Controller jack.
70+
- `SCR_EL3` IRQ/FIQ/EA routing is carried over from the ZCU102 fix and is
71+
harmless on the A72.
72+
73+
## Files
74+
75+
Identical layout to `src/port/zcu102/` plus `jtag/` (PDI-based JTAG
76+
loader). See that port's README for per-file responsibilities. The
77+
differences listed in the table above are the only substantive
78+
Versal-specific code.

src/port/versal/board.h

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/* board.h
2+
*
3+
* Copyright (C) 2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfIP TCP/IP stack.
6+
*
7+
* wolfIP is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfIP is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20+
*
21+
* Xilinx Versal Gen 1 (VCxxxx / VMK180 board) PS register base
22+
* addresses and GIC SPI IDs. Values are derived from the Versal ACAP
23+
* Technical Reference Manual (AM011), the VMK180 board user guide,
24+
* and the published `versal.dtsi` device tree. No Xilinx BSP header
25+
* (xparameters.h) or xilstandalone code is referenced.
26+
*
27+
* UNTESTED ON HARDWARE -- code-only scaffold while the lab board is
28+
* unavailable. The structure mirrors src/port/zcu102/. Key
29+
* differences from ZynqMP are:
30+
* - Cortex-A72 (not A53), with PLM handoff at EL2
31+
* - GICv3 distributor + redistributor (no GICv2 legacy GICC)
32+
* - ARM PL011 UART (not Cadence)
33+
* - 2 GEMs (GEM0/GEM1) instead of 4; on-board RJ45 is GEM0 on VMK180
34+
*/
35+
#ifndef VERSAL_BOARD_H
36+
#define VERSAL_BOARD_H
37+
38+
#include <stdint.h>
39+
40+
/* ---------------------------------------------------------------------
41+
* Memory map (Versal PS)
42+
* ------------------------------------------------------------------- */
43+
#define DDR_BASE 0x00000000UL
44+
#define DDR_SIZE 0x80000000UL /* 2 GB lower bank */
45+
46+
/* OCM on Versal lives at 0xFFFC0000 (256 KB). Same as ZynqMP. */
47+
#define OCM_BASE 0xFFFC0000UL
48+
#define OCM_SIZE 0x00040000UL
49+
50+
/* ---------------------------------------------------------------------
51+
* PS peripherals
52+
* ------------------------------------------------------------------- */
53+
#define UART0_BASE 0xFF000000UL /* PL011 */
54+
#define UART1_BASE 0xFF010000UL /* PL011 */
55+
56+
#define GEM0_BASE 0xFF0C0000UL /* on-board GEM (VMK180) */
57+
#define GEM1_BASE 0xFF0D0000UL
58+
59+
#define CRL_APB_BASE 0xFF5E0000UL /* LPD clock & reset */
60+
#define IOU_SLCR_BASE 0xFF180000UL
61+
62+
/* GICv3: distributor + redistributor */
63+
#define GICD_BASE 0xF9000000UL
64+
#define GICR_BASE 0xF9080000UL /* per-CPU redistributors */
65+
66+
/* ---------------------------------------------------------------------
67+
* GIC SPI numbers as GIC INTIDs (ARM GIC numbering: SPI N -> INTID 32+N).
68+
* Versal versal.dtsi:
69+
* GEM0: GIC_SPI 56 -> INTID 88
70+
* GEM1: GIC_SPI 58 -> INTID 90
71+
* ------------------------------------------------------------------- */
72+
#define IRQ_GEM0 (32 + 56) /* GIC_SPI 56 -> INTID 88,
73+
* on-board VMK180 RJ45 */
74+
#define IRQ_GEM1 (32 + 58) /* GIC_SPI 58 -> INTID 90 */
75+
76+
/* ---------------------------------------------------------------------
77+
* CRL clock and reset registers (LPD). Versal's CRL register map is NOT
78+
* the same as ZynqMP: the GEM clock/reset offsets differ. Verified
79+
* against the Versal PSM firmware crl.h (Vitis embeddedsw):
80+
* CRL.GEM0_REF_CTRL = CRL + 0x118 (CLKACT bit 25, DIVISOR0 [13:8],
81+
* SRCSEL [2:0])
82+
* CRL.RST_GEM0 = CRL + 0x308 (RESET bit 0)
83+
* ------------------------------------------------------------------- */
84+
#define CRL_APB_GEM0_REF_CTRL (CRL_APB_BASE + 0x118) /* Versal CRL.GEM0_REF_CTRL */
85+
#define CRL_GEM0_RST (CRL_APB_BASE + 0x308) /* Versal CRL.RST_GEM0 */
86+
#define CRL_GEM0_REF_CTRL_CLKACT (1u << 25)
87+
#define CRL_RST_GEM0_RESET (1u << 0)
88+
89+
/* ---------------------------------------------------------------------
90+
* PL011 UART0 - on-board USB-UART on VMK180
91+
* ------------------------------------------------------------------- */
92+
#define UART_BAUD 115200
93+
94+
/* MAC address for eth0. Locally-administered, even first octet. */
95+
#ifndef WOLFIP_MAC_0
96+
#define WOLFIP_MAC_0 0x02
97+
#endif
98+
#ifndef WOLFIP_MAC_1
99+
#define WOLFIP_MAC_1 0x00
100+
#endif
101+
#ifndef WOLFIP_MAC_2
102+
#define WOLFIP_MAC_2 0x5A
103+
#endif
104+
#ifndef WOLFIP_MAC_3
105+
#define WOLFIP_MAC_3 0x11
106+
#endif
107+
#ifndef WOLFIP_MAC_4
108+
#define WOLFIP_MAC_4 0x22
109+
#endif
110+
#ifndef WOLFIP_MAC_5
111+
#define WOLFIP_MAC_5 0x33
112+
#endif
113+
114+
#endif /* VERSAL_BOARD_H */

src/port/versal/config.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* config.h
2+
*
3+
* Copyright (C) 2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfIP TCP/IP stack.
6+
*
7+
* wolfIP is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation; either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* wolfIP is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20+
*
21+
* wolfIP configuration for Xilinx ZCU102 (UltraScale+ MPSoC, A53-0 EL3
22+
* bare-metal). UDP-only profile aimed at deterministic DAL-C use.
23+
*/
24+
#ifndef WOLF_CONFIG_H
25+
#define WOLF_CONFIG_H
26+
27+
#ifndef CONFIG_IPFILTER
28+
#define CONFIG_IPFILTER 0
29+
#endif
30+
31+
#define ETHERNET
32+
#define LINK_MTU 1536
33+
34+
/* UDP-only profile in intent: the application does not call
35+
* wolfIP_sock_socket() with IPSTACK_SOCK_STREAM. MAX_TCPSOCKETS is set
36+
* to a small non-zero value only because core wolfIP currently sizes
37+
* its timer heap via MAX_TIMERS = MAX_TCPSOCKETS * 3, and DHCP / ARP
38+
* aging need timers. With MAX_TCPSOCKETS=0 the timer-heap insert path
39+
* is permanently full and DHCP cannot schedule its retransmit timer.
40+
* A core wolfIP follow-up should decouple MAX_TIMERS from
41+
* MAX_TCPSOCKETS so DAL-C builds can truly opt TCP code out at
42+
* compile time. */
43+
#define MAX_TCPSOCKETS 2
44+
#define MAX_UDPSOCKETS 4
45+
#define MAX_ICMPSOCKETS 1
46+
#define RXBUF_SIZE (LINK_MTU * 4)
47+
#define TXBUF_SIZE (LINK_MTU * 4)
48+
49+
#define MAX_NEIGHBORS 16
50+
51+
#ifndef WOLFIP_MAX_INTERFACES
52+
#define WOLFIP_MAX_INTERFACES 1
53+
#endif
54+
55+
#ifndef WOLFIP_ENABLE_FORWARDING
56+
#define WOLFIP_ENABLE_FORWARDING 0
57+
#endif
58+
59+
#ifndef WOLFIP_ENABLE_LOOPBACK
60+
#define WOLFIP_ENABLE_LOOPBACK 0
61+
#endif
62+
63+
#ifndef WOLFIP_ENABLE_DHCP
64+
#define WOLFIP_ENABLE_DHCP 1
65+
#endif
66+
67+
/* Static IP fallback (used if DHCP is disabled or times out). */
68+
#define WOLFIP_IP "192.168.1.100"
69+
#define WOLFIP_NETMASK "255.255.255.0"
70+
#define WOLFIP_GW "192.168.1.1"
71+
#define WOLFIP_STATIC_DNS_IP "8.8.8.8"
72+
73+
#if WOLFIP_ENABLE_DHCP
74+
#define DHCP
75+
#define DHCP_DISCOVER_RETRIES 2
76+
#define DHCP_REQUEST_RETRIES 2
77+
#endif
78+
79+
/* Hardware debug: define for verbose GEM / MDIO / DHCP logging. */
80+
/* #define DEBUG_HW */
81+
82+
#endif /* WOLF_CONFIG_H */

0 commit comments

Comments
 (0)