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.

MSP-EXP430FR5994: DMA race with UART receive

Part Number: MSP-EXP430FR5994
Other Parts Discussed in Thread: BQ79616

Hi,

My setup is EXP-MSP430FR5994 (external crystal for clock); BQ76600 EVM (UART); custom BQ79616. I am using MSP430 DMA for UART receive. I am running into the race condition outlined below along with solutions tried. The only solution that is helping with consistent BQ initialization is explicit clearing of DMAEN before transmitting data through the UART.

Without explicit clearing of DMAEN in uartSend, BQ initialization sometimes fails after the first throw away read during the BQ auto address sequence.
DMA transfer size variable uartRxLen displays the correct value (and matches the register width);
Content of the local dma receive buffer dma_value has all the receive bytes; but the value in the DMA1SZ register = 1and the DMA ISR does not trigger. I cannot capture the exact moment when the failure happens.
Other solutions tried in the place of clearing DMAEN listed below. None of these helped with the race condition seen:
1. Removing exit from LPM in DMA ISR (no entry into LPM exists)
2. 250 ms BQ measurement time (master timer ISR is currently 125 ms)
3. Receiving the first byte on UART ISR; disable uart Rx ISR and then initiate DMA transfer for remaining receive bytes.

I want pointers on what else I can try to address this problem.

Priya

  • 1) Can you post your main() function? I haven't seen any obvious control-flow things in what you've posted so far.

    2) Since the condition seems clear enough (DMAEN is still on after DMA seems complete), I would next try a "mousetrap", maybe something like:

    if (BQUART_DMAxCTL & DMAEN) {   // We expect it isn't
        asm volatile (" nop ");     // Breakpoint here and see how we got here
        BQUART_DMAxCTL &= ~DMAEN;   // Keep things rolling nonetheless
    }

  • Here is the main function. I will put the "trap" code in the uartSend function and see what I find.

    #include "bspFuncs.h"
    #include "timer.h"
    #include "clock.h"
    
    #ifdef BQ_SPI
       #include <BQSpiInitFuncs.h>
       #include <BQSpiMeasure.h>
       #include "spiCommands.h"
    #endif
    
    #ifdef BQ_UART
       #include <BQUartInitFuncs.h>
       #include <BQUartMeasure.h>
       #include "uartCommands.h"
    #endif
    
    
    /**
     * main.c
     */
    int main(void)
    {
    
    	WDTCTL = WDTPW | WDTHOLD;	// stop watchdog timer
    	
        Clock_Init();
    
       #ifdef BQ_UART
          configure_pins_BQUart();
          UART_Init();
          DMA_Init();
       #endif
    
       #ifdef BQ_SPI
          configure_pins_BQSpi();
          SPI_Init();
       #endif
    
        Timer_Init();
        TA0_Start();
    
        __enable_interrupt();
    
       #ifdef BQ_SPI
          SpiReadyComm();
       #endif
    
       #ifdef BQ_UART
          UartReadyComm();
       #endif
    
    
       while(1)
       {
          if(1==Get_TimerFlag())
          {
              Set_TimerFlag(0);
              #ifdef BQ_SPI
                  BQ_SPI_APP();      //make measurements every 125 ms
              #endif
    
              #ifdef BQ_UART
                  BQ_UART_APP();      //make measurements every 125 ms
              #endif
    
              updateTimers();
          }
    
        }
    
    }
    

  • I am unable to halt at this induced breakpoint. It appears that a transition on the DMAEN bit is helping without which the BQ init process sometimes fails.

       if ((BQUART_DMAxCTL & DMAEN) == 1) {   // We expect it isn't
            asm volatile (" nop ");     // Breakpoint here and see how we got here
            BQUART_DMAxCTL &= ~DMAEN;   // Keep things rolling nonetheless
        }

    If I put this code in the uartSend, The BQ init fails but the breakpoint is not reached.

    The problem happens in this part of the BQ init sequece:

    //SYNCRHONIZE THE DLL WITH A THROW-AWAY READ
    ReadReg(0, OTP_ECC_DATAIN1, autoaddr_response_frame, 1, 0, FRMWRT_STK_R);
    while (dmaDataReady == 0);

    This is the first ReadReg command that is issued. The flow of the ReadReg commands upto uartSend is available in the uartCommands.c file I uploaded on the linked thread. Majority of this was TI provided code modified to use the MSP430 uart.

  • This form fails since DMAEN is not =1. If you're required to use a comparison, you can try:

    > if ((BQUART_DMAxCTL & DMAEN) != 0) {

  •     if ((BQUART_DMAxCTL & DMAEN) != 0) {   // We expect it isn't
             asm volatile (" nop ");     // Breakpoint here and see how we got here
             BQUART_DMAxCTL &= ~DMAEN;   // Keep things rolling nonetheless
         }
    

    If I debug with a breakpoint in this code, for some reason the IDE session goes straight to this breakpoint and halts. I don't understand why. At the time it halts, uartRxLen is 0. It looks like DMAEN is always 1 unlike how it was without this check.

    Other than this, nothing was amiss in the register contents.

  • DMA_Init() does this:

    >  BQUART_DMAxCTL |= DMADT_0|DMADSTINCR_3|DMASRCINCR_0|DMASRCBYTE__WORD|DMADSTBYTE__WORD|DMA_TRIGGER_RISINGEDGE|DMAEN;

    I don't know if this is triggering your specific symptom, but it generally doesn't seem like a good idea to set DMAEN before you need it.

  • I changed the DMA_Init to 

    BQUART_DMAxCTL |= DMADT_0|DMADSTINCR_3|DMASRCINCR_0|DMASRCBYTE__WORD|DMADSTBYTE__WORD|DMA_TRIGGER_RISINGEDGE;

    As soon as I start the debug session, the execution still halts at the breakpoint. Why is DMAEN defaulting to 1?

  • DMAEN doesn't set (to 1) by itself, so my next step would be to use CCS Search (toolbar item that looks like a pencil eraser) for DMAEN, then for uartSend, to see if there's some usage I had forgotten about.

    Next would be a breakpoint at uartSend (the only code that sets DMAEN=1) to see if there's some unexpected caller in the call stack. This step will be a little trickier, since the breakpoint will probably interfere with the serial receive, but (since you're closing in on the cause) you shouldn't have to do this many times.

  • There is no forgotten call to DMAEN or uartSend in the project. 

    As far as looking at the call stack how do I do this? I put a continuous watch on DMA1CTL DMAEN bit. I can see it becomes 1 soon after the debug session is started and halts at the breakpoint. How do I track the source of this write to DMAEN?

    Here is the stack usage view in CCS

  • When you stop at a breakpoint, the call stack (top left View in my CCS) shows a list of who-called-whom, with the current function at the top. 

    My operating hypothesis is that some function is calling uartSend and then not properly (or at all) waiting for the DMA to complete. Then the next call to uartSend trips over the "dangling" DMAEN. That means you're trying to find the previous caller (perhaps the first ever), which isn't easy in the general case, but you seem to have a very repeatable sequence which fails very early.

  • Yes, looking at the top left helped tremendously! Though uartSend is called only in one place, both WriteReg and ReadReg use uartSend. DMA transfer only has to be initiated for ReadReg. Appreciate your help, many thanks.

**Attention** This is a public forum