Determining where in a buffer a DMA is when it's execution is stopped.

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

cross mob
Anonymous
Not applicable

Hello, I've actually got a couple questions, one more trivial that I just haven't tested and one more specific which I haven't be able to find anything about in the DMA Application notes.

   

1) When you disable the signal to drq, will a DMA stop operating even if the TD is only partially complete it's Transfer Count? ie, you have an ADC's eoc feeding into a DMA which transfers that data to a array of 200 bytes (100 samples at 2 Bytes per sample) and you suppress the eoc signal (say, with a control register and an AND gate) so the DMA no longer receives the EOC rising edge, will the DMA pause even though the TD hasn't finished it's Transfer Count?

   

2) Once the DMA is paused, is there any good way to figure out where in the buffer execution ceased? In particular, I want to use an ADC to fill a ring buffer in memory until until a coarse event occurs (ADC output goes above a set level), then pause the ring buffer and starting from the latest value work backwards to the start of the event. 

Is there a way to do this? Or perhaps I need to do something tricky like synchronize a second DMA set the next value to -1 every time the DMA runs so I have a known value I can search for?

   

Thanks for looking at this,

   

Sean

0 Likes
7 Replies
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

1) When the DMA/TD is configured to require a DMA request for each transfer, then supressing the DRA signal will stop the DMA.

   

2) you can read the current target address from the TD.

0 Likes
Anonymous
Not applicable

Hi hli,

   

Thanks for the responses. I've implemented and tested, and you are indeed correct on number 1: suppressing the signal stops the DMA requests from triggering bursts, which stops the DMA and it's associated TDs from continuing to operate.

   

However, I've been having a lot of trouble with 2), CyDmaTdGetConfiguration(MyTD[0], TransferCount, nextTD, Configuration) returns the initial transfer count (in my case 2048) and not the current transfer count. The same happens with CyDmaTdGetAddress(MyTD[0], &SourceAddres, &DestinationAddress) where my destination buffer's address is 0x1fffa2c4, and the TD returns 0xa2c4, which is the initial address and not the current address. The DMA is running in real time and I haven't seen any differing output.

Is there something I'm missing?

0 Likes
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

When you look at the "advanced DMA" application note, there are examples of how to modify a TD dynamically (by setting the addresses). This could be reversed, so this AN could give you a hint of how to proceed. I'm not sure whether the API methods read the actual values or the configuration that has been set.

0 Likes
Anonymous
Not applicable

Hmm, I'm taking a lot, but running into a lot of issues.

   

First, because of the way transfer counts work, I can't easily add a second TD that points to the DestinationAddress field and outputs that to another variable in RAM, because my ring buffer TD has to have a transfer length large enough to fill the entire ring buffer and it doesn't go to the next TD until all the ring buffer is filled. This defeats my purpose. It looks like to make this work, I would need to make a very complicated DMA arrangement that handles the ring buffer activity in a complex way, where it sends an interrupt every time the buffer is full to reset the base address to the first address, which in a free running situation seems fraught with risk of buffer overrun.

   

A simpler solution seems to be accessing the TD registers directly and reading from there after I've paused the DMA, as I can use the same DMAC_TDMEM[Storage_DMA_TD[0]].TD1[2] call as the chaining DMA calls use directly in my code.

But this just leads to the REAL problem, which is that the DMA register values do NOT seem to update with the execution of the DMA. I don't know where it's tracking the decrease of the transfer count, and the increment of the source address, but it's not done in the DMAC_TDMEM location, as those values in memory do not change from the configuration during exectuion. I can watch the memory addresses where transfer count and destination address are stored from the DMAC_TDMEM[Storage_DMA_TD[0]].TD1[2] call, and they do not change over time. 

Are there tools for watching the memory values within a range for a specific value, such that I can try and run the program and figure out what memory location the DMA is using for it's internal counting, then I can try and backtrack through their struct sizes to figure out a safe way to reference that location?

0 Likes
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

When I read the Registers TRM correctly,  DMAC_TDMEM[Storage_DMA_TD[0]].TD1[2] access the upper 8 bits of the destination address. Does this change for your setup? Maybe you need to use  DMAC_TDMEM[Storage_DMA_TD[0]].TD1[3]? The transfer count is in .TD0[0]/[1].

0 Likes
Anonymous
Not applicable

Hi hli, 

   

I managed to figure it out after watching the program ram in debugger and setting up a number of tests.

   

You are partially correct on that, though TD1[3] seems to be the most significant bits and TD1[2] the least significant bits (must be the endianness of the memory). Both are necessary to get the lower 16 bits of the address,

   

However, that doesn't cover the real issue, which is that using DMAC_TDMEM[Storage_DMA_TD[0]] moved to a location in the TD queue where the default values appear to be kept and stored (at least in my case where I have preserve initial values enabled, so I don't need to manually reset the starting point each cycle). The actual real time values from the execution of my two DMAs are located in DMAC_TDMEM[0] and DMAC_TDMEM[1], so (DMAC_TDMEM[1].TD1[3] << 😎 | DMAC_TDMEM[1].TD1[2] successfully fetches the lower 16 of the current destination address. Likewise, the remaining transaction count is in TD0[1] and TD0[0].

I think it's a result of this behaviour of the Preserve function:
"If TDs are preserved, the channel uses a separate TD memory (corresponding to the channel number) to track the ongoing transaction; otherwise the original TD configuration registers are used as working registers to track the ongoing transaction." So my DMA 0 uses TD channel 0 for the running memory, and likewise with TD1, and my actual TDs like Storage_DMA_TD[0] which gets allocated to me is allocated at the end of the list as TD 0x7F where they are preserved. I think I misread and assumed that the special register was used to preserve the settings and the normal was still tracked the ongoing progress, but thinking about the implications that would entail I see why it's not the case.

   

Now I just need to see if the DMA Channel variable gets the value of 0x00 or 0x01 in order to more safely access the correct TD memory struct, but hard coding will also work as long as I don't change the order in which the DMA are instantiated.

Thank you very much for your help in figuring this all out.

   

Sean

0 Likes

One debugging tool often used is to initialize your data buffer with a repetitive sequence of hex values such as 0xFEEDBEEF or CCCC. It is easy to see what gets overwritten and what does not.

0 Likes