Skip to content

Commit b95f0cb

Browse files
committed
docs: Add documentation for rp2 DMA support.
Signed-off-by: Nicko van Someren <nicko@nicko.org>
1 parent 9feb068 commit b95f0cb

File tree

3 files changed

+306
-0
lines changed

3 files changed

+306
-0
lines changed

docs/library/rp2.DMA.rst

+298
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
.. currentmodule:: rp2
2+
.. _rp2.DMA:
3+
4+
class DMA -- access to the RP2040's DMA controller
5+
==================================================
6+
7+
The :class:`DMA` class offers access to the RP2040's Direct Memory Access (DMA)
8+
controller, providing the ability move data between memory blocks and/or IO registers. The DMA
9+
controller has its own, separate read and write bus master connections onto the bus fabric and each
10+
DMA channel can independently read data from one address and write it back to another address,
11+
optionally incrementing one or both pointers, allowing it to perform transfers on behalf of the
12+
process while the processor carries out other tasks or enters a low power state. The RP2040's DMA
13+
controller has 12 independent DMA channels that can run concurrently. For full details of the
14+
RP2040's DMA system see section 2.5 of the `RP2040 Datasheet
15+
<https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf>`_.
16+
17+
18+
Examples
19+
--------
20+
21+
The simplest use of the DMA controller is to move data from one block of memory to another.
22+
This can be accomplished with the following code::
23+
24+
a = bytearray(32*1024)
25+
b = bytearray(32*1024)
26+
d = rp2.DMA()
27+
c = d.pack_ctrl() # Just use the default control value.
28+
# The count is in 'transfers', which defaults to four-byte words, so divide length by 4
29+
d.config(read=a, write=b, count=len(a)//4, ctrl=c, trigger=True)
30+
# Wait for completion
31+
while d.active():
32+
pass
33+
34+
Note that while this example sits in an idle loop while it waits for the transfer to complete,
35+
the program could just as well do some useful work in this time instead.
36+
37+
Another, perhaps more common use of the DMA controller is to transfer between memory and an IO
38+
peripheral. In this situation the address of the IO register does not change for each transfer but
39+
the memory address needs to be incremented. It is also necessary to control the pace of the
40+
transfer so as to not write data before it can be accepted by a peripheral or read it before the
41+
data is ready, and this can be controlled with the ``treq_sel`` field of the DMA channel's control
42+
register. The various fields of the control register for each DMA channel can be packed
43+
using the :meth:`DMA.pack_ctrl()` method and unpacked using the :meth:`DMA.unpack_ctrl()`
44+
static method. Code to transfer data from a byte array to the TX FIFO of a PIO state machine,
45+
one byte at a time, looks like this::
46+
47+
# pio_num is index of the PIO block being used, sm_num is the state machine in that block.
48+
# my_state_machine is an rp2.PIO() instance.
49+
DATA_REQUEST_INDEX = (pio_num << 3) + sm_num
50+
51+
src_data = bytearray(1024)
52+
d = rp2.DMA()
53+
54+
# Transfer bytes, rather than words, don't increment the write address and pace the transfer.
55+
c = d.pack_ctrl(size=0, inc_write=False, c.treq_sel=DATA_REQUEST_INDEX)
56+
57+
d.config(
58+
read=src_data,
59+
write=my_state_machine,
60+
count=len(src_data),
61+
ctrl=c,
62+
trigger=True
63+
)
64+
65+
Note that in this example the value given for the write address is the PIO state machine to which
66+
we are sending the data. This works because PIO state machines present the buffer protocol, allowing
67+
direct access to their data FIFO registers.
68+
69+
70+
Constructor
71+
-----------
72+
73+
.. class:: DMA
74+
75+
Claim one of the DMA controller channels for exclusive use.
76+
77+
78+
Methods
79+
-------
80+
81+
.. method:: DMA.config(read=None, write=None, count=None, ctrl=None, trigger=False)
82+
83+
Configure the DMA registers for the channel and optionally start the transfer.
84+
85+
:param read: The address from which the DMA controller will start reading data or
86+
an object that will provide data to be read. It can be an integer or any
87+
object that supports the buffer protocol.
88+
:param write: The address to which the DMA controller will start writing or an
89+
object into which data will be written. It can be an integer or any object
90+
that supports the buffer protocol.
91+
:param count: The number of bus transfers that will execute before this channel
92+
stops. Note that this is the number of transfers, not the number of bytes.
93+
If the transfers are 2 or 4 bytes wide then the total amount of data moved
94+
(and thus the size of required buffer) needs to be multiplied accordingly.
95+
:param ctrl: The value for the DMA control register. This is an integer value
96+
that is typically packed using the :meth:`DMA.pack_ctrl()`.
97+
:param trigger: Optionally commence the transfer immediately.
98+
99+
100+
.. method:: DMA.irq(handler=None, hard=False)
101+
102+
Returns the IRQ object for this DMA channel and optionally configures it.
103+
104+
.. method:: DMA.close()
105+
106+
Release the claim on the underlying DMA channel and free the interrupt
107+
handler. The :class:`DMA` object can not be used after this operation.
108+
109+
.. method:: DMA.pack_ctrl(default=None, **kwargs)
110+
111+
Pack the values provided in the keyword arguments into the named fields of a new control
112+
register value. Any field that is not provided will be set to a default value. The
113+
default will either be taken from the provided ``default`` value, or if that is not
114+
given, a default suitable for the current channel; setting this to the current value
115+
of the `DMA.ctrl` attribute provides an easy way to override a subset of the fields.
116+
117+
The keys for the keyword arguments can be any key returned by the :meth:`DMA.unpack_ctrl()`
118+
method. The writable values are:
119+
120+
:param enable: ``BOOL`` Set to enable the channel (default: ``True``).
121+
122+
:param high_pri: ``BOOL`` Make this channel's bus traffic high priority (default: ``False``).
123+
124+
:param size: ``int`` Transfer size: 0=byte, 1=half word, 2=word (default: 2).
125+
126+
:param inc_read: ``BOOL`` Increment the read address after each transfer (default: ``True``).
127+
128+
:param inc_write: ``BOOL`` Increment the write address after each transfer (default: ``True``).
129+
130+
:param ring_size: ``int`` If non-zero, only the bottom ``ring_size`` bits of the one
131+
address register will change when an address is incremented, causing the
132+
address to wrap at the next ``1 << ring_size`` byte boundary. Which
133+
address is wrapped is controlled by the ``ring_sel`` flag. A zero value
134+
disables address wrapping.
135+
136+
:param ring_sel: ``BOOL`` Set to ``False`` to have the ``ring_size`` apply to the read address
137+
or ``True`` to apply to the write address.
138+
139+
:param chain_to: ``int`` The channel number for a channel to trigger after this transfer
140+
completes. Setting this value to this DMA object's own channel number disables
141+
chaining (this is the default).
142+
143+
:param treq_sel: ``int`` Select a Transfer Request signal. See section 2.5.3 in the RP2040 datasheet
144+
for details.
145+
146+
:param irq_quiet: ``BOOL`` Do not generate interrupt at the end of each transfer. Interrupts will
147+
instead be generated when a zero value is written to the trigger register, which
148+
will halt a sequence of chained transfers (default: ``False``).
149+
150+
:param bswap: ``BOOL`` If set to true, bytes in words or half-words will be reversed before writing
151+
(default: ``True``).
152+
153+
:param sniff_en: ``BOOL`` Set to ``True`` to allow data to be accessed by the chips sniff hardware
154+
(default: ``False``).
155+
156+
:param write_err: ``BOOL`` Setting this to true will a previously reported write error.
157+
158+
:param read_err: ``BOOL`` Setting this to true will a previously reported read error.
159+
160+
See the description of the ``CH0_CTRL_TRIG`` register in section 2.5.7 of the RP2040 datasheet
161+
for details of all of these fields.
162+
163+
164+
.. method:: DMA.unpack_ctrl(value)
165+
166+
Unpack a value for a DMA channel control register into a dictionary with key/value pairs
167+
for each of the fields in the control register.
168+
169+
:param value: The ``ctrl`` register value to unpack.
170+
171+
This method will return values for all the keys that can be passed to ``DMA.pack_ctrl``.
172+
In addition, it will also return the read-only flags in the control register: ``busy``,
173+
which goes high when a transfer starts and low when it ends, and ``ahb_err``, which is
174+
the logical OR of the ``read_err`` and ``write_err`` flags. These values will be ignored
175+
when packing, so that the dictionary created by unpacking a control register can be used
176+
directly as the keyword arguments for packing.
177+
178+
179+
.. method:: DMA.active([value])
180+
181+
Gets or sets whether the DMA channel is currently running.
182+
183+
>>> sm.active()
184+
0
185+
>>> sm.active(1)
186+
>>> while sm.active():
187+
... pass
188+
189+
190+
Attributes
191+
----------
192+
193+
.. attribute:: DMA.read
194+
195+
This attribute reflects the address from which the next bus transfer
196+
will read. May be written with either an integer or an object
197+
that supports the buffer protocol and doing so has immediate effect.
198+
199+
.. attribute:: DMA.write
200+
201+
This attribute reflects the address to which the next bus transfer
202+
will write. May be written with either an integer or an object
203+
that supports the buffer protocol and doing so has immediate effect.
204+
205+
.. attribute:: DMA.count
206+
207+
Reading this attribute will return the number of remaining bus
208+
transfers in the *current* transfer sequence. Writing this attribute
209+
sets the total number of transfers to be the *next* transfer sequence.
210+
211+
.. attribute:: DMA.ctrl
212+
213+
This attribute reflects DMA channel control register. It is typically written
214+
with an integer packed using the :meth:`DMA.pack_ctrl()` method. The returned
215+
register value can be unpacked using the :meth:`DMA.unpack_ctrl()` method.
216+
217+
.. attribute:: DMA.channel
218+
219+
The channel number of the DMA channel. This can be passed in the ``chain_to``
220+
argument of `DMA.pack_ctrl()` on another channel to allow DMA chaining.
221+
222+
.. attribute:: DMA.registers
223+
224+
This attribute is an array-like object that allows direct access to
225+
the DMA channel's registers. The index is by word, rather than by byte,
226+
so the register indices are the register address offsets divided by 4.
227+
See the RP2040 data sheet for register details.
228+
229+
230+
Chaining and trigger register access
231+
------------------------------------
232+
233+
The DMA controller in the RP2040 offers a couple advanced features to allow one DMA channel
234+
to initiate a transfer on another channel. One is the use of the ``chain_to`` value in the
235+
control register and the other is writing to one of the DMA channel's registers that has a
236+
trigger effect. When coupled with the ability to have one DMA channel write directly to the
237+
`DMA.registers` of another channel, this allows for complex transactions to be performed
238+
without any CPU intervention.
239+
240+
Below is an example of using both chaining and register
241+
triggering to implement gathering of multiple blocks of data into a single destination. Full
242+
details of these features can be found in section 2.5 of the RP2040 data sheet and the code
243+
below is a Pythonic version of the example in sub-section 2.5.6.2.
244+
245+
.. code-block:: python
246+
247+
from rp2 import DMA
248+
from uctypes import addressof
249+
from array import array
250+
251+
def gather_strings(string_list, buf):
252+
# We use two DMA channels. The first sends lengths and source addresses from the gather
253+
# list to the registers of the second. The second copies the data itself.
254+
gather_dma = DMA()
255+
buffer_dma = DMA()
256+
257+
# Pack up length/address pairs to be sent to the registers.
258+
gather_list = array("I")
259+
260+
for s in string_list:
261+
gather_list.append(len(s))
262+
gather_list.append(addressof(s))
263+
264+
gather_list.append(0)
265+
gather_list.append(0)
266+
267+
# When writing to the registers of the second DMA channel, we need to wrap the
268+
# write address on an 8-byte (1<<3 bytes) boundary. We write to the ``TRANS_COUNT``
269+
# and ``READ_ADD_TRIG`` registers in the last register alias (registers 14 and 15).
270+
gather_ctrl = gather_dma.pack_ctrl(ring_size=3, ring_sel=True)
271+
gather_dma.config(
272+
read=gather_list, write=buffer_dma.registers[14:16],
273+
count=2, ctrl=gather_ctrl
274+
)
275+
276+
# When copying the data, the transfer size is single bytes, and when completed we need
277+
# to chain back to the start another gather DMA transaction.
278+
buffer_ctrl = buffer_dma.pack_ctrl(size=0, chain_to=gather_dma.channel)
279+
# The read and count values will be set by the other DMA channel.
280+
buffer_dma.config(write=buf, ctrl=buffer_ctrl)
281+
282+
# Set the transfer in motion.
283+
gather_dma.active(1)
284+
285+
# Wait until all the register values have been sent
286+
end_address = addressof(gather_list) + 4 * len(gather_list)
287+
while gather_dma.read != end_address:
288+
pass
289+
290+
input = ["This is ", "a ", "test", " of the scatter", " gather", " process"]
291+
output = bytearray(256)
292+
293+
gather_strings(input, output)
294+
295+
print(output)
296+
297+
This example idles while waiting for the transfer to complete, but alternatively it could
298+
set an interrupt handler and return immediately.

docs/library/rp2.StateMachine.rst

+7
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,10 @@ Methods
140140

141141
Optionally configure it.
142142

143+
Buffer protocol
144+
---------------
145+
146+
The StateMachine class supports the `buffer protocol`, allowing direct access to the transmit
147+
and receive FIFOs for each state machine. This is primarily in order to allow StateMachine
148+
objects to be passed directly as the read or write parameters when configuring a `rp2.DMA()`
149+
channel.

docs/library/rp2.rst

+1
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,4 @@ Classes
244244
rp2.Flash.rst
245245
rp2.PIO.rst
246246
rp2.StateMachine.rst
247+
rp2.DMA.rst

0 commit comments

Comments
 (0)