This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/TM4C1294NCPDT: TM4C1294NCPDT DMA with SPI receive data problem

Part Number: TM4C1294NCPDT
Other Parts Discussed in Thread: ADS127L01

Tool/software: Code Composer Studio

Hi,

I am using TM4C1294NCPDT to receive the  measurements from ADS127L01 ADC. I am working recently on adding DMA functionality to my older projects. What my code does is it provides the SPI bus for communication with ADC, then data is send over tcp socket to the client device. Since, I need periodically to move data from fixed address to my ethernet buffer I decided to use DMA on RX FIFO with PING-PONG configuration. I did use it before in some other applications. The final result on the client side is almost the same as without the DMA functionality, the only difference is the last 2 samples of a buffer iare always corrupted. 

I fill rdata2 buffer of size 700 (array of short type, 2 byte variable).  RX DMA on SPI peripherial gets triggered with 4 or more words in RX FIFO  and this is how I increment the pointer to rdata2 buffer. Code below presents the RX DMA interrupt, transfer variable is a size of a single DMA transfer and is set to 4, (condition on index size is based on 708 not 700 because there is some fixed starting frame (of size 8) in the ethernet packet) variable index is being reset to 8.

Basically, in this interrupt I keep track of the destination address, increment the Res_dest2 pointer (pointing to the rdata2 buffer) and reset it to &rdata2[8] once all if filled.

//*****************************************************************************
//
// This is the handler for SSI1, serviced after DMA RX transfer
//
//*****************************************************************************
void IntSSI1(void)
{
//    test_variable=test_variable+1;

    int ui32Mode1;
    int ui32Mode2;
    ui32Mode1 = uDMAChannelModeGet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT);
    ui32Mode2 = uDMAChannelModeGet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT);

    index=index+transfer;


    if(index<708)//There is space in the buffer so increment destination address by transfer(4)
    {
        //
        // Primary configuration, configure and re-enable after stop of transfer
        //
        Res_dest2 = Res_dest2 + transfer;
        if (ui32Mode1 == UDMA_MODE_STOP)
        {
            test_variable=test_variable+1;

            uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
            UDMA_MODE_PINGPONG,(void *)(SSI1_BASE + 0x008),     //0x008 is SSIDR register
            Res_dest2, //Increment address
            transfer); // # of items to transfer, transfer size is 4 if burst DMA is used for SSI FIFO (uses half of the FIFO of size 8)
        }
        //
        // Secondary configuration, ,configure and re-enable after stop of transfer
        //
        if (ui32Mode2 == UDMA_MODE_STOP)
        {
            test_variable2=test_variable2+1;

            uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT,
            UDMA_MODE_PINGPONG,(void *)(SSI1_BASE + 0x008),     //0x008 is SSIDR register
            Res_dest2, //Increment address
            transfer); // # of items to transfer, transfer size is 4 if burst DMA is used for SSI FIFO (uses half of the FIFO of size 8)
        }
    }

    else if(index>=708)//Destination address needs to be reset
    {
        Res_dest2=&rdata2[8];
        //
        // Primary configuration, configure and re-enable after stop of transfer
        //
        if (ui32Mode1 == UDMA_MODE_STOP)
        {

            uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
            UDMA_MODE_PINGPONG,(void *)(SSI1_BASE + 0x008),     //0x008 is SSIDR register
            Res_dest2, //Increment address
            transfer); // # of items to transfer, transfer size is 4 if burst DMA is used for SSI FIFO (uses half of the FIFO of size 8)
        }
        //
        // Secondary configuration, ,configure and re-enable after stop of transfer
        //
        if (ui32Mode2 == UDMA_MODE_STOP)
        {

            uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT,
            UDMA_MODE_PINGPONG,(void *)(SSI1_BASE + 0x008),     //0x008 is SSIDR register
            Res_dest2, //Increment address
            transfer); // # of items to transfer, transfer size is 4 if burst DMA is used for SSI FIFO (uses half of the FIFO of size 8)
        }

        send_sign=1;//Flag to send data over TCP socket
    }

My buffer is of type short and it always points to the data register of SPI module ( (void *)(SSI1_BASE + 0x008)). The initialization code for DMA is as follows:

dma_conf(Res_dest2, pui8ControlTable, transfer); , where:

Res_dest2=&rdata2[8] and

void dma_conf(unsigned short * Buf,unsigned char * ControlTable,unsigned int Transfer_size)
{
    //
    // Enable the uDMA controller at the system level.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);

    //
    // Enable the uDMA controller.
    //
    uDMAEnable();

    //
    // Point at the control table to use for channel control structures. Passed as input argument
    //
    uDMAControlBaseSet(ControlTable);

    //
    // Disable all atributes
    //
    uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI1RX,UDMA_ATTR_ALL);

    //
    // Enable particular DMA channel, 24 for SSI1 RX
    //
    uDMAChannelAttributeEnable(UDMA_CHANNEL_SSI1RX, UDMA_ATTR_USEBURST);//uDMAChannelAttributeEnable() function can be used to assign different peripherials to channel (page 680 is DS)

    //
    // Configure primary settings, use 32 bit uint variable size. Use burst transfer UDMA_NEXT_USEBURST, SSI FIFO buffer loads results to one the same address so no SRC increment
    //
    uDMAChannelControlSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
                              UDMA_SIZE_16 | UDMA_DST_INC_16 |      //The address increment value must be equal or greater than the value of the dest/src size (DSTSIZE).
                              UDMA_SRC_INC_NONE |
//                              UDMA_ARB_8);     //Arbitration size must be at least of number of items peripherial can accomodate (in case of SSI FIFO it is half, so 4 frames)
                              UDMA_ARB_4 );     //Arbitration size must be at least of number of items peripherial can accomodate (in case of SSI FIFO it is half, so 4 frames)

    //
    // Assign buffer, there will be 8 transfers (size of Buf)
    //
    uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_PRI_SELECT,
                           UDMA_MODE_PINGPONG,
                               (void *)(SSI1_BASE + 0x008),     //0x008 is SSIDR register
                               Buf,
//                               sizeof(Buf));
                               Transfer_size);


    uDMAChannelControlSet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT,
                              UDMA_SIZE_16 | UDMA_DST_INC_16 |      //The address increment value must be equal or greater than the value of the dest/src size (DSTSIZE).
                              UDMA_SRC_INC_NONE |
//                              UDMA_ARB_8);     //Arbitration size must be at least of number of items peripherial can accomodate (in case of SSI FIFO it is half, so 4 frames)
                              UDMA_ARB_4 );     //Arbitration size must be at least of number of items peripherial can accomodate (in case of SSI FIFO it is half, so 4 frames)

    //
    // Assign buffer, there will be 8 transfers (size of Buf)
    //
    uDMAChannelTransferSet(UDMA_CHANNEL_SSI1RX | UDMA_ALT_SELECT,
                           UDMA_MODE_PINGPONG,
                               (void *)(SSI1_BASE + 0x008),     //0x008 is SSIDR register
                               Buf,
//                               sizeof(Buf));
                               Transfer_size);

    //
    // As soon as the channels are enabled, the peripheral will
    // issue a transfer request and the data transfers will begin.
    //
    uDMAChannelEnable(UDMA_CHANNEL_SSI1RX);


}

This data is received by the client and this is what gets on the output when reference sinusoidal signal is fed to ADC during test:

In zoom:

Since rdata2 contained raw measurements and actually 2 ADC frames represent one measurement index 349 and 350 correspond to last 4 values in the rdata2 array (rdata2[704]-[707]). As can be seen last 2 measurements are always corrupted but actually measurements with indices 1049, 1050 have correct values for indices 699 and 700. This is consistent through out hole measurement independent if it lasts 10 seconds or minute. It looks like the last DMA transmission for each frame is delayed and updates last 4 elements of rdata2 for the next frame.

After debugging I see that this is not a matter of ethernet communication or data conversion because inspection of rdata2 in MCU memory reveals that is happens at the MCU side. I have run out of ideas how this can happen. I did check indexing many times and didn't find any cause. Other thing is that other points are perfectly fine so there is something wrong with last DMA transfer (last DMA transfer of size 4 corresponds exactly to 2 measurements values).

The SPI clock is 15MHz with 16bit frame, SSI_FRF_MOTO_MODE_1 (required by slave ADC), working as master.

I will appreciate any ideas and comments. 

  • Hi Lukasz,

    If your theory about the DMA lag is correct, wouldn't it be possible to put a clause in that when changing frames, you do one last transfer to update the elements in your buffer? That seems like it would fairly easy to do and then test to me.

    Alternatively, when you start a few frame buffer if you don't need the last 4 values, you can set them to a specific value like 0xFF or 0xAA and then see if they update. If they do and the result is wrong, then we know the DMA is updating but with wrong data, but if they don't that would prove the theory that there is a lag when updating the buffer.

    Reading through the details, I don't have a better theory to offer at this point. I think trying to do what I mentioned above would help give us clarity one way or another to see if the issue is DMA lag or lays elsewhere.
  • I do not intend this posting to be 'provocative'  - instead seek to understand your 'motivation' - and then to suggest a possible, 'work-around.'

    Lukasz Huchel said:
    The  final result on the client side is  'almost the same as without the DMA functionality,'  the only difference is the last 2 samples of a buffer are always corrupted. 

    So - based upon your 'quote' (above) it is known:

    • the added µDMA functionality  yields (ALMOST the SAME) as  past - Non-µDMA  Results.
    • and now - the last 2 samples w/in your buffer are (ALWAYS CORRUPTED!)

    And you've (clearly) committed good:  'time/energy/focus' - in this process.     Thus - is it fair/proper to ask - as the 'final result' is 'much the same' - yet (now) burdened w/ errors - does not this exercise (seem) w/out real merit?     (I've asked several (other) Tech Biz Owners - we (all) agree on this point.)

    You (likely) had some, 'Improvement Goals' - justifying your time/effort - yet those are not visible here.    I can't detect that you are (directly) asking for (any) µDMA 'Speed Up' - instead only those final '2 errant measurements' - appear your target.

    As those errant measurements are (now) known - and predictable/repeatable - cannot you:

    • reject (only those final 2 measurements) ... or
    • increase the measurement total  - so that you achieve the (exact) number of  'required, error-free, samples.'

    Not all 'hoped for goals can be achieved' - you've made notable effort - without clearly identifying the 'LOST PERFORMANCE GAINS' (again 'hoped for') - your justification  for (more) time/effort - proves difficult to understand...

  • Hi Ralph and cb1_mobile,

    Thank you for responses and help.

    As for Ralph suggestion I did the shift test and is as I have mentioned in the original question. Last DMA transfer (4 SPI frames) is shifted hole buffer apart. Red is the non-modified data, green are just the samples that were shifted (699 to 349, 750 to 350 etc...).Graphs below:

    Zoomed:

    I have made other experiment too. I count up to 704 instead of 708 in my buffer. It means that data [704-707] is going always to be zero. It truly is and the problem than shifts to samples [700-703]. This means that the problem always happens with last DMA transfer. I have tried inserting a slight delay between last DMA transfer and sending whole frame (rdata2), without any improvement (it is a kindof expected since DMA ISR is handled once DMA is done so delay shouldn't help).

    I have been working today on other approach with software DMA (SW configuration). I let the SSI RX FIFO interrupt notify me when 4 measurements are in the FIFO and then I trigger SW DMA. With this approach I get really good response:

    As cb1_mobile mentioned I theoretically can live with 2 samples being corrupted, especially that I can somehow now compensate for that. I just wanted to understand fully the problem, even for future reference.

    I can use SW DMA in my case, it is fine. However, theoretically ping-pong configuration is recommended for periodical stream from peripherial.

    If you have any thoughts I will discuss with pleasure,

    Thank You.

  • Hi, Thank you for the response. The device is working without DMA functionality. I am testing it to decrease the burden on MCU for future development. I want to save time for memory access. I have posted detailed update below. Thank you
  • Thank you - especially (now) for your mention of, 'Your desire to decrease the burden upon the MCU.'     May it be noted that (obtaining) the 'degree of such decrease' should be a 'high-goal' - otherwise you may suffer the label of, 'wishful thinker!'     And - if such measurement is 'not known' - how can you (further optimize) or aid robustness?

    It is respected that you strive for such (exact) understanding - but have 'all other' important aspects of your development been fully completed - and then - exhaustively 'Test/Verified?'    Seizing upon 'just one' aspect of your project (even one challenging & enjoyable) proves (almost) 'destructive' - especially if it causes 'compromise' ... elsewhere.     (which - in my experience - too often - 'proves the case.'    Firm/I work  regularly w/'VCs' (capital raising firms) and such 'tendency' is (well known) and raises concern!)

    Returning now to your (focused) issue:  "This means that the problem always happens with LAST DMA transfer."    Have you exhaustively examined - ANY & ALL DIFFERENCES - between (ALL) successful earlier transfers - and the (dreaded) - ALWAYS  FAILING -  'Last µDMA Transfer?'    It (must) be true - that (some) difference exists - and may be in your: Sequencing, the µDMA ISR, or (somewhere) w/in the µDMA Transfer - itself.    Again - have you  (seriously & deeply)  searched for any/all differences - presented by that 'Last µDMA Transfer?'    You should 'think deeply' - as to how you can best 'identify' - any such difference(s)!

    My group is & remains BIG FANS of 'KISS.'     Cannot you simplify this issue - perhaps:

    • create brand new - greatly 'Size Reduced Code' - sufficient to accomplish a (much smaller such Transfer) - yet ELIMINATING ALL OTHER Program Features and/or Aspects!    (the theory being - that 'some (yet unknown) 'outside influence' impacts that 'LAST Transfer!'     (even if unlikely - especially if unlikely - such should be considered!    More than once - this method has SAVED my team!)
    • change the sequencing of your 'Transfer Code.'   Perhaps there is some 'sensitivity.'
    • again - very closely examine - to insure - that the µDMA content remains 'pure' - and that 'ONLY and ALWAYS' - that 'Last Transfer' - proves the villain!  

    By your 'making the effort' to present a VASTLY SCALED DOWN code version of your issue - and then presenting that (reduced) version here - you (may) encourage others (perhaps even motivated vendor others) to (further) examine - and armed w/their (sure) 'insider knowledge' - just maybe a solution - will emerge.    (Placing the burden - exclusively upon 'JUST ONE'  (you) - proves 'not famed' as an  effective 'Issue Resolving Mechanism!)  ... ...  Not to ask - just how - I (surely) know...