Software Development Workflow¶
This page covers the software side of openwifi development: rebuilding and deploying the driver, sdrctl, and the other user_space/ tools. Building the FPGA bitstream and turning it into a loadable image are on the FPGA Development page. The steps here take built software onto the board.
The prebuilt SD image may be older than the current repo. Before doing serious work, copy the repo's user_space/ files onto the board, and rebuild the driver against the matching kernel.
If you are in the middle of editing code and want the steps, start with the quick reference below and follow its links for detail.
Environment setup¶
Set up the shared host toolchain first. Environment Setup covers the Vivado and Vitis install and the Ubuntu build packages. The software builds use all four variables:
export XILINX_DIR=/opt/Xilinx # dir containing Vitis/, Vivado/, etc.
export OPENWIFI_HW_IMG_DIR=/path/to/openwifi-hw-img
export BOARD_NAME=zed_fmcs2 # your board
export ARCH_BIT=32 # 32 for Zynq-7000, 64 for Zynq UltraScale+ (e.g. ZCU102)
For the exact toolchain, kernel, and image versions these builds expect, see Versions this wiki targets.
Quick reference from code change to running board¶
Find the row that matches what you changed and follow its link to the full instructions. The commands assume the usual setup: sources on your PC, the board reachable at 192.168.10.122 (the default address from Getting Started), and the environment variables set.
| You changed | Rebuild & deploy | Take effect |
|---|---|---|
Driver C code (driver/, including driver/side_ch/) |
On the PC: cd driver && ./make_all.sh $XILINX_DIR $ARCH_BIT, then scp the .ko files to the board's openwifi/ directory |
./wgd.sh on the board, no reboot needed (details) |
sdrctl source (user_space/sdrctl_src/) |
scp the source to the board, then compile on the board with make |
The new binary replaces openwifi/sdrctl immediately (details) |
side_ch_ctl source (user_space/side_ch_ctl_src/) |
scp the source to the board, then on the board: gcc -o side_ch_ctl side_ch_ctl.c |
Run it. If you also changed side_ch.ko, reload that like any other driver module (details) |
inject_80211 / analyze_80211 (user_space/inject_80211/) |
scp the source to the board, then on the board: make |
Run it (usage) |
Helper scripts (user_space/*.sh, *.py) |
Nothing to compile. Copy them to the board with scp. The Python display scripts (side_info_display.py, iq_capture.py, and others) run on the PC instead |
Run them |
| FPGA Verilog / IP cores | On the PC: rebuild the bitstream (FPGA Development), then boot_bin_gen.sh and scp system_top.bit.bin to the board |
./wgd.sh, no reboot needed (details) |
| Kernel config or device tree | Rebuild on the PC, then transfer and populate on the board. Full step-by-step in Updating a board to a newly built kernel, short form under bulk helpers | Reboot. A kernel version change also needs the driver rebuilt and the populate script run twice, and sdrctl/side_ch_ctl rebuilt to avoid crashes from the mismatch |
The driver iteration loop¶
The most common cycle is changing driver code and testing it on the board. Once set up, each round takes less than a minute.
One-time setup (per PC / per kernel version):
- Set the environment variables and install the build packages.
- Prepare the kernel source the driver builds against:
cd openwifi/user_space && ./prepare_kernel.sh $XILINX_DIR $ARCH_BIT.
Each round:
- Edit the driver code on the PC.
- Compile:
cd openwifi/driver && ./make_all.sh $XILINX_DIR $ARCH_BIT(add extra args for conditional-compile macros). - Copy to the board:
scp `find ./ -name \*.ko` root@192.168.10.122:openwifi/. - On the board:
./wgd.shreloads the modules live. Make suresystem_top.bit.binis not in the directory unless you also want the FPGA image reloaded (details). - Check it loaded:
dmesg | tail -20should show the driver initializing without symbol/version errors (if it does show them, see the note on kernel mismatch), andip ashould listsdr0. - Reloading recreates
sdr0from scratch, so restart whatever mode you were in: AP (hostapd/fosdem.sh), client (wpa_supplicant), or your monitor-mode setup (Operating Modes).
For print-style debugging, add printk calls in the driver and watch them live on the board with dmesg -w in a second ssh session.
Rebuilding the driver¶
-
Prepare the Analog Devices kernel source once (this is what the driver builds against):
cd openwifi/user_space ./prepare_kernel.sh $XILINX_DIR $ARCH_BIT -
Compile the driver:
cd openwifi/driver ./make_all.sh $XILINX_DIR $ARCH_BIT # Extra args beyond these two become "#define" macros in pre_def.h # for conditional compilation (see below). -
Copy the
.kofiles to the board:cd openwifi/driver scp `find ./ -name \*.ko` root@192.168.10.122:openwifi/ -
On the board,
./wgd.shloads the new driver (and reloads the FPGA image ifsystem_top.bit.binis present in the same directory).
Symbol or version errors on load mean a kernel mismatch
The kernel in the SD image is usually older than the one your driver was built against. Fix it by putting the freshly built kernel image into the BOOT partition: adi-linux/arch/arm/boot/uImage (32-bit) or adi-linux-64/arch/arm64/boot/Image (64-bit). The full procedure is in Updating a board to a newly built kernel.
Conditional compilation¶
Passing extra arguments to make_all.sh turns them into #define macros in pre_def.h, so you can gate driver code blocks per build. Combined with the FPGA's equivalent Verilog-macro mechanism, this is how you produce feature variants. See dynamic reloading for a clean way to keep several variants side by side.
Reloading driver and FPGA without rebooting¶
wgd.sh can reload the driver and/or FPGA live and switch between different builds with no reboot and no power cycle. Keep your on-board files current with user_space/ to use it.
Driver only. Ensure system_top.bit.bin is not in the directory. wgd.sh then loads just the .ko files.
Forcing or skipping the FPGA reload
When system_top.bit.bin is present, whether wgd.sh reprograms the FPGA depends on the system. On Kuiper and OpenWrt it reloads the FPGA image, the historical default. On a Buildroot image it keeps the bitstream U-Boot already loaded when the RF chain is up, to avoid a redundant reprogram. Override either default explicitly with an environment variable:
OPENWIFI_RELOAD_FPGA=0 ./wgd.sh # never reprogram the FPGA, load only the driver
OPENWIFI_RELOAD_FPGA=1 ./wgd.sh # force an FPGA reprogram from system_top.bit.bin
Driver + FPGA. Generate the reloadable bitstream and put it beside the driver files (the .xsa input comes from the FPGA build, see FPGA Development):
cd openwifi/user_space
./drv_and_fpga_package_gen.sh $OPENWIFI_HW_IMG_DIR $XILINX_DIR $BOARD_NAME
# produces system_top.bit.bin AND drv_and_fpga.tar.gz
Then run ./wgd.sh on the board as usual.
From a single package file (recommended). drv_and_fpga_package_gen.sh also bundles everything into drv_and_fpga.tar.gz (driver .kos, FPGA image, and related source). Rename it meaningfully per branch/variant and load it directly:
./wgd.sh ./drv_and_fpga_myvariant.tar.gz
This makes it easy to keep, share, and switch between variants. To build a variant, either work on a separate branch or use conditional-compile arguments (driver make_all.sh extra arguments, FPGA Verilog macros). Rename the package to record which options are on. drv_and_fpga_package_gen.sh calls make_all.sh without extra arguments by default. If you rely on conditional-compile flags, add them there too.
From a target directory. Put a driver+FPGA set in its own directory and load it explicitly, so different versions live in different directories:
./wgd.sh $TARGET_DIR
Full wgd.sh usage. wgd.sh prints this at the start of every run. There is no dedicated help flag, so ./wgd.sh -h prints the usage and then fails, because it treats -h as a missing directory.
- no argument: load the driver
.kofiles and the FPGA image (ifsystem_top.bit.binexists) from the current directory, withtest_mode=0 - a numeric first argument is assigned to
test_mode(loads everything from the current directory) remotedownloads the files and then loads them. An optional second argument names the target directory, an optional third setstest_mode- any other first argument that is not a
.tar.gzfile is treated as a directory to load from. An optional second argument setstest_mode - a
.tar.gzfile is unpacked and loaded from the unpacked directory. An optional second argument setstest_mode OPENWIFI_RELOAD_FPGA=0/1(environment variable) explicitly skips or forces the FPGA reprogram, overriding the per-system default described above
test_mode¶
insmod sdr.ko test_mode=<value> (or passing the value to wgd.sh or fosdem.sh) toggles experimental features via the test_mode global in sdr.c. Two bits are in use. Bit 0 turns on A-MPDU aggregation (default off), so ./wgd.sh 1 gives you aggregation. Bit 1 advertises the short guard interval, which only the driver source documents. See Wi-Fi 4 & Wi-Fi 6 Features for both in practice.
Rebuilding sdrctl¶
sdrctl is compiled on the board:
# from host, push the source:
cd openwifi/user_space/sdrctl_src
scp `find ./ -name \*` root@192.168.10.122:openwifi/sdrctl_src/
# on the board:
cd ~/openwifi/sdrctl_src/ && make clean && make && cp sdrctl ../ && cd ..
A successful build finishes without errors, and the new binary lands at ~/openwifi/sdrctl.
Bulk update helpers¶
For larger updates (kernel, modules, device tree, rootfs) there are paired host/board scripts:
- Kernel + modules + device tree: on the host,
prepare_kernel.sh,boot_bin_gen.sh, andtransfer_kernel_image_module_to_board.sh. On the board,populate_kernel_image_module_reboot.sh(run it again after the first reboot if the kernel version changed, so symlinks point at the new version). The whole flow, with the verification steps that get you back to a workingsdr0, is written out in Updating a board to a newly built kernel. - Driver + user space: on the host,
make_all.shandtransfer_driver_userspace_to_board.sh. On the board,populate_driver_userspace.sh. - Over FTP (optional): set up an anonymous FTP server on the PC, rooted at your
openwifidirectory. On the board,./sdcard_boot_update.sh $BOARD_NAMEpullsuImage,BOOT.BIN, anddevicetree.dtbinto the boot partition, after which you power-cycle../wgd.sh remotethen pulls the driver files and brings upsdr0. Anonymous FTP has no authentication, so use it on trusted lab networks only. - rootfs as a disk: on the PC, File manager → Connect to Server →
sftp://root@192.168.10.122/root(passwordopenwifi). - It is also worth refreshing the ADI rootfs tools. On the board, clone
linux_image_ADI-scripts, runapt update, and then runadi_update_tools.sh(see the ADI Kuiper update guide).
Building a full SD image from scratch¶
Three base operating systems are supported: ADI Kuiper (similar to Debian or Ubuntu), OpenWrt (router-style with LuCI), and Buildroot (a small, fast-booting deployment image for the ANTSDR boards). Building SD Images covers flashing, rootfs edits, update_sdcard.sh, the OpenWrt Docker build, and the Buildroot build. Kuiper builds need Vivado 2022.2 (with Vitis) and the flex bison libssl-dev device-tree-compiler u-boot-tools packages. The OpenWrt build only needs Docker, and the Buildroot build needs only its host packages plus a prebuilt system_top.xsa.