[HN Gopher] A closer look at Raspberry Pi RP2040 Programmable IOs
       ___________________________________________________________________
        
       A closer look at Raspberry Pi RP2040 Programmable IOs
        
       Author : beshrkayali
       Score  : 74 points
       Date   : 2021-02-14 09:28 UTC (2 days ago)
        
 (HTM) web link (www.cnx-software.com)
 (TXT) w3m dump (www.cnx-software.com)
        
       | pkaye wrote:
       | The Cypress FX3 is another chip with programmable IO which people
       | have used to make low cost logic analyzers.
        
       | gchadwick wrote:
       | I've just been getting to grips with the PIO, they are a lot of
       | fun to program. Whilst the instruction set is very limited with
       | only enough memory for 32 of them you can do some pretty
       | sophisticated things. A key part of the flexibility is the
       | ability to execute incoming data as instructions. You can stream
       | in a mixture of data and instructions via the DMA, the PIO
       | separates the two and uses the instructions to modify execution
       | so a simple loop becomes a lot more flexible. You can easily sync
       | several of them up to. The scanvideo library:
       | https://github.com/raspberrypi/pico-extras/blob/master/src/c...
       | which is for producing video signals (like VGA) on the fly with
       | scanlines supplied by the CPU is a good example of what's
       | possible. You can have multiple scanline PIO running at once,
       | each decoding a compressed RLE format including transparency and
       | it composes them all together on the fly.
       | 
       | If you've ever played one of the Zachtronics games (in particular
       | Shenzhen IO) it feels like the programming in one of those games.
       | Tight constraints but major possibilities when you think about it
       | for a while.
        
       | MatthiasWandel wrote:
       | I have been puzzling over how to program quadrature decoding with
       | the PIO instructions. Not many chips have quadrature decoding
       | built in.
        
         | [deleted]
        
         | yetihehe wrote:
         | If you want to do it in pio, one of solutions would be some
         | elaborate series of jump instructions to detect what changed
         | and then in which direction. Every jump string would terminate
         | with instruction sending direction to queue. Otherwise you
         | could just output pins on every change into some array through
         | dma and read that array on main processor and calculate current
         | position in batched mode.
        
           | MatthiasWandel wrote:
           | I guess the ting to do would be to somehow wait on a change
           | on either line. Could be done with a wait on just one line if
           | I only want half counts instead of quarter counts
        
             | yetihehe wrote:
             | Or just continuously poll input pins and compare with
             | scratch register. When anything changes, execute check
             | logic. Probably best one is just waiting on pin changes and
             | sending them to main processor for handling. I have
             | multiple quadrature decoder in plans, but nothing started
             | yet and this is approach I will be taking if no one makes
             | anything better until then. When you check 16 pins for
             | changes, you can easily handle 8 decoders with just one PIO
             | state machine and some cunning decoding logic. I wouldn't
             | be surprised, if someone implements quadrature decoding
             | with hardware interpolator. It's one nice piece of
             | hardware. You can do single cycle 2d texture mapping or
             | rotozooming with that thing.
        
         | dragontamer wrote:
         | Quadrature decoding is just 4-state (aka: 2-bit) gray-code
         | decoding.
         | 
         | 0 -> 1 -> 2 -> 3 -> 0 -> 1 -> 2 -> 3... is counting up.
         | Formally: newstate - oldstate == 1.
         | 
         | 0 -> 3 -> 2 -> 1 -> 0 -> ... is counting down. Formally:
         | newstate - oldstate == 3.
         | 
         | 0 -> 2 is ambiguous. Carry on the "same direction as the last
         | time". Formally: newstate - oldstate == 2.
         | 
         | 0 -> 0 is standing still, no movement. newstate - oldstate == 0
         | 
         | -------
         | 
         | Except of course: 0, 1, 2, 3 are gray coded, not normal binary
         | coded.
         | 
         | * 0 == 00
         | 
         | * 1 == 01
         | 
         | * 2 == 11
         | 
         | * 3 == 10
         | 
         | Anyway, convert the 00 / 01 / 11 / 10 raw hardware bit-stream
         | into the above numbers (0, 1, 2, 3). Then it becomes VERY easy.
        
           | MatthiasWandel wrote:
           | The question was not how quadrature encoding works, but how
           | to implement it with the limited PIO instructions
        
             | dragontamer wrote:
             | Hmmm.
             | 
             | Any I/O is basically compression. So lets think about what
             | we're actually outputting. We're converting a raw bitstream
             | from two GPIO pins paired (Ex: 00, 01, 01, 01, 11, 11, 10,
             | 00, 01, 11), into a singular message: such as +6.
             | 
             | There are two applications of a quadrature encoder that I
             | can think of.
             | 
             | 1. Virtual Pot -- Converting the messages into a kind of
             | "-100 to +100 slider", such as a volume control knob. In
             | this case, you want a regular update schedule at human-
             | interface speeds (~1000 updates / second or slower).
             | 
             | 2. Rotational Velocity sensor -- Converting the messages
             | into a speed. You're willing to batch the bitstream up as
             | slowly as possible, maybe waiting for a rollover event
             | (+128 or -128). And you just interrupt on those overflows.
             | 
             | -------
             | 
             | Since the input format is already set, we now think about
             | the output format: how to represent +6 and or -12?
             | 
             | Based on the PIO instruction set, it seems like +6 and -12
             | probably would be easiest as two separate registers in two
             | different state-machines.
             | 
             | Every time an "increment" is detected, the FIFO-associated
             | with +1 gets a +1 bit added to it. Every time a "decrement"
             | is detected, the 2nd FIFO associated with -1 gets a +1 bit
             | added to it.
             | 
             | ----
             | 
             | In pseudocode:                   IncrementSide(){
             | currentState = 00; // default                  while(1){
             | nextState = in(GPIO0) | in(GPIO1);
             | if(currentState == 00 and nextState == 01){
             | push 1;                 }                 if(currentState
             | == 01 and nextState == 11){                     push 1;
             | }                 if(currentState == 11 and nextState ==
             | 10){                     push 1;                 }
             | if(currentState == 10 and nextState == 00){
             | push 1;                 }                 currentState =
             | nextState;             }         }
             | 
             | "nextState" and "currentState" probably is just the X and Y
             | registers of the PIO state machines. The above is "very
             | pseudocode" as I don't really understand PIO yet.
             | 
             | I'm saying "push 1", but "push 1 into the OSR". It looks
             | like the OSR has some kind of auto-push mechanism, but if
             | that doesn't work then a manual-push might be needed in the
             | code proper. Hard to say from the docs alone.
             | 
             | DecrementSide would be just the inverted if-statements:
             | if(currentState == 00 and nextState == 11){
             | push 1; // Decrement side checking for a decrement
             | }
             | 
             | --------
             | 
             | This above methodology would only work for #2 ("velocity"),
             | and fails for #1 ("positional"), because the increment-side
             | and decrement-side would be updating at varying rates.
             | 
             | I probably can make a positional-decoder instead of a
             | velocity one using similar principles. Or maybe by somehow
             | ensuring that the +1 and -1 messages go into the same FIFO
             | to be picked up by the host CPU.
             | 
             | The idea is route "0" messages to /dev/null, compressing
             | the input stream. The host still sees the important +1 and
             | -1 messages. The exact mechanism for that is up for debate,
             | but the simple if-else state machine above seems to
             | accomplish that to a limited extent.
             | 
             | ------
             | 
             | EDIT: Hmmm, maybe a singular FIFO can be done if we have
             | push1 and push0 for the two kinds of messages: 1
             | representing +1 and 0 representing -1.
             | 
             | Anyway, my point is that there's lots of solutions here. It
             | doesn't seem very difficult to me conceptually. Just a lot
             | of experimentation needed to know exactly how those 9
             | instructions work and the exact mechanisms of the ISR / OSR
             | / X reg / Y reg.
        
           | tekromancr wrote:
           | Don't you need more states, or an invalid state state to
           | account for bounces?
        
             | dragontamer wrote:
             | Due to the grey-code methodology, a bounce will be
             | interpreted as a "+1" followed by a "-1". (or
             | alternatively: a -1 followed by a +1) in all situations.
             | 
             | Ex: 00 -> 10 -> 00 is interpreted as 0 -> 3 -> 0, or -1
             | transition then a +1 transition.
             | 
             | 01 -> 11 -> 01 is interpreted as 1 -> 2 -> 1, or +1
             | followed by a -1 transition.
             | 
             | All possible combinations of bounces (00 -> 01 -> 00, 00 ->
             | 10 -> 00, 01 -> 11 -> 01, 01 -> 00 -> 01, etc. etc.) have
             | this +1 / -1 or -1/+1 property.
        
         | mmastrac wrote:
         | Funny enough, this popped up when I was googling RPI PIO just
         | now:
         | 
         | https://twitter.com/ZodiusInfuser/status/1357067388928335873
        
         | IshKebab wrote:
         | Quadrature decoding is extremely common in microcontrollers,
         | though the peripheral may not be called a quadrature decoder.
         | Normally they just make the counter peripheral flexible enough
         | to do it.
         | 
         | Example for the ESP32: https://github.com/espressif/esp-
         | idf/tree/73db142/examples/p...
         | 
         | I would not be surprised if you don't need PIO for this on this
         | chip.
        
       | sfrigon wrote:
       | What stops major SoC designers to come up with a standard
       | "programmable IOs" interface for all their IOs, a little bit like
       | these PIOs, instead of shipping hundreds of flavors of the same
       | CPU with different IO options? I guess it's more expensive to
       | design & manufacture a truly general purpose IO, but doesn't the
       | cost of warehouse and the risk of not having a market for that
       | specific SoC outweigh the initial cost? It would also lower the
       | number of pins on the SoC. e.g. You only want HDMI & SATA, here's
       | the VHDL for it, you can even individually select the pins you
       | want to use.
        
         | dragontamer wrote:
         | > You only want HDMI & SATA
         | 
         | SATA is a 6Gbps port, while HDMI is a 10Gbps port.
         | 
         | The PIO ports discussed here are on the order of 100kHz,
         | roughly 1-million times slower than HDMI, and 600,000 times
         | slower than SATA.
        
           | peterburkimsher wrote:
           | It actually is possible to get HDMI on the RP2040, if you're
           | willing to have lower resolution.
           | 
           | https://hackaday.com/2021/02/12/bitbanged-dvi-on-a-
           | raspberry...
        
             | sfrigon wrote:
             | Wow that's nice.
             | 
             | Even though I did not intend to say we should have SATA and
             | HDMI on the RP2040 itself (I didn't know if it was
             | possible), it still proves that having realtime control on
             | the IOs opens the door for way more functionalities than
             | SPI/I2C/UART specific ports. All of it using the same SoC
             | and potentially less pins.
             | 
             | Having the same level of control on any device would be
             | beneficial in my opinion.
        
           | sfrigon wrote:
           | Right, but I didn't mean for this specific device. I meant
           | for higher end SoCs, as in the RaspberryPi4/BeagleBone Black
           | or even higher end SoCs. The RP2040 could be used as an
           | inspiration to provide the same level of freedom to other
           | SoCs.
        
             | sfrigon wrote:
             | Well actually, it would be nice to replace any specific
             | interface with a generic FPGA-like interface. But of course
             | what you can implement with it would be limited to the
             | speed of the CPU / peripheral.
        
             | dragontamer wrote:
             | My overall point is that GHz-speed decoding in a flexible
             | manner seems... difficult... to say the least.
             | 
             | Your discussion point of "here's a VHDL block" seems to
             | understand the general issue. You need a non-trivial amount
             | of FPGA-magic (LUTs) to implement logic and routing at
             | those speeds. SATA has some kind of error-correction code
             | if I remember correctly... so its not exactly easy to parse
             | those messages.
        
               | sfrigon wrote:
               | I absolutely agree that this would be non-trivial and a
               | lot of magic is required to make it happen. But it would
               | need to be done only once, after that it can be shared to
               | all the users, a little bit like a GPU firmware/driver.
               | 
               | I am just surprised that this is not more widespread
               | among major players as a way to reduce costs and increase
               | flexibility. Though I'm pretty sure I'm overlooking the
               | core of the issue here haha
        
               | mbreese wrote:
               | I think one major factor is that it would increase unit
               | costs in many cases. We are talking cheap chips that are
               | sold in high volume. So any small increase in cost gets
               | multiplied quickly. Combine that with competition (your
               | competitor provides a less flexible chip, but it is $0.20
               | cheaper and has the IO ports you need), and you can see
               | why we have the mess we do.
               | 
               | I think I'm many cases the flexibility is great during
               | the prototype phase. But those are used at lower volume.
               | When you move to production, you'd want to have the
               | cheapest BOM as possible.
        
           | magicalhippo wrote:
           | > The PIO ports discussed here are on the order of 100kHz
           | 
           | That's not what the datasheet[1] says:
           | 
           |  _When outputting DPI, PIO can sustain 360 Mb /s during the
           | active scanline period when running from a 48 MHz system
           | clock. In this example, one state machine is handling
           | frame/scanline timing and generating the pixel clock, while
           | another is handling the pixel data, and unpacking run-length-
           | encoded scanlines._
           | 
           | Still not SATA speeds though.
           | 
           | [1]: https://datasheets.raspberrypi.org/rp2040/rp2040-datashe
           | et.p...
        
           | gchadwick wrote:
           | > HDMI is a 10Gbps port.
           | 
           | > The PIO ports discussed here are on the order of 100kHz
           | 
           | PIO runs at the system clock, 125 MHz by default, overclocks
           | of over 400 MHz have been reported stable. A single PIO can
           | clock out 32 bits every cycle (with a DMA and a memory system
           | that can feed this), giving you a total of 4 Gbps.
           | 
           | Running at full throttle like that, especially for a decent
           | length of time is tricky if you want to actually do anything
           | other than blast out bits but 16 or 8 bits per cycle is a lot
           | more straight forward, so 2 or 1 Gbps.
           | 
           | DVI output has already been demonstrated, running two
           | displays at 480p: https://github.com/Wren6991/picodvi
           | 
           | The DVI is maybe more of a party trick than something you'd
           | do in production hardware but it does demonstrate how capable
           | the PIO can be. You could happily implement the same concept
           | in a more performant device and reach 10 Gbps or more in a
           | reasonable way.
        
             | fpgaminer wrote:
             | I was skeptical at first, but read the github and yeah, not
             | only does it work, it passes eye mask tests. Crazy.
        
         | monocasa wrote:
         | You sort of have that with generic SERDES blocks.
        
         | petra wrote:
         | What stops them? Mostly business considerations.
         | 
         | The differentiate their price according to features and so they
         | extract more money. And by writing your code to specific
         | peripherials, it's harder to switch to another mcu.
         | 
         | And they have large libraries of proven hardware peripherials
         | and code which make it harder for competitors to enter. Why
         | would they want to compete with open-source pio libraries?
         | 
         | The raspberry pi foundation doesn't care about all that. So
         | they created this chip.
        
       ___________________________________________________________________
       (page generated 2021-02-16 23:01 UTC)