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.

TM4C1294KCPDT: Short SPI uDMA transfers not tripping SSI_DMARX interrupt

Part Number: TM4C1294KCPDT


I have a SPI flash on SPI0 on a TM4C1294. The transfers varies in length from single byte commands to a couple of kb when reading blocks.

I'm controlling the frame select pin with a GPIO from software, and using the SSI_DMARX interrupt to finish the transfer and shut off the frame pin.

This works great 100% of the time with transfers that are more than a few bytes, but for short transfers the interrupt doesn't trigger every time.

I'm guessing I have a race condition somehow, but I've tried everything I and think of, so now reaching out for help.

Here's the init code:

  // Set up uDMA for external serial memories
  ROM_uDMAEnable();
  memset(uDMAControlTable, 0, sizeof(uDMAControlTable));
  ROM_uDMAControlBaseSet(uDMAControlTable);
  ROM_IntEnable(INT_UDMAERR);

  // SSI0 FOR EXT FLASH & FRAM
  SSIConfigSetExpClk(SSI0_BASE, SystemCoreClock, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 16000000, 8);
  ROM_SSIEnable(SSI0_BASE);

  ROM_uDMAChannelAssign(UDMA_CH10_SSI0RX);
  ROM_uDMAChannelAssign(UDMA_CH11_SSI0TX);

  ROM_uDMAChannelAttributeDisable(UDMA_CH10_SSI0RX, UDMA_ATTR_ALL);  //UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
  ROM_uDMAChannelAttributeEnable(UDMA_CH10_SSI0RX, UDMA_ATTR_USEBURST);
  ROM_uDMAChannelControlSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4);

  ROM_uDMAChannelAttributeDisable(UDMA_CH11_SSI0TX, UDMA_ATTR_ALL);  //UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
  ROM_uDMAChannelAttributeEnable(UDMA_CH11_SSI0TX, UDMA_ATTR_USEBURST);
  ROM_uDMAChannelControlSet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_DST_INC_NONE | UDMA_SRC_INC_8 | UDMA_ARB_4);

  ROM_SSIIntEnable(SSI0_BASE, SSI_DMARX);
  ROM_IntEnable(INT_SSI0);

..and here's the function that runs a transfer:

void  bsp_ssimem(uint16_t n, uint8_t *buf)
{
    uint16_t  c, r;
    uint32_t  a;

    r = n;
    while(r) {
        // DMA can only do 1024 bytes in one go, so chunk it up:
        if(r > 1024)
          c = 1024;
        else
          c = r;

        ROM_SSIDMADisable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX);   // Disable while reconfiguring
        EVENT_CLEAR(bsp_events, EV_SSI);                         // make sure flag is down

        HWREG(SSI0_BASE + SSI_O_CR1) &= ~SSI_CR1_EOT;   // See errata

        while(ROM_SSIDataGetNonBlocking(SSI0_BASE, &a)) {}          // Flush FIFO

        // Set up DMA to send and receive
        ROM_uDMAChannelTransferSet(UDMA_CH11_SSI0TX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, buf, (void *)(SSI0_BASE + SSI_O_DR), c);
        ROM_uDMAChannelTransferSet(UDMA_CH10_SSI0RX | UDMA_PRI_SELECT, UDMA_MODE_BASIC, (void *)(SSI0_BASE + SSI_O_DR), buf, c);

        ROM_uDMAChannelEnable(UDMA_CH10_SSI0RX);
        ROM_uDMAChannelEnable(UDMA_CH11_SSI0TX);              
        ROM_SSIDMAEnable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX);   // GO!

        if(EVENT_WAIT_TIMEOUT(bsp_events, EV_SSI, 5) == 0)          // Wait for interrupt, but no more than 50ms
            DMAErrCount++;

        r -= c;
        buf += c;
    }
}

Finally here's the ISR.

void  SSI0_IRQHandler(void) // ext flash/ram
{
    uint32_t  f;

    ROM_SSIDMADisable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX);

    f = ROM_SSIIntStatus(SSI0_BASE, false);
    ROM_SSIIntClear(SSI0_BASE, f);    // Clear any interrupts

    EVENT_SET(bsp_events, EV_SSI);
}

This works 99.9% of the time, but randomly the interrupt is not triggered, and it's more frequent for shorter transfers. When I slow down the SPI clock to 1MHz, it works better, but I still have single byte transfers failing occasionally.

I would very much appreciate any ideas or recommendations.

Thanks,

 // Anders

  • May my small group (awaiting a horserace bit later) loudly applaud your "terrific" First Post?     Absolutely outstanding - detailed, exactingly organized - well explained.

    We don't use your MCU (120 MHz & external bus/display drive - have not "kept up" w/M4s of others) yet a high percent of our tech business flows from such remote, "Diagnostics."

    Might your "slight & random" failures result from (any) other MCU usage - which may delay or somehow inhibit - that SSI_DMARX interrupt?     That's not stated.

    Your code signals that you are "errata aware" - yet is that specific errata the most current?      And might the listing of, "Non (exact/overlapping) errata - somehow yield or contribute - to your issue?"

    Just as you have recognized - and implemented - a "slowed SPI clock" has improved matters.      Yet - to best (and fastest) troubleshoot - might "speeding the SPI clock" increase the incidence of such interrupt failures - and thus provide, "more clues - in much shorter time-frame" - aiding your analysis.

    I'm sufficiently unskilled & unaware w/your MCU to be able to quickly/easily note - "how or even if" your "longer data transfers" create conditions or settings - which "best enable" the SSI_DMARX interrupt to assert...     That path may warrant your further investigation...      Again - great job!

  • I appreciate the applause very much! Your reply certainly brightened my Monday morning!

    To your first point, I have isolated this issue by disabling all other activity except the system tick. The frequency of the missed interrupts does not appear to be affected by other activity.

    I'm only looking at the most recent errata and manual, but I'll dig up previous revisions and see if that reveals any clues.

    Increasing the clock speed worsens the problem. It clearly appears to be timing related.

    I could skip using the uDMA for short (1-4 byte) transfers - actually that may even save a few CPU cycles not having to reconfigure the uDMA and processing an interrupt - but I would really like to understand the root cause of this before implementing a workaround that seems-to-work-most-of-the-time.

    Thank you again for the encouragement, sensible advice, and please share any other ideas!

    </A>

  • Your posts - both opening & this one - deserve such noting.     (vast number (still) arrive here - proclaiming, "Does not work" ...  and not one word of issue description!)

    Skipping the µDMA for short transfers is (not) satisfying - we "Solve via Avoidance" - which is not (always) an option!

    My point in having you raise, "SPI clock speed"is to:

    • generate the failure conditions faster
    • and in greater number

    And it is hoped (and experience has revealed) that in such instances - "Faster Arriving & Increased "Issue Data" prove "friends" - not enemies.    

    What happens if you launch a, "Series of short (1-4 byte) transfers" only?

    What happens when you follow several "Long transfers" - with a short one?

    What happens when the sequence of short transfers is (many) seconds apart?

    Suggest that you "really/deeply" identify (everything) which is involved in triggering that  "SSI_DMARX" interrupt.     You may then present (known) roadblocks before each of the trigger "elements" - with the goal to glean (some) understanding of (relative) sensitivity.

    And - meaning no offense - how do you (really) know that the interrupt has not triggered?     Could it have (partially) triggered?     Could it have been "cleared" prematurely - by (some) mechanism - yet to be discovered?     Best method to confirm the "successful entry, full progression thru, and exit" from such function is to take known actions - "just upon function entry - and just prior to function exit."

    These challenges yield great mental "highs" when they are identified & corrected - "not so much" while they "over-challenge."     Perhaps this laundry list speeds your identification & excise of the gremlin...

  • Problem solved after beating on this all day.
    The issue had nothing to do with the uDMA or SPI. It was simply an interrupt priority issue.

    For anyone interested, I'm using Rowley's CTL, and they clearly warn about ISR priority. Specifically, any ISR using CTL services (in my case the events) must be set to lower priority than ISR's that don't use CTL services.

    Lesson learned -- again. Read *all* the documents.

    It really helps to be forced to explain the issue when trying to troubleshoot something like this, so I very much appreciate the dialog here. I usually subject my (non-programmer) wife to these ramblings, so she is also very happy to be unaware this time ;)

    'til next time,
    </A>
  • For the record - and to "improve" your (otherwise) excellent postings - no such mention of "Rowley CTL" appeared - until "just now!" Such would have appeared w/in my laundry list - had I "been aware." (i.e. that Rowley "fact" was NOT in evidence...)

    Agreed that dialog - always in compliance w/KISS - often provides the shortest time/distance to success...