UART RX DMA Interrupt Signaling & IDLE condition

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

UART RX DMA Interrupt Signaling & IDLE condition

7,021 Views
pmt
Contributor V

I have a UART RX DMA driver written but I am looking for the best method to invoke a receive interrupt such that I can use event signaling to implement proper blocking I/O calls.  Ideally, you want to invoke an interrupt after a certain amount of continuous characters are received (but not every character), and you want to invoke an interrupt after the line goes idle for a certain time after reception of at least one character.  I would settle for just the idle interrupt functionality alone.

Now, the Kinetis UART/DMA peripheral set is amazingly good from my experience thus far.  But I am trying to make sense of the idle interrupt generation.  And it seems at least this implementation is really flawed, unless my understanding is incorrect.  You can invoke an interrupt after the line goes idle for 10, 11, or 12 bits times (depending on mode), however in order to service this interrupt you have to read the status register (which has side effects to all the status bits), but even worse you have to then read the Data register, in that order.

Now how pray tell can this be achieved in the context of an RX DMA read which has already taken place before the ISR can even be serviced?  The read of the data register in the ISR will cause a FIFO misalignment because the FIFO is empty.

Is there a certain nuanced setup?  How are others handling this?  It would be nice if this interrupt was a self clearing one shot in hardware.  I don't understand what the current implementation is good for other than for non-DMA functionality.

pmt 

3 Replies

1,685 Views
alejandrolozan1
NXP Employee
NXP Employee

Hi,

Sorry for the dealy, I know this is already solved. I just want to publish this information:

You are right in that there is a possibility for a conflict when using any of the S1 status bits to generate an interrupt while at the same time using UART DMA requests. The problem is that the mechanism to clear the S1 status bits is to read the S1 register when the bit is set, then read (or sometimes write) the D register. The issue is that this means there isn’t any way to tell the UART that you ONLY want to clear the IDLE bit. The software procedure will clear all of the S1 bits including the RDRF bit that would be used for DMA requests.

So you are right to be concerned about this; however in this case the character received and idle received conditions cannot occur simultaneously. As long as there isn’t a lot of latency in the system, then that should help to maintain an order of operations and prevent the IDLE interrupt from clearing an RxDMA request accidentally.

As long as there isn’t excessive latency in the system, then the DMA will have always read out any received characters before an IDLE condition could be detected. So reading the D register to clear the IDLE shouldn’t cause a loss of characters in most cases. It will cause underflows, but those are easy to take care of. Just test the FIFO to verify it is empty before attempting to clear IDLE (this would detect a situation where latency prevented the DMA from running before the IDLE interrupt), clear IDLE, then write 1 to SFIFO[RXUF] to clear the underflow flag (this can be done safely by making sure there wasn’t an underrun previously, so you would know this was a false underrun condition that doesn’t’ indicate an actual problem in the system).

1,685 Views
pmt
Contributor V

Thanks Alejandro for the helpful advice. You can see my other related discussions under these topics:

Phantom ILDMAS, UART IDLE line DMA Enable

Missing critical RX UART DMA Event Functionality in Freescale Parts

The non-atomicity of the S1 register, coupled with the non-atomicity of the S1, D register sequence needed to clear the IDLE condition (and the subsequent clearing of potential FIFO underflow conditions), coupled with the fact that ARM is a LOAD/STORE architecture not capable of atomic read/modify/write cycles to memory, coupled with the fact that DMA and ARM are two truly concurrent threads of execution is a real issue. And it’s not just clearing the IDLE condition, but the need to re-enable IDLE is also fraught with its own set of non-atomic hazards. 

See where I’m going with this?  In the end the layers of workarounds could work, but unfortunately all require software intervention and low latency under all conditions, and some critical assumptions to make it work.  This is what the DMA is supposed to relieve you from.  It’s not good enough that something work %99.9999 of the time.  It _has_ to work %100 of the time by design.  There is just too much guesswork here. 

Add to this the fact that I never really got the IDLE condition to properly trigger the way it should, I eventually gave up on using IDLE altogether.  IDLE seems to be tied in with WAKE and SLEEP features in ways that I don’t understand.  I’m afraid whoever deleted the ILDMAS functionality from the K60 DMA peripheral set did not understand the true purpose of this function and must have thought it a non-critical feature.  ILDMAS would have provided a way to atomically clear IDLE with the DMA engine, and also provide an interrupt to allow a signaling mechanism to the RT/OS.    

This is a shame, because the Kinetis UART / DMA is fully capable of driving a proper UART RX ring buffer with no processor intervention.  You just need the IDLE signaling mechanism to be able to wake the RT/OS UART device driver without the alternative of interrupting on every single UART character. 

In the end this is what I had to do in my driver, interrupt on every character.  The interrupt handler does not move any data (DMA does this) but it is used to throw an RT/OS event to provide a waking mechanism when there is a blocking call waiting on UART input.  When you are cranking data at very high sustained speeds this really taxes the CPU.

I am hopeful Freescale will improve this in later versions of the peripheral set.  It seems that %70 of the peripheral provides atomic bit set and clear mechanisms for things like status and configuration registers.  Freescale just needs to get on the ball and get the other %30 percent, which is now especially important in the ARM LOAD/STORE architecture world.

If Freescale wants to know how to fix it from an RT/OS driver perspective please read my rantings/suggestions in the “Missing critical RX UART DMA Event Functionality in Freescale Parts thread.

Thanks!

0 Kudos

1,685 Views
stel
Contributor I

Hi, I am trying to get DMA1 to receive 10 characters without CPU intervention, over UART5, Kinetis K60:

I successfully managed to transfer 10 chars over UART5 using DMA0, no CPU intervention.

I post my  UART DMA1 settings, I thing I have something wrong in it, or missing something (

    _DMA0_ISR,        /* 0x10  0x00000040   -   ivINT_DMA0

   

    _DMA1_ISR,      /* 0x10  0x00000044   -   ivINT_DMA1

) in vectors.c

uint32_t UART5_RX_DMA1_init(uint8_t length_of_transfer)

  /* Enable Clock gating for the DMA1 and DMA MUX */

//   _bsp_edma_enable(1);// /* Enable DMAMUX clock */ <  

  SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;///  /* Enable DMA clock */ <  

    DMAMUX_CHCFG1 = (1<<7)|12;   //Select UART5 RX as channel source

  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

   

    //DMAMUX_CHCFG1 = (1 << 7 ) | 13;

   

    DMA_CR = 0;

    /////////////////////////////////////////////////////////////////////////////////////////////////

   // DMAMUX_CHCFG1 = 0x00;   //Disable DMA MUX channel first //Channel Configuration Register (DMAMUX_CHCFG0)

    /////////////////////////////////////////////////////////////////////////////////////////////////////////

 

   // DMA_TCD1_SADDR  = (uint32_t)0x400EB007;   //Set source address UART5_D  / UART5 data register used to receive data

    DMA_TCD1_SADDR = (uint32_t)&UART5_D;

    DMA_TCD1_DADDR  = (uint32_t)&buffer_RX.buffer[0];   //Set destination

 

    DMA_TCD1_NBYTES_MLNO = 1; //TCD Minor Byte Count (Minor Loop Disabled)

    DMA_TCD1_ATTR = 0 ;// 8-bit transfer, closed model of // TCD Transfer Attributes: Source data transfer size

    DMA_TCD1_SOFF = 1; //Sign-extended offset applied to the current source address to form the next-state value as each source read is completed.

    DMA_TCD1_DOFF = 0; //Sign-extended offset applied to the current destination address to form the next-state value as each destination write is completed.

    DMA_TCD1_SLAST = 0; //Adjustment value added to the source address at the completion of the major iteration count. This value

              //can be applied to restore the source address to the initial value, or adjust the address to reference the next data structure.

    DMA_TCD1_DLASTSGA = 0; // Destination last address adjustment or the memory address for the next transfer control descriptor to be loaded into this channel (scatter/gather).

    /*

     * If (TCDn_CSR[ESG] = 0) then

  • Adjustment value added to the destination address at the completion of the major iteration count.

  This value can apply to restore the destination address to the initial value or adjust the address to reference the next data structure

  else

  • This address points to the beginning of a 0-modulo-32-byte region containing the next transfer

  control descriptor to be loaded into this channel. This channel reload is performed as the major

  iteration count completes. The scatter/gather address must be 0-modulo-32-byte, else a

  configuration error is reported.

     */

    DMA_TCD1_CITER_ELINKNO = length_of_transfer;

    DMA_TCD1_BITER_ELINKNO = length_of_transfer;

   

    //DMAMUX_CHCFG1 = (1 << 7 ) | 12;

//    DMAMUX_CHCFG1 = (1<<7)|12;   //Select UART5 RX as channel source

  /*

   * Current Major Iteration Count

  This 9-bit (ELINK = 1) or 15-bit (ELINK = 0) count represents the current major loop count for the channel.

  It is decremented each time the minor loop is completed and updated in the transfer control descriptor memory. After the major iteration count is exhausted, the channel performs a number of operations (e.g.,

  final source and destination address calculations), optionally generating an interrupt to signal channel completion before reloading the CITER field from the beginning iteration count (BITER) field.

  NOTE: When the CITER field is initially loaded by software, it must be set to the same value as that contained in the BITER field.

  NOTE: If the channel is configured to execute a single service request, the initial values of BITER and CITER should be 0x0001.

   */

  //  DMA_TCD1_CSR = 0;

    DMA_TCD1_CSR = 1;

    /*

     * Channel Start

  If this flag is set, the channel is requesting service. The eDMA hardware automatically clears this flag after

  the channel begins execution.

  0 The channel is not explicitly started

  1 The channel is explicitly started via a software initiated service request

     */

  //  DMA_TCD1_CSR &= ~DMA_CSR_INTMAJOR_MASK;

   DMA_TCD0_CSR |= DMA_CSR_INTMAJOR_MASK;

    /*

     * Enable an interrupt when major iteration count completes

  If this flag is set, the channel generates an interrupt request by setting the appropriate bit in the INT when

  the current major iteration count reaches zero.

  0 The end-of-major loop interrupt is disabled

  1 The end-of-major loop interrupt is enabled

     */

   ////////////////////////////////////////////////////////////////////////////////////

//   DMAMUX_CHCFG1 |= DMAMUX_CHCFG_ENBL_MASK;   //Enable the DMA MUX channel

   ///////////////////////////////////////////////////////////////////////////////////////////

    DMA_TCD1_CSR |= DMA_CSR_DREQ_MASK;

  /*

   * Disable Request

  If this flag is set, the eDMA hardware automatically clears the corresponding ERQ bit when the current

  major iteration count reaches zero.

  0 The channel’s ERQ bit is not affected

  1 The channel’s ERQ bit is cleared when the major loop is complete

   */

//   NVICISER0 |= (1 << 0) ;// enable interrupts NVICISERn = 1 << m, where N = 0/32, m = 0% 32

    NVICISER0 |= (1 << 0) ;// enable interrupts NVICISERn = 1 << m, where N = 0/32, m = 0% 32

   NVICICPR0 |= 1 << 0;  

    enable_irq(1);

    DMA_ERQ |= (1 << 1) ; // Start

   

    return MQX_OK;

     

}

Thank you,

Stel

0 Kudos