This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Understanding/Learning DMA

Genius 3300 points

Hi,

I am starter in DMA, using TM4C123gxl. 

Which site/AN should I follow about DMA & example code with TM4C123gxl.

One thing I didn't understand(with very basic level of reading), if I have to put CPU in sleep mode while DMA is trasnferring data. Then why to put to CPU in sleep mode. Why not use CPU to transfer data at first place.

Possible reason i get now is: CPU is releievd, so can be put in sleep so lower current consumption. Second DMA transfer are much faster as they are hardware controlled.Right?

  • Hello VT,

    Luis Afonos's page is a good resource for DMA

    sites.google.com/.../home

    As for the second part of your question: Where did you read that CPU is to be put to Sleep Mode when DMA is transferring data?

    Regards
    Amit
  • Hi Amit,

    Earlier I had read some stuff from his website. But now he had put a request permission on the page.
    I had requested earulier also but didn't get back mail.

    I had read that since CPU & DMA share same bus, so cpu has to put to halt. otherwise there will be bus contention.
  • Hello VT,

    That is incorrect. CPU and DMA can work on the same bus, and the bus arbitration will ensure that the highest priority goes to CPU.

    Regards
    Amit
  • You are mostly correct - The CPU gets to use the bus before the uDMA engine..

    The DMA engine should be faster than the CPU if it's not competing for the bus. Perhaps Amit knows more. The DMA engine doesn't have to fetch instructions (not often) so it doesn't have the overhead of the CPU. The other big advantage it has is that there is no interrupt service overhead for the DMA engine, so it is more reliable in low-latency situations. Putting the CPU into WFI should lower your power consumption and in some cases improve your performance. It comes out of sleep and goes directly to the ISR, without the overhead of polling.
  • Hello Robert,

    That is correct. The DMA is more efficient if there is burst transfer than a single request transfer. Some peripherals however do generate Single Request only

    Regards
    Amit
  • 1. I have written code for software transfer as below. Is it the correct sequence.
    2. Can the buffer size(in code below) be of any size 1 to 1024, or it has to be in binary step size only like 1,2,4,...1024.
    I have tried value 3 , code worked.
    3. Is there no need to select uDMAChannelAttributeEnable for SW DMA??
    4. What is RAM stripping.
    5. Plz correct me if I am wrong here. After uDMAChannelRequest(UDMA_CHANNEL_SW); I think there are three ways DMA will exit. I have made a interrupt for it. Either it will go isr_dma_sw() or isr_dma_err(). In isr_dma_sw(), indication will be given whether it is complete or unknown error, while in isr_dma_err() there is error. In case of these two error, is there any extra action to require to reset DMA, or reconfiguring it will do the job. I have placed while((0U == g_dma_err) && (0U == g_dma_complete) && (0U == g_dma_unknown_err)); I think any of three condition will end the DMA operation.
    6. There is only one isr for DMA. Suppose I have two DMA working together. How will I know which has generated interrupt?
    7. Hi Amit you had said "CPU and DMA can work on the same bus, and the bus arbitration will ensure that the highest priority goes to CPU."
    In this example I never put CPU to sleep. After first 8 transfer DMA(UDMA_ARB_8), CPU will gets the bus since it is not in sleep.
    Then it will nit return bus to DMA since it never leaves the bus. So how it works here?

    /* controlt able used by udma , must always be 1024 bytes */   /* Query: waht is its purpose? */
    #if defined(ewarm)
    #pragma data_alignment=1024
    uint8_t dmaControlTable[1024];
    #elif defined(ccs)
    #pragma DATA_ALIGN(ui8ControlTable, 1024)
    uint8_t dmaControlTable[1024];
    #else
    uint8_t dmaControlTable[1024] __attribute__ ((aligned(1024)));
    #endif    
    
    
    /* source & destination buffers */
    #define BUFFER_SIZE    128
    uint8_t source_buffer[BUFFER_SIZE];              /* Query: array size can be only:1,2,4,8,16,32,64..512,1024  */
    uint8_t destination_buffer[BUFFER_SIZE];
    volatile uint8_t g_dma_err;
    volatile uint8_t g_dma_unknown_err;
    volatile uint8_t g_dma_complete;
    
    
    void isr_dma_sw(void)
    {
        uint32_t mode;
    
    /* Check for the primary control structure to indicate complete. */
        mode = uDMAChannelModeGet(UDMA_CHANNEL_SW);
        
    /* query: suppose I have 2 or more dma enabled, how to know which has caused this error,or only one has to enabled at one time */        
        if(mode == UDMA_MODE_STOP)
        {
        /* Increment the count of completed transfers. */
            g_dma_complete = 1U;
        }
        /* If the channel is not stopped, then something is wrong. */
        else
        {
            g_dma_unknown_err = 1U;   /* query: what to do in this case */
        }
        
    }    
    
    
    void isr_dma_err(void)
    {
        uint32_t status;
    
    /* check for err status */
    /* query: suppose I have 2 or more dma enabled, how to know which has caused this error,or only one has to enabled at one time */    
        status = uDMAErrorStatusGet();
    
    /* if err has occured */
        if(status)
        {
            uDMAErrorStatusClear();
            g_dma_err = 1U;
        }
        
    }    
    
    
    void udma_sw_example(void) 
    {   
        uint32_t cnt;
        uint32_t ready_count;
        
    /* fill in source_buffer */    
        for(cnt = 0U ; cnt < BUFFER_SIZE ; cnt++)
        {
            source_buffer[cnt] = cnt+1;
            destination_buffer[cnt] = 0U;
        }
        
    /* enable udma clock */ 
        /* only disable & reset it once */
        SysCtlPeripheralDisable(SYSCTL_PERIPH_UDMA);
        SysCtlPeripheralReset(SYSCTL_PERIPH_UDMA);
        
        /* enable periphral */
        SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        
        /* while not ready */
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }        
        
    /* enable udma */
        uDMAEnable();
        
    /* set base for channel control table */
        uDMAControlBaseSet(&dmaControlTable[0]);
        
    /* assign the channel */    
        uDMAChannelAssign(UDMA_CH30_SW);    
        
    /* clear any previous defined attribute */    
        uDMAChannelAttributeDisable(UDMA_CHANNEL_SW , UDMA_ATTR_ALL);
        
    /* Query: no need to select uDMAChannelAttributeEnable for SW DMA??  */    
        //uDMAChannelAttributeEnable(UDMA_CHANNEL_SW , );
        
    /* set the control pars */  
        uDMAChannelControlSet(UDMA_CHANNEL_SW | UDMA_PRI_SELECT , UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_8);
        
    /* transfer set */    
        uDMAChannelTransferSet(UDMA_CHANNEL_SW | UDMA_PRI_SELECT, UDMA_MODE_AUTO, source_buffer, destination_buffer, BUFFER_SIZE);
        
    /* clear any previous pending int */    
        uDMAIntClear(UDMA_CHANNEL_SW);
        uDMAErrorStatusClear();
        
        uDMAIntRegister(UDMA_INT_SW , isr_dma_sw);
        uDMAIntRegister(UDMA_INT_ERR , isr_dma_err);
        
    /* enable channel */    
        uDMAChannelEnable(UDMA_CHANNEL_SW);
        
    /* set request to start dma */    
        uDMAChannelRequest(UDMA_CHANNEL_SW);
        
    /* query: how will dma tke over memory bus, since CPU is also accesiing ram here? */    
        while((0U == g_dma_err) && (0U == g_dma_complete) && (0U == g_dma_unknown_err));
        
        while(1);
    }

  • Hello VT

    1. I have written code for software transfer as below. Is it the correct sequence.
    > If you can run the code on your TM4C123 LPad and get the expected results, then it must be fine.

    2. Can the buffer size(in code below) be of any size 1 to 1024, or it has to be in binary step size only like 1,2,4,...1024. I have tried value 3 , code worked.
    > The Transfer Size can be any value between 1-1024

    3. Is there no need to select uDMAChannelAttributeEnable for SW DMA??
    > No.

    4. What is RAM stripping.
    > It is an on-chip RAM organization which is transparent to the user.

    5. Plz correct me if I am wrong here. After uDMAChannelRequest(UDMA_CHANNEL_SW); I think there are three ways DMA will exit. I have made a interrupt for it. Either it will go isr_dma_sw() or isr_dma_err(). In isr_dma_sw(), indication will be given whether it is complete or unknown error, while in isr_dma_err() there is error. In case of these two error, is there any extra action to require to reset DMA, or reconfiguring it will do the job. I have placed while((0U == g_dma_err) && (0U == g_dma_complete) && (0U == g_dma_unknown_err)); I think any of three condition will end the DMA operation.
    > Most of the errors that I have seen is due to an access to incorrect or un-configured address space. So the action of the program must be ensure to ensure that such

    6. There is only one isr for DMA. Suppose I have two DMA working together. How will I know which has generated interrupt?
    > There is only one DMA. So at any given time there would be only one channel being processed and one interrupt.

    7. Hi Amit you had said "CPU and DMA can work on the same bus, and the bus arbitration will ensure that the highest priority goes to CPU." In this example I never put CPU to sleep. After first 8 transfer DMA(UDMA_ARB_8), CPU will gets the bus since it is not in sleep.
    Then it will nit return bus to DMA since it never leaves the bus. So how it works here?
    > That is not true. When performing access the CPU does not present address on every access and allows the DMA to cycle steal. ALso the arbitration between CPU and DMA are on the peripheral access and SRAM address, which will not be 100% monopolized by the CPU.

    Regards
    Amit
  • Hi Amit,

    1. Attached is one of example code I found. It from TI. Line 180 & 181 shows two DMA together enabled, is it correct?

    2. You had said "When performing access the CPU does not present address on every access and allows the DMA to cycle steal. ALso the arbitration between CPU and DMA are on the peripheral access and SRAM address, which will not be 100% monopolized by the CPU"

    What I got here is, there is two times DMA can access RAM. One when CPU does not present address on access & second in between CPU access, DMA get access to bus also, but it is random?

    // Lab12b - UART / DMA Ping-Pong
    
    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_uart.h"
    #include "driverlib/fpu.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    
    // Buffer definitions
    #define UART_TXBUF_SIZE         256
    #define UART_RXBUF_SIZE         256
    static uint8_t g_pui8TxBuf[UART_TXBUF_SIZE];
    static uint8_t g_pui8RxPing[UART_RXBUF_SIZE];
    static uint8_t g_pui8RxPong[UART_RXBUF_SIZE];
    
    // Error counter
    static uint32_t g_ui32DMAErrCount = 0;
    
    // Transfer counters
    static uint32_t g_ui32RxPingCount = 0;
    static uint32_t g_ui32RxPongCount = 0;
    
    // uDMA control table aligned to 1024-byte boundary
    #pragma DATA_ALIGN(ucControlTable, 1024)
    uint8_t ucControlTable[1024];
    
    // Library error routine
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    // uDMA error handler
    void
    uDMAErrorHandler(void)
    {
        uint32_t ui32Status;
    
        ui32Status = ROM_uDMAErrorStatusGet();
    
        if(ui32Status)
        {
            ROM_uDMAErrorStatusClear();
            g_ui32DMAErrCount++;
        }
    }
    
    // UART interrupt handler. Called on completion of uDMA transfer
    void
    UART1IntHandler(void)
    {
        uint32_t ui32Status;
        uint32_t ui32Mode;
    
        ui32Status = ROM_UARTIntStatus(UART1_BASE, 1);
    
        ROM_UARTIntClear(UART1_BASE, ui32Status);
    
        ui32Mode = ROM_uDMAChannelModeGet(UDMA_CHANNEL_UART1RX | UDMA_PRI_SELECT);
    
        if(ui32Mode == UDMA_MODE_STOP)
        {
            g_ui32RxPingCount++;
    
            ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART1RX | UDMA_PRI_SELECT,
                                       UDMA_MODE_PINGPONG,
                                       (void *)(UART1_BASE + UART_O_DR),
                                       g_pui8RxPing, sizeof(g_pui8RxPing));
        }
    
        ui32Mode = ROM_uDMAChannelModeGet(UDMA_CHANNEL_UART1RX | UDMA_ALT_SELECT);
    
        if(ui32Mode == UDMA_MODE_STOP)
        {
            g_ui32RxPongCount++;
    
            ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART1RX | UDMA_ALT_SELECT,
                                       UDMA_MODE_PINGPONG,
                                       (void *)(UART1_BASE + UART_O_DR),
                                       g_pui8RxPong, sizeof(g_pui8RxPong));
        }
    
        if(!ROM_uDMAChannelIsEnabled(UDMA_CHANNEL_UART1TX))
        {
    
        	ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART1TX | UDMA_PRI_SELECT,
                                       UDMA_MODE_BASIC, g_pui8TxBuf,
                                       (void *)(UART1_BASE + UART_O_DR),
                                       sizeof(g_pui8TxBuf));
    
            ROM_uDMAChannelEnable(UDMA_CHANNEL_UART1TX);
        }
    }
    
    // Initialize UART uDMA transfer
    void
    InitUART1Transfer(void)
    {
        uint32_t ui32Idx;
    
        // Initialize transmit buffer with some data
        for(ui32Idx = 0; ui32Idx < UART_TXBUF_SIZE; ui32Idx++)
        {
            g_pui8TxBuf[ui32Idx] = ui32Idx;
        }
    
        // Enable UART1 and make sure it can run while the CPU sleeps
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
        ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART1);
    
        // Configure and enable the UART with DMA
        ROM_UARTConfigSetExpClk(UART1_BASE, ROM_SysCtlClockGet(), 115200,
                                UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                                UART_CONFIG_PAR_NONE);
    
        ROM_UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
    
        ROM_UARTEnable(UART1_BASE);
        ROM_UARTDMAEnable(UART1_BASE, UART_DMA_RX | UART_DMA_TX);
    
        	// Loopback mode
        HWREG(UART1_BASE + UART_O_CTL) |= UART_CTL_LBE;
    
        ROM_IntEnable(INT_UART1);
    
        // Receive channel setup for ping and pong
        ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART1RX,
                                        UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
                                        UDMA_ATTR_HIGH_PRIORITY |
                                        UDMA_ATTR_REQMASK);
    
        ROM_uDMAChannelControlSet(UDMA_CHANNEL_UART1RX | UDMA_PRI_SELECT,
                                  UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
                                  UDMA_ARB_4);
    
        ROM_uDMAChannelControlSet(UDMA_CHANNEL_UART1RX | UDMA_ALT_SELECT,
                                  UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
                                  UDMA_ARB_4);
    
        ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART1RX | UDMA_PRI_SELECT,
                                   UDMA_MODE_PINGPONG,
                                   (void *)(UART1_BASE + UART_O_DR),
                                   g_pui8RxPing, sizeof(g_pui8RxPing));
    
        ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART1RX | UDMA_ALT_SELECT,
                                   UDMA_MODE_PINGPONG,
                                   (void *)(UART1_BASE + UART_O_DR),
                                   g_pui8RxPong, sizeof(g_pui8RxPong));
    
        // Transmit channel setup for a basic transfer
        ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART1TX,
                                        UDMA_ATTR_ALTSELECT |
                                        UDMA_ATTR_HIGH_PRIORITY |
                                        UDMA_ATTR_REQMASK);
    
        ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_UART1TX, UDMA_ATTR_USEBURST);
    
    
        ROM_uDMAChannelControlSet(UDMA_CHANNEL_UART1TX | UDMA_PRI_SELECT,
                                  UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
                                  UDMA_ARB_4);
    
        ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART1TX | UDMA_PRI_SELECT,
                                   UDMA_MODE_BASIC, g_pui8TxBuf,
                                   (void *)(UART1_BASE + UART_O_DR),
                                   sizeof(g_pui8TxBuf));
    
        // Enable both channels
        ROM_uDMAChannelEnable(UDMA_CHANNEL_UART1RX);
        ROM_uDMAChannelEnable(UDMA_CHANNEL_UART1TX);
    }
    
    // main code
    int
    main(void)
    {
        volatile uint32_t ui32Loop;
    
    
        ROM_FPULazyStackingEnable();
    
        ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                           SYSCTL_XTAL_16MHZ);
    
        ROM_SysCtlPeripheralClockGating(true);
    
        // GPIO setup for LEDs
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
        ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);
    
        // GPIO setup for UART
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART0);
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        // Enable uDMA
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
        ROM_SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA);
        ROM_IntEnable(INT_UDMAERR);
        ROM_uDMAEnable();
        ROM_uDMAControlBaseSet(ucControlTable);
    
        // Initialize the uDMA/UART transfer
        InitUART1Transfer();
    
        // Blink the LED while the transfers occur
        while(1)
        {
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
    
            SysCtlDelay(SysCtlClockGet() / 20 / 3);
    
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
    
            SysCtlDelay(SysCtlClockGet() / 20 / 3);
        }
    }
    

  • Hello VT,

    1. Attached is one of example code I found. It from TI. Line 180 & 181 shows two DMA together enabled, is it correct?
    > Yes it is correct. The channels are enabled. But note that enabling a channel does not mean it will transfer immediately. Only if the peripheral request is there will a transfer happen on an enabled channel.

    2. You had said "When performing access the CPU does not present address on every access and allows the DMA to cycle steal. ALso the arbitration between CPU and DMA are on the peripheral access and SRAM address, which will not be 100% monopolized by the CPU"
    > Yes, that is correct as well

    What I got here is, there is two times DMA can access RAM. One when CPU does not present address on access & second in between CPU access, DMA get access to bus also, but it is random?
    > Depends on the code structure. It may be deterministic.

    Regards
    Amit
  • btw, the access is public again ;)