Building the Firmware Image

It is recommenced to use the pre-build images, which can be found at PlutoSDR Firmware releases. found at M2k Firmware releases. These are build and tested by Analog Devices, and should work on your PlutoSDR without issues.

If you want to change functionality, you will need to follow these instructions. To test your image, it is recommended to use the RAM Boot rather than writing to flash the first time. This keep the default firmware while you are testing your image, so if something goes wrong - its a quick power cycle to fix things.

Prerequisites

  1. Make sure to download, or upgrade your Sources

    • Make sure you have the recommended/required Vivado versions. It’s likely listed in the archive section of the Xilinx web site. To check out what you need, look at the following tcl file (which was checked out in the sources link above).

      ~/github/temp/plutosdr-fw$
      
      grep -i REQUIRED_VIVADO_VERSION $(find ./ -name "adi*.tcl") | grep set
      
      set required_vivado_version "2021.1"
      
  2. You need Xilinx Vivado Design Suite to compile the Verilog into the FPGA bit file.

  3. You need Xilinx Vivado Vitis to compile C code for the ARM inside the AMD Zynq.

    • If you are using very old versions of Xilinx tools, you need to make sure that you have the 32-bit libraries (the Xilinx tools are distributed as 32-bit binaries).

~/github/temp/plutosdr-fw$
find /opt/Xilinx/ -name vivado -executable -type f | xargs file | grep ELF
/opt/Xilinx/Vivado/2021.2/bin/unwrapped/lnx64.o/vivado: ELF 64-bit LSB executable,
x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
for GNU/Linux 2.6.18, not stripped

If it reports as a 32-bit binary (which the above did not), and you are running on a 64-bit system, do:

~/devel$
dpkg --add-architecture i386
~/devel$
apt-get update
~/devel$
sudo apt-get install libc6:i386 libstdc++6:i386

There are a few other tools that are necessary in order to build your own Firmware Image.

~/devel$
sudo apt-get install git build-essential ccache device-tree-compiler \
    dfu-util fakeroot help2man libncurses5-dev libssl1.0-dev mtools rsync \
    u-boot-tools bc python cpio zip unzip file wget

Building

Hint

It is recommended that if you are building in a Virtual Machine (which is totally fine), the Base Memory be set to at least 4096 MBytes. The AMD Xilinx Vivado tools require at least 1.6 Gig of memory when compile for the XC7Z010. It has been reported that 2048 Mbytes is not enough, and will cause the tools to hang.

Starting the build process is just a matter of typing make within the firmware repository. The Makefile requires a few environmental variables being set, and of course the ARM GCC toolchain in the PATH. Some paths maybe adjusted to match your Xilinx Vivado and Vitis install folders.

~/devel$
cd plutosdr-fw # or m2k-fw
~/devel/plutosdr-fw$
export CROSS_COMPILE=arm-linux-gnueabihf-
~/devel/plutosdr-fw$
export PATH=$PATH:/opt/Xilinx/Vitis/2021.2/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin
~/devel/plutosdr-fw$
export VIVADO_SETTINGS=/opt/Xilinx/Vivado/2021.2/settings64.sh
~/devel/plutosdr-fw$
make

The initial build takes some time to complete, and also requires an INTERNET connection, since buildroot downloads the source packages from the WEB.

Hint

Depending on your distribution, you may need to force Vivado to use Gtk2. You can do that by adding:

~/devel/plutosdr-fw$
export SWT_GTK3=0

before you type make.

Build Artifacts

~/devel/plutosdr-fw$
ls -AGhl build
total 52M
-rw-rw-r-- 1 michael   69 Apr 19 17:45 boot.bif
-rw-rw-r-- 1 michael 446K Apr 19 17:45 boot.bin
-rw-rw-r-- 1 michael 446K Apr 19 17:45 boot.dfu
-rw-rw-r-- 1 michael 575K Apr 19 17:45 boot.frm
-rw-rw-r-- 1 michael 8,3M Apr 19 17:45 pluto.dfu
-rw-rw-r-- 1 michael 8,3M Apr 19 17:45 pluto.frm
-rw-rw-r-- 1 michael   33 Apr 19 17:45 pluto.frm.md5
-rw-rw-r-- 1 michael 8,3M Apr 19 17:45 pluto.itb
-rw-rw-r-- 1 michael  16M Apr 19 17:45 plutosdr-fw-v0.20.zip
-rw-rw-r-- 1 michael 471K Apr 19 17:45 plutosdr-jtag-bootstrap-v0.20.zip
-rw-r--r-- 1 michael 4,2M Apr 19 17:39 rootfs.cpio.gz
drwxrwxr-x 6 michael 4,0K Apr 19 17:45 sdk
-rw-rw-r-- 1 michael 940K Apr 19 17:45 system_top.bit
-rw-rw-r-- 1 michael 362K Apr 19 17:45 system_top.hdf
-rwxrwxr-x 1 michael 409K Apr 19 17:45 u-boot.elf
-rw-rw---- 1 michael 128K Apr 19 17:45 uboot-env.bin
-rw-rw---- 1 michael 129K Apr 19 17:45 uboot-env.dfu
-rw-rw-r-- 1 michael 4,6K Apr 19 17:45 uboot-env.txt
-rwxrwxr-x 1 michael 3,2M Apr 19 17:33 zImage
-rw-rw-r-- 1 michael  16K Apr 19 17:39 zynq-pluto-sdr.dtb
-rw-rw-r-- 1 michael  16K Apr 19 17:39 zynq-pluto-sdr-revb.dtb

Testing on the target

There is a script in the plutosdr_scripts repo that will quickly download a build artifact (the pluto.dfu file) to an USB attached PlutoSDR. This will load the image into RAM, and not write to flash, enabling easy testing of images. However, since it is loading into RAM, it is not a persistent update over power cycles or further reboots.

~/github/plutosdr-fw$
sudo ../plutosdr_scripts/pluto_ramboot
Found Pluto SDR at IP 192.168.3.1, running kernel:
    Linux pluto 5.4.0-00535-g9c04de11ae53 #1 SMP PREEMPT Fri Aug 20 13:01:03 CEST 2021 armv7l GNU/Linux
Found Pluto SDR in dfu mode and downloading ./build/pluto.dfu
successfully downloaded
Found new PlutoSDR at 192.168.3.1, running kernel:
    Linux pluto 5.4.0-00535-g9c04de11ae53 #1 SMP PREEMPT Tue Feb 15 16:17:50 EST 2022 armv7l GNU/Linux

Main targets

File

Comment

pluto.frm

Main PlutoSDR firmware file used with the USB Mass Storage Device

pluto.dfu

Main PlutoSDR firmware file used in DFU mode

boot.frm

First and Second Stage Bootloader (u-boot + fsbl + uEnv) used with the USB Mass Storage Device

boot.dfu

First and Second Stage Bootloader (u-boot + fsbl) used in DFU mode

uboot-env.dfu

u-boot default environment used in DFU mode

plutosdr-fw-vX.XX.zip

ZIP archive containg all of the files above

plutosdr-jtag-bootstrap-vX.XX.zip

ZIP archive containg u-boot and Vivao TCL used for JATG bootstrapping

Other intermediate targets

File

Comment

boot.bif

Boot Image Format file used to generate the Boot Image

boot.bin

Final Boot Image

pluto.frm.md5

md5sum of the pluto.frm file

pluto.itb

u-boot Flattened Image Tree

rootfs.cpio.gz

The Root Filesystem archive

sdk

Vivado/XSDK Build folder including the FSBL

system_top.bit

FPGA Bitstream (from HDF)

system_top.hdf

FPGA Hardware Description File exported by Vivado

u-boot.elf

u-boot ELF Binary

uboot-env.bin

u-boot default environment in binary format created form uboot-env.txt

uboot-env.txt

u-boot default environment in human readable text format

zImage

Compressed Linux Kernel Image

zynq-pluto-sdr.dtb

Device Tree Blob for Rev.A

zynq-pluto-sdr-revb.dtb

Device Tree Blob for Rev.B

How does it work

Warning

All these steps are automatically handled by make. They are just explained here, for those who are interested.

Build Linux kernel

Function

File

PlutoSDR Linux Kernel Config

zynq_pluto_defconfig

$
make -C linux ARCH=arm zynq_pluto_defconfig
$
make -C linux -j 8 \
    UIMAGE_LOADADDR=0x8000 \
    ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- \
    zImage
$
cp linux/arch/arm/boot/zImage build/zImage

Making custom kernel changes

Attention

Normal users should not need to change their kernel, and this is only described for advanced users, or developers who periodically forget things

The command

$
make -C linux ARCH=arm zynq_pluto_defconfig

copies the file from arch/arm/configs/zynq_pluto_defconfig to .config and

$
make -C linux -j 8 \
    ARCH=arm \
    CROSS_COMPILE=arm-xilinx-linux-gnueabi- \
    UIMAGE_LOADADDR=0x8000 \
    zImage

builds it.

If you want to make a custom kernel, the easiest thing to do, is modify the .config with

$
make -C linux ARCH=arm zynq_pluto_defconfig
$
make -C linux ARCH=arm menuconfig

and then make changes, save them, and then create the defconfig.

$
make -C linux ARCH=arm savedefconfig

Check your changes against the default image

$
diff -u ./linux/arch/arm/configs/zynq_pluto_defconfig linux/defconfig | less

And if you are sure things are what you want, store them to the default file.

$
cp ./linux/defconfig ./linux/arch/arm/configs/zynq_pluto_defconfig

then this will work next time you type make to build the firmware image.

Build Devicetrees

Function

File

PlutoSDR Rev.A Device Tree

zynq-pluto-sdr.dts

PlutoSDR Rev.B Device Tree

zynq-pluto-sdr-revb.dts

$
make -C linux -j 8 ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq-pluto-sdr.dtb
$
cp linux/arch/arm/boot/dts/zynq-pluto-sdr.dtb build/zynq-pluto-sdr.dtb
$
make -C linux -j 8 ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq-pluto-sdr-revb.dtb
$
cp linux/arch/arm/boot/dts/zynq-pluto-sdr-revb.dtb build/zynq-pluto-sdr-revb.dtb

Build Buildroot User Space

Function

File

PlutoSDR Buildroot Config

zynq_pluto_defconfig

$
make -C buildroot ARCH=arm zynq_pluto_defconfig
$
make -C buildroot TOOLCHAIN_EXTERNAL_INSTALL_DIR= \
    ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- \
    BUSYBOX_CONFIG_FILE=/home/michael/devel/pluto/plutosdr-fw/buildroot/board/pluto/busybox-1.25.0.config all
$
cp buildroot/output/images/rootfs.cpio.gz build/rootfs.cpio.gz

Configuring Buildroot

You need to copy over the correct file, to the .config, edit it (with menuconfig), and then save it to the right place so that the main build system will use the new file.

$
make -C buildroot ARCH=arm zynq_pluto_defconfig
$
make -C buildroot ARCH=arm menuconfig
$
make -C buildroot ARCH=arm savedefconfig
$
make

Build FPGA Hardware Description File

Function

File

Pluto HDL Project

Files

$
source /opt/Xilinx/Vivado/2021.2/settings64.sh
$
make -C hdl/projects/pluto
$
cp hdl/projects/pluto/pluto.sdk/system_top.hdf build/system_top.hdf

Build FPGA First Stage Bootloader (FSBL)

Function

File

Create FSBL TCL script

create_fsbl_project.tcl

$
source /opt/Xilinx/Vivado/2021.2/settings64.sh
$
xsdk -batch -source scripts/create_fsbl_project.tcl
$
cp build/sdk/hw_0/system_top.bit build/system_top.bit

Build multi component FIT image (Flattened Image Tree)

Function

File

PlutoSDR Image Tree Source

pluto.its

$
u-boot-xlnx/tools/mkimage -f scripts/pluto.its build/pluto.itb

Build Firmware DFU image

$
cp build/pluto.itb build/pluto.itb.tmp
$
dfu-suffix -a build/pluto.itb.tmp -v 0x0456 -p 0xb673
$
mv build/pluto.itb.tmp build/pluto.dfu

Build Firmware FRM image

$
md5sum build/pluto.itb | cut -d ' ' -f 1 > build/pluto.frm.md5
$
cat build/pluto.itb build/pluto.frm.md5 > build/pluto.frm

Build u-boot

Function

File

PlutoSDR u-boot Config

zynq_pluto_defconfig

PlutoSDR u-boot Device Tree

zynq-pluto-sdr.dts

$
make -C u-boot-xlnx ARCH=arm zynq_pluto_defconfig
$
make -C u-boot-xlnx ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- UBOOTVERSION="PlutoSDR v0.20-PlutoSDR"
$
cp u-boot-xlnx/u-boot build/u-boot.elf

Build Zynq Boot Image

$
echo img:{[bootloader] build/sdk/fsbl/Release/fsbl.elf build/u-boot.elf } > build/boot.bif
$
source /opt/Xilinx/Vivado/2021.2/settings64.sh
$
bootgen -image build/boot.bif -w -o build/boot.bin

Build Boot DFU Image

$
cp build/boot.bin build/boot.bin.tmp
$
dfu-suffix -a build/boot.bin.tmp -v 0x0456 -p 0xb673
$
mv build/boot.bin.tmp build/boot.dfu

Build u-boot default environment Image

$
CROSS_COMPILE=arm-xilinx-linux-gnueabi- scripts/get_default_envs.sh > build/uboot-env.txt
$
u-boot-xlnx/tools/mkenvimage -s 0x20000 -o build/uboot-env.bin build/uboot-env.txt

Build u-boot default environment DFU Image

$
cp build/uboot-env.bin build/uboot-env.bin.tmp
$
dfu-suffix -a build/uboot-env.bin.tmp -v 0x0456 -p 0xb673
$
mv build/uboot-env.bin.tmp build/uboot-env.dfu

Build Boot FRM image

$
cat build/boot.bin build/uboot-env.bin scripts/target_mtd_info.key | \
    tee build/boot.frm | md5sum | cut -d ' ' -f1 | tee -a build/boot.frm