It all started after a “simple” HDL tweak — you know, just adding one tiny custom IP and an extra DMA. Vivado was happy, bitstream generated, and the ZCU102 lights were blinking like a disco.Then came the real boss fight: getting Linux to boot.
Petalinux, ever so dramatic, threw warnings, dependency tantrums, and a “device-tree mismatch” that could make any engineer question their life choices.
If this story feels familiar — congratulations, you’ve entered the elite club of engineers customizing Analog Devices’ ADRV9009 platform. 🥂
This tutorial is your roadmap to sanity:We’ll rebuild the Linux image for the ADRV9009 + ZCU102 using Vivado and Petalinux 2024.2, tuned perfectly for your custom HDL design — no hidden scripts, no black magic, just clean, reproducible steps.
🧭 2. Overview: What We’re BuildingSo, what’s the mission?We’re not here to write a kernel from scratch or summon the Linux gods — we just want a stable, bootable Petalinux image that works seamlessly with a custom HDL design based on Analog Devices’ ADRV9009 + ZCU102 reference design.
🎯 Ob jectiveTo rebuild a Linux image (BOOT.BIN + image.ub) compatible with your modified HDL project, which includes:
ADRV9009 transceiver chain
- ADRV9009 transceiver chain
- Custom IP blocks (AXI Streaming Interface)
- An additional ADI DMA IP (AXI Memory Mapped Interface)
- Zynq UltraScale+ MPSoC (ZCU102)
At the end, you’ll have:
- A bootable SD card that runs Linux on your hardware
- The IIO drivers loaded correctly for your RF frontend
💡 Pro tip: Always match your Vivado and Petalinux versions — mixing toolchains is like cross-threading a BNC connector: it might fit, but you’ll regret it later.🧩 En d Goal
By the end of this tutorial, you’ll have a verified Linux image that:
- Boots successfully on your customized HDL design
- Recognizes ADRV9009, SPI, IIO, and DMA devices
- Enables debugging, streaming, and user-space testing
Before proceeding, ensure only these two things are ready:
✅ Petalinux 2024.2 is installed and its environment is sourced
✅ The custom HDL hardware (.xsa) file (with bitstream) is exported from Vivado
That’s it — you’re ready for the build.
🧰 4. Setting Up the EnvironmentWith your .xsa
ready and Petalinux installed, it’s time to prepare the build workspace.
🗂️ Create a Working Directory
First of all lets create a parent folder this folder will have all the image build stuff and the cloned meta-adi layer folder and than move into this folder.
saqib@saqib:~/Desktop/resources/RF_EMBEDDED$ mkdir drx-plinux
saqib@saqib:~/Desktop/resources/RF_EMBEDDED$ cd drx-plinux
🌐 Source the Petalinux Environment
Now source the petalinux tool chain into this folder specifically settings.sh file in petalinux installed directory.
🌐 Cloning Required Repositories
Now clone the meta-adi yocto layer by analog devices to the parent folder
saqib@saqib:~/Desktop/resources/RF_EMBEDDED/drx-plinux$ git clone https://github.com/analogdevicesinc/meta-adi.git
🧩 Create a New Petalinux Project
Inside your parent folder now create a petalinux project
petalinux-create project --template <PLATFORM> -n <PROJECT_NAME>, where <PLATFORM> is:
* zynqMP (for UltraScale + MPSoC)
* zynq (for Zynq)
* microblaze (for MicroBlaze)
(a) ⚙️ Now lets start building at this point I am assuming your.xsa file from your vivado is succesfully built.
petalinux-config --get-hw-description=<path to .xsa file>
💡 Tip: Make sure you point to the folder containing the.xsa f
ile —not the.xsa f
ile itself. Otherwise, you’ll hit the SDT error Introduced in AMD Xilinx’s newer Petalinux build workflow.
in my case the command was like this:
saqib@saqib:~/Desktop/resources/RF_EMBEDDED/drx-plinux/drx-plinux-img$ petalinux-config --get-hw-description=/home/saqib/Desktop/resources/RF_EMBEDDED/hdl-main/projects/adrv9009/zcu102_em1_DRX_v3_data_notlast_corrections
This opens the Petalinux System Configuration GUI, as shown in Figure 5.Navigate to Yocto Settings → User Layers → user layer 0 (New) and enter the path to your cloned repository:
../meta-adi/meta-adi-xilinx
Then Save and Exit to apply the changes. The complete flow is illustrated in the figures below.
💡 Top Tips:
Most settings are configured automatically based on the hardware design details specified in the XSA. The main hardware configuration settings that I find myself updating in the majority of my PetaLinux projects is under the Image Packaging Configuration tab. This is where the root filesystem type and relevant boot settings are configured. Especially for ZynqMP-based designs, my root filesystem lives on an SD card. And I also want it to be persistent (ie - it keeps changes made between power cycling). So under Root Filesystem type, change it to EXT4 (SD/eMMC/SATA/USB).📷
Figure 11 shows the log you should expect after you save and exit.
(b) Up to this point, we’ve successfully added the meta-adi-xilinx Yocto user layer. Now, we need to update the default device tree configuration by pointing the kernel to the correct.dts file. You can do this directly from the terminal using the following command:
echo "KERNEL_DTB=\"${dts_to_use}\"" >> project-spec/meta-user/conf/petalinuxbsp.conf
This ensures the Petalinux build uses your intended device tree blob (DTB) for the ADRV9009 + ZCU102 design.
In this example, for the Analog Devices ADRV9009 + ZCU102 reference design, the device tree file I’m using is:
zynqmp-zcu102-rev10-adrv9009-jesd204-fsm
💡 Note : Make sure not to include any file extension (like.dts o
r.dtb)
at the end of the name — only the base filename.
The figure below shows the exact terminal command used in my setup.
saqib@saqib:~/Desktop/resources/RF_EMBEDDED/drx-plinux/drx-plinux-img$ echo "KERNEL_DTB=\"$zynqmp-zcu102-rev10-adrv9009-jesd204-fsm\"" >> project-spec/meta-user/conf/petalinuxbsp.conf
⚠️ Important: After running the command, open the file project-spec/meta-user/conf/petalinuxbsp.conf
and verify that the full device tree name was added correctly.Sometimes parts of the name may get truncated, which can lead to endless “no file found” errors during the build — so it’s worth double-checking!
🧠 (c) The “Fun” Part — Custom Device Tree Modifications
Alright, now comes the most “enjoyable” part of the process — editing the device tree.
To start, we’ll intentionally let the first build fail (yes, on purpose). This generates a pl.dtsi
file — an auto-generated device tree created by Petalinux based on your .xsa
.
Now, here’s the catch: while AMD/Xilinx does a fine job handling their own IPs, everything else… let’s just say Petalinux’s guesses for third-party blocks can be optimistic at best.
In other words, you’ll often end up with a lovely collection of garbage nodes for your ADI IPs.
To fix this, Analog Devices takes the clean route — they delete the Petalinux-generated nodes for ADI IPs and overlay their own device tree with the correct parameters.
We’ll follow the same philosophy:
- Inspect what nodes Petalinux generated.
- Remove the incorrect ones using a cleanup file.
- Add the correct ADI device tree nodes in the
system-user.dtsi
file — the one that sits on top of everything else.
Because nothing says “custom embedded Linux” like deleting half of what your tools just proudly built.
In your project directory navigate to build folder and type
cd build
petalinux-build
From here on, grab a ☕ large cup of coffee (or three) — this part’s going to be a marathon. 🏃♂️💻Errors will pop up like whack-a-moles 🎯, but don’t worry — we’ll hunt them down one by one until Petalinux finally waves the white flag and builds. 🚩🔥❌ Error 1: License Checksum Mismatch
ERROR: jesd-status-dev-r0 do_populate_lic: QA Issue: jesd-status: The LIC_FILES_CHKSUM does not match for file://LICENSE.txt;md5=38c01601d5c4b84986a8f48ece946aa1
jesd-status: The new md5 checksum is 982e522e7ec5a0beed8de5114ebfea50
Ah yes — the classic LIC_FILES_CHKSUM
mismatch.This usually happens when the license text in the package was updated but the recipe still references the old checksum.
🩹 Fix: Simply copy the new MD5 checksum shown in the error log and replace the old one in the recipe file causing the issue.
In my case, the error pointed to:
meta-adi/meta-adi-xilinx/recipes-support/jesd-status/jesd-status_dev.b
So, I opened the .bb
file and replaced the outdated MD5 with the new one:
LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=982e522e7ec5a0beed8de5114ebfea50"
Repeat this for any other packages showing the same issue.It’s one of those harmless “welcome to Yocto” moments — tedious, but part of the initiation. 😅Moreover do not cancel the build even if it is continuing with some other tasks
❌ Error 2: Fetch Error
ERROR: linux-xlnx-6.6.40-adi-v2025.1+git-r0 do_fetch: Fetcher failure:
Fetch command export PSEUDO_DISABLED=1; export SSL_CERT_DIR=...
This one’s the classic “fetcher failure” — basically Petalinux’s way of saying “I couldn’t reach the internet, please fix your Wi-Fi.” 🌐
🩹 Fix:Just make sure you have a stable internet connection and then re-run the build:
petalinux-build
Nine times out of ten, this one vanishes the moment your connection behaves — no deep debugging required. ☕
❌ Error 3: Kernel Version Sanity Check Failed
ERROR: linux-xlnx-6.6.40-adi-v2025.1+git-r0 do_kernel_version_sanity_check:
Package Version (6.6.40-adi-v2025.1+git) does not match the kernel being built (6.12).
Please update the PV variable to match the kernel source or set
KERNEL_VERSION_SANITY_SKIP="1" in your recipe.
Ah, the infamous kernel version mismatch — Petalinux’s way of reminding us that it likes everything to match perfectly (even when it doesn’t matter).
🩹 Fix:This one’s harmless. The kernel version in your build environment and the one specified in the recipe just don’t line up.To silence the warning (and keep your sanity intact), simply add this line to your Linux recipe file:
KERNEL_VERSION_SANITY_SKIP = "1"
You’ll find the right recipe location mentioned in the last line of the error log — for example:
meta-xilinx/meta-xilinx-core/recipes-kernel/linux/linux-xlnx_6.6.40-v2024.2.bb
After editing, save the file and rebuild:
petalinux-build
Petalinux will happily stop complaining about version mismatches — because sometimes, ignorance is bliss. 😏
🧩 (d) Modifying the Device Tree
Finally — the build fails.Good news: that’s exactly what we wanted.Why? Because this failure generates the pl.dtsi
file — the auto-generated device tree derived from your .xsa
.
In my customized ADRV9009 + ZCU102 reference design, I added:
- 🧮 AXI-Stream-based DSP IP, and
- ⚙️ ADI AXI-DMA Controller IP (memory-mapped).
When Petalinux created the device tree, the new DMA got tied to a clock named misc_clk_0
.But ADI’s Yocto layer includes a cleanup mechanism that deletes many auto-generated nodes — including this clock.Result: build failure. (Figure 12 shows the “misc_clk_0 not found” error.)
🧠 What’s the Goal?
We’ll follow ADI’s clean-up flow to make our custom hardware play nicely:
- Generate
pl.dtsi
(done — via the failed build). - Identify your custom IP nodes in it.
- Explicitly delete those nodes through ADI’s pl-delete files.
- Add correct, hand-written versions of those nodes in
system-user.dtsi
.
🔍 Step 1 – Identify Custom Nodes
Navigate to:
components/plnx_workspace/device-tree/device-tree/
Open pl.dtsi
and find nodes that belong to your custom IPs.In my case:
axi_dmac_0: axi_dmac@80000000 {
clock-names = "m_dest_axi_aclk", "s_axi_aclk", "s_axis_aclk";
clocks = <&zynqmp_clk 72>, <&zynqmp_clk 71>, <&misc_clk_0>;
compatible = "xlnx,axi-dmac-1.0";
reg = <0x0 0x80000000 0x0 0x10000>;
};
This is our auto-generated node we’ll soon replace with a clean one.
🧹 Step 2 – Add Node to ADI’s Delete List
Go to the ADI layer:
meta-adi/meta-adi-xilinx/recipes-bsp/device-tree/files/
Open:
pl-delete-nodes-zynqmp-zcu102-rev10-adrv9009-jesd204-fsm.dtsi
At the top you’ll see an include such as:
/include/ "pl-delete-nodes-zynqmp-zcu102-hdl-adrv9009.dtsi"
That second file actually contains the delete commands.Open pl-delete-nodes-zynqmp-zcu102-hdl-adrv9009.dtsi
and add your node’s delete line:
/delete-node/ &axi_dmac_0;
✅ Why: this tells Petalinux to remove the autogenerated DMA node from pl.dtsi
during build.Later, we’ll re-add a properly defined version in system-user.dtsi
.
(Figure 13 shows the modified delete-node file.)
🧩 Step 3 – Add Clean Node in system-user.dtsi
Now edit:
project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
Append your new DMA node under the rest
of content:
/include/ "system-conf.dtsi"
/ {
};
&fpga_axi {
pdm_dmac: dma-controller@8000000 {
compatible = "adi,axi-dmac-1.00.a";
reg = <0x8000000 0x10000>; // Base address and size
dma-controller;
interrupts-extended = <>;
#dma-cells = <1>;
#clock-cells = <0>;
dma-coherent;
clocks = <&zynqmp_clk 73>;
};
};
💡 Tip:This top-level file overrides the deleted nodes and injects your clean definitions at build time.It’s how ADI layers remain modular — everything custom goes here, never directly inside pl.dtsi.
✅ Final Notes
- Deleting in
pl-delete-nodes...
ensures ADI’s cleanup doesn’t resurrect the wrong auto-generated config. - Adding to
system-user.dtsi
ensures Linux sees your DMA and clock bindings exactly as intended.
After this, cd.. from the build folder and re run your build again: re-run:
petalinux-build
and grab another ☕ — this time, it should pass.🎉 Voila! The Build Is Successful!
If everything went right (and you didn’t accidentally delete your sanity along the way), Petalinux will finally complete the build. 🏁
You should now see a success message on your terminal similar to Figure 14 — that beautiful moment every engineer lives for:
“Build finished successfully.”
Take a deep breath, sip that long-forgotten coffee ☕, and give yourself a nod — you just built a custom Linux image for ADRV9009 + ZCU102 like a pro.
Once the project has successfully built, it’s time to generate the boot binary — BOOT.BIN
.This file combines the essential boot components needed by your ZynqMP device to start up properly.
In my case, I’m booting the ZCU102 from an SD card, so the BOOT.BIN
must at least include:
- 🧩 The FSBL (First Stage Bootloader)
- ⚙️ The FPGA bitstream (.bit)
- 🚀 The U-Boot bootloader
All of these files are automatically generated inside your Petalinux project’s output directory:
<PetaLinux project path>/images/linux
Now package them together using:
petalinux-package --boot --fsbl ./images/linux/zynqmp_fsbl.elf \
--fpga ./images/linux/system.bit \
--u-boot
This command creates the BOOT.BIN
file in your images directory, ready to drop onto your SD card for booting.
💡 Tip:If you rebuild your project later and need to regenerate the boot image, use the --force f
lag to overwrite the existing file:
petalinux-package --boot --fsbl ./images/linux/zynqmp_fsbl.elf \
--fpga ./images/linux/system.bit \
--u-boot --force
My
screen log is shown in figure 12.
If you’re booting from an SD card (as opposed to booting directly into INITRD
or INITRAMFS
), PetaLinux provides a convenient built-in utility to create a complete SD card image.This tool packages everything into a .wic
image file that can be flashed onto the SD card using utilities such as balenaEtcher, Rufus, or dd.
⚙️ Command Overview
PetaLinux uses the petalinux-package
command with the --wic
flag to create this image.
In my setup (booting the ZCU102 board from an SD card), the following files are required in the boot partition of the SD card:
BOOT.BIN
→ Bootloader binary (FSBL + bitstream + U-Boot)image.ub
→ Combined Linux kernel and root filesystem imagesystem.dtb
→ Device Tree Blobboot.scr
→ Boot script for U-Boot
The root filesystem itself (rootfs.tar.gz
) is placed into the root partition of the SD card.
🧩 Full Command Example
Run the following command from your PetaLinux project directory:
petalinux-package --wic \
--bootfiles "BOOT.BIN image.ub system.dtb boot.scr" \
--rootfs-file ./images/linux/rootfs.tar.gz
petalinux-package --wic \
--bootfiles "BOOT.BIN image.ub system.dtb boot.scr" \
--rootfs-file ./images/linux/rootfs.tar.gz
🕒 What to Expect
- The process may take several minutes to complete, depending on your system’s I/O speed.
- The generated
.wic
image is typically around 6–7 GB, so ensure you have at least an 8 GB SD card available. - Once complete, the
.wic
image will appear in your project’simages/linux/
directory.
💡 Flashing the SD Card
You can now flash the .wic
image using imaging tools in my case I am using balena etcher.
Now it’s time for the fun part — bringing your freshly built Linux system to life!
- Insert the SD Card:Plug the SD card (with the PetaLinux image) into your board’s SD slot, as shown in Figure 15.
- Set the Boot Mode (ZCU102):For the ZCU102, configure the board to boot from the SD card by setting the boot mode switches(SW6 in case of ZCU102) to:
ON | OFF | OFF | OFF
(Refer to Figure 16 for switch positions.)
- Connect UART for Serial Console: Use a micro-USB cable to connect the UART-to-USB port on your ZCU102 to your host PC (see Figure 17).This connection will allow you to view boot logs and interact with the Linux console.
- Mount the ADRV9009 FMC Card:Attach the ADRV9009 transceiver board securely to the HPC1 FMC connector on the ZCU102, as shown in Figure 18.
- Open a Serial Terminal:Fire up your favorite serial terminal program — PuTTY, TeraTerm, or similar.Select the appropriate port (in my case
/dev/ttyUSB0
).Configure the baud rate to 115200.(If you’re using PuTTY and want more configuration details, you can refer to my Hackster project here: https://www.hackster.io/saqibsherawan/signal-transmission-and-reception-using-adrv9009-zcu102-b4d7aa).
Once everything is connected and powered up, you’re officially ready to boot into your custom PetaLinux + ADI system. Sit back, watch the boot logs roll, and enjoy the sweet moment when your Linux finally greets you — congratulations, you’ve built it from scratch! 🥳🐧
You have now successfully built a custom PetaLinux image for the ADRV9009 + ZCU102 platform, integrating your own HDL-based hardware design and ensuring device tree compatibility. This process demonstrated the complete flow — from preparing the environment and incorporating the meta-adi
layer, to resolving build-time issues and deploying the final image on hardware.
Your setup is now ready for higher-level system validation, driver integration, and application development on the Zynq UltraScale+ SoC. Whether you plan to extend functionality, perform performance measurements, or integrate real-time DSP workloads, this serves as a solid foundation for further development.
If you encounter any issues, build errors, or inconsistencies while following this tutorial, feel free to share them in the comments section. Feedback, suggestions, and troubleshooting experiences help refine and improve the guide for everyone in the community.
Comments