Skip to content

Commit bad81a1

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

File tree

3 files changed

+310
-0
lines changed

3 files changed

+310
-0
lines changed

docs/library/rp2.DMA.rst

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
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
10+
each DMA channel can independently read data from one address and write it back to another
11+
address, optionally incrementing one or both pointers, allowing it to perform transfers on behalf
12+
of the processor while the processor carries out other tasks or enters a low power state. The
13+
RP2040's DMA controller has 12 independent DMA channels that can run concurrently. For full
14+
details of the 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, 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 just the PIO state machine to
66+
which we are sending the data. This works because PIO state machines present the buffer protocol,
67+
allowing 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:
129+
``True``).
130+
131+
:param ring_size: ``int`` If non-zero, only the bottom ``ring_size`` bits of one
132+
address register will change when an address is incremented, causing the
133+
address to wrap at the next ``1 << ring_size`` byte boundary. Which
134+
address is wrapped is controlled by the ``ring_sel`` flag. A zero value
135+
disables address wrapping.
136+
137+
:param ring_sel: ``BOOL`` Set to ``False`` to have the ``ring_size`` apply to the read address
138+
or ``True`` to apply to the write address.
139+
140+
:param chain_to: ``int`` The channel number for a channel to trigger after this transfer
141+
completes. Setting this value to this DMA object's own channel number
142+
disables chaining (this is the default).
143+
144+
:param treq_sel: ``int`` Select a Transfer Request signal. See section 2.5.3 in the RP2040
145+
datasheet for details.
146+
147+
:param irq_quiet: ``BOOL`` Do not generate interrupt at the end of each transfer. Interrupts
148+
will instead be generated when a zero value is written to the trigger
149+
register, which will halt a sequence of chained transfers (default:
150+
``True``).
151+
152+
:param bswap: ``BOOL`` If set to true, bytes in words or half-words will be reversed before
153+
writing (default: ``True``).
154+
155+
:param sniff_en: ``BOOL`` Set to ``True`` to allow data to be accessed by the chips sniff
156+
hardware (default: ``False``).
157+
158+
:param write_err: ``BOOL`` Setting this to ``True`` will clear a previously reported write
159+
error.
160+
161+
:param read_err: ``BOOL`` Setting this to ``True`` will clear a previously reported read
162+
error.
163+
164+
See the description of the ``CH0_CTRL_TRIG`` register in section 2.5.7 of the RP2040 datasheet
165+
for details of all of these fields.
166+
167+
168+
.. method:: DMA.unpack_ctrl(value)
169+
170+
Unpack a value for a DMA channel control register into a dictionary with key/value pairs
171+
for each of the fields in the control register.
172+
173+
:param value: The ``ctrl`` register value to unpack.
174+
175+
This method will return values for all the keys that can be passed to ``DMA.pack_ctrl``.
176+
In addition, it will also return the read-only flags in the control register: ``busy``,
177+
which goes high when a transfer starts and low when it ends, and ``ahb_err``, which is
178+
the logical OR of the ``read_err`` and ``write_err`` flags. These values will be ignored
179+
when packing, so that the dictionary created by unpacking a control register can be used
180+
directly as the keyword arguments for packing.
181+
182+
183+
.. method:: DMA.active([value])
184+
185+
Gets or sets whether the DMA channel is currently running.
186+
187+
>>> sm.active()
188+
0
189+
>>> sm.active(1)
190+
>>> while sm.active():
191+
... pass
192+
193+
194+
Attributes
195+
----------
196+
197+
.. attribute:: DMA.read
198+
199+
This attribute reflects the address from which the next bus transfer
200+
will read. It may be written with either an integer or an object
201+
that supports the buffer protocol and doing so has immediate effect.
202+
203+
.. attribute:: DMA.write
204+
205+
This attribute reflects the address to which the next bus transfer
206+
will write. It may be written with either an integer or an object
207+
that supports the buffer protocol and doing so has immediate effect.
208+
209+
.. attribute:: DMA.count
210+
211+
Reading this attribute will return the number of remaining bus
212+
transfers in the *current* transfer sequence. Writing this attribute
213+
sets the total number of transfers to be the *next* transfer sequence.
214+
215+
.. attribute:: DMA.ctrl
216+
217+
This attribute reflects DMA channel control register. It is typically written
218+
with an integer packed using the :meth:`DMA.pack_ctrl()` method. The returned
219+
register value can be unpacked using the :meth:`DMA.unpack_ctrl()` method.
220+
221+
.. attribute:: DMA.channel
222+
223+
The channel number of the DMA channel. This can be passed in the ``chain_to``
224+
argument of `DMA.pack_ctrl()` on another channel to allow DMA chaining.
225+
226+
.. attribute:: DMA.registers
227+
228+
This attribute is an array-like object that allows direct access to
229+
the DMA channel's registers. The index is by word, rather than by byte,
230+
so the register indices are the register address offsets divided by 4.
231+
See the RP2040 data sheet for register details.
232+
233+
234+
Chaining and trigger register access
235+
------------------------------------
236+
237+
The DMA controller in the RP2040 offers a couple advanced features to allow one DMA channel
238+
to initiate a transfer on another channel. One is the use of the ``chain_to`` value in the
239+
control register and the other is writing to one of the DMA channel's registers that has a
240+
trigger effect. When coupled with the ability to have one DMA channel write directly to the
241+
`DMA.registers` of another channel, this allows for complex transactions to be performed
242+
without any CPU intervention.
243+
244+
Below is an example of using both chaining and register
245+
triggering to implement gathering of multiple blocks of data into a single destination. Full
246+
details of these features can be found in section 2.5 of the RP2040 data sheet and the code
247+
below is a Pythonic version of the example in sub-section 2.5.6.2.
248+
249+
.. code-block:: python
250+
251+
from rp2 import DMA
252+
from uctypes import addressof
253+
from array import array
254+
255+
def gather_strings(string_list, buf):
256+
# We use two DMA channels. The first sends lengths and source addresses from the gather
257+
# list to the registers of the second. The second copies the data itself.
258+
gather_dma = DMA()
259+
buffer_dma = DMA()
260+
261+
# Pack up length/address pairs to be sent to the registers.
262+
gather_list = array("I")
263+
264+
for s in string_list:
265+
gather_list.append(len(s))
266+
gather_list.append(addressof(s))
267+
268+
gather_list.append(0)
269+
gather_list.append(0)
270+
271+
# When writing to the registers of the second DMA channel, we need to wrap the
272+
# write address on an 8-byte (1<<3 bytes) boundary. We write to the ``TRANS_COUNT``
273+
# and ``READ_ADD_TRIG`` registers in the last register alias (registers 14 and 15).
274+
gather_ctrl = gather_dma.pack_ctrl(ring_size=3, ring_sel=True)
275+
gather_dma.config(
276+
read=gather_list, write=buffer_dma.registers[14:16],
277+
count=2, ctrl=gather_ctrl
278+
)
279+
280+
# When copying the data, the transfer size is single bytes, and when completed we need
281+
# to chain back to the start another gather DMA transaction.
282+
buffer_ctrl = buffer_dma.pack_ctrl(size=0, chain_to=gather_dma.channel)
283+
# The read and count values will be set by the other DMA channel.
284+
buffer_dma.config(write=buf, ctrl=buffer_ctrl)
285+
286+
# Set the transfer in motion.
287+
gather_dma.active(1)
288+
289+
# Wait until all the register values have been sent
290+
end_address = addressof(gather_list) + 4 * len(gather_list)
291+
while gather_dma.read != end_address:
292+
pass
293+
294+
input = ["This is ", "a ", "test", " of the scatter", " gather", " process"]
295+
output = bytearray(64)
296+
297+
print(output)
298+
gather_strings(input, output)
299+
print(output)
300+
301+
This example idles while waiting for the transfer to complete; alternatively it could
302+
set an interrupt handler and return immediately.

docs/library/rp2.StateMachine.rst

Lines changed: 7 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy