EDMA Major Linked Channel halts with no error indication

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

EDMA Major Linked Channel halts with no error indication

811 Views
langrind
Contributor II

Hi NXP Support and Community,

I am trying to understand why a linked DMA operation fails using EDMA on the i.MXRT1064. I have a simple program based on the SDK example evkmimxrt1064_edma_memory_to_memory.

First I verified that the example works correctly if I don't modify it. Then I modified it to use Major Channel Linking. Since the SDK driver doesn't support major linking, I am doing stuff directly to the TCD, but for the most part, I am simply calling the SDK EDMA driver library.

My program launches a 64-byte DMA using channel 0, which is linked to channel 1, which does a 16-byte DMA. The problem is that the second transfer only works if it is a single minor loop that performs the whole transfer (NYBYTES=16 and CITER=BITER=1). If I set it up as 4 minor loops of 4 bytes each, it stalls after the first 4 bytes. Furthermore, I don't see any indication of error, there's nothing in the EDMA ES register. Where else can I look for error indications?


Here is the code, and after the code is some debug output that shows the TCD and EDMA state. Any help is appreciated!

 

#define EXAMPLE_DMA DMA0 
#define EXAMPLE_DMAMUX DMAMUX
#define BIG_CHANNEL 0
#define SMALL_CHANNEL 1

edma_transfer_config_t bigTransferConfig = {0};
edma_transfer_config_t smallTransferConfig = {0};
edma_config_t userConfig;
edma_handle_t Big_EDMA_Handle;
edma_handle_t Small_EDMA_Handle;

/* Configure DMAMUX */
DMAMUX_Init(EXAMPLE_DMAMUX);
DMAMUX_EnableAlwaysOn(EXAMPLE_DMAMUX, BIG_CHANNEL, true);
DMAMUX_EnableAlwaysOn(EXAMPLE_DMAMUX, SMALL_CHANNEL, true);
DMAMUX_EnableChannel(EXAMPLE_DMAMUX, BIG_CHANNEL);
DMAMUX_EnableChannel(EXAMPLE_DMAMUX, SMALL_CHANNEL);

EDMA_GetDefaultConfig(&userConfig);
EDMA_Init(EXAMPLE_DMA, &userConfig);

EDMA_CreateHandle(&Big_EDMA_Handle, EXAMPLE_DMA, BIG_CHANNEL);
// EDMA_SetCallback(&Big_EDMA_Handle, EDMA_Callback, NULL); - Don't want a callback for the first DMA
EDMA_CreateHandle(&Small_EDMA_Handle, EXAMPLE_DMA, SMALL_CHANNEL);
EDMA_SetCallback(&Small_EDMA_Handle, EDMA_Callback, NULL);

EDMA_PrepareTransfer(&bigTransferConfig, src2Big, sizeof(src2Big[0]), dest2Big, sizeof(dest2Big[0]),
sizeof(src2Big[0]), sizeof(src2Big), kEDMA_MemoryToMemory);

EDMA_SubmitTransfer(&Big_EDMA_Handle, &bigTransferConfig);
// XXX disable interrupt for this channel, we only want the interrupt for the linked channel
Big_EDMA_Handle.base->TCD[BIG_CHANNEL].CSR &= ~DMA_CSR_INTMAJOR_MASK;

#ifdef THIS_WORKS
EDMA_PrepareTransfer(&smallTransferConfig, src2Small, sizeof(src2Small[0]), dest2Small, sizeof(dest2Small[0]),
sizeof(src2Small), sizeof(src2Small), kEDMA_MemoryToMemory);
#else
// This does not work (:
EDMA_PrepareTransfer(&smallTransferConfig, src2Small, sizeof(src2Small[0]), dest2Small, sizeof(dest2Small[0]),
sizeof(src2Small[0]), sizeof(src2Small), kEDMA_MemoryToMemory);
#endif
EDMA_SubmitTransfer(&Small_EDMA_Handle, &smallTransferConfig);

EDMA_SetChannelLink(Big_EDMA_Handle.base, BIG_CHANNEL, kEDMA_MajorLink, SMALL_CHANNEL);

#ifdef DEBUG_PRINT
edma_tcd_t bigTcdDump;
edma_tcd_t smallTcdDump;
EDMA_ReadTCD(Big_EDMA_Handle.base, BIG_CHANNEL, &bigTcdDump);
EDMA_ReadTCD(Small_EDMA_Handle.base, SMALL_CHANNEL, &smallTcdDump);
DumpTCD(&bigTcdDump, "Big TCD before");
DumpTCD(&smallTcdDump, "Small TCD before");
#endif

EDMA_StartTransfer(&Big_EDMA_Handle);

i = 100000;
/* Wait for EDMA transfer finish */
while (g_Transfer_Done != true && i-- > 0)
{
}

#ifdef DEBUG_PRINT
/* Print destination buffer */
PRINTF("\r\n\r\nEDMA memory to memory transfer example finish:\r\n");
PRINTF("Destination Buffer Big: ");
for (i = 0; i < BUFF_LENGTH2_BIG; i++)
{
PRINTF("0x%x ", dest2Big[i]);
}
PRINTF("\r\nDestination Buffer Small: ");
for (i = 0; i < BUFF_LENGTH2_SMALL; i++)
{
PRINTF("0x%x ", dest2Small[i]);
}
PRINTF("\r\nGuard Buffer: ");
for (i = 0; i < BUFF_LENGTH2_SMALL; i++)
{
PRINTF("%d ", dest2Buff[i]);
}

PRINTF("\r\n");

EDMA_ReadTCD(Big_EDMA_Handle.base, BIG_CHANNEL, &bigTcdDump);
EDMA_ReadTCD(Small_EDMA_Handle.base, SMALL_CHANNEL, &smallTcdDump);
DumpTCD(&bigTcdDump, "Big TCD after");
DumpTCD(&smallTcdDump, "Small TCD after");

DumpDMA(Small_EDMA_Handle.base,"DMA after");
#endif





Here are the dumps of the TCDs before and after and the test memory. You can see that the first four bytes of the transfer completed, but not the rest.

  
EDMA memory to memory linked transfer example begin:
Destination Buffer Big: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Destination Buffer Small: 0 0 0 0
Big TCD before
SADDR: 0x20000020
SOFF: 0x4
ATTR: 0x202 SMOD: 0x0 SSIZE: 0x2 DMOD: 0x0 DSIZE: 0x2
NBYTES: 0x4
SLAST: 0xffffffc0
DADDR: 0x20000090
DOFF: 0x4
CITER: 0x10
DLAST_SGA: 0x0
CSR: 0x128 START:0 INTMAJOR:0 INTHALF:0 DREQ:1 ESG:0 MAJORELINK:1 ACTIVE:0 DONE:0 MAJORLINKCH:1 BWC:0
BITER: 0x10
Small TCD before
SADDR: 0x20000070
SOFF: 0x4
ATTR: 0x202 SMOD: 0x0 SSIZE: 0x2 DMOD: 0x0 DSIZE: 0x2
NBYTES: 0x4
SLAST: 0xfffffff0
DADDR: 0x20000080
DOFF: 0x4
CITER: 0x4
DLAST_SGA: 0x0
CSR: 0xa START:0 INTMAJOR:1 INTHALF:0 DREQ:1 ESG:0 MAJORELINK:0 ACTIVE:0 DONE:0 MAJORLINKCH:0 BWC:0
BITER: 0x4


EDMA memory to memory transfer example finish:
Destination Buffer Big: 0x10000001 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10
Destination Buffer Small: 0xa1a2a3a4 0x0 0x0 0x0
Guard Buffer: 0 0 0 0
Big TCD after
SADDR: 0x20000020
SOFF: 0x4
ATTR: 0x202 SMOD: 0x0 SSIZE: 0x2 DMOD: 0x0 DSIZE: 0x2
NBYTES: 0x4
SLAST: 0x0
DADDR: 0x200000d0
DOFF: 0x4
CITER: 0x10
DLAST_SGA: 0x0
CSR: 0x1a8 START:0 INTMAJOR:0 INTHALF:0 DREQ:1 ESG:0 MAJORELINK:1 ACTIVE:0 DONE:1 MAJORLINKCH:1 BWC:0
BITER: 0x10
Small TCD after
SADDR: 0x20000074
SOFF: 0x4
ATTR: 0x202 SMOD: 0x0 SSIZE: 0x2 DMOD: 0x0 DSIZE: 0x2
NBYTES: 0x4
SLAST: 0x0
DADDR: 0x20000084
DOFF: 0x4
CITER: 0x3
DLAST_SGA: 0x0
CSR: 0xa START:0 INTMAJOR:1 INTHALF:0 DREQ:1 ESG:0 MAJORELINK:0 ACTIVE:0 DONE:0 MAJORLINKCH:0 BWC:0
BITER: 0x4
DMA after
CR: 0x490
ES: 0x0
ERQ: 0x0
EEI: 0x0
INT: 0x0
ERR: 0x0
HRS: 0x0
EARS: 0x0

 




 

 

 

Labels (1)
0 Kudos
5 Replies

806 Views
jeremyzhou
NXP Employee
NXP Employee

Hi,
Thank you for your interest in NXP Semiconductor products and for the opportunity to serve you.
I was wondering if you can demonstrate the software flow and upload the complete demo, as it can help me to figure the issue out when reviewing the code.
Looking forward to your reply.
Have a great day,
TIC

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

799 Views
langrind
Contributor II

Thanks for your response Jeremy. I've attached a gzipped tar of the example project.

The program flow is:

1) Start up like a typical SDK example

2) Print the contents of destination memory buffers to show that it is zeros

3) Initialize DMA and set up linked transfer

4) Print TCDs and some DMA regs

5) Launch the transfer

6) Wait for transfer complete or timeout (timeout is just a loop count)

7) Print the contents of destination memory buffers (shows that transfer completed partially)

Print TCDs and some DMA regs

 

FYI, I am using version 2.6.1 of the MIMXRT1064 SDK, and 11.0.1 of MCUXpresso.

All the best,

Nik

0 Kudos

786 Views
jeremyzhou
NXP Employee
NXP Employee

Hi,
Thanks for your reply.
According to your testing result, the BIG_CHANNEL DMA channel major loop completes, however, the SMALL_CHANNEL DMA channel only completes the first minor loop, as the link target channel initiates a channel service request via an internal mechanism that sets the DMA_TCDn_CSR[START] bit of the specified channel.
If you want to transfer all src2Small array values, you should configure the DMA to transfer all of the data in a single minor loop (that is, major loop counter = 1).
Hope this is clear.

Have a great day,
TIC

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

782 Views
langrind
Contributor II

Hi Jeremy,

Thanks for your reply, and for taking the time to look at my issue.

It is true that the transfer works completely if the major loop counter is set to 1 and NBYTES is set to the complete transfer size.

My question is more about "why?" Why must I set BITER=CITER=1? Why can't I have multiple minor loops? If I was concerned about managing bandwidth, or allowing for channel arbitration, I believe that setting BITER=1 would be a limitation.

I would think that it would work either way, and I would like to understand why it doesn't because I need to create more complex DMA scenarios.

Is there something inherent in Major Channel Linking that prohibits multiple minor loops?

You said "as the link target channel initiates a channel service request via an internal mechanism that sets the DMA_TCDn_CSR[START] bit of the specified channel." Are you suggesting that START only initiates a single minor loop? I don't think you are, but can you please explain more?

My other question is, how can I tell what happened? I think I should be able to interrogate EDMA registers to get an indication of what went wrong. But all I can see is that ACTIVE is clear, and so is DONE, so it seems that the channel is inactive, but not in an error state. Is this true?

I appreciate any insight you can offer, and I am grateful for your time and help.

Regards,

Nik

0 Kudos

765 Views
jeremyzhou
NXP Employee
NXP Employee

Hi,

Thanks for your reply.
1) My question is more about "why?" Why must I set BITER=CITER=1? Why can't I have multiple minor loops?
-- The channel linking or channel chaining is a mechanism where one channel initiates a service request to another channel (i.e. the bit TCDn_CSR[START] is set) when the first channel has terminated its minor or its major loop, depending on the configuration.
According to your code, the channel linking will happen once when the BIG_CHANNEL DMA channel major loop completes.
If you want to implement multiple minor loops, you should enable channel linking on minor loop complete.
In further, I'd highly recommend you to learn more information about the linking feature via the below link.
https://community.nxp.com/t5/Kinetis-Microcontrollers/What-is-and-how-to-configure-the-eDMA-channel-...

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos