I really enjoyed the first PYNQ workshop and also an Embedded Hour session that was a few days later. I thought the best use of time would be to get my head around all the ways you can control the IO on the PYNQ-Z2 board.
We were given an example of using the pattern generator and trance analyzer to toggle a few pins and monitor what happens, but the example given just took some pin names and magically knew how to access them. This example for instance generated output on D0-D2 and monitored D17-D19:
This great if you just want the pattern generator to do the work for you, but what if you want some finer control over these pins? What if you want to communicate with a device over I2C?
There are overlays for a fixed set of Pmod devices but I wanted to go a little off the beaten track and use another device. Well, for this you need to jump into another area of your Jupyter notebook and get familiar with the Microblaze side of things.
Microblaze is an interesting concept. Programming FPGAs is a world away from the probably more familiar world of microcontrollers. FPGAs are really powerful but there are times when a microcontroller is better suited to the job. Now, the Zynq device on the board already has an ARM microntroller inside it, but that's busy running Linux and doing all the stuff that makes PYNQ possible. What if you just need a little bit of precedural microcontroller goodness to help you along? That's where Microblaze comes in. It's a fairly small 16-bit microcontroller that's actually created withing the FPGA fabric.
I must admit it took me a while to find my way around the Microblaze abilities of the PYNQ board. There are a few examples but I couldn't actually find any comprehensive documentation. Under the microblaze_programming notebook I found some useful looking sections such as this:
IO on Arduino headers
However, I decided that I wanted to mess with some pins on the Arduino-style header. There were examples for Microblade targetting PMODA and I found another for the Pi pin headers (using %%microblaze base.RPI) and a bit of guesswork lead me to the following for targetting the pins I wanted and flashing an LED attached to pin A5 of the Arduino header:
Now, none of this is complicated, but it required some guesswork. Not really complicated quesswork, but guesswork all the same. Considering how well PYNQ is set up to be self-documenting and easy to work with I found this odd. In particular, I found the the A5 pin is referred to as "19" buried in a comment on this page. Other than that though, it was easy enough to create a Microblaze processor and get it running some simple C code - via a Python-based notebook.
I2C on Arduino headers
After finding an I2C example on the Raspberry Pi headers, I thought it should be reasonably simple adapting this to some different pins. Unfortunately this wasn't the case. This is the code I came up with, and despite trying lots of variations, whenever I executed the code, it hung. I couldn't see any way to find where it had hung without some clunky pin toggling either.
Take off and nuke it from orbit. It's the only way to be sure.
To make things more frustrating it seemed impossible to rest things without powering off the board. Killing the PYNQ Kernel had no effect. In fact, even if you have successful Microblaze code running (such as blinking and LED in an endless loop) there appears to be no way to stop it.
Sorry this blog is not a success story, but I believe in writing things up as they are - good or bad. I'm going to keep on plugging away at this when I get the chance between work and other projects.