CTIMER DMA requests/triggers, pacing the transfers

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

CTIMER DMA requests/triggers, pacing the transfers

1,861 Views
hemanth1
Contributor I

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...

Labels (1)
Tags (1)
4 Replies

1,597 Views
nick_ijzendoorn
Contributor II

Is it possible to output a PWM wave-form via DMA to a CTIMER->MR[] register? Where the DMA transfer is triggered by a CTIMER Match channel?

I Have the same problem Hemanth M is having. Where the complete transfer is done full speed even though I use kDMA_SingleTransfer as burst type.

ctimer_match_config_t matchConfigDMA;
matchConfigDMA.enableCounterReset = false;
matchConfigDMA.enableCounterStop = false;
matchConfigDMA.matchValue = 0;
matchConfigDMA.outControl = kCTIMER_Output_Toggle;
matchConfigDMA.outPinInitState = false;
matchConfigDMA.enableInterrupt = false;

// init first timer
ctimer_config_t config;
CTIMER_GetDefaultConfig(&config);
timerClock = CTIMERA_CLK_FREQ / (config.prescale + 1);
CTIMER_Init(CTIMER2, &config);
CTIMER_SetupPwm(CTIMER2, kCTIMER_Match_0, sineLookupTable[counta], PWM_FREQUENCY, timerClock, false);
CTIMER_SetupMatch(CTIMER2, kCTIMER_Match_1, &matchConfigDMA);

//** Attach CTimer Match request to DMA0**//
INPUTMUX_Init(INPUTMUX);
INPUTMUX_AttachSignal(INPUTMUX, 0, kINPUTMUX_Ctimer2M1ToDma0);

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


dma_channel_trigger_t channelTrigger;
channelTrigger.burst = kDMA_SingleTransfer ;

/** select channel trigger **/
channelTrigger.type = kDMA_HighLevelTrigger ; /// a word transfer each CMTIMER match trigger

//** A complete transfer when a sigle trigger configuration */
//channelTrigger.type = kDMA_NoTrigger ;
//channelTrigger.type = kDMA_LowLevelTrigger;
//channelTrigger.type = kDMA_FallingEdgeTrigger;
//channelTrigger.type = kDMA_RisingEdgeTrigger;

channelTrigger.wrap = kDMA_NoWrap; // Here I would like to enable SrcWrapping as well, when I do this however it starts incrementing the dst address... I'm assuming SrcWrapping makes the dma transfer contiguous.

dma_channel_config_t transferConfig;


DMA_PrepareChannelTransfer(&transferConfig, dmaOutputTableA, &CTIMER2->MR[0],
        DMA_CHANNEL_XFER(false, false, true, false, 2, kDMA_AddressInterleave1xWidth, kDMA_AddressInterleave0xWidth, (SINESTEPS*2)),
        kDMA_MemoryToMemory, &channelTrigger, NULL);

DMA_SubmitChannelTransfer(&g_DMA_Handle, &transferConfig);

DMA_StartTransfer(&g_DMA_Handle);

// create the DMA buffers
for (int idx = 0; idx < SINESTEPS; ++idx)
{
    dmaOutputTableA[idx] = (CTIMER2->MR[kCTIMER_Match_3] * (uint32_t)sineLookupTable[idx]) / 100U;
}

CTIMER_StartTimer(CTIMER2);

0 Kudos

1,597 Views
ie709758
Contributor I

Hi Nick,

 

To  debug the issue ,  I could recommend you to :

  • Enable Ctimer Match interrupts  and place a breakpoint into the CTimer Match Interrupt handler. That way you can check the value that is being writtten on the destination register  on each  CTIMER match.

  • Try to not configure  CTimer2 Match 0 for PWM  (avoid calling CTIMER_SetupPwm ), and verify that Ctimer Match 1 could generate  single word DMA transfer  on each match (using kDMA_HighLevelTrigger and kDMA_SingleTransfer  ) .

  • Check your CTimer2 Match1 configuration . For example, you could try to change  .matchValue from  0  to  CTIMER_CLK_FREQ/2  and  .enableCounterReset from  FALSE to TRUE.

As far I understand your application, it seems feasible from my point of view. However,  I do not have a similar example to share.

 

Regards,

Diego

0 Kudos

1,597 Views
nick_ijzendoorn
Contributor II

Thanks for your reply, I made the wrong assumption the problem was that I tried to do 16bit word transfers instead of 32bit.

For people who are interrested I included my main file which is now working.

The CTIMERs must be clocked at 150Mhz for the frequency calculation to go correctly.

For testing I connected a low pass RC filter to the PWM output pins.

Enabling the macro's USE_16BIT and/or USE_SRC_WRAPPING will break it though.

1,597 Views
diego_charles
NXP TechSupport
NXP TechSupport

Hi Hemanth :smileyhappy:,

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?

I have tried to replicate your issue. However the  high or low level triggers worked with my CTIMER and DMA  configurations. A single CTMER match triggered a DMA transfer  at full speed. 

I attached my code just  as a reference, I can not guarantee it will also work  for you.

 

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?

 

Since the CTIMER match is connected to the DMA channel interrupt input, After doing tests, I confirm your comments. After setting PERIPHREQEN, the DMA channel will wait a signal on its request input, if don´t arrives the trasnfer will not start.

The TRIGBURST bit on the CFG register of DMA channels, refers to the DMA hardware triggers configured on level trigger mode.

 

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".

Taking into account that each DMA channel have interrupt and request inputs.

 No matter if the CTIMER/SCtimer match request are mentioned as a DMA requests , they  are connected to the  DMA channels interrupt input using INPUTMUX registers.

For any general application requiring memory-to-memory DMA transfer, what other method do I have to pace the transfers to an internal counter?

As you may already considered, you  could enable the interrupt for a TIMER and Configure the DMA with SW trigger.  Then in the Timer  ISR trigger a DMA transfer. However using the timer directly trigger by hardware seems more efficient.

Are requests the only way to pace DMA transfers? Is there any advantage to using SCTimers rather CTIMERS for this?

Well, since they can help you to trigger DMA transfers, we would have to check which characteristics of both can be worthy for your application.

From the DMA point of view I am thinking on that the CTIMER has more trigger options ( 9 ) than the SCTIMER  (2 ) as seen in the register DMA0_ITRIG_INMUX register.

If   this helps, you could let me know.

Also,  I apologize for the delay on my response.

Regards,

Diego.

0 Kudos