diff --git a/ports/samd/dma_manager.c b/ports/samd/dma_manager.c new file mode 100644 index 0000000000..916b629a52 --- /dev/null +++ b/ports/samd/dma_manager.c @@ -0,0 +1,131 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/mpconfig.h" +#include "sam.h" +#include "dma_manager.h" +#include "samd_soc.h" + +// Set a number of dma channels managed here. samd21 has 21 dma channels, samd51 +// has 32 channels, as defined by the lib macro DMAC_CH_NUM. +// At first, we use a smaller number here to save RAM. May be increased as needed. + +#ifndef MICROPY_HW_DMA_CHANNELS +#if defined(MCU_SAMD21) +#define MICROPY_HW_DMA_CHANNELS 2 +#elif defined(MCU_SAMD51) +#define MICROPY_HW_DMA_CHANNELS 4 +#endif +#endif + +#if MICROPY_HW_DMA_CHANNELS > DMAC_CH_NUM +#error Number of DMA channels too large +#endif + +volatile DmacDescriptor dma_desc[MICROPY_HW_DMA_CHANNELS] __attribute__ ((aligned(16))); +static volatile DmacDescriptor dma_write_back[MICROPY_HW_DMA_CHANNELS] __attribute__ ((aligned(16))); + +// List of channel flags: true: channel used, false: channel available +static bool channel_list[MICROPY_HW_DMA_CHANNELS]; + +STATIC bool dma_initialized = false; + +// allocate_channel(): retrieve an available channel. Return the number or -1 +int allocate_dma_channel(void) { + for (int i = 0; i < MP_ARRAY_SIZE(channel_list); i++) { + if (channel_list[i] == false) { // Channel available + channel_list[i] = true; + return i; + } + } + mp_raise_ValueError(MP_ERROR_TEXT("no dma channel available")); +} + +// free_channel(n): Declare channel as free +void free_dma_channel(int n) { + if (n >= 0 && n < MP_ARRAY_SIZE(channel_list)) { + channel_list[n] = false; + } +} + +void dma_init(void) { + if (!dma_initialized) { + + // Enable the DMA clock + #if defined(MCU_SAMD21) + PM->AHBMASK.reg |= PM_AHBMASK_DMAC; + PM->APBBMASK.reg |= PM_APBBMASK_DMAC; + #elif defined(MCU_SAMD51) + MCLK->AHBMASK.reg |= MCLK_AHBMASK_DMAC; + #endif + // Setup the initial DMA configuration + DMAC->CTRL.reg = DMAC_CTRL_SWRST; + while (DMAC->CTRL.reg & DMAC_CTRL_SWRST) { + } + // Set the DMA descriptor pointers + DMAC->BASEADDR.reg = (uint32_t)dma_desc; + DMAC->WRBADDR.reg = (uint32_t)dma_write_back; + // Enable the DMA + DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf); + + dma_initialized = true; + } +} + +void dma_deinit(void) { + memset((uint8_t *)dma_desc, 0, sizeof(dma_desc)); + memset((uint8_t *)dma_write_back, 0, sizeof(dma_write_back)); + memset((uint8_t *)channel_list, 0, sizeof(channel_list)); + dma_initialized = false; + // Disable DMA + DMAC->CTRL.reg = 0; + for (int ch = 0; ch < DMAC_CH_NUM; ch++) { + dma_register_irq(ch, NULL); + } +} + +void dac_stop_dma(int dma_channel, bool wait) { + #if defined(MCU_SAMD21) + NVIC_DisableIRQ(DMAC_IRQn); + DMAC->CHID.reg = dma_channel; + DMAC->CHINTENCLR.reg = DMAC_CHINTENSET_TCMPL | DMAC_CHINTENSET_TERR | DMAC_CHINTENSET_SUSP; + DMAC->CHCTRLA.reg = 0; + while (wait && DMAC->CHCTRLA.bit.ENABLE) { + } + #elif defined(MCU_SAMD51) + if (0 <= dma_channel && dma_channel < 4) { + NVIC_DisableIRQ(DMAC_0_IRQn + dma_channel); + } else if (dma_channel >= 4) { + NVIC_DisableIRQ(DMAC_4_IRQn); + } + DMAC->Channel[dma_channel].CHINTENCLR.reg = + DMAC_CHINTENSET_TCMPL | DMAC_CHINTENSET_TERR | DMAC_CHINTENSET_SUSP; + DMAC->Channel[dma_channel].CHCTRLA.reg = 0; + while (wait && DMAC->Channel[dma_channel].CHCTRLA.bit.ENABLE) { + } + #endif +} diff --git a/ports/samd/dma_manager.h b/ports/samd/dma_manager.h new file mode 100644 index 0000000000..33c950ebd7 --- /dev/null +++ b/ports/samd/dma_manager.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Robert Hammelrath + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_SAMD_DMACHANNEL_H +#define MICROPY_INCLUDED_SAMD_DMACHANNEL_H + +#include "py/runtime.h" + +int allocate_dma_channel(void); +void free_dma_channel(int n); +void dma_init(void); +void dma_deinit(void); + +extern volatile DmacDescriptor dma_desc[]; + +#endif // MICROPY_INCLUDED_SAMD_DMACHANNEL_H diff --git a/ports/samd/samd_isr.c b/ports/samd/samd_isr.c index 7c4c1d060f..6888d8a1dc 100644 --- a/ports/samd/samd_isr.c +++ b/ports/samd/samd_isr.c @@ -164,6 +164,51 @@ void Sercom7_Handler(void) { } #endif +// DMAC IRQ handler support +#if defined(MCU_SAMD21) +#define DMAC_FIRST_CHANNEL 0 +#else +#define DMAC_FIRST_CHANNEL 4 +#endif + +void (*dma_irq_handler_table[DMAC_CH_NUM])(int num) = {}; + +void dma_register_irq(int dma_channel, void (*dma_irq_handler)) { + if (dma_channel < DMAC_CH_NUM) { + dma_irq_handler_table[dma_channel] = dma_irq_handler; + } +} + +void DMAC0_Handler(void) { + if (dma_irq_handler_table[0]) { + dma_irq_handler_table[0](0); + } +} +void DMAC1_Handler(void) { + if (dma_irq_handler_table[1]) { + dma_irq_handler_table[1](1); + } +} +void DMAC2_Handler(void) { + if (dma_irq_handler_table[2]) { + dma_irq_handler_table[2](2); + } +} +void DMAC3_Handler(void) { + if (dma_irq_handler_table[3]) { + dma_irq_handler_table[3](3); + } +} +void DMACn_Handler(void) { + for (uint32_t mask = 1 << DMAC_FIRST_CHANNEL, dma_channel = DMAC_FIRST_CHANNEL; + dma_channel < DMAC_CH_NUM; + mask <<= 1, dma_channel += 1) { + if ((DMAC->INTSTATUS.reg & mask) && dma_irq_handler_table[dma_channel]) { + dma_irq_handler_table[dma_channel](dma_channel); + } + } +} + #if defined(MCU_SAMD21) const ISR isr_vector[] __attribute__((section(".isr_vector"))) = { (ISR)&_estack, @@ -188,7 +233,7 @@ const ISR isr_vector[] __attribute__((section(".isr_vector"))) = { 0, // 3 Real-Time Counter (RTC) &EIC_Handler, // 4 External Interrupt Controller (EIC) 0, // 5 Non-Volatile Memory Controller (NVMCTRL) - 0, // 6 Direct Memory Access Controller (DMAC) + &DMACn_Handler, // 6 Direct Memory Access Controller (DMAC) USB_Handler_wrapper,// 7 Universal Serial Bus (USB) 0, // 8 Event System Interface (EVSYS) &Sercom0_Handler, // 9 Serial Communication Interface 0 (SERCOM0) @@ -261,11 +306,11 @@ const ISR isr_vector[] __attribute__((section(".isr_vector"))) = { 0, // 28 Frequency Meter (FREQM) 0, // 29 Non-Volatile Memory Controller (NVMCTRL): NVMCTRL_0 - _7 0, // 30 Non-Volatile Memory Controller (NVMCTRL): NVMCTRL_8 - _10 - 0, // 31 Direct Memory Access Controller (DMAC): DMAC_SUSP_0, DMAC_TCMPL_0, DMAC_TERR_0 - 0, // 32 Direct Memory Access Controller (DMAC): DMAC_SUSP_1, DMAC_TCMPL_1, DMAC_TERR_1 - 0, // 33 Direct Memory Access Controller (DMAC): DMAC_SUSP_2, DMAC_TCMPL_2, DMAC_TERR_2 - 0, // 34 Direct Memory Access Controller (DMAC): DMAC_SUSP_3, DMAC_TCMPL_3, DMAC_TERR_3 - 0, // 35 Direct Memory Access Controller (DMAC): DMAC_SUSP_4 - _31, DMAC_TCMPL_4 _31, DMAC_TERR_4- _31 + &DMAC0_Handler, // 31 Direct Memory Access Controller (DMAC): DMAC_SUSP_0, DMAC_TCMPL_0, DMAC_TERR_0 + &DMAC1_Handler, // 32 Direct Memory Access Controller (DMAC): DMAC_SUSP_1, DMAC_TCMPL_1, DMAC_TERR_1 + &DMAC2_Handler, // 33 Direct Memory Access Controller (DMAC): DMAC_SUSP_2, DMAC_TCMPL_2, DMAC_TERR_2 + &DMAC3_Handler, // 34 Direct Memory Access Controller (DMAC): DMAC_SUSP_3, DMAC_TCMPL_3, DMAC_TERR_3 + &DMACn_Handler, // 35 Direct Memory Access Controller (DMAC): DMAC_SUSP_4 - _31, DMAC_TCMPL_4 _31, DMAC_TERR_4- _31 0, // 36 Event System Interface (EVSYS): EVSYS_EVD_0, EVSYS_OVR_0 0, // 37 Event System Interface (EVSYS): EVSYS_EVD_1, EVSYS_OVR_1 0, // 38 Event System Interface (EVSYS): EVSYS_EVD_2, EVSYS_OVR_2 diff --git a/ports/samd/samd_soc.h b/ports/samd/samd_soc.h index 707d2f8edf..2477775735 100644 --- a/ports/samd/samd_soc.h +++ b/ports/samd/samd_soc.h @@ -43,6 +43,7 @@ void USB_Handler_wrapper(void); void sercom_enable(Sercom *spi, int state); void sercom_register_irq(int sercom_id, void (*sercom_irq_handler)); +void dma_register_irq(int dma_channel, void (*dma_irq_handler)); // Each device has a unique 128-bit serial number. The uniqueness of the serial number is // guaranteed only when using all 128 bits.