Problem with DMA increment Burst (more than 1 burst) for destination peripheral (SOLVED)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Problem with DMA increment Burst (more than 1 burst) for destination peripheral (SOLVED)

1,367 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by andersonsouza.eng on Sat Aug 15 12:15:52 MST 2015
Hello everyone, is my first publication here. So if i make some mistake with rules, please tell me.
And sorry for my weak english.

The board I'm using is the LPC-Link2, with a LPC4370.

My problem is with DMA, I'm trying to make a burst from a source array variable to 4 MATCHREL registers (SCT Peripheral).
The idea is simple, read the source array and do a burst of 4 words for the 4 MATHCREL, doing this continuasly, until the source ends.
Very similar, with read the ADC fifo, for example, but is the inverse. Instead of read the fifo and out into array, I want read a array and write in to the MATCHREL Registers.

The problem occours with use the burst and the address increment of the destination.

For example, in ADC, to read the FIFO I can do:
SRCADDR = (uint32_t) &LPC_ADCHS->FIFO_OUTPUT[0];
DSTADDR = (uint32_t) &dest;
CONTROL = (8 & 0xFFF) //Number of Transfers (2 Transfers with 4 bursts of 32 bits)
 |0x1 << 12 //burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
 |0x1 << 15
 |0x2 << 18 //Transfer width 0=8bits, 1=16bits, 2=32bits
 |0x2 << 21 //Transfer width 0=8bits, 1=16bits, 2=32bits
 |0x1 << 24 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
 |0x0 << 25 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
 |0x0 << 26 // source not increment
|0x1 << 27 // dest increment
|0x0 << 31; //Disable Interrupt

After running the DMA, the resut is:
dest[0] = FIFO_OUTPUT[0]; <1 BURST REQUEST
dest[1] = FIFO_OUTPUT[1];
dest[2] = FIFO_OUTPUT[2];
dest[3] = FIFO_OUTPUT[3];
dest[4] = FIFO_OUTPUT[0]; <2 BURST REQUEST
dest[5] = FIFO_OUTPUT[1];
dest[6] = FIFO_OUTPUT[2];
dest[7] = FIFO_OUTPUT[3];
That is right, repair that the source increment is disabled, but the burst makes the DMA read FIFO_OUTPUT from [0] to [4], and come back to FIFO_OUTPUT[0] on the second Burst Request.

So, I try to do the same for write in the MATCHREL registers from SCT peripheral.
SRCADDR = (uint32_t) &source;
DSTADDR = (uint32_t) &LPC_SCT->MATCHREL;
CONTROL = (8 & 0xFFF) //Number of Transfers (2 Transfers with 4 bursts of 32 bits)
  |0x1 << 12 //burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
 |0x1 << 15
  |0x2 << 18 //Transfer width 0=8bits, 1=16bits, 2=32bits
 |0x2 << 21 //Transfer width 0=8bits, 1=16bits, 2=32bits
 |0x0 << 24 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
 |0x1 << 25 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
 |0x1 << 26 // source increment
 |0x0 << 27 // dest not increment
 |0x0 << 31; //Disable Interrupt

That results in:
MATCHREL[0] = source[3]; <- 1 BURST REQUEST
MATCHREL[1] = 0; //not changed
MATCHREL[2] = 0; //not changed
MATCHREL[3] = 0; //not changed

MATCHREL[0] = source[7]; <- 2 BURST REQUEST
MATCHREL[1] = 0; //not changed
MATCHREL[2] = 0; //not changed
MATCHREL[3] = 0; //not changed

The DMA doesn't increment the address for the destination, but the burst is set for 4 Words.
Why this is different from the ADC example ? Where the source should not increment, but increment with the Burst configuration.

If this doesn't work, the solution that I can think is to use a linked list (LLI) with destAddress aways set to MATCHREL[0] and increment the destination. This is really the best way to do that ? It will costs a lot of memory in my application.

Thanks,
I hope someone can help me


//------------------------------------------------------------------------------------------------------------------------------------------------------------------------//

Here is the code that I use to make this tests, it uses a software burst request (SOFTBREQ) to control when the request will ocour:
#include "board.h"

//LPC_CREG->DMAMUX
#define DMAMUX_PER10_SCT_DMA_R0 (2 << 20)// Selects DMA to peripheral connection for DMA peripheral 10.

//LPC_GPDMA->CH[0].CONTROL
#define TRANSFER_SIZE(0x04 << 0) // A write to this field sets the size of the transfer when the DMA Controller is the flow controller.
#defineSRC_BURST_SIZE_1(0x00 << 12) // Source burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
#defineSRC_BURST_SIZE_4(0x01 << 12) // Source burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
#defineSRC_BURST_SIZE_8(0x02 << 12) // Source burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256

#defineDST_BURST_SIZE_1(0x00 << 15) // Destination burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
#defineDST_BURST_SIZE_4(0x01 << 15) // Destination burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
#defineDST_BURST_SIZE_8(0x02 << 15) // Destination burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256

#define SRC_WIDTH_8(0x00 << 18) // Byte (8-bit)
#define SRC_WIDTH_16(0x01 << 18) // HalfWord (16-bit)
#define SRC_WIDTH_32(0x02 << 18) // Word (32-bit)
#define DST_WIDTH_8(0x00 << 21) // Byte (8-bit)
#define DST_WIDTH_16(0x01 << 21) // halfWord (16-bit)
#define DST_WIDTH_32(0x02 << 21) // Word (32-bit)
 //Remark: Only Master1 can access a peripheral. Master0 can only access memory.
#define SRC_USE_AHB_0(0x00 << 24) // AHB Master 0 selected for source transfer.
#define SRC_USE_AHB_1(0x01 << 24) // AHB Master 1 selected for source transfer.
#define DST_USE_AHB_0(0x00 << 25) // AHB Master 0 selected for destination transfer.
#define DST_USE_AHB_1(0x01 << 25) // AHB Master 1 selected for destination transfer.
#define SRC_INCREMENT_ENABLE(1 << 26) // The source address is incremented after each transfer
#define SRC_INCREMENT_DISABLE(0 << 26) // The source address is not incremented after each transfer
#define DST_INCREMENT_ENABLE(1 << 27) // The destination address is incremented after each transfer
#define DST_INCREMENT_DISABLE(0 << 27) // The destination address is not incremented after each transfer
#define TERMINALCT_INT_DISABLE  (0 << 31) // The terminal count interrupt is disabled
#define TERMINALCT_INT_ENABLE  (1 << 31) // The terminal count interrupt is enabled

//LPC_GPDMA->CH[0].CONFIG
#define DMA_ENABLE(1 << 0)
#define DMA_DISABLE(0 << 0)
#define SRCPERIPHERAL_SCT_DMA_R0(0x0A << 1)// SSP0 transmit/I2S0 DMA request 2/SCT DMA request 0
#define DESTPERIPHERAL_SCT_DMA_R0(0x0A << 6)// SSP0 transmit/I2S0 DMA request 2/SCT DMA request 0
#define FLOWCONTROL_M2P_PCTRL(0x05 << 11)// Memory to peripheral (peripheral control)
#define FLOWCONTROL_M2P_DMACTRL(0x01 << 11)// Memory to peripheral (DMA control)
#define FLOWCONTROL_M2M_DMACTRL(0x00 << 11)// Memory to peripheral (DMA control)
#define TERMINAL_COUNT_INTERRUPT_MASK_ENABLE(0x01 << 15) // When cleared, this bit masks out the terminal count interrupt of the relevant channel
#define TERMINAL_COUNT_INTERRUPT_MASK_DISABLE(0x00 << 15) // When cleared, this bit masks out the terminal count interrupt of the relevant channel
#define HALT_DISABLE(0 << 18)// Halt disable = enable DMA Request
#define HALT_ENABLE(1 << 18)// This value can be used with the Active and Channel Enable bits to cleanly disable a DMA channel


/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/

#define SOURCEARRAYSIZE (256)


/**
 * @brief GPDMA Linker List Item structure type definition
 */
typedef struct {
uint32_t SRCADDR;/**< Source Address */
uint32_t DSTADDR;/**< Destination address */
uint32_t NextLLI;/**< Next LLI address, otherwise set to '0' */
uint32_t CONTROL;/**< GPDMA Control of this LLI */
} GPDMA_LLI_Type;
static GPDMA_LLI_Type DMA_LLI_ARRAY[200];

/* Source and destination DMA areas */
static uint32_t source[SOURCEARRAYSIZE], dest[SOURCEARRAYSIZE];

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/



/*****************************************************************************
 * Private functions
 ****************************************************************************/


static void setupDMATrigger(void)
{

/* Initialize GPDMA controller */
Chip_Clock_EnableOpts(CLK_MX_DMA, true, true, 1); //Start the clock of DMA

//Disable DMA
LPC_GPDMA->CONFIG &= ~1;

/* Clear all DMA interrupt and error flag */
LPC_GPDMA->INTTCCLEAR = 0xFF; //clears channel terminal count interrupt
LPC_GPDMA->INTERRCLR = 0xFF; //clears channel error interrupt.

/* Clear all DMA interrupt and error flag */
LPC_GPDMA->INTTCCLEAR = 0xFF; //clears channel terminal count interrupt
LPC_GPDMA->INTERRCLR = 0xFF; //clears channel error interrupt.


LPC_GPDMA->CH[0].SRCADDR = (uint32_t) &source;
//DMA_LLI_ARRAY[0].SRCADDR = (uint32_t) &LPC_ADCHS->FIFO_OUTPUT;
LPC_GPDMA->CH[0].DESTADDR = (uint32_t) &LPC_SCT->MATCHREL;
//DMA_LLI_ARRAY[0].DSTADDR = (uint32_t) &dest;
LPC_GPDMA->CH[0].LLI = 0;
LPC_GPDMA->CH[0].CONTROL = (8 & 0xFFF) //Number of Transfers (2 Transfers with 4 bursts of 32 bits)
    |0x1 << 12 //burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
   |0x1 << 15
    |0x2 << 18 //Transfer width 0=8bits, 1=16bits, 2=32bits
   |0x2 << 21 //Transfer width 0=8bits, 1=16bits, 2=32bits
   |0x0 << 24 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
   |0x1 << 25 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
   |0x1 << 26 // source increment
   |0x0 << 27 // dest not increment
   |0x0 << 31; //Disable Interrupt

LPC_GPDMA->CH[0].CONFIG |= DMA_ENABLE
|DESTPERIPHERAL_SCT_DMA_R0
|0x01 << 11 //Memory2Peripheral - DMA Control
|( (1 & 0x01) << 15)
|HALT_DISABLE;


/* Enable GPDMA interrupt */
NVIC_EnableIRQ(DMA_IRQn);

//Just to make easy the identification of changes
LPC_SCT->MATCHREL[0].U = (uint32_t) 0x7FFFFFFF;
LPC_SCT->MATCHREL[1].U = (uint32_t) 0x7FFFFFFF;
LPC_SCT->MATCHREL[2].U = (uint32_t) 0x7FFFFFFF;
LPC_SCT->MATCHREL[3].U = (uint32_t) 0x7FFFFFFF;
LPC_SCT->MATCHREL[4].U = (uint32_t) 0x7FFFFFFF;
LPC_SCT->MATCHREL[5].U = (uint32_t) 0x7FFFFFFF;
LPC_SCT->MATCHREL[6].U = (uint32_t) 0x7FFFFFFF;
LPC_SCT->MATCHREL[7].U = (uint32_t) 0x7FFFFFFF;

//Enable DMA
LPC_GPDMA->CONFIG |= 1;
}

/* Put some varying data in the source buffer */
static void fillSourceBuff(void)
{
int i;
static uint32_t seed;

for (i = 0; i < SOURCEARRAYSIZE; i++) {
seed = i + 1;
source = seed;
dest = 0x7FFFFFFF;
dest = i;
}
}

/*****************************************************************************
 * Public functions
 ****************************************************************************/

/**
 * @briefDMA interrupt handler
 * @returnNothing
 */
void DMA_IRQHandler(void)
{
Board_LED_Toggle(0);
LPC_SCT->MATCHREL;

if (Chip_GPDMA_Interrupt(LPC_GPDMA, 0) == SUCCESS) {
Chip_GPDMA_ClearIntPending(LPC_GPDMA, GPDMA_STATCLR_INTTC, 0);
}else{
Chip_GPDMA_ClearIntPending(LPC_GPDMA, GPDMA_STATCLR_INTERR, 0);
}
}


/**
 * @briefMain routine for DMA timer trigger example
 * @returnNothing
 */
int main(void)
{


SystemCoreClockUpdate();
Board_Init();

/* Setup source data */
fillSourceBuff();

/* Setup DMA for memory to memory transfer on timer match event */
setupDMATrigger();

/* Idle in background waiting for DMA events */
int i = 0;
while (1) {

LPC_GPDMA->SOFTBREQ = 1<<10;
i++;

LPC_GPDMA->SOFTBREQ = 1<<10;
i++;

}

return 0;
}


Original Attachment has been moved to: periph_dma_timertrig.c.zip

Labels (1)
0 Kudos
2 Replies

760 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by andersonsouza.eng on Sat Aug 15 19:36:57 MST 2015
Hello Wolfgang,


Quote: wmues
If you want to write to all match registers, you have to do a destination increment.

The only time, you can do WITHOUT increment is with a FIFO. This is why your ADC example is working without source increment.


This explain a lot !!!
Could you give a reference for that information?
I read the UM10503 User Manual (chapters about HSADC, DMA and SCT), but I didn't see that information.


Quote: wmues

Have you thought of a DMA transfer which will reload the destination address with the start value?



Genius idea !!! .. Haha ..
I followed your tip, and have worked perfectly.
The CH1 DMA has less priority and after CH0 transfers finish, the DESTADDR of the CH0 is refreshed to the original source address.
The trigger of two channels are the same.

start_address = &LPC_SCT->MATCHREL;
LPC_GPDMA->CH[1].SRCADDR = (uint32_t) &start_address;
LPC_GPDMA->CH[1].DESTADDR = (uint32_t) &(LPC_GPDMA->CH[0].DESTADDR);
LPC_GPDMA->CH[1].LLI = 0;
LPC_GPDMA->CH[1].CONTROL =  (4 & 0xFFF) //Number of Transfers (2 Transfers with 4 bursts of 32 bits)
      |0x0 << 12 //burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
    |0x0 << 15
      |0x2 << 18 //Transfer width 0=8bits, 1=16bits, 2=32bits
    |0x2 << 21 //Transfer width 0=8bits, 1=16bits, 2=32bits
     |0x0 << 24 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
    |0x1 << 25 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
    |0x0 << 26 // source not increment
    |0x0 << 27 // dest not increment
     |0x0 << 31; //Disable Interrupt
LPC_GPDMA->CH[1].CONFIG = DMA_ENABLE
     |DESTPERIPHERAL_SCT_DMA_R0
   |0x01 << 11 //Memory2Peripheral - DMA Control
   |( (1 & 0x01) << 15)
   |HALT_DISABLE;


LPC_GPDMA->CH[0].SRCADDR = (uint32_t) &source;
LPC_GPDMA->CH[0].DESTADDR = (uint32_t) &LPC_SCT->MATCHREL;
LPC_GPDMA->CH[0].LLI = 0;
LPC_GPDMA->CH[0].CONTROL = (16 & 0xFFF) //Number of Transfers (2 Transfers with 4 bursts of 32 bits)
     |0x1 << 12 //burst size 0=1, 1=4, 2=8, 3=16, 4=32, 5=64,6=128, 7=256
    |0x1 << 15
     |0x2 << 18 //Transfer width 0=8bits, 1=16bits, 2=32bits
    |0x2 << 21 //Transfer width 0=8bits, 1=16bits, 2=32bits
    |0x0 << 24 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
   |0x1 << 25 //0 - AHB_0 (Just Memory), 1 - AHB_1(Memory+Peripherals)
   |0x1 << 26 // source increment
   |0x1 << 27 // dest increment
   |0x1 << 31; //Disable Interrupt

LPC_GPDMA->CH[0].CONFIG = DMA_ENABLE
|DESTPERIPHERAL_SCT_DMA_R0
|0x01 << 11 //Memory2Peripheral - DMA Control
|( (1 & 0x01) << 15)
|HALT_DISABLE;



Thanks for the reply,
It was very helpful

Regards,
Anderson.
0 Kudos

760 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by wmues on Sat Aug 15 14:13:01 MST 2015
If you want to write to all match registers, you have to do a destination increment.

The only time, you can do WITHOUT increment is with a FIFO. This is why your ADC example is working without source increment.

Have you thought of a DMA transfer which will reload the destination address with the start value?

regards
Wolfgang


0 Kudos