Programable IO
This manual summarizes the Programmable I/O (PIO) instruction set of the Raspberry Pi Pico, including MOV, IN, OUT, SET, side-set, PUSH, PULL, JMP, IRQ, WAIT, DELAY, and program wrapping. The content is organized into sections with detailed explanations and practical examples.
Overview of PIO Architecture
State Machines: Each PIO block contains 4 independent state machines.
Instruction Memory: Shared by all state machines.
Shift Registers: - OSR (Output Shift Register): holds data pulled from TX FIFO, used by OUT. - ISR (Input Shift Register): holds data collected by IN, used by PUSH.
FIFOs: - TX FIFO (CPU/DMA → PIO): data supplied to PIO programs. - RX FIFO (PIO → CPU/DMA): data sent from PIO back to CPU.
Flow:
CPU -> TX FIFO -> OSR -> OUT -> GPIO CPU <- RX FIFO <- ISR <- IN <- GPIO
MOV Instruction
Syntax:
MOV destination, source
Copies data directly between registers, pins, PC, or shift registers.
Destinations: PINS, X, Y, EXEC, PC, ISR, OSR
Sources: PINS, X, Y, NULL, STATUS, ISR, OSR
Examples:
MOV X, PINS ; copy pin state into X
MOV PINS, Y ; write register Y to pins
MOV PC, X ; jump to address held in X
MOV EXEC, Y ; execute instruction held in Y
IN Instruction
Syntax:
IN source, count
Shift count bits into ISR from a source.
Sources: PINS, X, Y, NULL, STATUS, ISR, OSR.
Example:
IN PINS, 8 ; shift 8 pin bits into ISR
PUSH ; ISR -> RX FIFO -> CPU
OUT Instruction
Syntax:
OUT destination, count
Shift count bits from OSR into a destination.
Destinations: PINS, X, Y, PC, ISR, OSR, PINDIRS.
Example:
PULL ; TX FIFO -> OSR
OUT PINS, 8 ; shift 8 bits from OSR to pins
SET Instruction
Syntax:
SET dest, value
Load an immediate value (0–31) into destination.
Destinations: PINS, X, Y, PINDIRS.
Example:
SET PINS, 1 ; set output pin high
SET PINS, 0 ; set output pin low
Side-set
Side-set allows pins to be set in parallel with every instruction without extra instructions.
Declaration:
.side_set n [opt] [pindirs]
n = number of side-set bits (0–5)
opt = optional; default 0 if not written
pindirs = MSB bit controls pin direction (0=input, 1=output)
Execution order per instruction: 1. Apply side-set to pins 2. Execute instruction 3. Wait for delay (if any)
Examples:
**SPI clock + MOSI example**::
.side_set 1 opt
bitloop:
out pins, 1 side 1 [1] ; send MOSI bit + clock high
nop side 0 [1] ; clock low
**Blink LED example**::
.side_set 1
nop side 1 [9] ; LED high 10 cycles
nop side 0 [9] ; LED low 10 cycles
**Using pindirs**::
.side_set 2 pindirs
nop side 2 ; b10: DIR=1, value=0
nop side 3 ; b11: DIR=1, value=1
nop side 0 ; b00: DIR=0, input
PUSH Instruction
Syntax:
PUSH [block]
Transfers ISR → RX FIFO. Clears ISR.
block: waits if FIFO full.
Example:
IN PINS, 8
PUSH ; CPU can now read RX FIFO
PULL Instruction
Syntax:
PULL [block]
Transfers TX FIFO → OSR.
block: waits if FIFO empty.
Example:
PULL
OUT PINS, 8 ; send data from CPU to pins
Explanation of PUSH/PULL
PULL goes TX FIFO → OSR → OUT → GPIO (output path).
PUSH goes IN → ISR → RX FIFO → CPU (input path).
JMP Instruction
Syntax:
JMP condition, target
Conditions: - Unconditional - !X, !Y (jump if X/Y == 0) - X–, Y– (decrement, jump if result >= 0) - X!=Y - PIN (jump if pin is 1) - !OSRE (jump if OSR empty)
Examples:
set x, 4
loop:
jmp x-- loop ; loop until x < 0
jmp !x done ; jump if X==0
jmp pin got_high ; jump if pin is high
WAIT Instruction
Syntax:
WAIT polarity source index
polarity: 0=wait for low, 1=wait for high
source: pin, irq, tx
Examples:
wait 0 pin 0 ; wait for GPIO0=0 (UART start)
wait 1 irq 0 ; wait for IRQ0 set
wait 1 tx ; wait until TX FIFO has data
IRQ Instruction
Syntax:
IRQ set n
IRQ clear n
IRQ wait n
Each PIO block has 8 IRQ flags.
Used for signalling CPU or other SMs.
Examples:
irq set 0 ; notify CPU
irq wait 1 ; wait until CPU sets IRQ1
DELAY Field
Each instruction can specify a delay [N] (0–31).
Execution time = 1 (instruction) + N (delay).
Examples:
nop [5] ; nop but hold 6 cycles
Used with side-set for timing:
nop side 1 [9] ; LED high 10 cycles
nop side 0 [9] ; LED low 10 cycles
Program Wrapping
Wrap defines auto-loop region of code.
Directives:
.wrap_target
... code ...
.wrap
When execution reaches .wrap, PC automatically jumps back to .wrap_target.
If no wrap is defined, program halts at end unless JMP is used.
Example:
.program blink
.side_set 1
.wrap_target
nop side 1 [9]
nop side 0 [9]
.wrap
This loops automatically without a manual JMP.
Summary
MOV: Copy between registers/pins/PC/EXEC.
IN: Collect bits into ISR.
OUT: Send bits from OSR.
SET: Load immediate into dest.
Side-set: Parallel pin updates.
PUSH: ISR → RX FIFO → CPU.
PULL: TX FIFO → OSR.
JMP: Conditional/unconditional jumps.
WAIT: Pause until pin/irq/tx condition.
IRQ: Raise, clear, or wait for interrupt flags.
DELAY: Per-instruction extra cycles.
WRAP: Define auto-loop section.
Together these make PIO a tiny but powerful state machine controller for bit-banging protocols, signal generation, and precise I/O timing.