EmbeddedRelated.com
Forums

UART stuck waiting for THRE

Started by mattwoolsey May 10, 2008
At 04:18 PM 5/10/2008 -0400, Paul Claessen wrote:
>The value of 'ch' is irrelevant for this problem.
>Even if there was a sign problem, even if the compiler decided to generate
>a random value, the UART
>still should simply send whatever it was it got.

Yep, I misread = for ==.

>Besides, it works the first time, he sees the character coming out the
>UART, and then the next time
>it HANGS in a loop that doesn't even address the 'ch' parameter!

I took that as an indication that the problem was somewhere else.

Robert

http://www.aeolusdevelopment.com/

From the Divided by a Common Language File (Edited to protect the guilty)
ME - "I'd like to get Price and delivery for connector Part # XXXXX"
Dist./Rep - "$X.XX Lead time 37 days"
ME - "Anything we can do about lead time? 37 days seems a bit high."
Dist./Rep - "that is the lead time given because our stock is live.... we
currently have stock."

An Engineer's Guide to the LPC2100 Series

At 08:20 PM 5/10/2008 +0000, mattwoolsey wrote:
>That's not a comparison. It is writing ch to UxTHR. Besides, the
>problem is in the previous line with the while loop.

Oops, quite right. I think that's bad style, but it's not the problem.

>The assembly of the bad code looks like this:
>
>//------------------
>
> sendchar PROC
>;;;82 //int sendchar (char ch)
>;;;83 { /* Write character to Serial Port */
>000034 e1a01000 MOV r1,r0
>;;;84
>;;;85 while (!(UxLSR & 0x20));
>000038 e1a00000 MOV r0,r0
> |L1.60|
>00003c e59f003c LDR r0,|L1.128|
>000040 e5d00014 LDRB r0,[r0,#0x14]
>000044 e3100020 TST r0,#0x20
>000048 0afffffb BEQ |L1.60|

That looks OK

>;;;86
>;;;87 return (UxTHR = ch);
>00004c e20100ff AND r0,r1,#0xff
>000050 e59f2028 LDR r2,|L1.128|
>000054 e5c20000 STRB r0,[r2,#0]
>;;;88 }
>000058 e12fff1e BX lr
>;;;89
> ENDP
>
> |L1.128|
>000080 e000c000 DCD 0xe000c000
>
>//--------
>
>The assembly for the while loop looks ok to me, although I haven't
>done a lot of ARM assembly.
>
>And here's the working version:
>
>//--------
>
> sendchar PROC
>;;;84
>;;;85 while (!(UxLSR & 0x20));
>000034 e1a00000 MOV r0,r0
> |L1.56|
>000038 e59f1038 LDR r1,|L1.120|
>00003c e5d11014 LDRB r1,[r1,#0x14]
>000040 e3110020 TST r1,#0x20
>000044 0afffffb BEQ |L1.56|

That looks identical, other than register usage.

>;;;86
>;;;87 return (UxTHR = ch);
>000048 e59f1028 LDR r1,|L1.120|
>00004c e5c10000 STRB r0,[r1,#0]
>;;;88 }
>000050 e12fff1e BX lr
>;;;89
> ENDP
>
> |L1.120|
>000078 e000c000 DCD 0xe000c000
Somehow I think the problem is somewhere else entirely. Are you running
any interrupts? Why are you sure it's stuck in that loop?

In that loop there are only a few possibilities
- You are addressing the wrong location (not likely since it's
reloaded)
- The location is really returning a value without the 6'th bit set

For the second to be true the character must not have been sent. For it
not to have been sent there must be some reason separate from this piece of
code.

Both are easily checkable walking through the loop if you are running a
JTAG debugger.

Robert

http://www.aeolusdevelopment.com/

From the Divided by a Common Language File (Edited to protect the guilty)
ME - "I'd like to get Price and delivery for connector Part # XXXXX"
Dist./Rep - "$X.XX Lead time 37 days"
ME - "Anything we can do about lead time? 37 days seems a bit high."
Dist./Rep - "that is the lead time given because our stock is live.... we
currently have stock."
At 05:10 PM 5/10/2008 -0400, Robert Adsett wrote:
>At 08:20 PM 5/10/2008 +0000, mattwoolsey wrote:
> >That's not a comparison. It is writing ch to UxTHR. Besides, the
> >problem is in the previous line with the while loop.
>
>Oops, quite right. I think that's bad style, but it's not the problem.
Let me revise that assessment a bit. The code is not at all clear. It
happens in this case to do what you want but I don't think it is at
guaranteed to do so.

Drifting off course a bit but I understand the value of an assignment
expression to be the value of the left hand side of the assignment is the
value of x=y; should be the value of x. The trouble is U0THR is write
only. The read value of that location should be U0RBR. A compiler could
return that value instead. All kinds of questions pop up here but I think
depending on either behaviour is a bad idea and writing it more clearly
should not change the generated code.

Robert

"C is known as a language that gives you enough rope to shoot yourself in
the foot." -- David Brown in comp.arch.embedded
http://www.aeolusdevelopment.com/
----- Original Message -----
From: "Robert Adsett"
To: ; ;
Sent: Saturday, May 10, 2008 5:30 PM
Subject: Re: [lpc2000] Re: UART stuck waiting for THRE
> At 05:10 PM 5/10/2008 -0400, Robert Adsett wrote:
>>At 08:20 PM 5/10/2008 +0000, mattwoolsey wrote:
>> >That's not a comparison. It is writing ch to UxTHR. Besides, the
>> >problem is in the previous line with the while loop.
>>
>>Oops, quite right. I think that's bad style, but it's not the problem.
> Let me revise that assessment a bit. The code is not at all clear. It
> happens in this case to do what you want but I don't think it is at
> guaranteed to do so.
>
> Drifting off course a bit but I understand the value of an assignment
> expression to be the value of the left hand side of the assignment is the
> value of x=y; should be the value of x.
> The trouble is U0THR is write only. The read value of that location should be U0RBR.

==> [Paul] No trouble at all. The code just WROTE (R0) to that register.
It's not going to read it back. It still has that value in R0, and that's what the
calling code sees as the return value.
But again, even IF that return statement would return garbage, that still doesn't
explain the hang in the previous statement!
> A compiler could return that value instead.
==> [Paul] No it couldn't. It has NO CLUE that U0BRB is the 'read'
counter part of U0THR.
But again, there is no reason for the compiler to generate ANY 'read back register'
code.

~ Paul Claessen

> All kinds of questions pop up here but I think
> depending on either behaviour is a bad idea and writing it more clearly
> should not change the generated code.
>
> Robert
>
> "C is known as a language that gives you enough rope to shoot yourself in
> the foot." -- David Brown in comp.arch.embedded
> http://www.aeolusdevelopment.com/

At 05:50 PM 5/10/2008 -0400, Paul Claessen wrote:

>----- Original Message -----
>From: "Robert Adsett"
>To: ; ;
>
>Sent: Saturday, May 10, 2008 5:30 PM
>Subject: Re: [lpc2000] Re: UART stuck waiting for THRE
> > At 05:10 PM 5/10/2008 -0400, Robert Adsett wrote:
> >>At 08:20 PM 5/10/2008 +0000, mattwoolsey wrote:
> >> >That's not a comparison. It is writing ch to UxTHR. Besides, the
> >> >problem is in the previous line with the while loop.
> >>
> >>Oops, quite right. I think that's bad style, but it's not the problem.
> >
> >
> > Let me revise that assessment a bit. The code is not at all clear. It
> > happens in this case to do what you want but I don't think it is at
> > guaranteed to do so.
> >
> > Drifting off course a bit but I understand the value of an assignment
> > expression to be the value of the left hand side of the assignment is the
> > value of x=y; should be the value of x.
> > The trouble is U0THR is write only. The read value of that location
> should be U0RBR.
>
>==> [Paul] No trouble at all. The code just WROTE (R0) to that register.
>It's not going to read it back. It still has that value in R0, and that's
>what the
>calling code sees as the return value.

That's what it happens to use. I'm not convinced it must (or even should).

>But again, even IF that return statement would return garbage, that still
>doesn't
>explain the hang in the previous statement!

Agreed.

> > A compiler could return that value instead.
>==> [Paul] No it couldn't. It has NO CLUE that U0BRB is the 'read'
>counter part of U0THR.

Yes it could. The value of U0THR = x; (1) is U0THR. U0THR is defined as
the location that U0THR is at. When you read that location you get
U0RBR. Thus the value of U0THR = x; should be U0RBR ;)

The compiler is operating under the erroneous assumption that what is
written to the location should be read from it. Actually if U0THR is
volatile I think this may be a bug in the compiler although not one many
are going to run into I hope(2).

You could make a philosophical argument that for a write only register you
should return the value assigned to it.

Robert

1 - This assertion I'm not entirely sure of although this reference
http://msdn.microsoft.com/en-us/library/474dd6e2(VS.80).aspx agrees with
this interpretation. Even if this and other references I've read are wrong
in this interpretation the fact that that position is widespread is reason
enough to rewrite in a clearer fashion.

2 - If it's volatile the read cannot be optimized out but must actually
take place.

Robert

"C is known as a language that gives you enough rope to shoot yourself in
the foot." -- David Brown in comp.arch.embedded
http://www.aeolusdevelopment.com/
Okay, I agree. I now see that U0THR is indeed at the same register address as U0RBR.
And I'm willing to agree that it's probably cleaner to re-arrange the code as:

U0THR = ch;
return ch;

Even though the volatile construct does NOT imply that, in code generated for one C statement, code
should read back a memory location it just wrote, rather than using the value it just wrote to that
very same memory location.
It's only use is to prevent code optimizers (which work across multiple C statements) from making
certain assumptions about the contents of memory locations based on previous C code.
So, no compiler bug there.
And yes, it's probably theoratically conceivable, for convoluted C code and a processor with a very
limited number of registers, that code is generated that reads back the memory location it has
written to previously (ugh). Not because it's volatile, but because it simply couldn't hold the
value it in a register!
Since C doesn't have a notion of 'write only' storage, it's the programmer's responsibility to use
the above mentioned 'cleaner' code.
Realizing what we're dealing with at the hardware level, and the consequences for our code is, I
guess, what sets us embedded guys apart from 'regular' programmers.

Anyway, I think we also both agree, that this has nothing to do with the original problem.

;-)

~ Paul Claessen
----- Original Message -----
From: "Robert Adsett"
To: ;
Sent: Saturday, May 10, 2008 6:11 PM
Subject: Re: [lpc2000] Re: UART stuck waiting for THRE
> At 05:50 PM 5/10/2008 -0400, Paul Claessen wrote:
>
>>----- Original Message -----
>>From: "Robert Adsett"
>>To: ; ;
>>
>>Sent: Saturday, May 10, 2008 5:30 PM
>>Subject: Re: [lpc2000] Re: UART stuck waiting for THRE
>> > At 05:10 PM 5/10/2008 -0400, Robert Adsett wrote:
>> >>At 08:20 PM 5/10/2008 +0000, mattwoolsey wrote:
>> >> >That's not a comparison. It is writing ch to UxTHR. Besides, the
>> >> >problem is in the previous line with the while loop.
>> >>
>> >>Oops, quite right. I think that's bad style, but it's not the problem.
>> >
>> >
>> > Let me revise that assessment a bit. The code is not at all clear. It
>> > happens in this case to do what you want but I don't think it is at
>> > guaranteed to do so.
>> >
>> > Drifting off course a bit but I understand the value of an assignment
>> > expression to be the value of the left hand side of the assignment is the
>> > value of x=y; should be the value of x.
>> > The trouble is U0THR is write only. The read value of that location
>> should be U0RBR.
>>
>>==> [Paul] No trouble at all. The code just WROTE (R0) to that register.
>>It's not going to read it back. It still has that value in R0, and that's
>>what the
>>calling code sees as the return value.
>
> That's what it happens to use. I'm not convinced it must (or even should).
>
>>But again, even IF that return statement would return garbage, that still
>>doesn't
>>explain the hang in the previous statement!
>
> Agreed.
>
>> > A compiler could return that value instead.
>>==> [Paul] No it couldn't. It has NO CLUE that U0BRB is the 'read'
>>counter part of U0THR.
>
> Yes it could. The value of U0THR = x; (1) is U0THR. U0THR is defined as
> the location that U0THR is at. When you read that location you get
> U0RBR. Thus the value of U0THR = x; should be U0RBR ;)
>
> The compiler is operating under the erroneous assumption that what is
> written to the location should be read from it. Actually if U0THR is
> volatile I think this may be a bug in the compiler although not one many
> are going to run into I hope(2).
>
> You could make a philosophical argument that for a write only register you
> should return the value assigned to it.
>
> Robert
>
> 1 - This assertion I'm not entirely sure of although this reference
> http://msdn.microsoft.com/en-us/library/474dd6e2(VS.80).aspx agrees with
> this interpretation. Even if this and other references I've read are wrong
> in this interpretation the fact that that position is widespread is reason
> enough to rewrite in a clearer fashion.
>
> 2 - If it's volatile the read cannot be optimized out but must actually
> take place.
>
> Robert
>
> "C is known as a language that gives you enough rope to shoot yourself in
> the foot." -- David Brown in comp.arch.embedded
> http://www.aeolusdevelopment.com/
> No!
> Only writes to U0THR will clear the 0x20 flag in U0LSR.
>
> ~ Paul Claessen
>

My mistake. I read this from comments in driver code. Since the driver
is interrupt driver, it gets the interrupt source from U0IIR and only
reads U0LSR to check the error flags and these are really cleared by
the read - the THRE flag (0x20) is not cleared as you point out.

Sorry.

Regards

Mark

At 06:54 PM 5/10/2008 -0400, Paul Claessen wrote:
>Okay, I agree. I now see that U0THR is indeed at the same register address
>as U0RBR.
>And I'm willing to agree that it's probably cleaner to re-arrange the code as:
>
>U0THR = ch;
>return ch;

This also raises the question of why the routine returns what the caller
should already know. There may be a reason but I'd certainly ask the
question in a review. Makes no difference as to whether the code works though.
>Even though the volatile construct does NOT imply that, in code generated
>for one C statement, code
>should read back a memory location it just wrote, rather than using the
>value it just wrote to that
>very same memory location.
>It's only use is to prevent code optimizers (which work across multiple C
>statements) from making
>certain assumptions about the contents of memory locations based on
>previous C code.

Not quite. It's a little more specific than that. All access's to
volatile variables must take place and in the specified order. Every read
and every write. The compiler is not allowed to optimize any volatile
access out.

Since the value of U0THR=ch; is U0THR I assert that constitutes an access
and it must therefore take place. It's not that a write implies a read,
it's that return x; implies a read of x; So yes I think not reading it
would be a bug.

>So, no compiler bug there.
>And yes, it's probably theoratically conceivable, for convoluted C code
>and a processor with a very
>limited number of registers, that code is generated that reads back the
>memory location it has
>written to previously (ugh). Not because it's volatile, but because it
>simply couldn't hold the
>value it in a register!

Nope, if return( U0THR=ch); does not constitute a read of U0THR and UOTHR
is volatile then there must not be a read of U0THR.

If U0THR is volatile there must be exactly 1 or exactly 0 reads of that
location depending on whether the return constitutes an access. I believe
the read would be required. In either case depending on such obscure
behaviour is a bad idea.

There are not many constructs that rely on using the value of an assignment
statement and all that I'm aware of can be written more clearly. There is
no reason to obfuscate code with this mechanism that I'm aware of. For
that reason I wouldn't have a lot of sympathy for someone code failing
because they relied on the proper behaviour of this construct.

>Since C doesn't have a notion of 'write only' storage, it's the
>programmer's responsibility to use
>the above mentioned 'cleaner' code.
>Realizing what we're dealing with at the hardware level, and the
>consequences for our code is, I
>guess, what sets us embedded guys apart from 'regular' programmers.

Indeed it's a weakness in C that such registers don't map well to the
language. Also write only registers are clearly the spawn of evil :)
>Anyway, I think we also both agree, that this has nothing to do with the
>original problem.
>
>;-)

Oh absolutely. I suspect the original problem is not even in this routine
but I've not a lot to base that on.

Robert

"C is known as a language that gives you enough rope to shoot yourself in
the foot." -- David Brown in comp.arch.embedded
http://www.aeolusdevelopment.com/
--- In l..., Robert Adsett
wrote:
>
> At 08:20 PM 5/10/2008 +0000, mattwoolsey wrote:
> >That's not a comparison. It is writing ch to UxTHR. Besides, the
> >problem is in the previous line with the while loop.
>
> Oops, quite right. I think that's bad style, but it's not the
problem.
>
> >The assembly of the bad code looks like this:
> >
> >//------------------
> >
> > sendchar PROC
> >;;;82 //int sendchar (char ch)
> >;;;83 { /* Write character to Serial Port */
> >000034 e1a01000 MOV r1,r0
> >;;;84
> >;;;85 while (!(UxLSR & 0x20));
> >000038 e1a00000 MOV r0,r0
> > |L1.60|
> >00003c e59f003c LDR r0,|L1.128|
> >000040 e5d00014 LDRB r0,[r0,#0x14]
> >000044 e3100020 TST r0,#0x20
> >000048 0afffffb BEQ |L1.60|
>
> That looks OK
>
> >;;;86
> >;;;87 return (UxTHR = ch);
> >00004c e20100ff AND r0,r1,#0xff
> >000050 e59f2028 LDR r2,|L1.128|
> >000054 e5c20000 STRB r0,[r2,#0]
> >;;;88 }
> >000058 e12fff1e BX lr
> >;;;89
> > ENDP
> >
> > |L1.128|
> >000080 e000c000 DCD 0xe000c000
> >
> >//--------
> >
> >The assembly for the while loop looks ok to me, although I haven't
> >done a lot of ARM assembly.
> >
> >And here's the working version:
> >
> >//--------
> >
> > sendchar PROC
> >;;;84
> >;;;85 while (!(UxLSR & 0x20));
> >000034 e1a00000 MOV r0,r0
> > |L1.56|
> >000038 e59f1038 LDR r1,|L1.120|
> >00003c e5d11014 LDRB r1,[r1,#0x14]
> >000040 e3110020 TST r1,#0x20
> >000044 0afffffb BEQ |L1.56|
>
> That looks identical, other than register usage.
>
> >;;;86
> >;;;87 return (UxTHR = ch);
> >000048 e59f1028 LDR r1,|L1.120|
> >00004c e5c10000 STRB r0,[r1,#0]
> >;;;88 }
> >000050 e12fff1e BX lr
> >;;;89
> > ENDP
> >
> > |L1.120|
> >000078 e000c000 DCD 0xe000c000
> Somehow I think the problem is somewhere else entirely. Are you
running
> any interrupts? Why are you sure it's stuck in that loop?

No interrupts are running. I know it's stuck in the loop, because I
can comment out that line and then all the characters get sent.

>
> In that loop there are only a few possibilities
> - You are addressing the wrong location (not likely since
it's
> reloaded)
> - The location is really returning a value without the
6'th bit set
>
> For the second to be true the character must not have been sent.
For it
> not to have been sent there must be some reason separate from this
piece of
> code.
>
> Both are easily checkable walking through the loop if you are
running a
> JTAG debugger.

Sorry, no JTAG on this one. This is a really small board design that
doesn't even have room for the JTAG header. I already had a big
application up and running. Then decided to try the latest Keil
tools with the new compiler and things quit working. Even the serial
port wouldn't work. So I went back to the bare minimum and ran into
this strange issue.

>
> Robert
>
> http://www.aeolusdevelopment.com/
>
> From the Divided by a Common Language File (Edited to protect the
guilty)
> ME - "I'd like to get Price and delivery for connector Part # XXXXX"
> Dist./Rep - "$X.XX Lead time 37 days"
> ME - "Anything we can do about lead time? 37 days seems a bit
high."
> Dist./Rep - "that is the lead time given because our stock is
live.... we
> currently have stock."
>

At 01:57 AM 5/11/2008 +0000, mattwoolsey wrote:
>--- In l..., Robert Adsett
>wrote:
>
> > Somehow I think the problem is somewhere else entirely. Are you
>running
> > any interrupts? Why are you sure it's stuck in that loop?
>
>No interrupts are running. I know it's stuck in the loop, because I
>can comment out that line and then all the characters get sent.

That's a hint, but it doesn't prove it's stuck there. Have you heard the
term Heisenbug?

> > In that loop there are only a few possibilities
> > - You are addressing the wrong location (not likely since
>it's
> > reloaded)
> > - The location is really returning a value without the
>6'th bit set
> >
> > For the second to be true the character must not have been sent.
>For it
> > not to have been sent there must be some reason separate from this
>piece of
> > code.
> >
> > Both are easily checkable walking through the loop if you are
>running a
> > JTAG debugger.
>
>Sorry, no JTAG on this one. This is a really small board design that
>doesn't even have room for the JTAG header. I already had a big
>application up and running. Then decided to try the latest Keil
>tools with the new compiler and things quit working. Even the serial
>port wouldn't work. So I went back to the bare minimum and ran into
>this strange issue.

Well I don't see how it can be in your loop, so time to prove if it really
is. Steal an output pin. Set it high just before you enter the loop and
low when you exit. If it sticks high you will start to convince me.

Robert

"A million lines of code costs $20m to $40m ..... a million times
the cost of the flash chips it lives in. .... Yet accounting screams
over each added penny in recurring costs, while chanting the
dual mantras 'software is free,' and 'hey, it's only a software
change.'" Jack Ganssle in "A Million Lines of Code"

Robert Adsett - http://www.aeolusdevelopment.com/