diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 6b0b1f9eae..0824396a47 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -670,6 +670,9 @@ typedef struct { #define WDT_HAS_INIT (1) /** + * @name sam0 DMA peripheral + * @{ + * * The sam0 DMA peripheral has a number of channels. Each channel is a separate * data stream, triggered by a configurable trigger when enabled, or triggered * by software (not yet supported). In theory each DMA channel is equal and can @@ -680,6 +683,35 @@ typedef struct { * destination, are kept in RAM and are read when the channel is enabled and * triggered. On the SAML21 platform, these descriptors must reside in the LP * SRAM. + * + * The DMA addresses supplied must point to the **end** of the array to be + * transferred. When address increment is enabled this means that the supplied + * src or dst argument must point to array + length. When increment is disabled, + * the source or destination address can be used directly. The calculation of + * the end of the array must be done by the calling function, because the + * beatsize and the increment can usually be hardcoded there and doesn't have to + * be retrieved from the DMA register configuration. + * See also section 20.6.2.7 of the SAM D21/DA1 Family Data Sheet. + * + * Example: + * ``` + * void transfer_data(void *src, void *dst, size_t len) + * { + * dma_t channel = dma_acquire_channel() + * if (channel == 0xff) { + * return -E_BUSY; + * } + * + * dma_setup(channel, DMA_TRIGGER_MY_PERIH, 0, true); + * dma_prepare(channel, DMAC_BTCTRL_BEATSIZE_BYTE_Val, + * (uint8_t*)src + len, (uint8_t*)dst + len, len); + * + * dma_start(channel); + * dma_wait(channel); + * + * dma_release_channel(channel); + * } + * ``` */ /** @@ -691,10 +723,13 @@ typedef struct { /** * @brief Move the DMA descriptors to the LP SRAM. Required on the SAML21 */ -#if defined(CPU_FAM_SAML21) +#if defined(CPU_FAM_SAML21) || defined(DOXYGEN) #define DMA_DESCRIPTOR_IN_LPSRAM #endif +/** + * @brief Extra attributes required for instantiating DMA descriptors. + */ #ifdef DMA_DESCRIPTOR_IN_LPSRAM #define DMA_DESCRIPTOR_ATTRS __attribute__((section(".backup.bss"))) #else @@ -753,15 +788,18 @@ void dma_setup(dma_t dma, unsigned trigger, uint8_t prio, bool irq); /** * @brief Prepare the DMA channel for an individual transfer. * + * @note When increment is enabled for source or destination, the @p src + * and/or @p dst must point to the **end** of the array. + * * @param dma DMA channel reference * @param width Transfer beat size to use * @param src Source address for the transfer * @param dst Destination address for the transfer - * @param len Number of beats to transfer + * @param num Number of beats to transfer * @param incr Which of the addresses to increment after a beat */ void dma_prepare(dma_t dma, uint8_t width, const void *src, void *dst, - size_t len, dma_incr_t incr); + size_t num, dma_incr_t incr); /** * @brief Prepare a transfer without modifying the destination address @@ -771,16 +809,19 @@ void dma_prepare(dma_t dma, uint8_t width, const void *src, void *dst, * peripheral address, leaving the destination address and related settings * untouched * - * @note This only touches the source address, length and source increment - * settings. Be sure to initialize the full descriptor beforehand with - * @ref dma_prepare + * @note This only touches the source address, number of transfers and source + * increment settings. Be sure to initialize the full descriptor + * beforehand with @ref dma_prepare + * + * @note When increment is enabled for source, the @p src must point to the + * **end** of the array. * * @param dma DMA channel reference * @param src Source address for the transfer - * @param len Number of beats to transfer + * @param num Number of beats to transfer * @param incr Whether to increment the source address after a beat */ -void dma_prepare_src(dma_t dma, const void *src, size_t len, bool incr); +void dma_prepare_src(dma_t dma, const void *src, size_t num, bool incr); /** * @brief Prepare a transfer without modifying the source address @@ -790,67 +831,82 @@ void dma_prepare_src(dma_t dma, const void *src, size_t len, bool incr); * peripheral address, leaving the source address and related settings * untouched * - * @note This only touches the destination address, length and destination - * increment settings. Be sure to initialize the full descriptor beforehand with - * @ref dma_prepare + * @note This only touches the destination address, the number of transfers + * and destination increment settings. Be sure to initialize the full + * descriptor beforehand with @ref dma_prepare + * + * @note When increment is enabled for destination, @p dst must point to the + * **end** of the array. * * @param dma DMA channel reference * @param dst Destination address for the transfer - * @param len Number of beats to transfer + * @param num Number of beats to transfer * @param incr Whether to increment the destination address after a beat */ -void dma_prepare_dst(dma_t dma, void *dst, size_t len, bool incr); +void dma_prepare_dst(dma_t dma, void *dst, size_t num, bool incr); /** * @brief Append a second transfer descriptor after the default channel * descriptor. * - * @note Only a single extra transfer descriptor is supported for now. - * @note @p descriptor must remain valid throughout the full transfer duration + * @note Only a single extra transfer descriptor is supported for now. + * + * @note @p next must remain valid throughout the full transfer duration + * + * @note When increment is enabled for source or destination, @p src + * and/or @p dst must point to the **end** of the array. * * @param dma DMA channel reference to add the descriptor to * @param descriptor Extra transfer descriptor to append * @param width Transfer beat size to use * @param src Source address for the transfer * @param dst Destination address for the transfer - * @param len Number of beats to transfer + * @param num Number of beats to transfer * @param incr Which of the addresses to increment after a beat */ void dma_append(dma_t dma, DmacDescriptor *descriptor, uint8_t width, - const void *src, void *dst, size_t len, dma_incr_t incr); + const void *src, void *dst, size_t num, dma_incr_t incr); /** * @brief Append a second transfer descriptor after the default channel * descriptor, copying destination and block size from the initial * descriptor. * - * @note Only a single extra transfer descriptor is supported for now. - * @note @p descriptor must remain valid throughout the full transfer duration + * @note Only a single extra transfer descriptor is supported for now. + * + * @note @p next must remain valid throughout the full transfer duration + * + * @note When increment is enabled for source, @p src must point to the + * **end** of the array. * * @param dma DMA channel reference to add the descriptor to * @param next Extra transfer descriptor to append * @param src Source address for the transfer - * @param len Number of beats to transfer + * @param num Number of beats to transfer * @param incr Whether to increment the source address after a beat */ void dma_append_src(dma_t dma, DmacDescriptor *next, const void *src, - size_t len, bool incr); + size_t num, bool incr); /** * @brief Append a second transfer descriptor after the default channel * descriptor, copying source and block size from the initial * descriptor. * - * @note Only a single extra transfer descriptor is supported for now. - * @note @p descriptor must remain valid throughout the full transfer duration + * @note Only a single extra transfer descriptor is supported for now. + * + * @note @p next must remain valid throughout the full transfer duration + * + * @note When increment is enabled for destination, @p dst must point to the + * **end** of the array. * * @param dma DMA channel reference to add the descriptor to * @param next Extra transfer descriptor to append * @param dst Destination address for the transfer - * @param len Number of beats to transfer + * @param num Number of beats to transfer * @param incr Whether to increment the source address after a beat */ -void dma_append_dst(dma_t dma, DmacDescriptor *next, void *dst, size_t len, +void dma_append_dst(dma_t dma, DmacDescriptor *next, void *dst, size_t num, bool incr); /** @@ -880,6 +936,7 @@ void dma_wait(dma_t dma); * @param dma DMA channel reference */ void dma_cancel(dma_t dma); +/** @} */ #ifdef __cplusplus } diff --git a/cpu/sam0_common/periph/dma.c b/cpu/sam0_common/periph/dma.c index b65188152b..813c4760b6 100644 --- a/cpu/sam0_common/periph/dma.c +++ b/cpu/sam0_common/periph/dma.c @@ -134,9 +134,9 @@ static inline void _set_destination(DmacDescriptor *descr, void *dst) descr->DSTADDR.reg = (uint32_t)dst; } -static inline void _set_len(DmacDescriptor *descr, size_t len) +static inline void _set_num(DmacDescriptor *descr, size_t num) { - descr->BTCNT.reg = len; + descr->BTCNT.reg = num; } static inline void _set_next_descriptor(DmacDescriptor *descr, void *next) @@ -170,12 +170,12 @@ void dma_setup(dma_t dma, unsigned trigger, uint8_t prio, bool irq) #endif } -void dma_prepare(dma_t dma, uint8_t width, const void *src, void *dst, size_t len, - uint8_t incr) +void dma_prepare(dma_t dma, uint8_t width, const void *src, void *dst, + size_t num, uint8_t incr) { - DEBUG("[DMA]: Prepare %u, len: %u\n", dma, (unsigned)len); + DEBUG("[DMA]: Prepare %u, num: %u\n", dma, (unsigned)num); DmacDescriptor *descr = &descriptors[dma]; - _set_len(descr, len); + _set_num(descr, num); _set_source(descr, src); _set_destination(descr, dst); descr->DESCADDR.reg = (uint32_t)NULL; @@ -184,24 +184,22 @@ void dma_prepare(dma_t dma, uint8_t width, const void *src, void *dst, size_t le DMAC_BTCTRL_VALID; } -void dma_prepare_src(dma_t dma, const void *src, size_t len, - bool incr) +void dma_prepare_src(dma_t dma, const void *src, size_t num, bool incr) { - DEBUG("[dma]: %u: prep src %p, %u, %u\n", dma, src, (unsigned)len, incr); + DEBUG("[dma]: %u: prep src %p, %u, %u\n", dma, src, (unsigned)num, incr); DmacDescriptor *descr = &descriptors[dma]; - _set_len(descr, len); + _set_num(descr, num); _set_source(descr, src); descr->BTCTRL.reg = (descr->BTCTRL.reg & ~DMAC_BTCTRL_SRCINC) | (incr << DMAC_BTCTRL_SRCINC_Pos); _set_next_descriptor(descr, NULL); } -void dma_prepare_dst(dma_t dma, void *dst, size_t len, - bool incr) +void dma_prepare_dst(dma_t dma, void *dst, size_t num, bool incr) { - DEBUG("[dma]: %u: prep dst %p, %u, %u\n", dma, dst, (unsigned)len, incr); + DEBUG("[dma]: %u: prep dst %p, %u, %u\n", dma, dst, (unsigned)num, incr); DmacDescriptor *descr = &descriptors[dma]; - _set_len(descr, len); + _set_num(descr, num); _set_destination(descr, dst); descr->BTCTRL.reg = (descr->BTCTRL.reg & ~DMAC_BTCTRL_DSTINC) | (incr << DMAC_BTCTRL_DSTINC_Pos); @@ -209,39 +207,39 @@ void dma_prepare_dst(dma_t dma, void *dst, size_t len, } void _fmt_append(DmacDescriptor *descr, DmacDescriptor *next, - const void *src, void *dst, size_t len) + const void *src, void *dst, size_t num) { /* Configure the full descriptor besides the BTCTRL data */ _set_next_descriptor(descr, next); _set_next_descriptor(next, NULL); _set_source(next, src); - _set_len(next, len); + _set_num(next, num); _set_destination(next, dst); } void dma_append(dma_t dma, DmacDescriptor *next, uint8_t width, - const void *src, void *dst, size_t len, dma_incr_t incr) + const void *src, void *dst, size_t num, dma_incr_t incr) { DmacDescriptor *descr = &descriptors[dma]; next->BTCTRL.reg = width << DMAC_BTCTRL_BEATSIZE_Pos | incr << DMAC_BTCTRL_SRCINC_Pos | DMAC_BTCTRL_VALID; - _fmt_append(descr, next, src, dst, len); + _fmt_append(descr, next, src, dst, num); } void dma_append_src(dma_t dma, DmacDescriptor *next, const void *src, - size_t len, bool incr) + size_t num, bool incr) { DmacDescriptor *descr = &descriptors[dma]; /* Copy the original descriptor config and modify the increment */ next->BTCTRL.reg = (descr->BTCTRL.reg & ~DMAC_BTCTRL_SRCINC) | (incr << DMAC_BTCTRL_SRCINC_Pos); - _fmt_append(descr, next, src, (void *)descr->DSTADDR.reg, len); + _fmt_append(descr, next, src, (void *)descr->DSTADDR.reg, num); } -void dma_append_dst(dma_t dma, DmacDescriptor *next, void *dst, size_t len, +void dma_append_dst(dma_t dma, DmacDescriptor *next, void *dst, size_t num, bool incr) { DmacDescriptor *descr = &descriptors[dma]; @@ -249,7 +247,7 @@ void dma_append_dst(dma_t dma, DmacDescriptor *next, void *dst, size_t len, /* Copy the original descriptor config and modify the increment */ next->BTCTRL.reg = (descr->BTCTRL.reg & ~DMAC_BTCTRL_DSTINC) | (incr << DMAC_BTCTRL_DSTINC_Pos); - _fmt_append(descr, next, (void *)descr->SRCADDR.reg, dst, len); + _fmt_append(descr, next, (void *)descr->SRCADDR.reg, dst, num); } void dma_start(dma_t dma)