docs/rp2: Add reference for PIO assembly instructions, and PIO tutorial.

This commit is contained in:
NitiKaur 2021-07-07 02:40:32 +05:30 committed by Damien George
parent d42cba0d22
commit baa5a76fc0
3 changed files with 280 additions and 0 deletions

View File

@ -72,6 +72,158 @@ For running PIO programs, see :class:`rp2.StateMachine`.
an error assembling a PIO program. an error assembling a PIO program.
PIO assembly language instructions
----------------------------------
PIO state machines are programmed in a custom assembly language with nine core
PIO-machine instructions. In MicroPython, PIO assembly routines are written as
a Python function with the decorator ``@rp2.asm_pio()``, and they use Python
syntax. Such routines support standard Python variables and arithmetic, as well
as the following custom functions that encode PIO instructions and direct the
assembler. See sec 3.4 of the RP2040 datasheet for further details.
wrap_target()
Specify the location where execution continues after program wrapping.
By default this is the start of the PIO routine.
wrap()
Specify the location where the program finishes and wraps around.
If this directive is not used then it is added automatically at the end of
the PIO routine. Wrapping does not cost any execution cycles.
label(label)
Define a label called *label* at the current location. *label* can be a
string or integer.
word(instr, label=None)
Insert an arbitrary 16-bit word in the assembled output.
- *instr*: the 16-bit value
- *label*: if given, look up the label and logical-or the label's value with
*instr*
jmp(...)
This instruction takes two forms:
jmp(label)
- *label*: label to jump to unconditionally
jmp(cond, label)
- *cond*: the condition to check, one of:
- ``not_x``, ``not_y``: true if register is zero
- ``x_dec``, ``y_dec``: true if register is non-zero, and do post
decrement
- ``x_not_y``: true if X is not equal to Y
- ``pin``: true if the input pin is set
- ``not_osre``: true if OSR is not empty (hasn't reached its
threshold)
- *label*: label to jump to if condition is true
wait(polarity, src, index)
Block, waiting for high/low on a pin or IRQ line.
- *polarity*: 0 or 1, whether to wait for a low or high value
- *src*: one of: ``gpio`` (absolute pin), ``pin`` (pin relative to
StateMachine's ``in_base`` argument), ``irq``
- *index*: 0-31, the index for *src*
in_(src, bit_count)
Shift data in from *src* to ISR.
- *src*: one of: ``pins``, ``x``, ``y``, ``null``, ``isr``, ``osr``
- *bit_count*: number of bits to shift in (1-32)
out(dest, bit_count)
Shift data out from OSR to *dest*.
- *dest*: one of: ``pins``, ``x``, ``y``, ``pindirs``, ``pc``, ``isr``,
``exec``
- *bit_count*: number of bits to shift out (1-32)
push(...)
Push ISR to the RX FIFO, then clear ISR to zero.
This instruction takes the following forms:
- push()
- push(block)
- push(noblock)
- push(iffull)
- push(iffull, block)
- push(iffull, noblock)
If ``block`` is used then the instruction stalls if the RX FIFO is full.
The default is to block. If ``iffull`` is used then it only pushes if the
input shift count has reached its threshold.
pull(...)
Pull from the TX FIFO into OSR.
This instruction takes the following forms:
- pull()
- pull(block)
- pull(noblock)
- pull(ifempty)
- pull(ifempty, block)
- pull(ifempty, noblock)
If ``block`` is used then the instruction stalls if the TX FIFO is empty.
The default is to block. If ``ifempty`` is used then it only pulls if the
output shift count has reached its threshold.
mov(dest, src)
Move into *dest* the value from *src*.
- *dest*: one of: ``pins``, ``x``, ``y``, ``exec``, ``pc``, ``isr``, ``osr``
- *src*: one of: ``pins``, ``x``, ``y``, ``null``, ``status``, ``isr``,
``osr``; this argument can be optionally modified by wrapping it in
``invert()`` or ``reverse()`` (but not both together)
irq(...)
Set or clear an IRQ flag.
This instruction takes two forms:
irq(index)
- *index*: 0-7, or ``rel(0)`` to ``rel(7)``
irq(mode, index)
- *mode*: one of: ``block``, ``clear``
- *index*: 0-7, or ``rel(0)`` to ``rel(7)``
If ``block`` is used then the instruction stalls until the flag is cleared
by another entity. If ``clear`` is used then the flag is cleared instead of
being set. Relative IRQ indices add the state machine ID to the IRQ index
with modulo-4 addition. IRQs 0-3 are visible from to the processor, 4-7 are
internal to the state machines.
set(dest, data)
Set *dest* with the value *data*.
- *dest*: ``pins``, ``x``, ``y``, ``pindirs``
- *data*: value (0-31)
nop()
This is a pseudoinstruction that assembles to ``mov(y, y)`` and has no side
effect.
.side(value)
This is a modifier which can be applied to any instruction, and is used to
control side-set pin values.
- *value*: the value (bits) to output on the side-set pins
.delay(value)
This is a modifier which can be applied to any instruction, and specifies
how many cycles to delay for after the instruction executes.
- *value*: cycles to delay, 0-31 (maximum value reduced if side-set pins are
used)
[value]
This is a modifier and is equivalent to ``.delay(value)``.
Classes Classes
------- -------

View File

@ -4,3 +4,8 @@ Getting started with MicroPython on the RP2xxx
============================================== ==============================================
Let's get started! Let's get started!
.. toctree::
:maxdepth: 1
pio.rst

123
docs/rp2/tutorial/pio.rst Normal file
View File

@ -0,0 +1,123 @@
Programmable IO
===============
The RP2040 has hardware support for standard communication protocols like I2C,
SPI and UART. For protocols where there is no hardware support, or where there
is a requirement of custom I/O behaviour, Programmable Input Output (PIO) comes
into play. Also, some MicroPython applications make use of a technique called
bit banging in which pins are rapidly turned on and off to transmit data. This
can make the entire process slow as the processor concentrates on bit banging
rather than executing other logic. However, PIO allows bit banging to happen
in the background while the CPU is executing the main work.
Along with the two central Cortex-M0+ processing cores, the RP2040 has two PIO
blocks each of which has four independent state machines. These state machines
can transfer data to/from other entities using First-In-First-Out (FIFO) buffers,
which allow the state machine and main processor to work independently yet also
synchronise their data. Each FIFO has four words (each of 32 bits) which can be
linked to the DMA to transfer larger amounts of data.
All PIO instructions follow a common pattern::
<instruction> .side(<side_set_value>) [<delay_value>]
The side-set ``.side(...)`` and delay ``[...]`` parts are both optional, and if
specified allow the instruction to perform more than one operation. This keeps
PIO programs small and efficient.
There are nine instructions which perform the following tasks:
- ``jmp()`` transfers control to a different part of the code
- ``wait()`` pauses until a particular action happens
- ``in_()`` shifts the bits from a source (scratch register or set of pins) to the
input shift register
- ``out()`` shifts the bits from the output shift register to a destination
- ``push()`` sends data to the RX FIFO
- ``pull()`` receives data from the TX FIFO
- ``mov()`` moves data from a source to a destination
- ``irq()`` sets or clears an IRQ flag
- ``set()`` writes a literal value to a destination
The instruction modifiers are:
- ``.side()`` sets the side-set pins at the start of the instruction
- ``[]`` delays for a certain number of cycles after execution of the instruction
There are also directives:
- ``wrap_target()`` specifies where the program execution will get continued from
- ``wrap()`` specifies the instruction where the control flow of the program will
get wrapped from
- ``label()`` sets a label for use with ``jmp()`` instructions
- ``word()`` emits a raw 16-bit value which acts as an instruction in the program
An example
----------
Take the ``pio_1hz.py`` example for a simple understanding of how to use the PIO
and state machines. Below is the code for reference.
.. code-block:: python3
# Example using PIO to blink an LED and raise an IRQ at 1Hz.
import time
from machine import Pin
import rp2
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz():
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
irq(rel(0))
set(pins, 1)
set(x, 31) [5]
label("delay_high")
nop() [29]
jmp(x_dec, "delay_high")
# Cycles: 1 + 7 + 32 * (30 + 1) = 1000
set(pins, 0)
set(x, 31) [6]
label("delay_low")
nop() [29]
jmp(x_dec, "delay_low")
# Create the StateMachine with the blink_1hz program, outputting on Pin(25).
sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))
# Set the IRQ handler to print the millisecond timestamp.
sm.irq(lambda p: print(time.ticks_ms()))
# Start the StateMachine.
sm.active(1)
This creates an instance of class :class:`rp2.StateMachine` which runs the
``blink_1hz`` program at 2000Hz, and connects to pin 25. The ``blink_1hz``
program uses the PIO to blink an LED connected to this pin at 1Hz, and also
raises an IRQ as the LED turns on. This IRQ then calls the ``lambda`` function
which prints out a millisecond timestamp.
The ``blink_1hz`` program is a PIO assembler routine. It connects to a single
pin which is configured as an output and starts out low. The instructions do
the following:
- ``irq(rel(0))`` raises the IRQ associated with the state machine.
- The LED is turned on via the ``set(pins, 1)`` instruction.
- The value 31 is put into register X, and then there is a delay for 5 more
cycles, specified by the ``[5]``.
- The ``nop() [29]`` instruction waits for 30 cycles.
- The ``jmp(x_dec, "delay_high")`` will keep looping to the ``delay_high`` label
as long as the register X is non-zero, and will also post-decrement X. Since
X starts with the value 31 this jump will happen 31 times, so the ``nop() [29]``
runs 32 times in total (note there is also one instruction cycle taken by the
``jmp`` for each of these 32 loops).
- ``set(pins, 0)`` will turn the LED off by setting pin 25 low.
- Another 32 loops of ``nop() [29]`` and ``jmp(...)`` will execute.
- Because ``wrap_target()`` and ``wrap()`` are not specified, their default will
be used and execution of the program will wrap around from the bottom to the
top. This wrapping does not cost any execution cycles.
The entire routine takes exactly 2000 cycles of the state machine. Setting the
frequency of the state machine to 2000Hz makes the LED blink at 1Hz.