Yahoo Groups archive

Lpc2000

Index last updated: 2026-04-28 23:31 UTC

Message

Re: [lpc2000] Missing THRE interrupt?

2004-07-02 by Robert Adsett

Unfortunately, no-one appears to have seen an obvious problem. :(

I've kept on it and I think I may have found where/when the problem occurs.

At 01:43 PM 6/23/04 -0400, you wrote:
>I've been working on a interrupt driven serial interface and have run into
>an odd problem.  I seem to be missing THRE interrupts.

I've pretty well confirmed this now.  THRE interrupts are not always 
occurring when they should be.

The key section of ISR code looks like this:

  temp_id = U0IIR;
  while( ( temp_id & PENDING_MASK) == 0) {
     switch(temp_id & IDENT_MASK) {

                 /*  DATA_AVAILABLE occurs when data has passed the FIFO */
                 /* threshold.  DATA_TIMEOUT occurs when data has been   */
                 /* sitting in the FIFO for longer than the timeout      */
                 /* period (3.5-4.5 character times).  In either case we */
                 /* call Receive to empty the FIFO.                      */
          case DATA_AVAILABLE:
          case DATA_TIMEOUT:
               receive();
               break;

          case TRANSMIT_AVAILABLE:
               transmit();
               break;

          case LINE_STATUS:
          default:
               break;
          }
     temp_id = U0IIR;
     }

IDENT_MASK appropriately masks off the identification bits and the case 
values correspond to the various interrupt sources.

It will continually loop on the interrupt identification register until no 
more pending interrupts are flagged.  This is the usual way that '550 type 
uarts are serviced.  Since THRE interrupts (TRANSMIT_AVAILABLE case above) 
are cleared simply by reading the IIR care is taken that the IIR is read 
only once per loop.  I verified that there was no code generation issue.

Using this as the core of the ISR the transmit would periodically stop as 
if the THRE interrupt had not happened.

One of the early steps was to add a simple piece of code to check the LSR 
to see if there was an indication of the holding register being empty and 
if it was act as if a THRE interrupt had occurred.  When this was added 
transmission no longer stopped.  Clearly not very satisfactory as a 
solution since it conceivably could stop but not restart until something 
was received.

Also ran a test where the transmission pipe was restarted if appeared to 
stop by inserted a character in the holding register.  That also worked.

I also tested transmission only.  It proceeded without any signs of 
stopping.  A reception only test also worked

Instrumenting the transmit and receive interrupt sources it was apparent 
that the transmission and reception interrupts moved in and out of phase 
during operation.  Also it became clear that the missing THRE interrupt 
consistently occurred (didn't occur?) just as they moved into phase.

I now had an outline of the symptoms:
   -  It behaves as if THRE interrupts go missing
   - It occurs when transmission and reception interrupts occur in close 
proximity.

With that description and the fact that the THRE interrupt is cleared by 
reading the IIR I came up with a speculative cause:
   - speculation - there is a race condition in the uart such that if a 
THRE interrupt occurs in close (in time) to the reading of the IIR, the IIR 
will not hold the THRE indication (in particular bit 0 will be set) but the 
uart state machine will see the IIR as being read and clear the condition.

If that is the case then reading IIR only once in the interrupt may not 
cause the problem since I would expect the IRQ to be set only once bit 0 of 
the IIR has been cleared.  So I modified the main block as follows:

  temp_id = U0IIR;
  if( ( temp_id & PENDING_MASK) == 0) {
     switch(temp_id & IDENT_MASK) {

                 /*  DATA_AVAILABLE occurs when data has passed the FIFO */
                 /* threshold.  DATA_TIMEOUT occurs when data has been   */
                 /* sitting in the FIFO for longer than the timeout      */
                 /* period (3.5-4.5 character times).  In either case we */
                 /* call Receive to empty the FIFO.                      */
          case DATA_AVAILABLE:
          case DATA_TIMEOUT:
               receive();
               break;

          case TRANSMIT_AVAILABLE:
               transmit();
               break;

          case LINE_STATUS:
          default:
               break;
          }
     }

The if is probably not needed but it shows the similarity to the first 
version clearly.  This version works.  There are no transmission halts.

In summary:
   - At this point I've just about convinced myself that THRE interrupts do 
indeed go missing, maybe due to an internal race condition in the LPC (HW 
not SW)
   - The condition only occurs if transmission and reception are occurring 
simultaneously
   - One other point I have not mentioned even when it does occur it can 
take 500K to 1.5M or so before it happens.
   - Enabling or disabling the FIFO has no effect.

Given these conditions I would expect to see it rarely (if at all) for 
normal download/upload programs (they don't usually run a transmit and 
receive simultaneously) or terminal interface type programs.

Any comments?

Have I missed a possibility?  (especially have I missed something I can 
test for)

I'll probably do a little more testing but at the moment it looks like I 
will use the technique of reading IIR only once in an interrupt.

Robert

" 'Freedom' has no meaning of itself.  There are always restrictions,
be they legal, genetic, or physical.  If you don't believe me, try to
chew a radio signal. "

                         Kelvin Throop, III

Attachments

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.