Skip to content

Commit 77f08b7

Browse files
nickovsdpgeorge
authored andcommitted
docs/library/rp2.DMA: Add documentation for rp2 DMA support.
Signed-off-by: Nicko van Someren <nicko@nicko.org>
1 parent 1c6012b commit 77f08b7

3 files changed

Lines changed: 301 additions & 0 deletions

File tree

docs/library/rp2.DMA.rst

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

244+
rp2.DMA.rst
244245
rp2.Flash.rst
245246
rp2.PIO.rst
246247
rp2.StateMachine.rst

0 commit comments

Comments
 (0)