Skip to content

Commit d321022

Browse files
authored
Merge pull request #46 from Bruin-Spacecraft-Group/main
add async transfer
2 parents d31fca2 + fd82c8b commit d321022

File tree

2 files changed

+132
-32
lines changed

2 files changed

+132
-32
lines changed

samd/dma.c

Lines changed: 108 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ COMPILER_ALIGNED(16) static DmacDescriptor write_back_descriptors[DMA_CHANNEL_CO
4949
#define FIRST_SERCOM_TX_TRIGSRC 0x05
5050
#endif
5151

52+
static bool dma_allocated[DMA_CHANNEL_COUNT];
53+
54+
uint8_t dma_allocate_channel(bool audio_channel) {
55+
uint8_t channel;
56+
uint8_t lim = audio_channel ? AUDIO_DMA_CHANNEL_COUNT : DMA_CHANNEL_COUNT;
57+
for (channel = (audio_channel ? 0 : AUDIO_DMA_CHANNEL_COUNT); channel < lim; channel++) {
58+
if (!dma_allocated[channel]) {
59+
dma_allocated[channel] = true;
60+
return channel;
61+
}
62+
}
63+
return channel; // i.e., return failure
64+
}
65+
66+
void dma_free_channel(uint8_t channel) {
67+
assert(dma_allocated[channel]);
68+
dma_disable_channel(channel);
69+
dma_allocated[channel] = false;
70+
}
71+
5272
void init_shared_dma(void) {
5373
// Turn on the clocks
5474
#ifdef SAM_D5X_E5X
@@ -77,13 +97,21 @@ void init_shared_dma(void) {
7797
// If buffer_out is a real buffer, ignore tx.
7898
// DMAs buffer_out -> dest
7999
// DMAs src -> buffer_in
80-
static int32_t shared_dma_transfer(void* peripheral,
100+
dma_descr_t shared_dma_transfer_start(void* peripheral,
81101
const uint8_t* buffer_out, volatile uint32_t* dest,
82102
volatile uint32_t* src, uint8_t* buffer_in,
83103
uint32_t length, uint8_t tx) {
84-
if (!dma_channel_free(SHARED_TX_CHANNEL) ||
85-
(buffer_in != NULL && !dma_channel_free(SHARED_RX_CHANNEL))) {
86-
return -1;
104+
dma_descr_t res;
105+
res.progress = 0;
106+
107+
uint8_t tx_channel = dma_allocate_channel(false);
108+
uint8_t rx_channel = dma_allocate_channel(false);
109+
110+
if ((tx_channel >= DMA_CHANNEL_COUNT) || (rx_channel >= DMA_CHANNEL_COUNT) ||
111+
!dma_channel_free(tx_channel) ||
112+
(buffer_in != NULL && !dma_channel_free(rx_channel))) {
113+
res.failure = -1;
114+
return res;
87115
}
88116

89117
uint32_t beat_size = DMAC_BTCTRL_BEATSIZE_BYTE;
@@ -96,26 +124,27 @@ static int32_t shared_dma_transfer(void* peripheral,
96124
// Check input alignment on word boundaries.
97125
if ((((uint32_t) buffer_in) & 0x3) != 0 ||
98126
(((uint32_t) buffer_out) & 0x3) != 0) {
99-
return -3;
127+
res.failure = -3;
128+
return res;
100129
}
101130
beat_size = DMAC_BTCTRL_BEATSIZE_WORD | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_DSTINC;
102131
beat_length /= 4;
103132
sercom = false;
104133
if (buffer_out != NULL) {
105-
dma_configure(SHARED_TX_CHANNEL, QSPI_DMAC_ID_TX, false);
134+
dma_configure(tx_channel, QSPI_DMAC_ID_TX, false);
106135
tx_active = true;
107136
} else {
108-
dma_configure(SHARED_RX_CHANNEL, QSPI_DMAC_ID_RX, false);
137+
dma_configure(rx_channel, QSPI_DMAC_ID_RX, false);
109138
rx_active = true;
110139
}
111140

112141
} else {
113142
#endif
114143

115-
dma_configure(SHARED_TX_CHANNEL, sercom_index(peripheral) * 2 + FIRST_SERCOM_TX_TRIGSRC, false);
144+
dma_configure(tx_channel, sercom_index(peripheral) * 2 + FIRST_SERCOM_TX_TRIGSRC, false);
116145
tx_active = true;
117146
if (buffer_in != NULL) {
118-
dma_configure(SHARED_RX_CHANNEL, sercom_index(peripheral) * 2 + FIRST_SERCOM_RX_TRIGSRC, false);
147+
dma_configure(rx_channel, sercom_index(peripheral) * 2 + FIRST_SERCOM_RX_TRIGSRC, false);
119148
rx_active = true;
120149
}
121150

@@ -125,7 +154,7 @@ static int32_t shared_dma_transfer(void* peripheral,
125154

126155
// Set up RX first.
127156
if (rx_active) {
128-
DmacDescriptor* rx_descriptor = &dma_descriptors[SHARED_RX_CHANNEL];
157+
DmacDescriptor* rx_descriptor = &dma_descriptors[rx_channel];
129158
rx_descriptor->BTCTRL.reg = beat_size | DMAC_BTCTRL_DSTINC;
130159
rx_descriptor->BTCNT.reg = beat_length;
131160
rx_descriptor->SRCADDR.reg = ((uint32_t) src);
@@ -140,7 +169,7 @@ static int32_t shared_dma_transfer(void* peripheral,
140169

141170
// Set up TX second.
142171
if (tx_active) {
143-
DmacDescriptor* tx_descriptor = &dma_descriptors[SHARED_TX_CHANNEL];
172+
DmacDescriptor* tx_descriptor = &dma_descriptors[tx_channel];
144173
tx_descriptor->BTCTRL.reg = beat_size;
145174
tx_descriptor->BTCNT.reg = beat_length;
146175

@@ -155,23 +184,25 @@ static int32_t shared_dma_transfer(void* peripheral,
155184
}
156185
if (sercom) {
157186
SercomSpi *s = &((Sercom*) peripheral)->SPI;
187+
// TODO: test if this operation is necessary or if it's just a waste of time and space
188+
// Section 35.8.7 of the datasheet lists both of these bits as read-only, so this shouldn't do anything
158189
s->INTFLAG.reg = SERCOM_SPI_INTFLAG_RXC | SERCOM_SPI_INTFLAG_DRE;
159190
}
160191

161192
// Start the RX job first so we don't miss the first byte. The TX job clocks the output.
162193
// Disable interrupts during startup to make sure both RX and TX start at just about the same time.
163194
mp_hal_disable_all_interrupts();
164195
if (rx_active) {
165-
dma_enable_channel(SHARED_RX_CHANNEL);
196+
dma_enable_channel(rx_channel);
166197
}
167198
if (tx_active) {
168-
dma_enable_channel(SHARED_TX_CHANNEL);
199+
dma_enable_channel(tx_channel);
169200
}
170201
mp_hal_enable_all_interrupts();
171202

172203
if (!sercom) {
173204
if (rx_active) {
174-
DMAC->SWTRIGCTRL.reg |= (1 << SHARED_RX_CHANNEL);
205+
DMAC->SWTRIGCTRL.reg |= (1 << rx_channel);
175206
}
176207
}
177208

@@ -184,11 +215,11 @@ static int32_t shared_dma_transfer(void* peripheral,
184215
for (int i = 0; i < 10 && !is_okay; i++) {
185216
bool complete = true;
186217
if (rx_active) {
187-
if (DMAC->Channel[SHARED_RX_CHANNEL].CHSTATUS.reg & 0x3)
218+
if (DMAC->Channel[rx_channel].CHSTATUS.reg & 0x3)
188219
complete = false;
189220
}
190221
if (tx_active) {
191-
if (DMAC->Channel[SHARED_TX_CHANNEL].CHSTATUS.reg & 0x3)
222+
if (DMAC->Channel[tx_channel].CHSTATUS.reg & 0x3)
192223
complete = false;
193224
}
194225
is_okay = is_okay || (DMAC->ACTIVE.bit.ABUSY || complete);
@@ -202,23 +233,46 @@ static int32_t shared_dma_transfer(void* peripheral,
202233
}
203234
}
204235
#endif
236+
res.peripheral = peripheral;
237+
res.length = length;
238+
res.rx_channel = rx_channel;
239+
res.tx_channel = tx_channel;
240+
res.rx_active = rx_active;
241+
res.tx_active = tx_active;
242+
res.sercom = sercom;
243+
res.failure = 0;
244+
return res;
245+
}
205246

206-
// busy-wait for the RX and TX DMAs to either complete or encounter an error
207-
if (rx_active) {
208-
while ((dma_transfer_status(SHARED_RX_CHANNEL) & 0x3) == 0) {}
247+
bool shared_dma_transfer_finished(dma_descr_t descr) {
248+
if (descr.failure != 0) {
249+
return true;
209250
}
210-
if (tx_active) {
211-
while ((dma_transfer_status(SHARED_TX_CHANNEL) & 0x3) == 0) {}
251+
252+
if (descr.progress < 1 && descr.rx_active) {
253+
if ((dma_transfer_status(descr.rx_channel) & 0x3) == 0) {
254+
return false;
255+
}
256+
descr.progress = 1;
257+
}
258+
if (descr.progress < 2 && descr.tx_active) {
259+
if ((dma_transfer_status(descr.tx_channel) & 0x3) == 0) {
260+
return false;
261+
}
262+
descr.progress = 2;
212263
}
213264

214-
if (sercom) {
215-
Sercom* s = (Sercom*) peripheral;
265+
if (descr.progress < 3 && descr.sercom) {
266+
Sercom* s = (Sercom*) descr.peripheral;
216267
// Wait for the SPI transfer to complete.
217-
while (s->SPI.INTFLAG.bit.TXC == 0) {}
268+
if (s->SPI.INTFLAG.bit.TXC == 0) {
269+
return false;
270+
}
271+
descr.progress = 3;
218272

219273
// This transmit will cause the RX buffer overflow but we're OK with that.
220274
// So, read the garbage and clear the overflow flag.
221-
if (!rx_active) {
275+
if (!descr.rx_active) {
222276
while (s->SPI.INTFLAG.bit.RXC == 1) {
223277
s->SPI.DATA.reg;
224278
}
@@ -227,13 +281,39 @@ static int32_t shared_dma_transfer(void* peripheral,
227281
}
228282
}
229283

230-
if ((!rx_active || dma_transfer_status(SHARED_RX_CHANNEL) == DMAC_CHINTFLAG_TCMPL) &&
231-
(!tx_active || dma_transfer_status(SHARED_TX_CHANNEL) == DMAC_CHINTFLAG_TCMPL)) {
232-
return length;
284+
return true;
285+
}
286+
287+
int shared_dma_transfer_close(dma_descr_t descr) {
288+
dma_free_channel(descr.tx_channel);
289+
dma_free_channel(descr.rx_channel);
290+
291+
if (descr.failure != 0) {
292+
return descr.failure;
293+
}
294+
295+
if ((!descr.rx_active || dma_transfer_status(descr.rx_channel) == DMAC_CHINTFLAG_TCMPL) &&
296+
(!descr.tx_active || dma_transfer_status(descr.tx_channel) == DMAC_CHINTFLAG_TCMPL)) {
297+
return descr.length;
233298
}
234299
return -2;
235300
}
236301

302+
// Do write and read simultaneously. If buffer_out is NULL, write the tx byte over and over.
303+
// If buffer_out is a real buffer, ignore tx.
304+
// DMAs buffer_out -> dest
305+
// DMAs src -> buffer_in
306+
static int32_t shared_dma_transfer(void* peripheral,
307+
const uint8_t* buffer_out, volatile uint32_t* dest,
308+
volatile uint32_t* src, uint8_t* buffer_in,
309+
uint32_t length, uint8_t tx) {
310+
dma_descr_t descr = shared_dma_transfer_start(peripheral, buffer_out, dest, src, buffer_in, length, tx);
311+
if (descr.failure != 0) {
312+
return descr.failure;
313+
}
314+
while (!shared_dma_transfer_finished(descr)) {}
315+
return shared_dma_transfer_close(descr);
316+
}
237317

238318
int32_t sercom_dma_transfer(Sercom* sercom, const uint8_t* buffer_out, uint8_t* buffer_in,
239319
uint32_t length) {

samd/dma.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@
3535
// We allocate DMA resources for the entire lifecycle of the board (not the
3636
// vm) because the general_dma resource will be shared between the REPL and SPI
3737
// flash. Both uses must block each other in order to prevent conflict.
38-
#define AUDIO_DMA_CHANNEL_COUNT 3
39-
#define DMA_CHANNEL_COUNT (AUDIO_DMA_CHANNEL_COUNT + 2)
40-
#define SHARED_TX_CHANNEL (DMA_CHANNEL_COUNT - 2)
41-
#define SHARED_RX_CHANNEL (DMA_CHANNEL_COUNT - 1)
38+
#define AUDIO_DMA_CHANNEL_COUNT 4
39+
#define DMA_CHANNEL_COUNT 32
40+
41+
uint8_t dma_allocate_channel(bool audio_channel);
42+
void dma_free_channel(uint8_t channel);
4243

4344
void init_shared_dma(void);
4445

@@ -53,6 +54,25 @@ int32_t sercom_dma_write(Sercom* sercom, const uint8_t* buffer, uint32_t length)
5354
int32_t sercom_dma_read(Sercom* sercom, uint8_t* buffer, uint32_t length, uint8_t tx);
5455
int32_t sercom_dma_transfer(Sercom* sercom, const uint8_t* buffer_out, uint8_t* buffer_in, uint32_t length);
5556

57+
typedef struct {
58+
void* peripheral;
59+
uint32_t length;
60+
uint8_t progress;
61+
uint8_t rx_channel;
62+
uint8_t tx_channel;
63+
bool rx_active;
64+
bool tx_active;
65+
bool sercom;
66+
int8_t failure;
67+
} dma_descr_t;
68+
69+
dma_descr_t shared_dma_transfer_start(void* peripheral,
70+
const uint8_t* buffer_out, volatile uint32_t* dest,
71+
volatile uint32_t* src, uint8_t* buffer_in,
72+
uint32_t length, uint8_t tx);
73+
bool shared_dma_transfer_finished(dma_descr_t descr);
74+
int shared_dma_transfer_close(dma_descr_t descr);
75+
5676
void dma_configure(uint8_t channel_number, uint8_t trigsrc, bool output_event);
5777
void dma_enable_channel(uint8_t channel_number);
5878
void dma_disable_channel(uint8_t channel_number);

0 commit comments

Comments
 (0)