Yahoo Groups archive

Lpc2000

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

Thread

Missing THRE interrupt?

Missing THRE interrupt?

2004-06-23 by Robert Adsett

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'm running a simple echo type program for initial testing.  I've got a 
file I'm feeding in via realterm to test (It just sits there pumping the 
file out over and over).  When I run the program it will eventually stop 
(I've never got a full three file dumps sucessfully).

I started adding bit toggles to see why it was stopping and determined that 
the SW transmit buffer was filling up and the interrupt was not emptying 
it.  I have a flag (I call it a prime flag) to indicated that the transmit 
interrupt has emptied the pipe and needs restarted.  At that point I 
figured there was a bug in my interrupt routine such that the prime flag 
was not being set when it was supposed to be but I haven't been able to 
find it.

Finally today I instrumented the interrupt response with a number of bit 
toggles and it started to look like the reason the prime flag was not being 
set was because the THRE interrupt was not being fired.  I added a short 
piece to the end of the interrupt to check for this:

  if( (U0LSR & TX_MASK) != 0) {
     temp_id = U0IIR;
     if( (temp_id & PENDING_MASK) == 0) {
          if( (temp_id & IDENT_MASK) == TRANSMIT_AVAILABLE) {
               IOSET = 0x200;
               transmit();
               IOCLR = 0x200;
               }
          }
      else {
          IOSET = 0x8000;
          transmit();
          IOCLR = 0x8000;
          }
     }

The only way that the IOSET/IOCLR 0x8000 should occur is if 1) I've ended 
the main body of the interrupt, 2) THRE is set in the line status register 
and 3) the THRE interrupt is not set.

Much to my surprise that pin is being toggled which indicated to me that 
THRE is being set (as it should be) but the IIR is not being set.

Now the THRE interrupt source is the only one that can be cleared simply by 
reading the IIR so an extra read of the IIR would cause a problem but I 
don't see that in my code.  Main interrupt body below:

  temp_id = U0IIR;
/* IOCLR = 0x8000;*/
  while( ( temp_id & PENDING_MASK) == 0) {
     IOSET = 0x100;
     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:
               IOSET = 0x400;
               receive();
               IOCLR = 0x400;
               break;
               /*doc!!*/
          case TRANSMIT_AVAILABLE:
               IOSET = 0x200;
               transmit();
               IOCLR = 0x200;
               break;

          case LINE_STATUS:
          default:
               break;
          }
     IOCLR = 0x100;
/*    IOSET = 0x8000;*/
     temp_id = U0IIR;
/*    IOCLR = 0x8000;*/
     }

U0IIR is declared volatile and I also checked the generated assembly and it 
appears to be read only once per loop.

At this point it appears to be acting as if the THRE interrupt is swallowed 
by the hardware if it occurs at the same time as a RDA interrupt.  I'm 
loathe to believe that, I suspect I've done something silly that I can't 
see because I'm too close to it.

Oh and the FIFOs are off.  I turned them off shortly before doing the full 
instrumentation just to eliminate them as a problem source.

What I have here won't do as a workaround, I have an idea of what would be 
needed for that if indeed the THRE interrupt is indeed being swallowed by 
the HW but I want to identify the problem rather than just put in a 
symptomatic workaround.

Finally, the test program has been running something like 7x longer than 
the longest it ran before putting in the missing THRE interrupt as I sit 
here typing.  I'll let it run for a while yet while I think.

Anyone else seen this behaviour or have any ideas as to what I've missed.?

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

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

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.