Skip to content

Commit 91233ba

Browse files
andryblacksalkinium
authored andcommitted
[rp] Add DMA module
1 parent cd5c740 commit 91233ba

File tree

2 files changed

+372
-0
lines changed

2 files changed

+372
-0
lines changed

src/modm/platform/dma/rp/dma.hpp.in

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
/*
2+
* Copyright (c) 2022, Andrey Kunitsyn
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#pragma once
13+
14+
#include <cstdint>
15+
#include <algorithm>
16+
#include "../device.hpp"
17+
#include <hardware/structs/dma.h>
18+
#include <hardware/regs/dreq.h>
19+
20+
namespace modm::platform
21+
{
22+
23+
/// @ingroup modm_platform_dma
24+
struct DmaBase
25+
{
26+
using IrqHandler = void (*)(void);
27+
28+
enum class Channel : uint32_t
29+
{
30+
%% for channel in dma.channel
31+
Channel{{channel.name}},
32+
%% endfor
33+
};
34+
enum class Request : uint32_t
35+
{
36+
Pio0_Tx0 = DREQ_PIO0_TX0,
37+
Pio0_Tx1 = DREQ_PIO0_TX1,
38+
Pio0_Tx2 = DREQ_PIO0_TX2,
39+
Pio0_Tx3 = DREQ_PIO0_TX3,
40+
Pio0_Rx0 = DREQ_PIO0_RX0,
41+
Pio0_Rx1 = DREQ_PIO0_RX1,
42+
Pio0_Rx2 = DREQ_PIO0_RX2,
43+
Pio0_Rx3 = DREQ_PIO0_RX3,
44+
Pio1_Tx0 = DREQ_PIO1_TX0,
45+
Pio1_Tx1 = DREQ_PIO1_TX1,
46+
Pio1_Tx2 = DREQ_PIO1_TX2,
47+
Pio1_Tx3 = DREQ_PIO1_TX3,
48+
Pio1_Rx0 = DREQ_PIO1_RX0,
49+
Pio1_Rx1 = DREQ_PIO1_RX1,
50+
Pio1_Rx2 = DREQ_PIO1_RX2,
51+
Pio1_Rx3 = DREQ_PIO1_RX3,
52+
Spi0_Tx = DREQ_SPI0_TX,
53+
Spi0_Rx = DREQ_SPI0_RX,
54+
Spi1_Tx = DREQ_SPI1_TX,
55+
Spi1_Rx = DREQ_SPI1_RX,
56+
Uart0_Tx = DREQ_UART0_TX,
57+
Uart0_Rx = DREQ_UART0_RX,
58+
Uart1_Tx = DREQ_UART1_TX,
59+
Uart1_Rx = DREQ_UART1_RX,
60+
Pwm_Wrap0 = DREQ_PWM_WRAP0,
61+
Pwm_Wrap1 = DREQ_PWM_WRAP1,
62+
Pwm_Wrap2 = DREQ_PWM_WRAP2,
63+
Pwm_Wrap3 = DREQ_PWM_WRAP3,
64+
Pwm_Wrap4 = DREQ_PWM_WRAP4,
65+
Pwm_Wrap5 = DREQ_PWM_WRAP5,
66+
Pwm_Wrap6 = DREQ_PWM_WRAP6,
67+
Pwm_Wrap7 = DREQ_PWM_WRAP7,
68+
I2c0_Tx = DREQ_I2C0_TX,
69+
I2c0_Rx = DREQ_I2C0_RX,
70+
I2c1_Tx = DREQ_I2C1_TX,
71+
I2c1_Rx = DREQ_I2C1_RX,
72+
Adc = DREQ_ADC,
73+
Xip_Stream = DREQ_XIP_STREAM,
74+
Xip_SsiTx = DREQ_XIP_SSITX,
75+
Xip_SsiRx = DREQ_XIP_SSIRX,
76+
Dma_Timer0 = DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER0,
77+
Dma_Timer1 = DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER1,
78+
Dma_Timer2 = DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER2,
79+
Dma_Timer3 = DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_TIMER3,
80+
Force = DMA_CH0_CTRL_TRIG_TREQ_SEL_VALUE_PERMANENT,
81+
};
82+
83+
enum class
84+
TransferDataSize : uint32_t
85+
{
86+
Byte = 0,
87+
Bit8 = Byte,
88+
HalfWord = 1,
89+
Bit16 = HalfWord,
90+
Word = 2,
91+
Bit32 = Word,
92+
};
93+
94+
enum class
95+
Priority : uint32_t
96+
{
97+
Normal = 0,
98+
High = 1,
99+
};
100+
};
101+
102+
/// @author Andrey Kunitsyn
103+
/// @ingroup modm_platform_dma
104+
class DmaController : public DmaBase
105+
{
106+
public:
107+
/**
108+
* Start multiple channels simultaneously
109+
*/
110+
template <class... Channels>
111+
static inline void start() {
112+
dma_hw->multi_channel_trigger = (Channels::mask | ...);
113+
}
114+
115+
class ChannelDummy
116+
{
117+
public:
118+
static constexpr uint32_t mask = 0;
119+
static void
120+
configure(TransferDataSize transferDataSize,
121+
bool readIncrement,
122+
bool writeIncrement) {
123+
(void)transferDataSize;
124+
(void)readIncrement;
125+
(void)writeIncrement;
126+
}
127+
static void
128+
start() {}
129+
static void
130+
setReadAddress(uintptr_t address) {
131+
(void)address;
132+
}
133+
static void
134+
setWriteAddress(uintptr_t address) {
135+
(void)address;
136+
}
137+
static void
138+
setReadIncrementMode(bool increment) {
139+
(void)increment;
140+
}
141+
static void
142+
setWriteIncrementMode(bool increment) {
143+
(void)increment;
144+
}
145+
static void
146+
setDataLength(std::size_t length) {
147+
(void)length;
148+
}
149+
150+
template <Request dmaRequest>
151+
static void
152+
setPeripheralRequest() {}
153+
static bool isBusy() { return false; }
154+
};
155+
156+
/// Class representing a DMA channel/stream
157+
template <DmaBase::Channel ChannelID>
158+
class Channel
159+
{
160+
public:
161+
static constexpr DmaBase::Channel name = ChannelID;
162+
static constexpr uint32_t idx = uint32_t(ChannelID);
163+
static constexpr uint32_t mask = (1u << idx);
164+
165+
/**
166+
* Configure the DMA channel
167+
*
168+
* Stops the DMA channel and writes the new values to its control register.
169+
*
170+
* @param[in] transferDataSize Size of data in transfer (byte, halfword, word)
171+
* @param[in] readIncrement Defines whether the read address is incremented
172+
* after a transfer completed
173+
* @param[in] writeIncrement Defines whether the write address is
174+
* incremented after a transfer completed
175+
*/
176+
static void
177+
configure(TransferDataSize transferDataSize, bool readIncrement, bool writeIncrement)
178+
{
179+
const uint32_t ctrl =
180+
(uint32_t(transferDataSize) << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB) |
181+
(readIncrement ? DMA_CH0_CTRL_TRIG_INCR_READ_BITS : 0) |
182+
(writeIncrement ? DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS : 0) |
183+
(idx << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB) |
184+
DMA_CH0_CTRL_TRIG_EN_BITS |
185+
DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS;
186+
// write to alt1, dnt start
187+
dma_hw->ch[idx].al1_ctrl = ctrl;
188+
}
189+
190+
/// Start the transfer of the DMA channel
191+
static void
192+
start()
193+
{
194+
dma_hw->multi_channel_trigger = mask;
195+
}
196+
197+
/// Stop a DMA channel transfer
198+
static void
199+
stop()
200+
{
201+
dma_hw->abort = mask;
202+
while (dma_hw->abort & mask) __NOP();
203+
}
204+
205+
/**
206+
* Set the read address of the DMA channel
207+
*
208+
* @note In Mem2Mem mode use this method to set the memory source address.
209+
*
210+
* @param[in] address Source address
211+
*/
212+
static void
213+
setReadAddress(uintptr_t address)
214+
{
215+
dma_hw->ch[idx].read_addr = address;
216+
}
217+
218+
/**
219+
* Set the write address of the DMA channel
220+
*
221+
* @note In Mem2Mem mode use this method to set the memory destination address.
222+
*
223+
* @param[in] address Destination address
224+
*/
225+
static void
226+
setWriteAddress(uintptr_t address)
227+
{
228+
dma_hw->ch[idx].write_addr = address;
229+
}
230+
231+
/**
232+
* Enable/disable read increment
233+
*
234+
* When enabled, the read address is incremented by the size of the data
235+
* (e.g. 1 for byte transfers, 4 for word transfers) after the transfer
236+
* completed.
237+
*
238+
* @param[in] increment Enable/disable
239+
*/
240+
static void
241+
setReadIncrementMode(bool increment)
242+
{
243+
if (increment) {
244+
hw_set_bits(&dma_hw->ch[idx].al1_ctrl,DMA_CH0_CTRL_TRIG_INCR_READ_BITS);
245+
} else {
246+
hw_clear_bits(&dma_hw->ch[idx].al1_ctrl,DMA_CH0_CTRL_TRIG_INCR_READ_BITS);
247+
}
248+
}
249+
250+
/**
251+
* Enable/disable write increment
252+
*
253+
* When enabled, the write address is incremented by the size of the data
254+
* (e.g. 1 for byte transfers, 4 for word transfers) after the transfer
255+
* completed.
256+
*
257+
* @param[in] increment Enable/disable
258+
*/
259+
static void
260+
setWriteIncrementMode(bool increment)
261+
{
262+
if (increment) {
263+
hw_set_bits(&dma_hw->ch[idx].al1_ctrl,DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS);
264+
} else {
265+
hw_clear_bits(&dma_hw->ch[idx].al1_ctrl,DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS);
266+
}
267+
}
268+
269+
/// Set the length of data to be transfered (num transactions, not bytes)
270+
static void
271+
setDataLength(std::size_t length)
272+
{
273+
dma_hw->ch[idx].transfer_count = length;
274+
}
275+
276+
/// Set the peripheral that operates the channel
277+
template <Request dmaRequest>
278+
static void
279+
setPeripheralRequest()
280+
{
281+
hw_write_masked(&dma_hw->ch[idx].al1_ctrl,
282+
(uint32_t(dmaRequest) << DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB),
283+
DMA_CH0_CTRL_TRIG_TREQ_SEL_BITS);
284+
}
285+
286+
/// Enable the specified interrupt of the channel
287+
static void
288+
enableInterrupt()
289+
{
290+
hw_clear_bits(&dma_hw->ch[idx].al1_ctrl,DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS);
291+
}
292+
293+
/// Disable the specified interrupt of the channel
294+
static void
295+
disableInterrupt()
296+
{
297+
hw_set_bits(&dma_hw->ch[idx].al1_ctrl,DMA_CH0_CTRL_TRIG_IRQ_QUIET_BITS);
298+
}
299+
300+
static inline bool isBusy()
301+
{
302+
return dma_hw->ch[idx].al1_ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS;
303+
}
304+
305+
static inline bool isCompleted()
306+
{
307+
return !isBusy();
308+
}
309+
310+
static inline void startWrite(const void* src,std::size_t count)
311+
{
312+
dma_hw->ch[idx].read_addr = (uint32_t)src;
313+
dma_hw->ch[idx].al1_transfer_count_trig = count;
314+
}
315+
316+
static inline void startRead(void* dst,std::size_t count)
317+
{
318+
dma_hw->ch[idx].write_addr = (uint32_t)dst;
319+
dma_hw->ch[idx].al1_transfer_count_trig = count;
320+
}
321+
322+
template <class OtherChannel>
323+
static inline void chainTo()
324+
{
325+
hw_write_masked(&dma_hw->ch[idx].al1_ctrl,
326+
(OtherChannel::idx << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB),
327+
DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS);
328+
}
329+
};
330+
};
331+
332+
/**
333+
* Derive DMA controller classes for convenience. Every derived class defines
334+
* the channels available on that controller.
335+
* @ingroup modm_platform_dma
336+
*/
337+
class Dma: public DmaController
338+
{
339+
public:
340+
%% for channel in dma.channel
341+
using Channel{{ channel.name }} = DmaController::Channel<DmaBase::Channel::Channel{{ channel.name }}>;
342+
%% endfor
343+
};
344+
345+
} // namespace modm::platform

src/modm/platform/dma/rp/module.lb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2022, Andrey Kunitsyn
5+
#
6+
# This file is part of the modm project.
7+
#
8+
# This Source Code Form is subject to the terms of the Mozilla Public
9+
# License, v. 2.0. If a copy of the MPL was not distributed with this
10+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
11+
# -----------------------------------------------------------------------------
12+
13+
def init(module):
14+
module.name = ":platform:dma"
15+
module.description = "Direct Memory Access (DMA)"
16+
17+
18+
def prepare(module, options):
19+
module.depends(":cmsis:device", ":platform:clockgen")
20+
return options[":target"].has_driver("dma:rp20*")
21+
22+
23+
def build(env):
24+
env.substitutions = {"dma": env[":target"].get_driver("dma")}
25+
env.outbasepath = "modm/src/modm/platform/dma"
26+
env.template("dma.hpp.in")
27+

0 commit comments

Comments
 (0)