S32K144 DMA modulo

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

S32K144 DMA modulo

Jump to solution
2,733 Views
arkadyosh
Contributor III

Hello,
I have problem with DMA and FTM. I used this combination to catch data from FTM registers to struct when signal on input pin changed(falling and rising edge). From time to time, very rarely, I have situation when I get interrupt from DMA but no data inside struct which is destination of DMA transfer. I cant't find scenario to reproduce it in release, but in debug mode with connected JLink when I pause the code, when signal on input pin is still changing, and resume it, problem arrives. DMA destination configured with modulo option. When I used configuration without circular buffer(modulo for destination) I can stop and pause code as many times as I want and this never happened.

Below some sample code. Any solution where is the problem?

#define CHANNELS_NO (8)

#define DMA_EDGE_MAX_COUNT (4)

typedef struct
{

struct
{
uint32_t cnsc;
uint32_t cnv;
}values[DMA_EDGE_MAX_COUNT ];
}str_data_ctrl;

volatile str_data_ctrl data_ctrl[CHANNELS_NO] __attribute__((aligned(32)));

void test_Init(void)
{

PORTC->PCR[15] &= ~PORT_PCR_MUX_MASK;
PORTC->PCR[15] |= PORT_PCR_MUX(2); // FTM1 CH3

// DMA configuration

DMAMUX->CHCFG[2] = 0;
DMAMUX->CHCFG[2] |= DMAMUX_CHCFG_SOURCE(EDMA_REQ_FTM1_CHANNEL_3);

DMAMUX->CHCFG[2] |= DMAMUX_CHCFG_ENBL_MASK;


DMA->TCD[2].SADDR = DMA_TCD_SADDR_SADDR(&(FTM1->CONTROLS[3].CnSC));
DMA->TCD[2].SOFF = DMA_TCD_SOFF_SOFF(1); // Src addr add x byte after transfer
DMA->TCD[2].ATTR = DMA_TCD_ATTR_SMOD(0) | // Src modulo feature not used
DMA_TCD_ATTR_SSIZE(0) | // Src read 8bit
DMA_TCD_ATTR_DMOD(5) | // Dest modulo feature not used
DMA_TCD_ATTR_DSIZE(0); // Dest 8bit
DMA->TCD[2].NBYTES.MLNO = DMA_TCD_NBYTES_MLNO_NBYTES(8); // Transfer N byte /minor loop
DMA->TCD[2].SLAST = DMA_TCD_SLAST_SLAST(-8); // Src addr change after major loop
DMA->TCD[2].DADDR = DMA_TCD_DADDR_DADDR(&data_ctrl[2].values[0].cnsc);// Dest.
DMA->TCD[2].DOFF = DMA_TCD_DOFF_DOFF(1); // dest adr offset after transfer
DMA->TCD[2].CITER.ELINKNO = DMA_TCD_CITER_ELINKNO_CITER(1) | // n minor loop iterations
DMA_TCD_CITER_ELINKNO_ELINK(0); // No minor loop chan link
DMA->TCD[2].DLASTSGA = DMA_TCD_DLASTSGA_DLASTSGA(0); // dest chg after major loop
DMA->TCD[2].CSR = DMA_TCD_CSR_START(0) | // Clear START status flag
DMA_TCD_CSR_INTMAJOR(1) | // IRQ after major loop
DMA_TCD_CSR_INTHALF(0) | // No IRQ after 1/2 major loop
DMA_TCD_CSR_DREQ(0) | // Disable chan after major loop
DMA_TCD_CSR_ESG(0) | // Disable Scatter Gather
DMA_TCD_CSR_MAJORELINK(0) | // No major loop chan link
DMA_TCD_CSR_ACTIVE(0) | // Clear ACTIVE status flag
DMA_TCD_CSR_DONE(0) | // Clear DONE status flag
DMA_TCD_CSR_MAJORLINKCH(0) | // Chan # if major loop ch link
DMA_TCD_CSR_BWC(0); // No eDMA stalls after R/W

DMA->TCD[2].BITER.ELINKNO = DMA_TCD_BITER_ELINKNO_BITER(1) | // Initial iteration count
DMA_TCD_BITER_ELINKNO_ELINK(0); // No minor loop chan link

DMA->SERQ = DMA_SERQ_SERQ(2); // start dma
DMA->SEEI = DMA_SEEI_SEEI(2); // enable error interrupt for channel

INT_SYS_ClearPending(DMA0_IRQn + 2);
INT_SYS_EnableIRQ(DMA0_IRQn + 2);

// TIMER configuration

FTM1->MODE |= FTM_MODE_WPDIS_MASK; // disable wr protect

FTM1->MODE &= ~FTM_MODE_FTMEN_MASK; // disable FTM

FTM1->SYNC |= FTM_SYNC_SYNCHOM_MASK; // mask channels outputs

FTM1->MOD = 0xFFFF;

FTM1->CNTIN = 0;

FTM1->CONTROLS[3].CnSC = FTM_CnSC_CHIE(1) | // channel interrupt enable FTM_CnSC_DMA_MASK | // DMA FTM_CnSC_MSB(0) | // mode select

FTM_CnSC_MSA(0) | // mode select

FTM_CnSC_ELSB(1) | // edge/level select

FTM_CnSC_ELSA(1);// edge/level select

FTM1->CONTROLS[3].CnV = 0;

FTM1->CNT = 0;

FTM1->SYNC &= ~FTM_SYNC_SYNCHOM_MASK; // mask channels outputs
FTM1->MODE |= FTM_MODE_FTMEN_MASK | FTM_MODE_FAULTM(2) | FTM_MODE_FAULTIE_MASK;
FTM1->SC = FTM_SC_TOIE(1) | // overflow interrupt enable
FTM_SC_PS(3); // prescaler = 8
FTM1->SC |= FTM_SC_CLKS(3);

INT_SYS_ClearPending(FTM1_Fault_IRQn);
INT_SYS_EnableIRQ(FTM1_Fault_IRQn);

INT_SYS_ClearPending(FTM1_Ch2_Ch3_IRQn);

INT_SYS_EnableIRQ(FTM1_Ch2_Ch3_IRQn);

}

void DMA2_IRQHandler(void)
{
DMA->CINT = DMA_CINT_CINT(2); // clear int.

int cnsc_cnt = 0;
int cnt = 0;
while(cnt < DMA_EDGE_MAX_COUNT )
{
if( data_ctrl[2].values[cnt].cnsc > 0)
{
cnsc_cnt++;
if( (data_ctrl[2].values[cnt].cnsc & FTM_CnSC_CHIS_MASK) != FTM_CnSC_CHIS_MASK) // falling edge
{
// do something
}
else // rising edge
{
// do something
}
}
data_ctrl[2].values[cnt].cnsc = 0;
data_ctrl[2].values[cnt].cnv = 0;
cnt++;
}

if(cnsc_cnt == 0)
PIN_TOGGLE; // this should never happen
}

Tags (5)
0 Kudos
1 Solution
2,476 Views
arkadyosh
Contributor III

Hello Daniel,

Thanks for your response. Today I found some race condition in my firmware which cause problem. I resolved this and everything works fine, theres no problem with DMA.

I want to have 1 minor loop(8 bytes) for each edge that is detected and trigger DMA interrupt after major loop. Source and destination addresses are offset and source address is change after each major loop(-8 bytes) but destination address is limited by modulo. This solution give me circular buffer for values from CnSC and CnVal registers. I achieved this with configuration from my first message.

BR, Arkadyosh

View solution in original post

0 Kudos
2 Replies
2,477 Views
arkadyosh
Contributor III

Hello Daniel,

Thanks for your response. Today I found some race condition in my firmware which cause problem. I resolved this and everything works fine, theres no problem with DMA.

I want to have 1 minor loop(8 bytes) for each edge that is detected and trigger DMA interrupt after major loop. Source and destination addresses are offset and source address is change after each major loop(-8 bytes) but destination address is limited by modulo. This solution give me circular buffer for values from CnSC and CnVal registers. I achieved this with configuration from my first message.

BR, Arkadyosh

0 Kudos
2,476 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hello arkadyosh,

Sorry for the delayed response.

If I understand, you want to have 4 minor loops (8bytes) for each edge that is detected and trigger DMA interrupt on the major loop complete only. The destination address would be offset after each minor loop whereas the source address would stay the same.

But I don't understand how you want to achieve that with the DMA configuration you posted.  

You have: CITER(1) one minor cycle, SMOD(0) without source modulo, DMOD(5) destination modulo,

It could be possible with this configuration:

DMA_TCD_ATTR_SMOD(3)

DMA_TCD_ATTR_SSIZE(2) 
DMA_TCD_ATTR_DMOD(0) 
DMA_TCD_ATTR_DSIZE(2) 

DMA_TCD_SOFF_SOFF(4);       
DMA_TCD_SLAST_SLAST(0);    

DMA_TCD_DOFF_DOFF(4);      

DMA_TCD_DLASTSGA_DLASTSGA(-32);    

DMA_TCD_NBYTES_MLNO_NBYTES(8);     
DMA_TCD_CITER_ELINKNO_CITER(4);     
DMA_TCD_BITER_ELINKNO_BITER(4); 

DMA_TCD_CSR_INTMAJOR(1); 

Where the modulo (8 bytes) is used on the source address.

The problem is that FTM1->CONTROLS[3].CnSC address is not aligned (0x40039024).

So, SMOD(3) = 8 bytes is not possible.

I think you could aligned the source address to 0x40039020 and read 16bytes per a minor loop with SMOD(4) 16bytes modulo.

BR, Daniel

0 Kudos