AnsweredAssumed Answered

CTIMER DMA requests/triggers, pacing the transfers

Question asked by Hemanth M on Jun 24, 2020
Latest reply on Aug 2, 2020 by Diego Charles

I got a DMA0 channel to successfully trigger from a CTIMER0 trigger source. The setup is as follows (using a custom board, not EVAL):

 

1. CTIMER0 with match1, with a capture input as source (10 MHz clock). Also successfully outputs to an External match output as a 5 MHz square wave by matching for 1 and resetting after.

ctimer_config_t config;
CTIMER_GetDefaultConfig(&config);
config.mode = kCTIMER_IncreaseOnRiseEdge;
config.input = kCTIMER_Capture_0;
config.prescale = 1;
CTIMER_Init(CTIMER0, &config);

 

ctimer_match_config_t matchConfig;
matchConfig.enableCounterReset = true;
matchConfig.enableCounterStop = false;
matchConfig.matchValue = 1;
matchConfig.outControl = kCTIMER_Output_Toggle;
matchConfig.outPinInitState = true;
matchConfig.enableInterrupt = false;
CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_1, &matchConfig);
CTIMER_StartTimer(CTIMER0);

 

2. INPUTMUX configured to connect CTIMER0's Match1 trigger to DMA0's channel 0.

INPUTMUX_AttachSignal(INPUTMUX, 0, kINPUTMUX_Ctimer0M1ToDma0);

 

3. Setup DMA0's channel 0 with the following config:

dma_channel_trigger_t channelTrigger;
channelTrigger.type = kDMA_HighLevelTrigger; //or kDMA_RisingEdgeTrigger
channelTrigger.burst = kDMA_LevelBurstTransfer; //or kDMA_EdgeBurstTransfer1
channelTrigger.wrap = kDMA_NoWrap;

 

dma_channel_config_t transferConfig;
DMA_Init(DMA0);
DMA_CreateHandle(&g_DMA_Handle, DMA0, 0);
DMA_EnableChannel(DMA0, 0);
DMA_SetCallback(&g_DMA_Handle, DMA_Callback, NULL);

DMA_PrepareChannelTransfer(&transferConfig, s_srcBuffer, (uint32_t*)&GPIO->MPIN[0],
   DMA_CHANNEL_XFER(false, false, true, false, kDMA_Transfer32BitWidth,
   kDMA_AddressInterleave1xWidth, kDMA_AddressInterleave0xWidth, 4000),
   kDMA_MemoryToMemory, &channelTrigger, NULL);

DMA_SubmitChannelTransfer(&g_DMA_Handle, &transferConfig);
DMA_StartTransfer(&g_DMA_Handle);

 

The DMA is supposed to push parallel data from memory to the GPIO's MPIN port, with the register updated once every CTIMER/match1 trigger. But the config that worked seems to go through all the transfers (1000) without pausing between triggers. Increasing the matchValue or slowing the input clock seems to have no effect (the output square wave is behaving as expected).

 

Some observations/questions:

Q. kDMA_RisingEdgeTrigger doesn't seem to work at all for this type of hardware trigger (CTIMER's). The DMA transfer never starts. Also tried:

CTIMER0->IR = 0xFF;

to clear any active DMA request (UM mentions it clears both interrupts and DMA requests) to create a new rising edge but nothing. Any reason why? kDMA_LowLevelTrigger too doesn't working so I am assuming the DMA trigger is never going low? Is there some bit to be set to make the DMA controller clear the request after every trigger?

 

Q. Setting PERIPHREQEN (with kDMA_MemoryToPeripheral) too stops it from triggering. This might not be relevant as we aren't interfacing with any peripheral and is simply a MemoryToMemory transfer. But UM mentions:

"Once triggered, transfer on a channel will be paced by DMA requests if the
PERIPHREQEN bit in the related CFG register is set. Otherwise, the transfer will proceed
at full speed."

while the DMA transfer config mentions the pacing can be controlled by the hardware triggers using trigger burst settings. Are the trigger burst settings referring to DMA triggers or DMA requests as mentioned under peripherals?

 

Q. CTIMER's section in the UM mentions DMA requests while the INPUTMUX for DMA mentions them to be DMA triggers, not requests. The wording is really confusing at times. This makes it slightly clear:

Up to two match registers can be used to generate DMA requests. These are
connected to DMA trigger inputs on this device.

But it is still confusing as something like the SPI interface mentions that they generate DMA requests AND are connected to DMA controllers requests "port".

 

Q. For any general application requiring memory-to-memory DMA transfer, what other method do I have to pace the transfers to an internal counter? Are requests the only way to pace DMA transfers? Is there any advantage to using SCTimers rather CTIMERS for this?

 

EDIT:

It seems to pace the requests properly when I use:

channelTrigger.type = kDMA_HighLevelTrigger;
channelTrigger.burst = kDMA_SingleTransfer;
channelTrigger.wrap = kDMA_NoWrap;

So kDMA_LevelBurstTransfer was the problem before.  The clocking seems to be perfect now,  but it still doesn't work with EdgeTrigger. Now to make a gapless/callback-free ping-pong transfer for this...

Outcomes