sekigon-gonnoc / Pico-PIO-USB

USB host/device implementation using PIO of raspberry pi pico (RP2040).
MIT License
1.36k stars 145 forks source link

💡[FEATURE] Can someone provide a detailed description of the PIO instructions? #85

Open shreeve opened 1 year ago

shreeve commented 1 year ago

The rp2040 is an amazing chip and the two PIO units are powerful! However, as I try to learn the exact timing, it's hard to get it right. For example, I see this helpful image in this repo:

timing

But, it's hard to follow exactly the exact timing, because we have D+ and D- and then the inverses of them (since their pins are inverted), etc. It's also hard for me to see exactly when the bits flip exactly how that impacts the PIO instructions. I've read through the data sheet, but it doesn't totally help answer my questions such as exactly when do instructions meet certain conditions and move to the next instruction or perform a jump.

Is there anyone here that understands at least the rx_pio code enough (and has some spare time) to be able to describe in more detail the actual PIO instructions below?

A few questions I have are:

1) Who fires the IRQ_RX_EOP interrupt? 2) When exactly does a command like jmp pin start move to the next instruction? If the pin is high for 3.05 cycles, does the jmp program move to the next instruction at the 4th cycle? Or, since the condition is still true at the start of the 3rd cycle and first fails when checking at the start of the 4th cycle, do we have to wait for that cycle to complete and then run the next instruction at the 5th cycle? I have the same question for wait instructions. 3) Maybe it would be easier to take one of the cases, such as only FS (Full speed) mode of USB and summarize the code from that context. Also, because we have FS and LS here and D+ and D- and they are also inverted, it is just sort of melting my brain trying to map it all out.

If someone could describe in detail what the 17 instruction edge detection program and the 15 instruction NRZI decoder are doing, it would be extremely helpful.

I think there might be many others that would also benefit from reading through a clear description.

Sorry to ask for something that seems like I should "do on my own", I've spent about 10 days looking at these 32 instructions and my (apparently) small brain is still having a problem understanding them.

If someone has the time to write up a quick summary, it would be greatly appreciated. Thanks!


.define public IRQ_RX_BS_ERR 1 ; bit stuffinc error
.define public IRQ_RX_EOP    2 ; eop detect flag
.define public IRQ_RX_START  3 ; packet start flag
.define public IRQ_DECODER   4 ; NRZI decoder trigger

.define db0 0
.define db1 1

; --[ edge detector ]---------------------------------------------------------

; USB signal edge and eop detector (17 instructions)
;            FS(12M)  LS(1.5M)
; Run at     96MHz    12MHz
; jmp_pin    D-       D+
; in_pin     D+       D-
;
; both D+ and D- pins should be input invert overridden

.program usb_edge_detector
.side_set 1

eop:
  irq wait IRQ_RX_EOP     side db0 ; TODO: who triggers this irq?
start:
  jmp pin start           side db1 ; wait for a falling edge
  irq IRQ_RX_START [1]    side db0 ; see pio_usb_device.c?

.wrap_target
pin_still_low:
  irq IRQ_DECODER [1]     side db0 ; trigger NRZI decoder

; resync on rising edge
pin_low:
  jmp pin pin_went_high   side db1
pin_went_low:
  jmp pin pin_went_high   side db1
  jmp pin pin_went_high   side db1
  jmp pin pin_went_high   side db1
  jmp pin pin_went_high   side db1
  jmp pin pin_went_high   side db1
.wrap

pin_still_high:
  mov x isr [2]           side db1
  jmp x-- eop             side db1 ; jump to eop if jmp_pin and in_pin are high because both inputs are inverted

; jump here on rising edge
pin_went_high:
  mov isr null            side db1
  in pins 1               side db0 ; capture pin to check for eop
  irq IRQ_DECODER         side db0 ; trigger NRZI decoder
  jmp pin pin_still_high  side db0
  jmp pin_went_low        side db1 ; jump to pin_went_low (not pin_low) to adjust interval of decoder trigger

.wrap

; --[ NRZI decoder ]----------------------------------------------------------

; USB NRZI data decoder (15 instructions)
;            FS(12M)  LS(1.5M)
; Run at     as fast as possible
; jmp_pin    D+       D-
;
; both D+ and D- pins should be input invert overridden
;
; NOTE: you must fill OSR by 1 and set 0 to x before runnning this program

.program usb_nrzi_decoder
.side_set 1 opt

start:
  ; set x 0 side db0

.wrap_target

set_y:
  set y 6

irq_wait:
  wait 1 irq IRQ_DECODER ; wait for edge detector
  jmp pin pin_high

pin_low:
  jmp !y flip side db0 ; ignore stuffed bit (no error)
  jmp !x K1   side db0
K2:
  ; x == 1
  in null 1
  jmp flip
K1:
  ; x == 0
  in osr 1
  jmp y-- irq_wait

pin_high:
  jmp !y flip side db1 ; ignore stuffed bit (no error)
  jmp !x J1   side db1
J2:
  ; x == 1
  in x 1
  jmp y-- irq_wait
J1:
  ; x == 0
  in null 1

flip:
  mov x ~x

.wrap