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