CRC check with GPDMA

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Not applicable
Dear all,

I want to calculate the CRC checksum of an Array of 8464 Bytes. I already calculated it with the use of FCE, but the data Transfer was controlled by the CPU.
Now i want to improve the solution by using the DMA as CRC data Transfer Controller. For the configuration of the DMA i used the code of "gunbrown" in his post as reference. Microcontroller : XMC4500-F144K1024 AB, Infineon Hexagon Application Kit.

I think i configured something bad, here is the code:


void DMA_Config(uint32_t Addr)
{
//De-assert reset signal for GPDMA0
SET_BIT(SCU_RESET->PRCLR2, SCU_RESET_PRCLR2_DMA0RS_Pos);
//Enable GPDMA0
SET_BIT(GPDMA0->DMACFGREG, GPDMA0_DMACFGREG_DMA_EN_Pos);

//Configure DMA channel
//Write the starting source register for GPDMA0_CH0
WR_REG(GPDMA0_CH0->SAR, GPDMA0_CH_SAR_SAR_Msk, GPDMA0_CH_SAR_SAR_Pos, Addr);
//Write the destination register for GPDMA0_CH0
WR_REG(GPDMA0_CH0->DAR, GPDMA0_CH_DAR_DAR_Msk, GPDMA0_CH_DAR_DAR_Pos, (uint32_t)(FCE_KE0));

//Configure DMA Control Register
GPDMA0_CH0->CTLL = (uint32_t) 0;
GPDMA0_CH0->CTLH = (uint32_t) 0;
GPDMA0_CH0->LLP = (uint32_t) 0;

//Write one block size
WR_REG(GPDMA0_CH0->CTLH, GPDMA0_CH_CTLH_BLOCK_TS_Msk, GPDMA0_CH_CTLH_BLOCK_TS_Pos, 1);

//Configure 32 bit width for source and destination register
WR_REG(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_SRC_TR_WIDTH_Msk, GPDMA0_CH_CTLL_SRC_TR_WIDTH_Pos, 0x02);
WR_REG(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_DST_TR_WIDTH_Msk, GPDMA0_CH_CTLL_DST_TR_WIDTH_Pos, 0x02);

//Increment for source and no increment for destination register
WR_REG(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_SINC_Msk, GPDMA0_CH_CTLL_SINC_Pos, 0x00);
WR_REG(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_DINC_Msk, GPDMA0_CH_CTLL_DINC_Pos, 0x02);

//Disable Scather, Gather, and linked list pointer for both source and destination register
CLR_BIT(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_DST_SCATTER_EN_Pos);
CLR_BIT(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_SRC_GATHER_EN_Pos);
CLR_BIT(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_LLP_DST_EN_Pos);
CLR_BIT(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_LLP_SRC_EN_Pos);

//1 burst transfer for source and destination register
WR_REG(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_SRC_MSIZE_Msk, GPDMA0_CH_CTLL_SRC_MSIZE_Pos, 0x00);
WR_REG(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_DEST_MSIZE_Msk, GPDMA0_CH_CTLL_DEST_MSIZE_Pos, 0x00);

//Set transfer type for memory to memory with GPDMA as flow controller
WR_REG(GPDMA0_CH0->CTLL, GPDMA0_CH_CTLL_TT_FC_Msk, GPDMA0_CH_CTLL_TT_FC_Pos, 0x00);

//Enable GPDMA0_CH0
GPDMA0->CHENREG |= (uint32_t)
(((uint32_t) 1 << GPDMA0_CHENREG_CH_Pos) &GPDMA0_CHENREG_CH_Msk)|\
(((uint32_t) 1<< GPDMA0_CHENREG_WE_CH_Pos) &GPDMA0_CHENREG_WE_CH_Msk);
}


The data Looks like this:

const unsigned char foo[8464] = {
0x2F,0x2A,0x2A,0x0D,0x0A,0x20,0x20,0x2A,0x2A,..................


And i call the function DMA_Config and check the crcvalue like this:

DMA_Config((uint32_t) foo);
CRCValueDMA = FCE_KE0->RES;


May i ask you to help me correcting the code? I also don't really understand where should i put in the DMA configuration the BUFFER_SIZE(=8464>>2) to inform the DMA Controller when to stop incrementing the source address (last four Bytes to the FCE)? Because i tried putting BLOCK_TS=BUFFER_SIZE, but the result is not correct. I also tried to set the Transfer type to "Memory to peripheral" , but than it does not even work (i mean it gives the Initial CRC value, seems like it does writes to FCE_KE0 nothing); if i Change to Memory to Memory than it does something, but the result is wrong...

Thank you in advance for your help.
Regards, Akos
0 Likes
13 Replies
Not applicable
akosodry wrote:


//Write the destination register for GPDMA0_CH0
WR_REG(GPDMA0_CH0->DAR, GPDMA0_CH_DAR_DAR_Msk, GPDMA0_CH_DAR_DAR_Pos, (uint32_t)(FCE_KE0));

Regards, Akos


I don't have used the FCE myself, but the destination looks suspicious.
Shouldn't the destination be the "input register" (IR) of the FCE?

After all, at the end you retrieve the result from FCE_KE0->RES, so I would have expected the input to be referenced by FCE_KE0->IR0.

But as I said, I have not used the FCE myself.
Keep also in mind that since you use 32 bit transfers on a char array, the block size for the DMA should be array_size/4.
I also think the array must be aligned to a 32bit boundary for 32bit transfers from the source.

Mike
0 Likes
Not applicable
Hello Mike,
thanks for the reply.

The FCE_KE Looks like this:

typedef struct { /*!< (@ 0x50020020) FCE_KE Structure */
__IO uint32_t IR; /*!< (@ 0x50020020) Input Register */
__I uint32_t RES; /*!< (@ 0x50020024) CRC Result Register */
__IO uint32_t CFG; /*!< (@ 0x50020028) CRC Configuration Register */
...
....
....
} FCE_KE_TypeDef;


So by giving (uint32_t)(FCE_KE0), the address will be the address of FCE_KE0->IR. Correct me if i am wrong.
Yes i used the for the block size=Array size/4, and the Array is also aligned for 32 bit.
But it still gives wrong crc value.
0 Likes
Not applicable
akosodry wrote:
Hello Mike,
thanks for the reply.

The FCE_KE Looks like this:

typedef struct { /*!< (@ 0x50020020) FCE_KE Structure */
__IO uint32_t IR; /*!< (@ 0x50020020) Input Register */
__I uint32_t RES; /*!< (@ 0x50020024) CRC Result Register */
__IO uint32_t CFG; /*!< (@ 0x50020028) CRC Configuration Register */
...
....
....
} FCE_KE_TypeDef;


So by giving (uint32_t)(FCE_KE0), the address will be the address of FCE_KE0->IR. Correct me if i am wrong.
Yes i used the for the block size=Array size/4, and the Array is also aligned for 32 bit.
But it still gives wrong crc value.


Looks good enough for me.
Another maybe silly question: Don't you have to wait until the block has been moved completely to the FCE before retrieving the result?
As far as I can see you read the result "immediately" after starting the DMA.
I would assume that the DMA will only have moved a few words from the array to the FCE before you read the result.
So maybe you should poll for "DMA done" before reading?
Again: I'm a beginner as well ...

Mike
0 Likes
Not applicable
Hello Mike,

Don't you have to wait until the block has been moved completely to the FCE before retrieving the result?
As far as I can see you read the result "immediately" after starting the DMA.
I would assume that the DMA will only have moved a few words from the array to the FCE before you read the result.
So maybe you should poll for "DMA done" before reading?


Yes you are right. It could be the Problem.
Do you have a reference code for polling the DMA done? I assume an Interrupt Routine should be written, hm?

Thanks for the remark.
0 Likes
Not applicable
akosodry wrote:
Hello Mike,



Yes you are right. It could be the Problem.
Do you have a reference code for polling the DMA done? I assume an Interrupt Routine should be written, hm?

Thanks for the remark.


Right now not, have to start real work.
But I'll check later, in a few hours and edit my answer if you have not found a way yourself.
From memory I'd say you can poll for the "end of DMA", so you don't need an ISR if you have nothing better to do.
Maybe two or three different bits can provide the info.

a) Channel enable bit -> disable
b) INT-Flag in a register
c) "DONE"-bit, I think adjacent to the block size (?)

But I don't have time now to go to the user manual right now -> later, if necessary

Mike
0 Likes
Not applicable
Dear Mike,

thank you for your remarks. Now it is working 🙂


DMA_Config((uint32_t) foo);
while (1) {
if(!((GPDMA0->CHENREG) & 1UL))
CRCValueDMA = FCE_KE0->RES;
}


I also tried with Interrupt handler by enabling in the DMA_Config
SET_BIT(GPDMA0_CH0->CTLL,GPDMA0_CH_CTLL_INT_EN_Pos);
and using

void GPDMA0_0_IRQHandler(void)
{
uint32_t asd;
if(RD_REG(GPDMA0_CH0->CTLH, GPDMA0_CH_CTLH_DONE_Msk,GPDMA0_CH_CTLH_DONE_Pos))
{
asd = FCE_KE0->RES;
asd = asd+1;

}
if(!((GPDMA0->CHENREG) & 1UL))
{
asd = FCE_KE0->RES;
asd = asd+1;

}

}


Here variable
asd=asd+1
has no sense, i just put there the breakpoints to see if Interrupt happens. However the Interrupt has never called. Any Idea?

QUESTION2: the BLOCK_TS is a 12 bit Register, what should i do if i want to process more than 4095 data (32 bit) items?
0 Likes
Not applicable
akosodry wrote:
Dear Mike,

thank you for your remarks. Now it is working 🙂


QUESTION2: the BLOCK_TS is a 12 bit Register, what should i do if i want to process more than 4095 data (32 bit) items?



Great!
For more than 4095 dwords you can use channel linking. This works only on CH0 an CH1 of GPDMA0.
So you would create a list of block descriptors, where the first n-1 blocks have length 4095 and the last one the remainder.

Must go to work again, hope this gives you a pointer where to look / what to do.

Mike

[Edit: In the XMC linked list operation needs only one channel, in my initial response I made an error]
0 Likes
Not applicable
"channel linking" that is what i thought too. Ergo, i have to read again the Manual 🙂
Thanks for the reply, thanks for the hints.
0 Likes
Not applicable
Miguel wrote:
Great!
For more than 4095 dwords you can use channel linking. This works only on CH0 an CH1 of GPDMA0.
With linking you can set up a second DMA which will reload the registers of the first DMA when the first DMA has terminated a block.
So you would create a list of block descriptors, where the first n-1 blocks have length 4095 and the last one the remainder.
One channel (CH0) would then transfer the blocks, while the second channel (say CH2) would reload the registers of CH0 every time CH0 has transferred the current block.
Then you wait for DONE of CH2 (or ISR). At that point the list has been processed completely in the background and probably as fast as possible.

Must go to work again, hope this gives you a pointer where to look / what to do.

Mike


Do you have any example code of the channel linking solution?
Regards, Akos
0 Likes
Not applicable
akosodry wrote:
Do you have any example code of the channel linking solution?
Regards, Akos


Unfortunately not. Today(!) i got my first boards, until now I just have read the user manual.
But my first own application will use GPDMA CH0 in linked list mode, that's why I had some idea about your problem at all.
I hope to get a first test code of my app. up and running until monday, but not earlier.
Then I could hopefully share my code, if still required.

Mike
0 Likes
Not applicable
Miguel wrote:
Unfortunately not. Today(!) i got my first boards, until now I just have read the user manual.
But my first own application will use GPDMA CH0 in linked list mode, that's why I had some idea about your problem at all.
I hope to get a first test code of my app. up and running until monday, but not earlier.
Then I could hopefully share my code, if still required.

Mike


Okey, thank you. I will also try to Show up in the Topic.
Inform me please if you have something.
Regards, Akos
0 Likes
Not applicable
After some struggling with my new toy I got the linked list DMA working.
Although my application is not CRC, the basic problem is the same:
I want to transfer a large buffer from memory to a fixed location by DMA. In my case the target is the DAC.
There are two differences you must take into account when you adapt my code:

1. In my case the DAC (target) triggers the request, in your case the source (memory)
2. I link the last block back to the first block to get a continuous block, you stop after the last block

First the definitions. My buffer never exceeds 8192 words. Therefore I also know that I will never need more than 5 blocks in the list.
The current amount of data in the buffer may vary later, here I have fixed it to 7500 for a test.


#define WAVEGEN_LUTSIZE_MAX 8192
#define WAVEGEN_LUTSIZE_CUR 7500 // later in a variable
#define GPDMA_BLOCKSIZE_MAX 2047

typedef struct
{
uint32_t sar;
uint32_t dar;
uint32_t llp;
uint32_t ctll;
uint32_t ctlh;
uint32_t dstat;
} GPDMA_LLI;

// A global LUT for the DAC
uint32_t wavegen_lut[WAVEGEN_LUTSIZE_MAX];

// A linked list for the GPDMA
GPDMA_LLI wavegen_list[5];


Now the initialization of the linked list. In my case I link the last block to the first, but you can stop there.
See comment, I have tested that as well, works.

	// Setup the circular linked list for the GPDMA
for (uint32_t p=0, i=0; i<5; p+=GPDMA_BLOCKSIZE_MAX, i++)
{
wavegen_list.sar=(uint32_t)&wavegen_lut

;
wavegen_list.ctll=0x10100124;

if ((WAVEGEN_LUTSIZE_CUR - p) <= GPDMA_BLOCKSIZE_MAX)
{
// Last block
wavegen_list.ctlh=WAVEGEN_LUTSIZE_CUR - p;
wavegen_list.llp=(uint32_t)&wavegen_list; // if set to 0 then stop after block
break;
}
else
{
wavegen_list.ctlh=GPDMA_BLOCKSIZE_MAX;
wavegen_list.llp=(uint32_t)&wavegen_list[i+1];
}
}



At last my initialization of the remaining registers of the GPDMA0.CH0.
You will use the FCE as the target and also change from target to source trigger.

			GPDMA0_CH0->DAR=(uint32_t)&DAC->DAC1DATA;
GPDMA0_CH0->LLP=(uint32_t)&wavegen_list[0];
GPDMA0_CH0->CTLL=0x10100124;

GPDMA0_CH0->CFGH=0x00001804;
GPDMA0_CH0->CFGL=0x00000800;


As said, this code works for me. I will now move on to my next problems, hope it helps though.

Mike
0 Likes
Travis
Employee
Employee
First solution authored Welcome! 500 replies posted
Hi guys,

Actually the DAVE3 CRC apps has got the option of DMA.

Best regards
Travis
0 Likes