Skip to content

Commit d4465af

Browse files
committed
added DMA memory to memory transfer and reworked dma to fix some issues with non 1 byte transfers
1 parent 6844fdb commit d4465af

1 file changed

Lines changed: 86 additions & 55 deletions

File tree

targets/core/nxp/lpc17xx/dma.hpp

Lines changed: 86 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ namespace klib::core::lpc17xx::io::detail::dma {
3232
*
3333
*/
3434
struct transfer_helper {
35-
// pointer to the start of the data
36-
uint8_t* data;
37-
3835
// requested size of the current endpoint
3936
uint32_t requested_size;
4037

@@ -203,10 +200,10 @@ namespace klib::core::lpc17xx::io {
203200
*
204201
*/
205202
static void irq_handler() {
206-
// make sure we have a valid pointer to data before we do anything
207-
if (!helper.data) [[unlikely]] {
208-
// if the pointer is invalid it means we are finished with the
209-
// transfer. Call the callback when this happens if we have one
203+
// check if we are done transmitting/receiving
204+
if (helper.transferred_size >= helper.requested_size) [[unlikely]] {
205+
// we are finished with the transfer. Call the callback
206+
// when this happens if we have one
210207
if (helper.callback) {
211208
// call the callback
212209
helper.callback();
@@ -218,59 +215,42 @@ namespace klib::core::lpc17xx::io {
218215
return;
219216
}
220217

221-
// check what type of transfer we were doing
222-
if constexpr (std::is_same_v<Source, klib::io::dma::memory> &&
223-
!std::is_same_v<Destination, klib::io::dma::memory>)
224-
{
225-
peripheral_memory_impl<true>();
226-
}
227-
else if constexpr (!std::is_same_v<Source, klib::io::dma::memory> &&
228-
std::is_same_v<Destination, klib::io::dma::memory>)
229-
{
230-
peripheral_memory_impl<false>();
231-
}
232-
else if constexpr (std::is_same_v<Source, klib::io::dma::memory> &&
233-
std::is_same_v<Destination, klib::io::dma::memory>)
234-
{
235-
// TODO: implement m2m
236-
}
237-
else {
238-
// TODO: implement p2p
239-
}
218+
// prepare the next interrupt
219+
peripheral_memory_impl<true>();
240220
}
241221

242222
/**
243223
* @brief Memory to peripheral implementation
244224
*
245225
*/
246-
template <bool M2P>
226+
template <bool Increment>
247227
static void peripheral_memory_impl() {
248228
// get the amount of data we still have to send
249229
const auto transfer_size = (helper.requested_size - helper.transferred_size);
250230

251231
// determine the amount we will send in the current transfer
252232
const auto current_transfer = klib::min(transfer_size, 0xfff);
253-
const bool needs_irq = transfer_size > 0xfff;
254233

255-
if constexpr (M2P) {
256-
// set the source and destination
257-
Dma::port->CH[Channel].SRCADDR = reinterpret_cast<uint32_t>(helper.data) + helper.transferred_size;
258-
}
259-
else {
260-
Dma::port->CH[Channel].DESTADDR = reinterpret_cast<uint32_t>(helper.data) + helper.transferred_size;
234+
// get the control register for the current channel
235+
const uint32_t control = Dma::port->CH[Channel].CONTROL;
236+
237+
// check if we need to move to the next address
238+
if constexpr (Increment) {
239+
if (control & (0x1 << 26)) {
240+
// advance by the source transfer width
241+
Dma::port->CH[Channel].SRCADDR = Dma::port->CH[Channel].SRCADDR + (1u << ((control >> 18) & 0x7));
242+
}
243+
if (control & (0x1 << 27)) {
244+
// advance by the destination transfer width
245+
Dma::port->CH[Channel].DESTADDR = Dma::port->CH[Channel].DESTADDR + (1u << ((control >> 21) & 0x7));
246+
}
261247
}
262248

263249
// set the amount of data we will transfer
264-
Dma::port->CH[Channel].CONTROL = (Dma::port->CH[Channel].CONTROL & (~0xfff)) | current_transfer;
250+
Dma::port->CH[Channel].CONTROL = (control & (~0xfff)) | current_transfer;
265251

266-
// check if we need a irq after the current
267-
if (!needs_irq) [[unlikely]] {
268-
// clear the helper items
269-
helper.data = nullptr;
270-
}
271-
else {
272-
helper.transferred_size += 0xfff;
273-
}
252+
// add the amount we are transferring right now
253+
helper.transferred_size += current_transfer;
274254

275255
// enable the channel
276256
Dma::port->CH[Channel].CONFIG |= 0x1;
@@ -318,15 +298,17 @@ namespace klib::core::lpc17xx::io {
318298
static_assert(!std::is_same_v<Destination, klib::io::dma::memory>, "Destination needs to be a peripheral for this function");
319299

320300
// get the amount of bytes we need to transmit
321-
const uint32_t size = source.size_bytes();
301+
const uint32_t size = (
302+
source.size_bytes() / klib::min(sizeof(T), sizeof(uint32_t))
303+
);
322304

323305
// set the dma destination
324306
Dma::port->CH[Channel].DESTADDR = reinterpret_cast<uint32_t>(Destination::template dma_data<0>());
307+
Dma::port->CH[Channel].SRCADDR = reinterpret_cast<uint32_t>(source.data());
325308

326309
// update the helper data with the information about the transfer
327310
// we know we are not writing to it so we can remove the const here
328311
// for the helper data
329-
helper.data = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(source.data()));
330312
helper.requested_size = size;
331313
helper.transferred_size = 0x00;
332314
helper.callback = callback;
@@ -335,7 +317,7 @@ namespace klib::core::lpc17xx::io {
335317
Dma::port->CH[Channel].CONTROL = (
336318
(detail::dma::get_memory_burst_size(size) << 12) |
337319
(detail::dma::get_peripheral_burst_size(Destination::template dma_burst_size<0>()) << 15) |
338-
(detail::dma::get_transfer_width(sizeof(T)) << 18) |
320+
(detail::dma::get_transfer_width(klib::min(sizeof(T), sizeof(uint32_t))) << 18) |
339321
(detail::dma::get_transfer_width(Destination::template dma_width<0>()) << 21) |
340322
(Destination::template dma_increment<0>() << 27) |
341323
(MemoryIncrement << 26) | (0x1 << 31)
@@ -357,7 +339,7 @@ namespace klib::core::lpc17xx::io {
357339
);
358340

359341
// start the transfer
360-
peripheral_memory_impl<true>();
342+
peripheral_memory_impl<false>();
361343
}
362344

363345
/**
@@ -369,18 +351,20 @@ namespace klib::core::lpc17xx::io {
369351
* @param size
370352
*/
371353
template <bool MemoryIncrement = true, typename T>
372-
static void read(const std::span<T>& destination, const detail::dma::interrupt_callback callback = nullptr) {
354+
static void read(const std::span<T>& destination, const detail::dma::interrupt_callback callback = nullptr) requires (!std::is_const_v<T>) {
373355
static_assert(!std::is_same_v<Source, klib::io::dma::memory>, "Source needs to be a peripheral for this function");
374356
static_assert(std::is_same_v<Destination, klib::io::dma::memory>, "Destination needs to be memory for this function");
375357

376-
// get the amount of bytes we need to transmit
377-
const uint32_t size = destination.size_bytes();
358+
// get the amount of bytes we need to receive
359+
const uint32_t size = (
360+
destination.size_bytes() / klib::min(Source::template dma_width<1>(), sizeof(uint32_t))
361+
);
378362

379363
// set the source and destination
380364
Dma::port->CH[Channel].SRCADDR = reinterpret_cast<uint32_t>(Source::template dma_data<1>());
365+
Dma::port->CH[Channel].DESTADDR = reinterpret_cast<uint32_t>(destination.data());
381366

382367
// update the helper data with the information about the transfer
383-
helper.data = reinterpret_cast<uint8_t*>(destination.data());
384368
helper.requested_size = size;
385369
helper.transferred_size = 0x00;
386370
helper.callback = callback;
@@ -390,7 +374,7 @@ namespace klib::core::lpc17xx::io {
390374
(detail::dma::get_peripheral_burst_size(Source::template dma_burst_size<1>()) << 12) |
391375
(detail::dma::get_memory_burst_size(size) << 15) |
392376
(detail::dma::get_transfer_width(Source::template dma_width<1>()) << 18) |
393-
(detail::dma::get_transfer_width(sizeof(T)) << 21) |
377+
(detail::dma::get_transfer_width(klib::min(sizeof(T), sizeof(uint32_t))) << 21) |
394378
(Source::template dma_increment<1>() << 26) |
395379
(MemoryIncrement << 27) | (0x1 << 31)
396380
);
@@ -422,10 +406,53 @@ namespace klib::core::lpc17xx::io {
422406
* @param destination
423407
* @param size
424408
*/
425-
template <bool MemoryIncrement = true>
426-
static void transfer(const uint8_t *const source, uint8_t *const destination, uint16_t size) {
409+
template <bool SourceMemoryIncrement = true, bool DestinationMemoryIncrement = true, typename T, typename G>
410+
static void transfer(const std::span<T>& source, const std::span<G>& destination, const detail::dma::interrupt_callback callback = nullptr) requires (!std::is_const_v<G>) {
427411
static_assert(std::is_same_v<Source, klib::io::dma::memory>, "Source needs to be memory for this function");
428412
static_assert(std::is_same_v<Destination, klib::io::dma::memory>, "Destination needs to be memory for this function");
413+
414+
// get the amount of bytes we need to move
415+
const uint32_t size = (
416+
klib::min(source.size_bytes(), destination.size_bytes()) / klib::min(sizeof(T), sizeof(uint32_t))
417+
);
418+
419+
// set the dma destination and source
420+
Dma::port->CH[Channel].DESTADDR = reinterpret_cast<uint32_t>(destination.data());
421+
Dma::port->CH[Channel].SRCADDR = reinterpret_cast<uint32_t>(source.data());
422+
423+
// update the helper data with the information about the transfer
424+
helper.requested_size = size;
425+
helper.transferred_size = 0x00;
426+
helper.callback = callback;
427+
428+
// setup the control register for the transfer
429+
Dma::port->CH[Channel].CONTROL = (
430+
(detail::dma::get_memory_burst_size(size) << 12) |
431+
(detail::dma::get_memory_burst_size(size) << 15) |
432+
(detail::dma::get_transfer_width(klib::min(sizeof(T), sizeof(uint32_t))) << 18) |
433+
(detail::dma::get_transfer_width(klib::min(sizeof(T), sizeof(uint32_t))) << 21) |
434+
(SourceMemoryIncrement << 26) |
435+
(DestinationMemoryIncrement << 27) |
436+
(0x1 << 31)
437+
);
438+
439+
// check if we need a interrupt
440+
const bool irq = (size > 0xfff) || (callback != nullptr);
441+
442+
// if we are enabling the interrupt make sure we clear the previous flags
443+
// to prevent a interrupt from triggering straight after enabling it
444+
if (irq) [[likely]] {
445+
Dma::port->INTTCCLEAR = 0x1 << Channel;
446+
}
447+
448+
// setup the channel
449+
Dma::port->CH[Channel].CONFIG = (
450+
(irq << 15) |
451+
(static_cast<uint32_t>(detail::dma::transfer_type::memory_to_memory) << 11)
452+
);
453+
454+
// start the transfer
455+
peripheral_memory_impl<false>();
429456
}
430457

431458
/**
@@ -448,7 +475,11 @@ namespace klib::core::lpc17xx::io {
448475
const uint32_t config = Dma::port->CH[Channel].CONFIG;
449476

450477
// return if the enabled or active flag is set
451-
return (config & 0x1) | (config & (0x1 << 17)) | (helper.data != nullptr);
478+
return (
479+
(config & 0x1) | (config & (0x1 << 17)) |
480+
(helper.callback != nullptr) |
481+
(helper.transferred_size < helper.requested_size)
482+
);
452483
}
453484

454485
/**

0 commit comments

Comments
 (0)