Yocto
The Yocto flow builds the Linux images with AMD’s Embedded Development Framework (EDF), the
announced successor to PetaLinux. It is driven by the same build.py runner as the other flows
and produces an image that exercises the Ethernet FMC ports in the same way as the PetaLinux
image, plus a few extras (the board Ethernet port on Zynq-7000, Wi-Fi on the PYNQ-ZU).
Note
For 2025.2 both the PetaLinux and Yocto flows are supported. From the next tool version onward, the PetaLinux flow for this repository will be retired and Yocto will be the only supported flow.
The Yocto flow supports all the Zynq-7000 and Zynq UltraScale+ targets of this repository.
How it works
The build does not depend on an AMD board machine configuration. It turns the Vivado XSA into a
System Device Tree (with sdtgen from Vitis) and generates a Yocto machine and device trees from it
(gen-machineconf parse-sdt). The PS configuration and the PL hardware of the design (AXI Ethernet,
AXI DMA, GMII-to-RGMII) therefore come straight from your Vivado design. The Ethernet FMC PHYs are
not in the XSA, so two device-tree fragments from this repository are added on top:
a port configuration (
Yocto/bsp/port-configs/ports-*) with the MAC address, PHY and MDIO bus of each port (ports-0123for the four-port Zynq UltraScale+ designs,ports-012-forzcu102_hpc1,ports-0123-axiethfor the Zynq-7000 designs), anda board file (
Yocto/bsp/<board>/.../system-user.dtsi) with board-specific fixes.
The bitstream is embedded in BOOT.BIN, and the FSBL programs the FPGA at boot. More details are
in Yocto/README.md and on the Advanced page.
Requirements
A physical or virtual machine running one of the supported Linux distributions (the Yocto flow cannot run on Windows or in WSL).
Vivado 2025.2 and Vitis 2025.2. The flow uses
sdtgen/xsct, which ship with Vitis. The build runner finds and sources the tools itself.Google’s repo tool on your
PATH, and the Yocto host packages. On Ubuntu 22.04 / 24.04:sudo apt-get install repo gawk wget git diffstat unzip texinfo gcc \ build-essential chrpath socat cpio python3 python3-pip python3-pexpect \ xz-utils debianutils iputils-ping python3-git python3-jinja2 \ python3-subunit zstd liblz4-tool file locales libacl1 bmap-tools
Disk space: a fresh Yocto workspace takes roughly 40 to 60 GB per target.
An internet connection for the first build (or an sstate mirror, see Offline build).
Build
Clone the repository (with its submodules) and change into it:
git clone --recurse-submodules https://github.com/fpgadeveloper/ethernet-fmc-zynq-gem.git cd ethernet-fmc-zynq-gem
Build the image, replacing
<target>with one of the target labels from the build instructions:./build.sh yocto --target <target>
This builds the Vivado project and XSA first if they don’t exist yet. The first build of a target
downloads the Yocto layers (repo sync) and sources and builds everything from scratch, so it
takes a long time; later builds are incremental. To also package the result:
./build.sh package --target <target>
Outputs
The output products are gathered into Yocto/<target>/images/linux/:
File |
Description |
|---|---|
|
Boot image (FSBL, PMU firmware and TF-A on Zynq UltraScale+, bitstream, U-Boot) |
|
U-Boot boot script; it builds the kernel command line |
|
Linux kernel ( |
|
Linux device tree |
|
Full SD card disk image (this is what you write to the card) |
|
Block map for |
|
Root file system tarball |
./build.sh package (or all) puts rootfs.wic.xz, rootfs.wic.bmap, BOOT.BIN and a short
readme.txt into bootimages/ethernet-fmc-zynq-gem_<target>_yocto-2025-2.zip.
Offline build
To build without downloading the Yocto shared-state cache, download and extract the AMD sstate
mirror for 2025.2 and write the path of the extracted directory, on a single line, into
Yocto/offline.txt. The build looks for the subdirectories aarch64/ (Zynq UltraScale+),
arm/ (Zynq-7000), microblaze/ (the Zynq UltraScale+ PMU firmware) and, optionally,
downloads/ (source mirror) under that path.
What is in the image
Item |
Value |
|---|---|
Distribution |
AMD Embedded Development Framework (Yocto scarthgap), Linux 6.12, systemd |
Hostname |
|
Login |
user |
Serial console |
PS UART 0 ( |
Networking |
|
Added tools |
|
The kernel command line is made by boot.scr from fixed arguments plus the board’s arguments
(BSP_EXTRA_BOOTARGS in Yocto/bsp/<board>/conf/local.conf.append):
Target |
Kernel command line |
|---|---|
Zynq UltraScale+ (AMD boards, PYNQ-ZU) |
|
UltraZed-EG, UltraZed-EV |
as above, but |
ZedBoard |
|
PicoZed 7030, ZC706 |
as ZedBoard, with |
The root file system is mounted read-only first and remounted read-write by systemd.
Prepare the SD card
The Yocto flow produces a full SD card image (rootfs.wic.xz) with all its partitions. Write it to
the SD card’s raw device, then copy BOOT.BIN onto the first partition. The card layout is:
Partition |
Type |
Contents |
|---|---|---|
1 |
FAT32 (ESP) |
|
2 |
ext4 ( |
kernel, |
3 |
ext4 |
root file system |
4 |
FAT32 |
empty, free for your data (Zynq UltraScale+) |
Warning
Writing an image to a raw block device cannot be undone. Be absolutely certain that you have identified the SD card’s device node before running the commands below; with the wrong device you can destroy the data on one of your hard drives.
Identify the SD card device. With the card unplugged, run
lsblk -o NAME,SIZE,RM,TYPE, insert the card, and run it again. The new entry, typically/dev/sdXwithRM=1(removable) and a size matching your card, is your target. ReplacesdXwith that device, and<target>with your target, below.Unmount any partitions that the desktop mounted automatically:
for p in /dev/sdX?*; do sudo umount "$p" 2>/dev/null; done
Write the image to the raw device. With
bmaptool(fast, writes only the used blocks):sudo bmaptool copy --bmap Yocto/<target>/images/linux/rootfs.wic.bmap \ Yocto/<target>/images/linux/rootfs.wic.xz \ /dev/sdX
Or, without
bmaptool:xzcat Yocto/<target>/images/linux/rootfs.wic.xz \ | sudo dd of=/dev/sdX bs=4M status=progress conv=fsync
Copy
BOOT.BINonto the first partition. The image leaves the first FAT32 partition without a boot image, and the BootROM only loadsBOOT.BINfrom there; without this step the board does not boot (nothing at all appears on the UART):sudo partprobe /dev/sdX sudo mkdir -p /mnt/sd_esp sudo mount /dev/sdX1 /mnt/sd_esp sudo cp Yocto/<target>/images/linux/BOOT.BIN /mnt/sd_esp/BOOT.BIN sync sudo umount /mnt/sd_esp && sudo rmdir /mnt/sd_esp
Eject the card cleanly so that all writes are flushed:
sudo eject /dev/sdX.
If you use the zip from bootimages/, run the same commands on rootfs.wic.xz,
rootfs.wic.bmap and BOOT.BIN from the zip.
Boot
Plug the SD card into the board and set the board to boot from the SD card. The boot-mode switch settings are the same as for PetaLinux; see Boot PetaLinux and the board’s user guide.
Plug the Ethernet FMC into the board’s FMC connector (see the target designs table for the connector) and check that its voltage variant matches the board’s VADJ (Choosing the FMC variant).
Connect the ports you want to use to your network or PC.
Connect the USB-UART to your PC and open a terminal emulator at 115200 baud (8N1), see UART terminal. On the PYNQ-ZU the console is the second of the two serial ports that the board’s USB-UART creates.
Power up the board.
The boot takes about 30 seconds from power-on to the login prompt. A boot on the PYNQ-ZU looks like this (shortened):
U-Boot 2025.01 ...
CPU: ZynqMP
Chip: zu5
Model: TUL PYNQ-ZU RevB
DRAM: 2 GiB (effective 4 GiB)
...
Bootmode: SD_MODE
Net:
ZYNQ GEM: ff0e0000, mdio bus ff0e0000, phyaddr 8, interface gmii
ZYNQ GEM: ff0d0000, mdio bus ff0d0000, phyaddr 8, interface gmii
ZYNQ GEM: ff0c0000, mdio bus ff0c0000, phyaddr 8, interface gmii
ZYNQ GEM: ff0b0000, mdio bus ff0b0000, phyaddr 8, interface gmii
...
Found U-Boot script /boot.scr
...
[ 0.000000] Linux version 6.12.40-xilinx-...
[ 0.000000] Kernel command line: earlycon console=ttyPS0,115200 clk_ignore_unused init_fatal_sh=1 root=/dev/mmcblk0p3 ro rootwait uio_pdrv_genirq.of_id=generic-uio cma=1536M
[ 2.190370] macb ff0b0000.ethernet eth0: Cadence GEM rev 0x50070106 at 0xff0b0000 irq 44 (00:0a:35:00:01:22)
[ 2.216109] macb ff0c0000.ethernet eth1: Cadence GEM rev 0x50070106 at 0xff0c0000 irq 45 (00:0a:35:00:01:23)
[ 2.241858] macb ff0d0000.ethernet eth2: Cadence GEM rev 0x50070106 at 0xff0d0000 irq 46 (00:0a:35:00:01:24)
[ 2.267498] macb ff0e0000.ethernet eth3: Cadence GEM rev 0x50070106 at 0xff0e0000 irq 47 (00:0a:35:00:01:25)
Welcome to AMD Embedded Development Framework Linux distribution 25.11.1+release-... (scarthgap)!
[ 3.810715] systemd[1]: Hostname set to <pynqzu-zynqgem-2025-2>.
...
[ 6.650358] macb ff0d0000.ethernet end1: renamed from eth2
[ 6.667891] macb ff0b0000.ethernet end3: renamed from eth0
[ 6.686063] macb ff0c0000.ethernet end2: renamed from eth1
[ 6.702032] macb ff0e0000.ethernet end0: renamed from eth3
...
[ OK ] Started Serial Getty on ttyPS0.
[ OK ] Reached target Multi-User System.
pynqzu-zynqgem-2025-2 login:
On the PYNQ-ZU, U-Boot also prints failed to set vqmmc-voltage to 3.3V three times before it
starts the kernel; the boot continues normally.
Log in
Log in as amd-edf. There is no password the first time; you are asked to choose one straight away:
pynqzu-zynqgem-2025-2 login: amd-edf
You are required to change your password immediately (administrator enforced).
New password:
Retype new password:
Use sudo (with the password you chose) for commands that need root. Once a port has an address,
you can also log in over SSH: ssh amd-edf@<board address>.
Use and test the Ethernet FMC ports
Every wired port that has a link gets an address by DHCP. List them, and identify each port by its MAC address:
ip -br addr
ip -br link
On the Zynq UltraScale+ targets FMC port 0 is end3 and FMC port 3 is end0; the full table for
every target, and how to check the link, read the PHY registers and measure the throughput with
iperf3 (with the results to expect), are on the Using and testing the ports page.
On the Zynq-7000 targets the board’s own Ethernet port is available too (end3, MAC
00:0a:35:00:01:26), and on zcu102_hpc1 the ZCU102’s own port is end0.
Wi-Fi on the PYNQ-ZU
The PYNQ-ZU has no Ethernet port of its own, but it has on-board Wi-Fi (a Microchip WILC3000 on the
PS SD1 interface). The Yocto image for pynqzu supports it as a Wi-Fi station (client):
the kernel’s
wilc1000-sdiodriver with WILC3000 support added, and the WILC3000 firmware (atmel/wilc3000_wifi_firmware-1.bin, version 16.1.2);wpa_supplicant,wpa_cli,wpa_passphraseandiw;a
wpa_supplicant@wlan0service that starts whenwlan0appears, and asystemd-networkdconfiguration that requests an address by DHCP onwlan0once it has associated;the
wifi-sta-setuphelper.
The image contains no Wi-Fi credentials. Until you set them up, wlan0 stays down and the
radio firmware is not loaded.
Connect to a network
Run wifi-sta-setup with the network name (SSID) and, optionally, your two-letter country code
(regulatory domain; the default is US). It reads the passphrase from its standard input, so that
the passphrase never appears on the command line. For example, to type the passphrase without it
being shown on the screen:
read -rs PSK; printf '%s\n' "$PSK" | sudo wifi-sta-setup '<SSID>' CA; unset PSK
(type the passphrase after the first command and press Enter). For an open network, use
sudo wifi-sta-setup --open '<SSID>' CA.
wifi-sta-setup writes /etc/wpa_supplicant/wpa_supplicant-wlan0.conf (readable by root only,
with the passphrase stored as a PSK hash, not in plain text), restarts wpa_supplicant@wlan0, and
waits up to 60 seconds for a DHCP address. On success it prints the address:
wlan0 up: 192.168.2.179/24
The first connection takes about 7 seconds. From then on the board connects by itself at every boot,
about 12 seconds after the kernel starts, and has a DHCP address a few seconds later. The wired
ports keep priority for the default route (route metric 10, wlan0 20).
To check the connection:
wpa_cli -i wlan0 status # wpa_state=COMPLETED when connected
iw dev wlan0 link # signal level and bit rate
ip -br addr show wlan0
To change the network, run wifi-sta-setup again. To stop using Wi-Fi, delete
/etc/wpa_supplicant/wpa_supplicant-wlan0.conf and run sudo systemctl stop wpa_supplicant@wlan0.
To write the configuration by hand instead, start from
/etc/wpa_supplicant/wpa_supplicant-wlan0.conf.example.
What to expect
The Wi-Fi throughput depends on your access point and the radio environment. As an example, with an access point on 2.4 GHz channel 6 at -45 dBm (72 Mbit/s link rate):
Test |
Result |
|---|---|
|
0% loss, average round trip 2.8 ms |
|
17 Mbit/s |
|
37 Mbit/s |
Wi-Fi power saving is switched off by the wpa_supplicant@wlan0 service, to keep the latency low.
BSP changes and fixes
The per-board changes of the Yocto flow live under Yocto/bsp/ (the full list is on the
Advanced page). The ones that change what you see on the board:
Ethernet FMC PHY wiring. The external PHYs are not in the XSA, so each target adds a port-configuration overlay that gives every active port its MAC address, PHY and MDIO bus (and, on Zynq UltraScale+, the GMII-to-RGMII converter at MDIO address 8).
Kernel arguments and hostname. The EDF boot flow ignores the usual kernel-argument settings, so the board’s arguments (console and
clk_ignore_unusedon Zynq-7000, thecma=size on all boards) were missing and the hostname was the genericamd-edf. The board arguments are now added toboot.scr, and every image has its own hostname,<board>-zynqgem-2025-2.Zynq-7000: kernel did not boot. The generated device tree lost the
xlnx,zynq-7000compatible string, so the kernel did not recognise the Zynq and stopped during early clock setup. The board file puts the compatible string back.Zynq-7000: board Ethernet port. The board’s own port (GEM0) is now described in the device tree (PHY and MAC address
00:0a:35:00:01:26) instead of being disabled. Disabling it was a workaround for a U-Boot crash on a GEM without a PHY description, which a described PHY avoids. The same change is in the PetaLinux BSPs.ZC706: board PHY address. The ZC706’s board PHY is at MDIO address 7, not 0 as on the ZedBoard and PicoZed. A ZC706 device-tree file corrects it; because it must override the shared port-configuration overlay, the build includes it after the overlay.
ZCU104: FMC not powered. The stock FSBL never switched on the FMC VADJ supply on the ZCU104, so no FMC port could work. The FSBL is patched to read the voltage record from the FMC card’s EEPROM and enable VADJ.
ZynqMP serial console. The generated device tree numbered the UARTs so that
ttyPS0was not the USB-UART on some boards; the board files fix the UART numbering so that the console is alwaysttyPS0.PYNQ-ZU: Wi-Fi. WILC3000 support added to the kernel’s
wilc1000driver, with its firmware and the station setup described above. Two further fixes make the first connection reliable:A station query sent by the network manager while
wlan0was still down (before the radio firmware had ever been started) was left queued in the driver and broke the following firmware start, so the first attempt to bring Wi-Fi up failed with “WLAN initialization FAILED” (a second attempt worked). The driver now refuses such requests until the firmware runs.wlan0is now brought up only bywpa_supplicant, once credentials exist, and the service retries the bring-up once if it fails.
PYNQ-ZU: serial console. The image no longer starts a login prompt on the second PS UART (
ttyPS1), which is not connected to the USB-UART on the PYNQ-ZU; that login service kept failing and left the system in the “degraded” state.