
Setting Up PYNQ on Legacy Zybo (xc7z010clg400-1): Complete Boot and WiFi Guide
Getting PYNQ running on the original Zybo takes more work than the official documentation suggests, because the official documentation does not target this board. The PYNQ project supports the Zybo Z7-10 and Zybo Z7-20, both of which use a different BSP configuration than the older xc7z010clg400-1 revision. If you have the original Zybo sitting on a shelf and want to run PYNQ on it, the official images will not work, and the failure is silent enough to send you chasing hardware problems that do not exist.
This post documents the full setup: flashing a compatible image, getting internet access through Ethernet sharing, compiling the RTL8188GU WiFi driver natively on the ARM core, and verifying the result. The hardware and software versions that this was tested on are listed below.
| Component | Version / Model | Notes |
|---|---|---|
| Board | Digilent Zybo (xc7z010clg400-1) | Original revision |
| SoC | Zynq-7000 (XC7Z010) | Dual-core ARM Cortex-A9 |
| PYNQ Image | 3.0.1 (community build) | Board-specific patched image |
| WiFi Dongle | D-Link USB | RTL8188GU chipset |
| Kernel Driver Source | lwfinger/rtw88 | Compiled natively on ARM |
| Host OS | Ubuntu 22.04 LTS | For flashing and DHCP sharing |
| SD Card | 16GB, Class 10 | Minimum recommended |
Why the Official Images Fail
The official PYNQ images for Zybo target the Z7 series boards, which use the xc7z010clg400-1 and xc7z020clg400-1 parts but with a completely different board support package. The Zybo Z7 has a different UART configuration, a different boot.bin, and a device tree that does not match the original Zybo’s peripheral map.
When you flash an official Zybo Z7 PYNQ image to the original board and power on, the board typically hangs before the kernel loads. HDMI shows nothing. The serial console, if you have a USB-UART adapter attached to the PMOD header, shows early U-Boot output and then silence. The symptom pattern is easy to misread as an SD card problem or a corrupted flash. It is neither. The boot.bin embedded in the image contains a first-stage bootloader and FPGA bitstream calibrated for the Z7 board’s memory controller and clock configuration. On the original Zybo, the memory initialization sequence fails or the device tree hands the kernel a peripheral map that does not correspond to anything on the actual hardware, and the kernel panics or hangs during init.
The fix is not to modify the official image. It is to use a community-maintained image that was built specifically for the original Zybo hardware.
Flashing a Compatible Image
Nick Petrovsky maintains a patched PYNQ image for the original Zybo at https://github.com/nick-petrovsky/PYNQ-ZYBO. The file you want is:
Zybo-3.0.1-fix-boot-bin-fix-havege.img.xz
The filename documents two specific fixes: a corrected boot.bin for the original board’s BSP and a havege entropy daemon fix that prevents the PYNQ boot from stalling while waiting for sufficient kernel entropy. Both are necessary. The stock 3.0.1 image stalls during first boot on this hardware because the havege service hangs waiting for entropy that never accumulates fast enough on the Cortex-A9 without hardware random number generation.
Flash the image to an SD card from a Linux host:
unxz Zybo-3.0.1-fix-boot-bin-fix-havege.img.xz
sudo dd if=Zybo-3.0.1-fix-boot-bin-fix-havege.img of=/dev/sdX bs=4M status=progress
sync
Confirm /dev/sdX is the SD card and not a mounted filesystem before running dd. The status=progress flag gives a live byte count so you know the write is proceeding. The sync after dd flushes any buffered writes that the OS has not committed to disk yet. Do not remove the card before sync completes.
If the flash fails partway through, reformat the card as FAT32 and retry. Some cards behave badly when dd writes over a filesystem that was partially mounted. Avoid USB hubs during flashing; direct connections are more reliable for large sequential writes.
Insert the SD card, set the Zybo boot jumper to SD mode (JP5 to the SD position), and apply power. With a USB-UART adapter on the PMOD UART header you will see U-Boot output followed by the kernel boot sequence. Without a serial connection, wait roughly 60 to 90 seconds for first boot to complete, after which the PYNQ Jupyter server will be accessible over Ethernet.
Getting Internet Access via Ethernet Sharing
The driver for the RTL8188GU chipset is not included in the PYNQ image. Compiling it natively requires internet access on the board. The cleanest way to get that without an existing WiFi connection is to share your laptop’s internet connection over Ethernet directly to the Zybo.
On Ubuntu, the simplest method is through NetworkManager’s connection sharing mode. Connect the Zybo to your laptop with an Ethernet cable, then set the wired connection to shared mode through the network settings: go to Settings, then Network, select the wired interface, open IPv4 settings, and set the method to “Shared to other computers.” This configures NetworkManager to run a DHCP server on the wired interface and NAT outbound traffic through your active internet connection.
After enabling sharing, the Zybo should obtain an IP in the 192.168.42.0/24 range automatically. Verify from the Zybo terminal:
ifconfig
You should see an address like 192.168.42.x on the eth0 interface. Test outbound connectivity:
ping google.com
If ping fails but ifconfig shows an IP, the issue is usually the NAT forwarding rule not being set correctly. Restart NetworkManager on the laptop (sudo systemctl restart NetworkManager), wait a few seconds, and replug the Ethernet cable to trigger a fresh DHCP lease. If a firewall is running on the laptop, check that it is not blocking forwarded packets on the shared interface.
Once ping succeeds, apt and git work normally and the board can reach package repositories and GitHub.
Compiling the WiFi Driver Natively
The D-Link USB dongle used here contains a Realtek RTL8188GU chipset. Confirm that the board sees the device after plugging it in:
lsusb
The relevant line will read something like:
Bus 001 Device 002: ID 2001:331c D-Link Corp. RTL8188GU
The specific USB ID varies by D-Link model revision, but the RTL8188GU identification should be visible. If lsusb shows nothing after plugging in, check that the PYNQ image’s USB host controller is initialized; this is usually fine on the original Zybo but worth confirming.
The driver source comes from the lwfinger/rtw88 repository, which maintains out-of-tree Realtek drivers compatible with recent kernel versions. Install the build dependencies first:
sudo apt update
sudo apt install build-essential git dkms
Clone the repository and build:
git clone https://github.com/lwfinger/rtw88.git
cd rtw88
make
sudo make install
The build compiles against the running kernel’s headers. If the headers are not installed, the Makefile will fail with a message about missing kernel source. Check the running kernel version with uname -r, then install the matching headers package:
sudo apt install linux-headers-$(uname -r)
On the PYNQ image, the kernel version string may not have a corresponding package in the default apt sources. If apt install linux-headers-$(uname -r) fails, check what is available with apt search linux-headers and install the closest matching version. A version mismatch between the headers and the running kernel will cause symbol resolution failures at module load time, not at compile time, which is a confusing failure mode.
After make install completes, load the module:
sudo modprobe rtw88_8188gu
Check dmesg for confirmation:
dmesg | grep rtw
A successful load shows lines indicating the driver attached to the USB device and registered a wireless interface. If modprobe fails with “Module not found,” the install step did not put the .ko file in the expected location. Running sudo depmod -a after install and before modprobe resolves this in most cases.
If the module loads according to dmesg but no wlan0 interface appears in ifconfig or ip link, the most common cause is a kernel version mismatch where the driver compiled against different headers than the running kernel exports. The module loads without error because the symbol mismatch is not always caught at load time, but the USB device is not claimed. In this case, reinstall the correct kernel headers, clean the build directory with make clean, rebuild, reinstall, and reload.
To persist the module across reboots, add it to the module load list:
echo "rtw88_8188gu" | sudo tee /etc/modules-load.d/rtw88.conf
Connecting to a Network
Once wlan0 is present, scan for available networks:
iw dev wlan0 scan | grep SSID
The full scan output is verbose. Grepping for SSID gives a readable list of visible networks.
Connect using nmcli:
sudo nmcli device wifi connect "SSID_NAME" password "PASSWORD"
NetworkManager handles association and DHCP. Verify the connection:
ifconfig wlan0
ping google.com
wlan0 should show a valid IP from your router’s DHCP pool. After confirming wireless works, the Ethernet sharing connection from the laptop can be disconnected.
The PYNQ Jupyter environment is accessible from any device on the same network at http://<board-ip>:9090. The default password for the PYNQ Jupyter server is xilinx.
Why Native Compilation
Cross-compiling the RTL8188GU driver for the Zynq ARM core is technically possible but adds a layer of toolchain setup that is easy to get wrong. The primary risk in cross-compilation for kernel modules is that the kernel headers on the host must exactly match the kernel running on the target. If the PYNQ image was built against a specific kernel configuration and you are cross-compiling against a different set of headers or a different toolchain version, the resulting module may appear to compile cleanly but fail to load due to unresolved symbols or incorrect ABI assumptions.
Compiling natively on the ARM core eliminates this class of problem entirely. The build system uses the headers that correspond to the exact running kernel, links against the symbols that are actually exported, and produces a module that the kernel will accept. The Cortex-A9 is slow enough that a kernel module build takes a few minutes rather than seconds, but this is not a practical concern when you are doing it once.
The dkms package installed earlier is worth keeping. DKMS registers the driver source and automatically recompiles it if the kernel is updated, which prevents the module from breaking on apt upgrades that pull in a new kernel version.
Failure Reference
| Symptom | Cause | Resolution |
|---|---|---|
| Board does not boot, no serial output | Wrong PYNQ image (Z7 BSP) | Use the community Zybo-3.0.1 image |
| Board boots but hangs at first boot | havege entropy stall | Same fix; the patched image addresses this |
| Ethernet connected but no IP | DHCP not enabled on host | Enable “Shared to other computers” in NM |
| IP assigned but ping fails | NAT forwarding blocked | Restart NetworkManager, check firewall |
| lsusb shows dongle, no wlan0 | Driver not loaded | modprobe rtw88_8188gu |
| modprobe fails with symbol errors | Kernel/header version mismatch | Reinstall matching headers, rebuild |
| Module loads, wlan0 absent | ABI mismatch (subtle header mismatch) | Clean build, reinstall headers, rebuild |
| Sluggish performance throughout | Insufficient power supply | Use a stable 5V/2A supply, not USB port |
