EmbeddedRelated.com
Forums

LPC2138 UART bug or docs problem?

Started by Stephen Pelc July 10, 2006
While testing a Flash copy routine for an LPC2138, I foolishly
inserted some test code and ran into the following problem.

1) The code runs in RAM
2) 14.7... MHz XTAL, clock = xtal*4, VPBDIV=1
3) All interrupts disabled
4) UART0 at 115200 baud.

If the UART character transmit loop is too tight when sending a
string, transmission fails.

dis emit
EMIT
( 4000.02DC 24801FE5 $..e ) ldr r8, [ pc, # $-24 ] (
@$400002C0 = $E000C000 )
( 4000.02E0 04A02CE5 . ,e ) str r10, [ r12, # $-04 ] !
( 4000.02E4 08A0A0E1 . a ) mov r10, r8
( 4000.02E8 14809AE5 ...e ) ldr r8, [ r10, # $14 ]
( 4000.02EC 208018E2 ..b ) and .s r8, r8, # $20
( 4000.02F0 FCFFFF0A |... ) b .eq # $400002E8
( 4000.02F4 00009CE5 ...e ) ldr r0, [ r12, # $00 ]
( 4000.02F8 00008AE5 ...e ) str r0, [ r10, # $00 ]
( 4000.02FC 0000A0E1 .. a ) mov r0, r0
( 4000.0300 0000A0E1 .. a ) mov r0, r0
( 4000.0304 0000A0E1 .. a ) mov r0, r0
( 4000.0308 0000A0E1 .. a ) mov r0, r0
( 4000.030C 04A09CE5 . .e ) ldr r10, [ r12, # $04 ]
( 4000.0310 08C08CE2 .@.b ) add r12, r12, # $08
( 4000.0314 0EF0A0E1 .p a ) mov pc, r14
60 bytes, 15 instructions.

The four "mov r0, r0" noops are ncessary for correct operation.
It appears that one must wait at least a complete VPB cycle or
two or so after writing the THR register before LSR changes.

Is this behaviour documented somewhere?

Stephen
--
Stephen Pelc, s...@mpeforth.com
MicroProcessor Engineering Ltd - More Real, Less Time
133 Hill Lane, Southampton SO15 5AF, England
tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
web: http://www.mpeforth.com - free VFX Forth downloads

An Engineer's Guide to the LPC2100 Series

You should poll for transmit register empty between writing bytes to the
UART.

On 7/10/06, Stephen Pelc wrote:
>
> While testing a Flash copy routine for an LPC2138, I foolishly
> inserted some test code and ran into the following problem.
>
> 1) The code runs in RAM
> 2) 14.7... MHz XTAL, clock = xtal*4, VPBDIV=1
> 3) All interrupts disabled
> 4) UART0 at 115200 baud.
>
> If the UART character transmit loop is too tight when sending a
> string, transmission fails.
>
> dis emit
> EMIT
> ( 4000.02DC 24801FE5 $..e ) ldr r8, [ pc, # $-24 ] (
> @$400002C0 = $E000C000 )
> ( 4000.02E0 04A02CE5 . ,e ) str r10, [ r12, # $-04 ] !
> ( 4000.02E4 08A0A0E1 . a ) mov r10, r8
> ( 4000.02E8 14809AE5 ...e ) ldr r8, [ r10, # $14 ]
> ( 4000.02EC 208018E2 ..b ) and .s r8, r8, # $20
> ( 4000.02F0 FCFFFF0A |... ) b .eq # $400002E8
> ( 4000.02F4 00009CE5 ...e ) ldr r0, [ r12, # $00 ]
> ( 4000.02F8 00008AE5 ...e ) str r0, [ r10, # $00 ]
> ( 4000.02FC 0000A0E1 .. a ) mov r0, r0
> ( 4000.0300 0000A0E1 .. a ) mov r0, r0
> ( 4000.0304 0000A0E1 .. a ) mov r0, r0
> ( 4000.0308 0000A0E1 .. a ) mov r0, r0
> ( 4000.030C 04A09CE5 . .e ) ldr r10, [ r12, # $04 ]
> ( 4000.0310 08C08CE2 .@.b ) add r12, r12, # $08
> ( 4000.0314 0EF0A0E1 .p a ) mov pc, r14
> 60 bytes, 15 instructions.
>
> The four "mov r0, r0" noops are ncessary for correct operation.
> It appears that one must wait at least a complete VPB cycle or
> two or so after writing the THR register before LSR changes.
>
> Is this behaviour documented somewhere?
>
> Stephen
> --
> Stephen Pelc, s...@mpeforth.com
> MicroProcessor Engineering Ltd - More Real, Less Time
> 133 Hill Lane, Southampton SO15 5AF, England
> tel: +44 (0)23 8063 1441, fax: +44 (0)23 8033 9691
> web: http://www.mpeforth.com - free VFX Forth downloads
>
>
>

--
Jim Parziale
n...@gmail.com
Malden, MA
Stephen Pelc wrote:
> While testing a Flash copy routine for an LPC2138, I foolishly
> inserted some test code and ran into the following problem.
>
> 1) The code runs in RAM
> 2) 14.7... MHz XTAL, clock = xtal*4, VPBDIV=1
> 3) All interrupts disabled
> 4) UART0 at 115200 baud.
>
> If the UART character transmit loop is too tight when sending a
> string, transmission fails.

Stephen,
I tidied up your code so I could read it and comment it, I guess this
has been compiled because it certainly could be optimized. Have I missed
something because the code looks fine and I am assuming that your
calling routine is very tight so that there are very few cycles between
polls.

If this is the case then I guess that there is a problem writing to the
UART too quickly. As it is a standard cell modeled after industry
standard UARTs such as the 16c552 etc then this is a good place to look
for more information. It would seem that uarts in general synch their
internal timing to 16 times the baud rate clock. I found a figure from a
datasheet that quoted 8-24 BAUDOUT cycles from write to THRE. Have you
tried testing this at the maximum data rate?

EMIT
ldr r8, [ pc, # $-24 ] ;r8 = uart base?
str r10, [ r12, # $-04 ] ! ;push r10 (tos) onto r12 stack
mov r10, r8 ;r10 = uart base
L1: ldr r8, [ r10, # $14 ] ;poll LSR
ands r8, r8, # $20 ;THRE? (tx holding reg empty)
beq L1
ldr r0, [ r12, # $00 ] ;yes, read data from r12 stack
str r0, [ r10, # $00 ] ;send data
mov r0, r0 ;wait wait
mov r0, r0
mov r0, r0
mov r0, r0
ldr r10, [ r12, # $04 ] ;set tos to next data item
add r12, r12, # $08 ;drop 2 items (entry + saved)
moV pc, r14 ;ret
*Peter*
Hi Stephen,

Something I find interesting (which has not been mentioned
yet) is that you seem to be sending things character-by-
character, and waiting for THRE before sending each
character.

I see discussions as to whether THRE is ready quickly
enough. But that doesn't sound strictly relevant.

What happened to the 16-character transmit FIFO?
(In the LPC user manual, when describing the U0FCR
there is a line saying "FIFO Enable must be set
for proper UART operation").

Is the FIFO enabled?*
What do you mean when you say Transmission fails?
- Does it send the first (say 16) characters and then
stop and stay stopped?
- Do you get total rubbish transmitted?
- Do you get (either whole or partial) characters dropped?
(It's not easy to distinguish between losing partial
characters and sending rubbish).

I "feel" that this might relate to the way the UART hangs
off the VPB off the AHPB rather than something intrinsicly
wrong with the UART peripheral.

Regards,
Danish

*If the FIFO is enabled, then you don't strictly need to
wait for THRE before sending the next character. It
shouldn't break things if you do choose to wait.
--- In l..., Peter Jakacki wrote:
>
> Stephen Pelc wrote:
> > While testing a Flash copy routine for an LPC2138, I foolishly
> > inserted some test code and ran into the following problem.
> >
> > 1) The code runs in RAM
> > 2) 14.7... MHz XTAL, clock = xtal*4, VPBDIV=1
> > 3) All interrupts disabled
> > 4) UART0 at 115200 baud.
> >
> > If the UART character transmit loop is too tight when sending a
> > string, transmission fails.
>
> Stephen,
> I tidied up your code so I could read it and comment it, I guess this
> has been compiled because it certainly could be optimized. Have I
missed
> something because the code looks fine and I am assuming that your
> calling routine is very tight so that there are very few cycles between
> polls.
>
...
>
> EMIT
> ldr r8, [ pc, # $-24 ] ;r8 = uart base?
> str r10, [ r12, # $-04 ] ! ;push r10 (tos) onto r12 stack
> mov r10, r8 ;r10 = uart base
> L1: ldr r8, [ r10, # $14 ] ;poll LSR
> ands r8, r8, # $20 ;THRE? (tx holding reg empty)
> beq L1
> ldr r0, [ r12, # $00 ] ;yes, read data from r12 stack
> str r0, [ r10, # $00 ] ;send data
> mov r0, r0 ;wait wait
> mov r0, r0
> mov r0, r0
> mov r0, r0
> ldr r10, [ r12, # $04 ] ;set tos to next data item
> add r12, r12, # $08 ;drop 2 items (entry + saved)
> moV pc, r14 ;ret
> *Peter*
>

--- In l..., "Danish Ali" wrote:
>
> Hi Stephen,
>
> Something I find interesting (which has not been mentioned
> yet) is that you seem to be sending things character-by-
> character, and waiting for THRE before sending each
> character.
>
> I see discussions as to whether THRE is ready quickly
> enough. But that doesn't sound strictly relevant.
>
> What happened to the 16-character transmit FIFO?
> (In the LPC user manual, when describing the U0FCR
> there is a line saying "FIFO Enable must be set
> for proper UART operation").
>
> Is the FIFO enabled?*
> What do you mean when you say Transmission fails?
> - Does it send the first (say 16) characters and then
> stop and stay stopped?
> - Do you get total rubbish transmitted?
> - Do you get (either whole or partial) characters dropped?
> (It's not easy to distinguish between losing partial
> characters and sending rubbish).
>
> I "feel" that this might relate to the way the UART hangs
> off the VPB off the AHPB rather than something intrinsicly
> wrong with the UART peripheral.
>
> Regards,
> Danish
>
> *If the FIFO is enabled, then you don't strictly need to
> wait for THRE before sending the next character. It
> shouldn't break things if you do choose to wait.

The code looks fine to me: if you're going to write to the THR it
seems reasonable to check if it's empty beforehand, even with FIFOs
enabled (it should be empty as the FIFO fills).

I'd agree with your main question, though: "what do you mean when
you say transmission fails?". Without more information, it's
difficult to say what's going on. I'd be slow to assume there's some
delay in setting or clearing THRE: the documentation is pretty
unambigouous in this. I'd look carefully at the FIFO control and TX
enable registers and make sure they're being used correctly first.

Having siad all that, I've just checked our own UART driver and it
never checks THRE...

Brendan.

Quoting brendanmurphy37 :
> The code looks fine to me: if you're going to write to the THR it
> seems reasonable to check if it's empty beforehand, even with FIFOs
> enabled (it should be empty as the FIFO fills).

I may be misunderstanding what you wrote. So apologies if you've said this.

Just a note the THRE will not be flagged until the FIFO is empty. Using the
transmitter in this fashion is the equivalent of disabling the transmit FIFO.

Robert
--- In l..., Robert Adsett wrote:
>
> Quoting brendanmurphy37 :
> > The code looks fine to me: if you're going to write to the THR it
> > seems reasonable to check if it's empty beforehand, even with FIFOs
> > enabled (it should be empty as the FIFO fills).
>
> I may be misunderstanding what you wrote. So apologies if you've said
this.

No you didn't misunderstand (and no need to apologise in any case). I
was going by what the Philips documentation says, which fairly
unambiguously says the bit is set when the THR (which it defines to be
the top of the FIFO) is empty.

It looks like I could well be wrong, however: if you look at something
like National's 16550 data sheet, it says that in FIFO mode the bit is
set when the entire FIFO is empty, which is definitely not the same
thing.

So I guess the question is whether the Philips UART is compatible
with "standard" 16550 behaviour, or is the documentation wrong?

Which brings us right back to the subject matter of the original
poster....

Unfortunately, I can't verify the behaviour myself: maybe someone else
can, or maybe Philips can comment?

>
> Just a note the THRE will not be flagged until the FIFO is empty.
Using the
> transmitter in this fashion is the equivalent of disabling the
transmit FIFO.
>
> Robert
>

> Posted by: "Danish Ali" d...@gleevaughan.co.uk dr_danish_ali

> Something I find interesting (which has not been mentioned
> yet) is that you seem to be sending things character-by-
> character, and waiting for THRE before sending each
> character.
>
> I see discussions as to whether THRE is ready quickly
> enough. But that doesn't sound strictly relevant.
>
> What happened to the 16-character transmit FIFO?
> (In the LPC user manual, when describing the U0FCR
> there is a line saying "FIFO Enable must be set
> for proper UART operation").

The routine is generic, and the FIFOs are enabled.

The routine is part of a remote update package. Part of it is used with
XModem over the UART so that we don't need separate ISP tools, only a
terminal emulator with Xmodem support. The new part is called after a new
application has been downloaded from a website and put into the upper half
of the flash at 0x40000. We then copy the new application from 0x40000 to
0x00000 and reboots the CPU. The first part has been running happily for a
couple of years. The main application uses an interrupt-driven receiver
and polled transmission. To perform the copy, the copy code is copied from
Flash to RAM and executed. CPU Interrupts are disabled in the CPSR, and
the UART interrupts are disabled. The UART messages are for debug and
logging only.

> Is the FIFO enabled?*
Yes

> What do you mean when you say Transmission fails?
We get garbage or no characters or just the end of the message.

> I "feel" that this might relate to the way the UART hangs
> off the VPB off the AHPB rather than something intrinsicly
> wrong with the UART peripheral.

My feeling is that THRE does not change until some time after the last
character has been written and that we were adding another character
before it had changed. BUT, this problem only emerged just before a site
visit, and I will not time to perform more thorough tests until next week.
If my suspicions are correct, the problem will get worse when the VPB
divider is changed from 1 to 4. On the other hand, the UART could be
perfectly innocent and there's an undiscovered UFO (Undocumented Feature
Occurrence) still lurking.

Thanks to you and also to everyone else who responded. Meanwhile, back to
counting the coins.

Stephen

At 02:21 PM 7/11/2006 +0000, brendanmurphy37 wrote:
>--- In l..., Robert Adsett wrote:
> >
> > Quoting brendanmurphy37 :
> > > The code looks fine to me: if you're going to write to the THR it
> > > seems reasonable to check if it's empty beforehand, even with FIFOs
> > > enabled (it should be empty as the FIFO fills).
> >
> > I may be misunderstanding what you wrote. So apologies if you've said
>this.
>
>No you didn't misunderstand (and no need to apologise in any case). I
>was going by what the Philips documentation says, which fairly
>unambiguously says the bit is set when the THR (which it defines to be
>the top of the FIFO) is empty.

Ahh, I think I see where the problem has arisen. I do believe it's a
recent documentation bug. The interrupt and status definitions refer to
the 'actual' THR, the one that holds a single character for transmission
just before the shift register. The FIFO documentation refers to the 'user
visible' THR that a program can write to. I think they used one term when
they should have used two.

We had a discussion about this some time back. The user manual at that
time didn't appear to have the statement you refer to, indeed as I remember
it more or less punted to existing 16550 documentation. At that time
someone(s) wanted to know how tell if the FIFO is full. Answer: you count
the number of characters you put in.

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
http://www.aeolusdevelopment.com/

--- In l..., Robert Adsett
wrote:
>
> At 02:21 PM 7/11/2006 +0000, brendanmurphy37 wrote:
> >--- In l..., Robert Adsett
wrote:
> > >
> > > Quoting brendanmurphy37 :
> > > > The code looks fine to me: if you're going to write to the
THR it
> > > > seems reasonable to check if it's empty beforehand, even
with FIFOs
> > > > enabled (it should be empty as the FIFO fills).
> > >
> > > I may be misunderstanding what you wrote. So apologies if
you've said
> >this.
> >
> >No you didn't misunderstand (and no need to apologise in any
case). I
> >was going by what the Philips documentation says, which fairly
> >unambiguously says the bit is set when the THR (which it defines
to be
> >the top of the FIFO) is empty.
>
> Ahh, I think I see where the problem has arisen. I do believe
it's a
> recent documentation bug. The interrupt and status definitions
refer to
> the 'actual' THR, the one that holds a single character for
transmission
> just before the shift register. The FIFO documentation refers to
the 'user
> visible' THR that a program can write to. I think they used one
term when
> they should have used two.
>
> We had a discussion about this some time back. The user manual at
that
> time didn't appear to have the statement you refer to, indeed as I
remember
> it more or less punted to existing 16550 documentation. At that
time
> someone(s) wanted to know how tell if the FIFO is full. Answer:
you count
> the number of characters you put in.
>
> Robert
>

Robert,

That makes a lot of sense. Regardless of how it's defined though, I
can't imagine this to be the root cause of the original problem
described. To my mind, it should be safe to poll THRE to see if it's
OK to write a character, and to assume it is set at the correct
point (i.e. with no delays), otherwise there would be little point
in having it.

Brendan.