It's no secret that I have quite the soft spot for Trenz Electronic's FPGA versions of the Raspberry Pi and Raspberry Pi Zero. On top of the neat factor of boasting the Raspberry Pi Zero layout and GPIO header, the ZynqberryZero's form factor is one of the most handy Zynq-7000 boards to have on hand to integrate into space/power constrained projects.
I recently set out to start one such project using one of my peripheral boards from Adafruit, prompting me to return to the Pi OS I originally created in PetaLinux 2019.2 for the regular Zynqberry board (TE0726). I quickly found that the CircuitPython library I needed for my Adafruit peripheral board required the target (the Zynq) be running a later version of Python than PetaLinux 2019.2 was capable of compiling. This lead me to building Pi OS "2.0" in PetaLinux 2022.1 for my ZynqberryZero.
Note: from the perspective of this project, the only real main difference between the Zynqberry and ZynqberryZero is the FPGA package pinout mapping to the 40-pin RPi GPIO header so it should be pretty straightforward to adapt this project for the regular Zynqberry board.
As with the first version of Pi OS for the Zynq, there are four main overall objective that need to be completed:
1: Enable the Zynq's SPI and I2C interfaces and route via EMIO to the appropriate pins of the Zynqberry's 40-pin header.2: Enable the I2C smbus and SPIdev kernel drivers in the PetaLinux project.3: Create a GPIO function class library Python package for the Zynqberry.4: Modify the core CircuitPython packages to add the Zynqberry framework.
As I mentioned, I'm using PetaLinux 2022.1 for this project. As far as I'm aware, this tutorial should work with 2022.2 as well. However, based on a few recent comments I've seen I do want to reiterate that mixing versions of Vivado, Vitis, and PetaLinux within the same project is a recipe for trouble. Meaning if you created a hardware design in Vivado 2022.1 and export it, you should only import it into Vitis/PetaLinux 2022.1. Do NOT try to import an XSA created in Vivado 2022.1 into a PetaLinux 2022.2 project.
Hardware DesignI previously created a base hardware design in Vivado 2022.1 for the ZynqberryZero supporting GPIO/SPI/I2C access to the 40-pin Raspberry Pi GPIO header. However, digging into the latest version of Adafruit's Circuit Python I found that it was easier to modify my hardware pinout to match what Adafruit Platform Detect and Blinka were expecting (which is the classic Raspberry Pi 40-pin pinout).
This included adding the second UART in the Zynq EMIO then connecting it to Raspberry Pi GPIO 14 and 15, enabling the second chip select for the Raspberry Pi SPI1 interface on GPIO 8, and finally narrowing the Zynq GPIO from 20 to 17. I had originally set more of the pins as standard GPIO figuring I was more likely to use the GPIO instead of UART.
I've attached the TCL file below for my block design with these modifications, along with the corresponding constraints files. To recreate this Vivado project, create a new project targeting the ZynqberryZero (TE0727) then source my TCL file instead of selecting the option to create a new block design.
From the TCL console of the new Vivado project:
source ./<path to TCL>/zynqberryzero_bd.tcl
Once the TCL script generates, the block design, import my constraints files, then carry on with the normal design flow for Vivado (create HDL wrapper, run synthesis, run implementation, generate a bitstream, and export the hardware with the bitstream included).
The block design in this TCL script covers the first objective of enabling the Zynq's SPI and I2C interfaces and routing via EMIO to the appropriate pins of the Zynqberry's 40-pin header. Here is a mapping of the pinout from the FPGA package pin on the Zynq, through the 40-pin header:
I have also created a base PetaLinux project for the ZynqberryZero in 2022.1, which I've linked separately here since it's also a little lengthy. It does cover all of the core requirements for starting this project.
This includes all of the kernel options such as I2C smbus and and SPIdev, as well as the simple frame buffer modules and the SSD1306 OLED kernel module that is common to any Adafruit boards one might having lying around.
Just to expand on this a bit, the I2C smbus and SPIdev drivers are what the Adafruit CircuitPython libraries use under the hood for the I2C and SPI interfaces on the 40-pin RPi header. The frame buffer module is necessary for any peripheral board/pHAT that has a display of some sort, and the SSD1306 OLED kernel model is the most commonly used that can't easily be installed on the target after the fact (after the fact = after the PetaLinux project is build and the embedded Linux image is deployed on the target).
CircuitPython Package Dependencies in PetaLinuxWhile the base PetaLinux project in my previous post covers the majority of the package dependencies CircuitPython needs, I did discover a few more with this latest version of the base CircuitPython libraries.
Add the following lines to <PetaLinux project path>/project-spec/meta-user/conf/user-rootfsconfig
:
# System tools
CONFIG_gsl
CONFIG_nano
CONFIG_cmake
# Python3
CONFIG_python3
CONFIG_python3-pip
CONFIG_python3-cffi
CONFIG_python3-numpy
CONFIG_python3-shell
CONFIG_python3-spidev
CONFIG_python3-pillow
CONFIG_python3-pyserial
CONFIG_python3-threading
CONFIG_python3-multiprocessing
Then launch the root filesystem configuration editor:
~/zynqberryzero_prj/zynqberryzero_os$ petalinux-config -c rootfs
Enable the following under Filesystem Packages:
- misc > python3, python3-threading, python3-dev, python3-multiprocessing, python3-shell, python3-setuptools
- misc > tiff > tiff
- libs > libjpeg-turbo > libjpeg-turbo, libturbojpeg, jpeg-tools, libjpeg-turbo-dev
- libs > zlib > zlib
- libs > freetype > freetype
- libs > libwebp > libwebp
- x11 > libs > libxcb > libxcb
- devel > tcltk > tcl > tcl
Under user packages, enable all of the options just added to the user-rootfsconfig
file.
You'll probably notice that a lot of these packages are Python3 related, which technically could be installed with pip3
after the fact on the target FPGA. However, I found that some of these packages such as Pillow require more RAM to be compiled on the target than the ZynqberryZero has available (512MB).
After adding these dependencies rebuild the PetaLinux project, create a new BOOT.BIN, and package a new.wic file for the SD card:
~/zynqberryzero_prj/zynqberryzero_os$ petalinux-build
~/zynqberryzero_prj/zynqberryzero_os$ petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga ./images/linux/system.bit --u-boot --force
~/zynqberryzero_prj/zynqberryzero_os$ petalinux-package --wic --bootfiles "boot.scr image.ub system.dtb" --rootfs-file ./images/linux/rootfs.tar.gz
GPIO Class Library Python Package for ZynqberryZeroIn my original installation of Adafruit CircuitPython on the Zynqberry, I had to create the equivalent RPI.GPIO Python package for the Zynqberry from scratch. The RPI.GPIO package is what CircuitPython uses to know the pinout and pin function to the 40-pin header.
Luckily, I had found a very similar Python package already existed for the Pynq board in the Jupyter Notebooks workflow. I stole the ps.py and gpio.py scripts as a starting point for the Zynqberry.GPIO Python package. This provided me the baseline functionality of Linux's Sysfs API for the Zynq-7000 GPIO routed via EMIO in Python. So I was able to create my own Zynqberry.GPIO Python package and generate a .whl
built distribution file to install it with following the instructions from the Python website here.
Even luckier still, since the Zynqberry and ZynqberryZero use the same Zynq-7000 FPGA the MIO/EMIO numbering system was the same I was able to just reuse my Zynqberry.GPIO built distribution without even needing to update it. I've attached the .whl
file below and the details about how I originally developed it can be read here. This covers the third objective.
With the Zynqberry's pinout Python package created, it needs to be added to the core CircuitPython libraries to bridge that final gap between the CircuitPython and the Zynq platform. The two base CircuitPython libraries that are needed for any project are Adafruit-Blinka and Adafruit-PlatformDetect. Platform Detect is a library that provides best-guess platform detection for a range of single-board computers and is pretty easy to read and deduce how to add your own SBC. Blinka is a collection of packages emulating the CircuitPython API for devices or hosts running CPython or MicroPython. These packages provide functionality such as analog input/output pins, software-driven interfaces for I2C, SPI, hardware-driven interfaces for I2C, SPI, UART, digital input/output pins, using pin identities from board+microcontroller packages, and so on.
Download the source distribution files from PyPi.org and extract them to a desired location in order to add any desired customizations. I downloaded what were the latest versions at the time of writing (Blinka 8.16.1 and Platform Detect 3.42.0). I've attached the archives of my modified Adafruit-Blinka and Adafruit-PlatformDetect below, and here's the list of what I modified (keyword search "zynqberry" to find the specific lines modified/added):
1) In Adafruit Blinka updated the following files in ./Adafruit-Blinka-8.16.1/src
:
- board.py
- busio.py
- digitalio.py
2) Created the directory ./Adafruit-Blinka-8.16.1/src/adafruit_blinka/board/zynqberry
, and added the following files:
- __init__.py
- pin.py
- zynqberry_40pin.py
3) Created the directory ./Adafruit-Blinka-8.16.1/src/adafruit_blinka/microcontroller/zynqberry_emio
, and added the following files:
- i2c.py
- __init__.py
- pin.py
- spi.py
4) In ./Adafruit-Blinka-8.16.1/src/microcontroller
, modified
- pin.py
- __init__.py
Once all of the modifications have been made to add the Zynqberry to the Adafruit Blinka source code, rebuild the Python package:
~/zynqberryzero_prj/Adafruit-Blinka-8.16.1$ python3 -m build
The .whl
file to install it on the target can be found in the ./dist
directory that will appear after a successful build (I attached my built versions so they are ready to go).
In Adafruit Platform Detect I updated the following files:
1) In ./python_packages/Adafruit-PlatformDetect3.42.0/adafruit_platformdetect
, modified
- board.py
2) In ./python_packages/Adafruit-PlatformDetect3.42.0/adafruit_platformdetect/constants
, modified
- boards.py
3) In ./python_packages/Adafruit-PlatformDetect3.42.0/adafruit_platformdetect/bin
, modified
- detect.py
After rebuilding the Python package for Platform Detect, it was ready to test out on the ZynqberryZero! So I placed everything in a single folder that needed to be transferred to the ZynqberryZero from my host computer:
- Adafruit_Blinka-8.16.1-py3-none-any.whl
- Adafruit_PlatformDetect-3.42.0-py3-none-any.whl
- Zynqberry.GPIO-0.0.1-py3-none-any.whl
- font5x8.bin (down from Adafruit here - this needs to be placed in any directory you're running Python scripts in that call CircuitPython)
After flashing the SD card with the new.wic and flashing the ZynqberryZero's QSPI with the new boot binary (see how to do that here). I connected the ZynqberryZero my router via a MicroUSB to Ethernet adapter to the ZynqberryZero's USB OTG port. Having the ZynqberryZero on my local network was necessary so I could transfer the Python .whl
files and corresponding Python test script from my host computer.
After getting the ZynqberryZero booted up, I found that for pip to work correctly, the current date/time need to be set:
zynqberryzero_os:~$ sudo date -s "04 MAR 2023 12:53:00"
Transferring the files from my host computer was easy using the scp command (I placed everything in one folder for convenience):
~zynqberryzero_prj/whls$ scp -O ./* petalinux@192.168.x.xxx:/home/petalinux
I installed the custom packages first for the petalinux
user but with sudo privileges so they were system-wide:
zynqberryzero_os:~$ sudo pip3 install ./Zynqberry.GPIO-0.0.1-py3-none-any.whl
zynqberryzero_os:~$ sudo pip3 install ./Adafruit_PlatformDetect-3.42.0-py3-none-any.whl
zynqberryzero_os:~$ sudo pip3 install ./Adafruit_Blinka-8.16.1-py3-none-any.whl
And yes, pip3 will whine at you about installed stuff as root. But the root user needs the packages installed because of the permissions to access the GPIO, SPIdev, and I2C SMbus devices.
Test CircuitPython on the ZynqberryZeroI wrote a quick SPIdev test script in Python using CircuitPython to access and write bytes out on MOSI:
import time
import busio
import board
import spidev
from digitalio import DigitalInOut, Direction, Pull
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
CS = DigitalInOut(board.CE1)
RESET = DigitalInOut(board.D25)
spi_dev = spidev.SpiDev()
# use /dev/spidev0.0
spi_dev.open(0,0)
while True:
spi_dev_rsp = spi_dev.xfer([0x00])
print('spi_dev_rsp = ', spi_dev_rsp)
time.sleep(1)
I ran the script on my ZynqberryZero to verify it could execute without errors:
Using my ADP3450's logic analyzer function I connected to the SPI interface on the 40-pin header to verify I was actually getting SCLK/CS/MOSI output in mode 0 as expected (I'll detail this test process in another post).
I played around with modifying the bytes transmitted with the SPIdev test Python script for "full" verification:
And that's it, CircuitPython is ready to go on the ZynqberryZero. Now back to my original plan of action for using one of my peripheral boards from Adafruit! Stay tuned!
Comments