SAM L10/L11 Direct Memory Access (DMAC)

Last modified by Microchip on 2023/11/09 09:01

Direct Memory Access Overview

The Direct Memory Access Controller (DMAC) contains both a Direct Memory Access engine and a Cyclic Redundancy Check (CRC) engine. The DMAC can transfer data between memories and peripherals, and thus, off-loads these tasks from the CPU. It enables high data transfer rates with minimum CPU intervention and frees up CPU time.

Data transfer rates

With access to all peripherals, the DMAC can handle the automatic transfer of data between communication modules. The DMA part of the DMAC has several DMA channels. All of these channels can receive different types of transfer triggers to generate transfer requests, from the DMA channels to the arbiter (see the block diagram below).

The arbiter will grant one DMA channel at a time to act as the active channel. When an active channel has been granted, the fetch engine of the DMAC will fetch a transfer descriptor from the SRAM and store it in the internal memory of the active channel, which will execute the data transmission. An ongoing data transfer of an active channel can be interrupted by a higher-prioritized DMA channel. The DMAC will write back the updated transfer descriptor from the internal memory of the active channel to SRAM, and grant the higher prioritized channel to start the transfer as the new active channel. Once a DMA channel is done with its transfer, interrupts, and events can be generated optionally.

The DMAC has four bus interfaces:

  • The data transfer bus is used for performing the actual DMA transfer.
  • The AHB/APB Bridge bus is used when writing and reading the I/O registers of the DMAC.
  • The descriptor fetch bus is used by the fetch engine to fetch transfer descriptors before data transfer can be started or continued.
  • The write-back bus is used to write the transfer descriptor back to SRAM.

All buses are AHB Master interfaces except the AHB/APB Bridge bus, which is an APB Slave interface.

The CRC engine can be used by the software to detect an accidental error in the transferred data and to take corrective action, such as requesting the data to be sent again or simply not using the incorrect data.

CRC engine diagram

You will find a concrete example on how to configure the DMA based on a SERCOM SPI transfer, at the bottom of this page.

Back to Top

DMA Features

  • Data transfer from:
    • Peripheral-to-peripheral
    • Peripheral-to-memory
    • Memory-to-peripheral
    • Memory-to-memory
  • Transfer trigger sources
    • Software
    • Events from the Event System
    • Dedicated requests from peripherals
  • SRAM-based transfer descriptors
    • Single transfer using one descriptor
    • Multi-buffer or circular buffer modes by linking multiple descriptors
  • Up to eight channels
    • Enable eight independent transfers
    • Automatic descriptor fetch for each channel
    • Suspend/resume operation support for each channel
  • Flexible arbitration scheme
    • Four configurable priority levels for each channel
    • Fixed or round-robin priority scheme within each priority level
  • From 1 to 256 kB data transfer in a single block transfer
  • Multiple addressing modes
    • Static
    • Configurable increment scheme
  • Optional interrupt generation
    • On block transfer complete
    • On error detection
    • On channel suspend
  • Four event inputs
    • One event input for each of the four least significant DMA channels
    • Can be selected to trigger normal transfers, periodic transfers, or conditional transfers
    • Can be selected to suspend or resume channel operation
  • Four event outputs
    • One output event for each of the four least significant DMA channels
    • Selectable generation on AHB, block, or transaction transfer complete
  • Error management supported by write-back function
    • Dedicated Write-Back memory section for each channel to store ongoing descriptor transfer
  • CRC polynomial software selectable to
    • CRC-16 (CRC-CCITT)
    • CRC-32 (IEEE® 802.3)

The DMA is a key peripheral to improve system bandwidth and is a low-power extension to key peripherals such as ADC, DAC, TC, or SERCOMs.

Back to Top

DMA Transfer Sizes

The DMAC can transfer data between memories and peripherals without interaction from the CPU. A unit of transfer by the DMAC is called a DMA Transaction, and these can be split into smaller data transfers (Blocks and Beats). The following figure shows the relationship between the different transfer sizes:

 DMA transaction diagram

  • Configurable BEAT size: 8-bit (byte); 16-bit (half-word); 32-bit (word).
  • Possibility to trig/sequence a transfer on:
    • Full DMA Transaction
    • A BLOCK
    • A BEAT
  • A multi-block transaction requires the use of descriptors.

Back to Top

DMA Transfer Descriptors

Together with the channel configurations, the transfer descriptors decide how a Block transfer should be executed. Before a DMA channel is enabled and receives a transfer trigger, its first transfer descriptor has to be initialized and valid. The first transfer descriptor describes the first Block transfer of a transaction. Descriptors are stored in internal SRAM, and they define each Block transfer to be performed.

  • The SRAM address of where the descriptor memory section is located must be written to the Description Base Address (BASEADDR) register. Application software should place the first descriptor at this address.
  • The SRAM address of where the write-back section should be located must be written to the Write-Back Memory Base Address (WRBADDR) register.

Refer to the product data sheet for more details.

To configure a Transfer Descriptor, five registers are required to be modified:

5 required registers to be modified

  • BTCTRL
    • Address increment enable and step size (BEAT)
    • BEAT size (byte, half-word, word)
    • End block action (none, interrupt and or suspend)
    • Event output configuration
    • Descriptor validity
  • BTCNT
    • Block transfer counter
  • SRCADDR
    • Source address of Block transfer
  • DSTADDR
    • Destination address of Block transfer
  • DESCADDR
    • Address of next descriptor in case of linked list (multi-block)
    • 0x00000000 in case of the last block

Back to Top

Arbitration and Priority Level

The priority level x of the arbiter can be enabled by setting the Priority Level x Enable bit in the control register (CTRL.LVLENx = 1). When a channel priority level is pending or the channel is transferring data, the corresponding Level Executing bit is set in the Active Channel and Levels register (ACTIVE.LVLEXx).

Each DMA channel supports a 4-level priority scheme. The priority level for a channel is configured by writing to the Channel Arbitration Level bit group in the Channel Control B register (CHCTRLB.LVL).

As long as all the priority levels are enabled, a channel with a higher priority level number will have priority over a channel with a lower priority level number. Each priority level x is enabled by setting the corresponding Priority Level x Enable bit in the Control register (CTRL.LVLENx = 1).

Within each priority level, the DMAC's arbiter can be configured to prioritize statically or dynamically.
Static Arbitration within a priority level is selected by writing a '0' to the Level x Round-Robin Scheduling Enable bit in the Priority Control 0 register (PRICTRL0.RRLVLENx). When static arbitration is selected, the arbiter will prioritize a low channel number over a high channel number as shown in the accompanying image. When using static arbitration, there is a risk of high channel numbers never being granted access as the active channel. This can be avoided using a dynamic arbitration scheme.

Back to Top

Static Scheduling

For the Static Scheduling case, the channel having the highest priority is the Lowest Channel (Channel 0), and the Highest Channel has the lowest priority, as illustrated in the accompanying image:

Static Scheduling case

Back to Top

Round Robin Scheduling

For the Round Robin Scheduling case, the last channel used keeps the highest priority, and the priority rounds over all the channels and loops to the last channel used in a circular order, as explained below:

Round Robin Scheduling case

Refer to the product datasheet for more details.

Back to Top

Interrupts

The DMAC channels have the following interrupt sources:

  • Transfer Complete (TCMPL): Indicates that a Block transfer is completed on the corresponding channel (Check the DMAC Configuration Example below).
  • Transfer Error (TERR): Indicates that a bus error has occurred during a Burst transfer, or that an invalid descriptor has been fetched.
  • Channel Suspend (SUSP): Indicates that the corresponding channel has been suspended.

Refer to the product datasheet for more details.

Back to Top

Events and Triggers

Silicon Errata and Data Sheet Clarification:
Linked descriptors using several concurrent channels may induce corruption of DMAC Write-Back descriptors. Therefore, multiple transfers must only be sequenced using linked descriptors on a single channel.

Back to Top

Output Events

The DMAC can generate the following output events:

  • Channel (CH): Generated when a Block transfer for a given channel has been completed, or when a Beat transfer within a Block transfer for a given channel has been completed.

Back to Top

Input Event

The DMAC can take the following actions on an input event:

  • Transfer and Periodic Transfer Trigger (TRIG): Normal transfer or periodic transfers on peripherals are enabled.
  • Conditional Transfer Trigger (CTRIG): Conditional transfers on peripherals are enabled.
  • Conditional Block Transfer Trigger (CBLOCK): Conditional Block transfers on peripherals are enabled.
  • Channel Suspend Operation (SUSPEND): Suspend a channel operation.
  • Channel Resume Operation (RESUME): Resume a suspended channel operation.
  • Skip Next Block Suspend Action (SSKIP): Skip the next block suspend transfer condition.
  • Increase Priority (INCPRI): Increase the channel priority.

Back to Top

Direct DMA Request (Trigger Sources)

Some peripherals feature a direct hardware connection to the DMAC, enabling the possibility of requesting a DMA Transfer based on a dedicated condition coming from one of those peripherals. The CHCTRLB.TRIGSRC bits define the peripheral trigger which is the source of the transfer.

The peripherals having this feature include:

  • RTC
  • DSU
  • SERCOMs
  • TCs
  • ADC (refer to the SleepWalking example)
  • DAC
  • PTC

Refer to the product data sheet for more details.

Back to Top

DMA in Low-power mode

Each DMA channel can be configured to operate in any Sleep mode. To be able to run in Standby, the RUNSTDBY bit in Channel Control A register (CHCTRLA.RUNSTDBY) must be written to "1". The DMAC can wake up the device using interrupts from any Sleep mode or perform actions through the Event System.

For channels with CHCTRLA.RUNSTDBY = 0, it is up to the software to stop DMA transfers on these channels and wait for completion before going on Standby mode, using the following sequence:

  1. Suspend the DMAC channels for which CHCTRLA.RUNSTDBY = 0.
  2. Check the SYNCBUSY bits of registers accessed by the DMAC channels being suspended.
  3. Go to sleep.
  4. When the device wakes up, resume the suspended channels.

Back to Top

DMAC Configuration Example

IMPORTANT NOTE

Enabled Protected Registers
Some registers are enable-protected, meaning they can only be written when the module is disabled. The enable-protection is denoted by the "Enable-Protected" property in each register description.

The following DMAC registers are enable-protected, meaning they can only be written when the DMAC is disabled (CTRL.DMAENABLE = 0):

  • Descriptor Base Memory Address register (BASEADDR).
  • Write-Back Memory Base Address register (WRBADDR).

The following DMAC bit is enable-protected, meaning that it can only be written when both the DMAC and CRC are disabled (CTRL.DMAENABLE = 0 and CTRL.CRCENABLE = 0):

  • Software Reset bit in the Control register (CTRL.SWRST).

The following DMA channel register is enable-protected, meaning that it can only be written when the corresponding DMA channel is disabled (CHCTRLA.ENABLE = 0):

  • Channel Control B (CHCTRLB) register, except the Command bit (CHCTRLB.CMD) and the ChannelArbitration Level bit (CHCTRLB.LVL).

The following DMA channel bit is enable-protected, meaning that it can only be written when the corresponding DMA channel is disabled:

  • Channel Software Reset bit in the Channel Control A register (CHCTRLA.SWRST).

The following CRC registers are enable-protected, meaning that they can only be written when the CRC is disabled (CTRL.CRCENABLE = 0):

  • CRC Control register (CRCCTRL).
  • CRC Checksum register (CRCCHKSUM).
/***
 *** This Example has no copyright and can be used by anyone.
 *** The following example is based on the Device File Package
 *** required to compile the macro definitions used.
 *** The Device File Package is available by downloading Atmel Studio 7.
 ***/


/*** Define SPI Channels ***/
#define SPI_TX_CHANNEL      1
#define SPI_RX_CHANNEL      0

/*** Create DMA descriptor in a global variable (128-bit aligned) ***/
volatile __attribute__((__aligned__(128))) DmacDescriptor descriptor_SPI[2];
volatile __attribute__((__aligned__(128))) DmacDescriptor write_back_descriptor_SPI[2];

/*** DMA RX Descriptor Initialization step by step
 *** - Validate the Descriptor
 *** - No Event out generated by the DMA
 *** - Channel will be disabled if it is the last block
 ***   transfer in the transaction and block interrupt
 *** - 8-bit bus transfer (BYTE)
 *** - No increment from the source address (SRCINC=0)
 *** - Enable Increment from the destination address (DSTINC=1)
 *** - Step size settings apply to the Destination address
 *** - Step Size => Next ADDR = ADDR + (BEATSIZE+1) * 1
 *** - DMA_BITCOUNT_VALUE SPI_DATA_SIZE transfer before waking up the core
 ***/

void DMA_RX_descriptor_init(void)
{
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.VALID = 1;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val;    
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val;    
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.SRCINC = 0;                        
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.DSTINC = 1;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_DST_Val;
 descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val;
 descriptor_SPI[SPI_RX_CHANNEL].BTCNT.reg = SPI_DATA_SIZE+1;

/*** Set source address and destination address
  *** source address is the SPI Data Register (SERCOM0->SPI.DATA)
  *** destination address in the RAM is the RX buffer (spi_rx_buffer[SPI_DATA_SIZE])
  ***/

 descriptor_SPI[SPI_RX_CHANNEL].SRCADDR.reg = (uint32_t) (&(SERCOM0->SPI.DATA));
 descriptor_SPI[SPI_RX_CHANNEL].DSTADDR.reg = (uint32_t) (&(spi_rx_buffer[SPI_DATA_SIZE]));

/*** Set next transfer descriptor_SPI_TX address ***/
 descriptor_SPI[SPI_RX_CHANNEL].DESCADDR.reg = (uint32_t) (&descriptor_SPI[SPI_TX_CHANNEL]);
}

/*** DMA RX Descriptor Initialization step by step
 *** - Validate the Descriptor
 *** - No Event out generated by the DMA
 *** - Channel will be disabled if it is the last block
 ***   transfer in the transaction and block interrupt
 *** - 8-bit bus transfer (BYTE)
 *** - Enable Increment from the  the source address (SRCINC=1)
 *** - No increment from the destination address (DSTINC=0)
 *** - Step size settings apply to the Source address
 *** - Step Size => Next ADDR = ADDR + (BEATSIZE+1) * 1
 *** - DMA_BITCOUNT_VALUE SPI_DATA_SIZE transfer before waking up the core
 ***/

void DMA_TX_descriptor_init(void)
{
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.VALID = 1;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.SRCINC = 1;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.DSTINC = 0;
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_SRC_Val;    
 descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val;
 descriptor_SPI[SPI_TX_CHANNEL].BTCNT.reg = SPI_DATA_SIZE+1;

/*** Set source address and destination address
  *** Source address in the RAM is the SPI Output buffer (spi_output_data[SPI_DATA_SIZE])
  *** Destination address is the SPI Data register (SERCOM0->SPI.DATA)
  ***/

 descriptor_SPI[SPI_TX_CHANNEL].SRCADDR.reg = (uint32_t) (&(spi_output_data[SPI_DATA_SIZE]));
 descriptor_SPI[SPI_TX_CHANNEL].DSTADDR.reg = (uint32_t) (&(SERCOM0->SPI.DATA));

/*** Set next transfer address to NULL ***/
 descriptor_SPI[SPI_TX_CHANNEL].DESCADDR.reg = NULL;
}

/*** DMA SPI TX and RX Channels initialization ***/
void DMA_init_SPI(void)
{
/***
  *** Setup descriptor base address
  *** and write back section base address
  ***/

 DMAC->BASEADDR.reg = (uint32_t)descriptor_SPI;
 DMAC->WRBADDR.reg = (uint32_t)write_back_descriptor_SPI;

/***
  *** Disable DMAC
  *** Enable all priority level at the same time
  *** Round Robin Arbitrer Scheme selected
  ***/

 DMAC->CTRL.reg &= ~(DMAC_CTRL_DMAENABLE);
 DMAC->CTRL.reg |= DMAC_CTRL_LVLEN(0xf);    
 DMAC->PRICTRL0.bit.RRLVLEN0 = 1;

/***
  *** Configure The TX SPI DMA Channel:
  *** DMAC Channel 1 is used
  *** Disable the channel before configuring it
  ***/

 DMAC->CHID.reg = DMAC_CHID_ID(SPI_TX_CHANNEL);
 DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;

/***
  *** Configure TX SPI DMA Channel: (continue...)
  *** DMA is used in STANDBY, therefore needs RUNSTDBY
  *** Channel Priority Level = 0
  *** SERCOM0 (SPI) is used as source to trigger the DMA TX
  *** The trigger action is done for every DMA BEAT
  ***/

 DMAC->CHCTRLA.reg = DMAC_CHCTRLA_RUNSTDBY;
 DMAC->CHCTRLB.reg = (DMAC_CHCTRLB_LVL(0x0)|    
                      DMAC_CHCTRLB_TRIGSRC(SERCOM0_DMAC_ID_TX)|
                      DMAC_CHCTRLB_TRIGACT_BEAT);

/*** Enabling DMA channel TX ***/
 DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
while(DMAC->CHSTATUS.bit.BUSY);  

/***
  *** Configure RX SPI DMA Channel:
  *** DMAC Channel 0 is used
  *** Disable the channel before configuring it
  ***/

 DMAC->CHID.reg = DMAC_CHID_ID(SPI_RX_CHANNEL);    
 DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;    

/***
  *** Configure RX SPI DMA Channel: (continue...)
  *** DMA is used in STANDBY, therefore needs RUNSTDBY
  *** Channel Priority Level = 0
  *** SERCOM0 (SPI) is used as source to trigger the DMA RX
  *** The trigger action is done for every DMA BEAT
  ***/

  DMAC->CHCTRLA.reg = DMAC_CHCTRLA_RUNSTDBY;
  DMAC->CHCTRLB.reg = ( DMAC_CHCTRLB_LVL(0x0)|    
                        DMAC_CHCTRLB_TRIGSRC(SERCOM0_DMAC_ID_RX)|
                        DMAC_CHCTRLB_TRIGACT_BEAT );

/*** enabling DMA interrupt on RX transfer completed ***/
 DMAC->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL;        

/*** Enabling DMA channel RX ***/
 DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
while(DMAC->CHSTATUS.bit.BUSY);  

/*** Enabling interrupt at core side ***/
 NVIC_EnableIRQ(DMAC_0_IRQn);

/*** Finally Enable the DMA
  *** (Enable Protected)
  ***/

 DMAC->CTRL.reg |= (DMAC_CTRL_DMAENABLE) ;    
}

Back to Top