I work a lot with the PYNQ framework as you will see from my Hackster projects and of course previous Chronicles.

One of the most interesting aspects of the PYNQ framework is the ability it gives us to interface quickly and easily with new sensors, actuators and devices.

Pynq Z2 with Matrix Creator

We implement these interfaces using IO Processors (IOP) within the base overlay or by adding them to our own custom overlays. Regular readers of the blog will remember we have had a brief look at the IOP previously.

However, in this blog we are going to take a deep depth into how we can write software for the IOP to interface with the external sensor, actuator or devices.

For this example, we are going to focus on the RPI IOP on the PYNQ Z2. This IOP provides a range of interfaces which can be connected to the 40-Pin RPI header.

As you can see in the diagram below, we can connect any combination of timers, SPI, IIC, GPIO, UART to the RPI connector using the configurable switch.

RPI IOP (📷: https://pynq.readthedocs.io/en/v2.4/pynq_libraries/rpi.html)

This means when we want to connect to an external device using the RPI IOP we have a wide range of choice interfacing standards.

As interfaces are already implemented in the PL, we do have a few constraints on the configuration of the different interfaces.

SPI - Master 8-bit transfer, 6.25 MHz, 16 deep FIFO

I2C - 100KHz, 7-bit addressing

UART - 115200 Baud

GPIO - 28 outputs

AXI timer - 1 PWM output

But how do we write the software which uses these capabilities?

Like always, we start in the Jupyter notebook where we will first download the base overlay so that it's capabilities are available to us.

Downloading the base overlay

As the RPI header shares pins with PmodA connector, we must also configure which of the interfaces we wish to use. We can do this using the function:

base.select_rpi()

When it comes to developing our application in the Jupyter notebook, we can use the ipython magic command of %% to target the RPI MicroBlaze.

So far so good, though like all MicroBlazes we need a BSP which allows us to drive the peripherals, but we also need additional source code to integrate the IOP and switch functionality.

Rather helpfully each of the IOP implemented, e.g. Pmod, Arduino and RPI have their own library installed as part of PYNQ lib.

The easiest way to see this is to explore the PYNQ GitHub.

Library support

Looking under the RPI lib directory and driving down, you will eventually find several include files.

Browsing through the list of include files, you will see man you are familiar with as part of traditional MicroBlaze BSP for example Xparameters.h.

You will also notice several other files you may not be familiar with including:

spi.h - provides drivers for the SPI

i2c.h - provides drivers for the I2C

gpio.h - provides drivers for the GPIO

timer.h - provides a drivers for the PWM timers

Circular_buffer.h - provides a circular buffer functionality

It is these files that let us send and receive data using that particular interface and route to the desired pin on the connector. This includes the necessary types and functions to work with that interface.

While each of the files have slightly different function calls depending upon the nature of the interface.

Opening and closing an interface is the same using the following approach:

Open the device type - if there are more than one instance available select either 0 or 1. If there is only one instance select 0.

Open the pins on the switch - this connects the signals from the interface e.g. SCL, SDA on a I2C interface to the external world

Close the Device - this closes the interface.

For each of the interfaces, each include files also provides read and write functions as appropriate. For example, I2C.h provides read and write functions, while SPI.h provides a SPI transfer facility.

GPIO.h provides functions that allow us to define a range of pins or a single pin, their directio, along with the ability to read and write those pin(s).

One tip for creating solutions which use these interfaces is to create separate interface configuration functions from those that read and write the interface. Otherwise, you will see corruption of the interface as the devices are closed and opened again.

SPI configuration in Jupyter

If the pin is static, for instance, a GPIO driving reset this can be left combined.

GPIO Juypter Code

All told this makes interfacing with the external sensor, actuator or device very simple and easily changable via the Jupyter environment as we create our system.

The code snippet uses I2C, SPI and GPIO to enable me to talk to the Matrix Creator for which I am currently creating a Hackster project.

Code snippet of IOP interfacing

All told, this makes interfacing with the external sensor, actuator or device very simple and easily changeable via the Jupyter environment as we create our system.

The code below snippet uses I2C, SPI and GPIO to enabling the PYNQ Z2 to communicate with the Matrix Creator. Of course, you then need to add in additional functionality to leverage the Matrix Creators capabilities.

RPI IOP function calls in the body Jupyter notebook

However, If we are having issues with the interfacing, remember we can also use the trace facility which is also provided by the base overlay in conjunction with waveDROM to observe the signals at the pins.

This saves us from having to break out the oscilloscope!

If you are interested in the Matrix Creator, keep an eye on my Hackster.io projects there will be some interesting projects appearing there soon based on it.

See My FPGA / SoC Projects: Adam Taylor on Hackster.io

Get the Code: ATaylorCEngFIET (Adam Taylor)

Access the MicroZed Chronicles Archives with over 300 articles on the FPGA / Zynq / Zynq MpSoC updated weekly at MicroZed Chronicles.