While there are a wide range of overlays for our PYNQ boards available from pynq.io, there will come a time when we need to create our own overlay.
Creating our own overlay is exactly what we are going to do in this blog; however, unlike the other blogs of the PYNQ Edition!, we will need to do some simple digital design using Vivado.
When we make an overlay we are creating a design that is loaded into the programmable logic (PL) half of the Zynq or Zynq MPSoC. We need to therefore start with a design which includes the correct configuration of our board. For this example, I am using the PYNQ-Z2, as such we can download the board definition files from the manufacturer TUL here.
These board definition files include the connections on the board (Pmod, Shield, RPI etc), and the configuration of the processor system (MIO, DDR,Clocks etc). Once we have these downloaded and installed in the directory below, we are ready to start a new project.
Once the new project is opened, we can create a new block diagram and add in the Zynq Processing System. To configure the processing system for the PYNQ-Z2, run the block automation option once the Zynq processing system has been added to the block diagram.
This example custom overlay is going to control up to 1,023 NeoPixels, using a custom HDL module placed in the PL. The PS will write in to a dual port block RAM, the second port of this is accessed by the custom IP block.
Each address in the BRAM contains the RGB settings for one NeoPixel, each color is represented by 8 bits, making a 24-bit pixel.
The base address in the block RAM defines how many NeoPixels are in the string. Once this is set, the custom IP block will cycle around the defined number of NeoPixels. The custom IP modules needs a 20 MHz clock, which is provided by the Processor System (PS) Fabric Clock 1.
The drive signal for a NeoPixel string is a single serial output bit; in this design, I have connected the serial output bit to PMODA pin 1.
We can define the pin connection using the synthesis view if you are not familiar with creating constraints in the XDC format. To use the Pmod Pin1 connector please route the signal to pin Y18 and set the IO standard to LVCMOS3v3, if you want to route the signal a different pin please check the schematics.
Once the design has finished implementing, to create a overlay we need not only the bit file which programs the PL but also a definition TCL file that defines the processing system interface and clock changes from the base overlay configuration. We can make this TCL file by using the command in the TCL window
write_bd_tcl — force ovelay.tcl
To work with the overlay in PYNQ, we need to create a Git repo from which is can be shared, downloaded and installed. While this is not strictly necessary, we can just FTP the TCL and bit files. But if we want to share with the community, we need a Git repo.
In this Git repo, we need to store the following files:
bit and tcl files both with identical names
Notebooks — An example notebook showing how it can be used
The file setup.py enables us to work with the python pip3 installer. We store the bit and tcl files in a directory structure which includes the board name. Doing so enables us to check during the installation process the bit file is intended for the correct board.
The setup file contains the information and functions necessary to download and transfer the bit, tcl and notebooks to the Pynq-Z2.
To add the overlay to our Pynq-Z2 board, we can use the command:
This will take a few minutes but once completed we will have both the overlay and the notebook example installed.
Back on the Jupyter homes page you will now notice a neo_pixel directory, underneath which you will see a notebook which outlines the basic commands to work with the NeoPixel overlay.
As we are going to be writing data to RAM in the PL, which memory mapped we will use the PYNQ MMIO package.
The first thing we will do is find the overlay and import it in to the design, we do this using the overlay package.
Once the overlay has been obtained, we can use the help function to learn ore about the overlay we just obtained.
We can then download the overlay in to the PL using the download function. To ensure we are using the right overlay, I also read back the time stamp and check a overlay is loaded.
To write into the block RAM with the NeoPixel values we are going to use the PYNQ MMIO function. This allows us to write and read addressed mapped devices in the PL.
The next cell therefore tests we can configure the MMIO and read and write the BRAM correctly. To ensure no issues are caused with the number of NeoPixels enabled, I use an address other than 0.
We are then ready to illuminate our first NeoPixel. To do so, we write the value 0x0f0f0f in to memory offset 0x4 — this will illuminate the NeoPixel a subdued white.
Finally, to enable the NeoPixel we inform the custom IP via the base register that there is one NeoPixel in the string.
This will result in the first NeoPixel illuminating as can be seen below.
We can then start to develop our own NeoPixel applications using PYNQ and this overlay — maybe a simple application such as a string, or a more complex application like the Game of Life or a NeoPixel cube.
It turns out creating our own custom overlay is not too difficult either. I am sure we will be creating others as we work with PYNQ going forward.