Pico PIO micropython script for bidirectional IO

257 views
Skip to first unread message

Kevin Price

unread,
Nov 9, 2021, 1:12:24 AM11/9/21
to retro-comp
I thought I'd share this script I wrote for doing bidirectional IO with a pi pico PIO and rc2014 in micropython. I'm sharing it because I think it will help others get started. Wiring details are in the file. 


I intend to add multiple "soft" registers by bringing across the lower address pins to the pico, and having them read along with the data pins. I intend to use the pico scanvideo library to create an ili9341 type interface to a vga display. 

The PIO is so fast I'm not sure you'd need wait states if you were reading/writing to buffers via dma or something.

Marten Feldtmann

unread,
Nov 10, 2021, 12:12:44 PM11/10/21
to retro-comp

Is this a usage of the undocumented 8080/6800 interface PIO ?

Marten

Kevin Price

unread,
Nov 11, 2021, 9:18:10 AM11/11/21
to retro-comp
No, It's for a microcontroller released by raspberry pi trading to talk with a z80. What makes their microcontroller different from an ESP32/propeller/etc that other people have used in projects like the UX board is that it has a peripheral called a programmable IO unit. The pico PIO can run four state machines, with each state machine being able to do a 32 bit IO operation every clock cycle (~200mhz by default). Each state machine can run at a fraction of the system clock. So it's possible to create pico PIO (not zilog pio) state machine that is capable of reacting with the precise timings it would normally require an FPGA/CPLD to achieve. You can implement i2c and uart for instance - without dedicated on chip peripherals.

Micropython is very easy to get started with compared to a C SDK or even arduino studio. It's interpreted so there isn't a compile/deploy step - the pi pico appears on your development PC as a thumb drive. You can pause execution, run commands, and print variables on the microcontroller like you can with mbasic on your z80. 

For me it makes developing something like a custom ux module/video chip a lot less frustrating than it would be. I don't have to count instruction or clock cycles, I can do wide transfers in one clock cycle (up to 32 bits shifted in every clock cycle from the pi pico pins). 






 

Marten Feldtmann

unread,
Nov 11, 2021, 3:23:03 PM11/11/21
to Kevin Price, retro-comp
No, the Picco PI is said to include a predefined 8080/6800 bus interface as a PIO, but it is sometimes mentioned in the documentation, but there is no example in the SDK showing how to use it.

So, my question was if you have found out how to use this feature.

The idea ist the same, use the Picco as a normal interface chip in an z80 system offering IO

Marten
--
Diese Nachricht wurde von meinem Android-Gerät mit K-9 Mail gesendet.

Dylan Hall

unread,
Nov 13, 2021, 2:27:01 AM11/13/21
to Kevin Price, retro-comp
Hi Kevin, any chance you have a circuit schematic you can share? I've been tinkering along similar lines and I'm keen to compare approaches :)

A couple of things I've found that might save you some time:

The Pico has a thing called an Input Synchroniser (a pair of back to back flip-flops on every GPIO pin) that adds two clock cycles meaning the state of any input is always 2 cycles behind reality. Not a big issue but a little confusing when looking at traces on a scope. More details are in the Pico docs. In the C-SDK you can disable these. I strongly advise against that. For lack of a more technical description, turning them off causes "weird stuff" to happen in the state machines. There's a warning in the docs about this I shouldn't have ignored!

I did some very rough benchmarking of MicroPython on the Pico and if I recall correctly it was around 50-100 clock cycles per "line of python". This doesn't seem to be an issue if you just want to read or write values to the bus, but if you want something more interactive then it's quite slow and absolutely you need the wait pin. For example, if the PIO reads the lower address pins, passes them up to the python interpreter which has a think, then pushes a result back to the PIO to write to the data bus. By contrast it is possible to have an interactive response using the C-SDK but you still only get a handful of clock cycles to respond before you need the wait pin. Overclocking the Pico buys you some more cycles :)

Using the C-SDK I've seen the occasional I/O request take longer than the rest. I suspect this is the USB controller generating interrupts which the cpu has to deal with. If they coincide with the I/O request it ends up taking longer. If you use the second core to run the code that deals with the PIO state machines that seems to fix the issue (I'd guess USB interrupts are tied to the first core). I'm not sure if something similar will apply under MicroPython.

I had the bright idea of replacing the 74LVC245 that talks to the data bus with a 74LVC4245 which does up translation to 5V. This caused me lots of issues with the circuit as that chip is quite bouncy driving the long tracks on the RC2014 bus. I don't recommend going down this path, I wasted a lot of hours staring at my scope trying to understand what was happening.

I've just recently replaced all the LVC parts in my circuit with AHC. They appear to be drop-in replacements but operate at about half the speed. That might sound like an odd thing to want but I've had a couple timing issues where the very fast LVC parts react too quickly making them glitch (the Pico see's an I/O request where none occurred). This could well be a side-effect of how I've designed my circuit. 

Dylan

  

--
You received this message because you are subscribed to the Google Groups "retro-comp" group.
To unsubscribe from this group and stop receiving emails from it, send an email to retro-comp+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/retro-comp/BA2DEEF6-082A-4C5C-B40F-E8129A73A88B%40dimap.de.

Kevin Price

unread,
Nov 13, 2021, 9:44:47 AM11/13/21
to retro-comp
This is my five minute easy IDA "sketch". Anything other than sustained DMA FIFO queues/dequeues would definitely need the wait pin under micropython. Any spurious requests generated I've attributed to my prototyping method (chained breadboards and 10" jumper wire), or the PIO being fast enough it picked up bounce/ripple from not having decoupling capacitors. Slowing the PIOs down to 50mhz allowed me to do 10 byte write/10 byte read cycles and verify the results on the z80 for about a half hour without major issue. I'm fine with errors here and there. If I really needed error free operation I'd have to put the thing in a grounded metal case, shorten the leads, etc. 

If I get to the point where I send a board to be fabbed (not rc2014 form factor, one off just for me), I will probably have two data bus 74lvc245s (total of three). Instead of changing the direction I'll just hardwire one +5v a to b, the other+3.3v b to a, and turn them off and on with the read and write signals. 



mockup.png

Dylan Hall

unread,
Nov 13, 2021, 4:45:57 PM11/13/21
to Kevin Price, retro-comp
Thanks! It looks like the difference in our approaches is that I'm doing the address decode in the Pico.
I've attached a schematic and some example code in case you're interested. The address decode is set to respond to 0x04-0x07. 

You might want to swap your IORQ for "IORQ and !M1" otherwise you may get a false trigger during the interrupt acknowledgement cycle.  

Do you have any example code using DMA to talk to the PIO engine? That's an area I've not had time to tinker with yet.

Dylan

Schematic_Pico Bus_2021-09-06.pdf
reader3_pcb.py

Kevin Price

unread,
Nov 14, 2021, 12:31:16 PM11/14/21
to retro-comp
You're right about the IRQ and !M1, thank you for catching that! I chose to go with hardware address initially because it would let me auto trip wait, and let me disable the 74lvc245. It also meant I could use the DMA more often on the Pico when writing/reading sequential data. I have not spent too much time understanding z80 interrupts. I'm just going in bits and pieces, fits and starts.

The scanvideo library is the only C SDK code I've looked at so far that uses DMA and PIO. 

Micropython does not expose a DMA interface, so I will have to write a micropython C module for outputting a VGA signal.

Kevin Price

unread,
Nov 14, 2021, 12:35:10 PM11/14/21
to retro-comp
I'm also really impressed with your circuit and schematic. I'm just schmucking about with this stuff by comparison. 

Dylan Hall

unread,
Nov 15, 2021, 12:20:32 AM11/15/21
to Kevin Price, retro-comp
I've been tinkering with the Pico and RC2014 since February and I've still not managed to make it do anything genuinely useful so it's nice to see someone else working with the same tools. Please keep posting your progress :)

I spent a little time trying to understand z80 interrupts and it looks like if you want to use them properly (IM2, daisy-chaining) it's surprisingly complicated so I've decided to abandon that feature in my next pcb iteration. That should simplify things considerably. 

Dylan

Kevin Price

unread,
Nov 18, 2021, 5:33:54 PM11/18/21
to retro-comp
It seems like a milestone worth sharing. I was able to write to "screen memory" from the z80. The official raspberry pi scanvideo library was too complex for me to wrap my head around, so I'm pre-generating an hsync and vsync buffer and clocking out a bit at the 640x480@60hz. It's a trick I saw done in FabGL. An example of just the DMA is in "/c/just_dma_vga.c" feeding the PIOs. 

Just shaking the table causes issues (see circuit picture), so I will not know how reliable the interface is until/unless I fabricate a board. 

docnotes/z80 at master · keyvin/docnotes (github.com)



IMG_20211118_170742308_HDR.jpgIMG_20211118_170738744.jpg

I'm most likely going to switch to creating a custom version of nascom basic for my sc126 for awhile. Hardware has always frustrated me so much, without very high end tools (single channel 1mhz scope doesn't count) it's so frustrating to work out what's happening. 


Reply all
Reply to author
Forward
0 new messages