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.

MSP430FR5989-EP: DMA channel stops triggering interrupts from UART A0 RX if a byte is received within a critical section

Part Number: MSP430FR5989-EP

Tool/software:

I am trying to understand if the behavior I am experiencing is what the errata DMA7 is describing. https://www.ti.com/lit/er/slaz523aa/slaz523aa.pdf?ts=1729560620562&ref_url=https%253A%252F%252Fwww.google.com%252F 

What I under stand from the errata is that if the interrupt containing register is accessed while an active interrupt is trigger, it will drop that interrupt. But only that interrupt, further interrupts after enabling the interrupt system should be fine. I also do not know what TI is referring to when saying "a module register" is this saying the UCS A0 A1 B0 B1 interrupt subsystem only? or does that extend out to all the other peripherals / CPU. For instance if I were to do a memory to memory dma transfer with a dma interrupt on transfer completion, would that also be classified under what is a called "a module register" since the dma is a module with registers. 

Though the case I find myself in is that if I disable interrupts as shown below and a interrupt request is generated from the peripheral while global interrupts are off. The DMA channel will stop receiving new interrupt requests outright, after enabling interrupts again. 

Currently I am guarding some code with the following functions to enable and disable interrupts.

void enter_critical_section()
{
  __asm__ volatile(
      "DINT                     \n\t" /* Disable interrupts */
      "NOP                      \n\t" /* Ensure DINT takes effect */
      :
      :
      : "cc");
}

void exit_critical_section()
{
  __asm__ volatile(
      "NOP                      \n\t"
      "EINT                     \n\t" /* Re-enable interrupts */
      "NOP                      \n\t"
      :
      :
      : "cc");
}

My dma channel is, DMA0 Channel 0 (highest priority) {I have also tested with the other 2 channels the behavior is the same}

I am connecting UART A0 RX to DMA0 channel 0 with interrupts for byte transfers.

Outside of this critical section, DMA + UART works as expected. but if it is triggered within a critical section, it then cuts out.

  • Hi, 

    I am trying to understand if the behavior I am experiencing is what the errata DMA7 is describing.

    For the DMA7 Errata, you can try workaround 1 to confirm whether you have trigger DMA7 errata.

    Workaround 1. Use a read of Interrupt Vector registers to clear interrupt flags and do not use readmodify-write instruction.

    ----------

    I also do not know what TI is referring to when saying "a module register" is this saying the UCS A0 A1 B0 B1 interrupt subsystem only? or does that extend out to all the other peripherals / CPU

    From the description in Errata, this module register may refer to a module that contain a interrupt flag register.

    All peripherals that have interrupt flag register will trigger this errata.

    a module register containing an interrupt flags

    ----------------

    For instance if I were to do a memory to memory dma transfer with a dma interrupt on transfer completion, would that also be classified under what is a called "a module register" since the dma is a module with registers. 

    DMA request starts executing.

    CPU access module's interrupt flags using read-modify-write instruction.

    The interrupt of the same module is triggered.

    Then this interrupt will loss.

    -------------

    Though the case I find myself in is that if I disable interrupts as shown below and a interrupt request is generated from the peripheral while global interrupts are off. The DMA channel will stop receiving new interrupt requests outright, after enabling interrupts again. 

    It seems that after [enter_critical_section] and [exit_critical_section] will affect the following DMA trigger.

    Did you try to clear and disable the affected interrupts before DINT and re-enable them after EINT?

    To make sure that the system does not generate an unprocessed interrupt request during DINT.

    Regards,

    Helic

  • I tried writing to the DMA register to disable its interrupts and enable it afterwards but that did not change the behavior.

    Code shown below attempting the workaround. I also tried without clearing the IFG field but there was no difference.

    __attribute__((flatten)) __attribute__((optimize("-O3"))) void enter_critical_section()
    {
      /* Must manually disable DMA interrupts for errata DMA7 */
      /* Step 1: Wait for ongoing DMA transfers to complete (if applicable) */
      get_dma_channel_0()->control.set_InterruptEnable(dma_control_register_t::InterruptEnable::DISABLE);
      get_dma_channel_1()->control.set_InterruptEnable(dma_control_register_t::InterruptEnable::DISABLE);
      get_dma_channel_2()->control.set_InterruptEnable(dma_control_register_t::InterruptEnable::DISABLE);
    
      /* It is known that all 3 DMA channels WILL be used with interrupts so there is no checking required here */
      [[maybe_unused]] volatile auto throw_away = DMAIV;
      DMA0CTL &= ~DMAIFG;
      DMA1CTL &= ~DMAIFG;
      DMA2CTL &= ~DMAIFG;
      __asm__ volatile(
          "DINT                     \n\t" /* Disable interrupts */
          "NOP                      \n\t" /* Ensure DINT takes effect */
          :
          :
          : "cc");
    }
    
    __attribute__((flatten)) __attribute__((optimize("-O3"))) void exit_critical_section()
    {
      [[maybe_unused]] volatile auto throw_away = DMAIV;
      DMA0CTL &= ~DMAIFG;
      DMA1CTL &= ~DMAIFG;
      DMA2CTL &= ~DMAIFG;
      __asm__ volatile(
          "NOP                      \n\t"
          "EINT                     \n\t" /* Re-enable interrupts */
          "NOP                      \n\t"
          :
          :
          : "cc");
      /* Must manually disable DMA interrupts for errata DMA7 */
      /* It is known that all 3 DMA channels WILL be used with interrupts so there is no checking required here */
      get_dma_channel_0()->control.set_InterruptEnable(dma_control_register_t::InterruptEnable::ENABLE);
      get_dma_channel_1()->control.set_InterruptEnable(dma_control_register_t::InterruptEnable::ENABLE);
      get_dma_channel_2()->control.set_InterruptEnable(dma_control_register_t::InterruptEnable::ENABLE);
    }

    I also tried enabling 

    DMACTL4 |= DMARMWDIS;
    but likewise, no change in behavior.
  • 1) Your critical section functions appear to be no better than the compiler provided functions. GCC provides these under many names:__dint(), _DINT(), _disable_interrupts(), etc.

    2) DMA7 requires a very specific chain of events. First the CPU begins execution of a read-modify-write instruction on an IFG register. Then while that is in progress, the DMAC takes over. Finally, the stalled RMW instruction completes.

    Consider what could happen with something like "UCA0IFG &=~UCRXIFG;"

    The CPU reads the IFG register, clears the RXIFG flag, then the DMAC takes control. While the DMAC is performing its transfer, another flag in the IFG gets set. Then when the DMAC lets the CPU complete the instruction, it doesn't read the IFG register again before writing. Since that bit in the IFG was clear before the instruction started, it gets cleared and the interrupt is lost.

**Attention** This is a public forum