EmbeddedRelated.com
Forums

CCS 4 question

Started by FIRAT KOCAK January 30, 2011
On 31/01/2011 07:59, Fırat Koçak wrote:
> Hi David,
>
> What i understand from what you have written is that Compiler, while
> targeting the code, eliminates the piece of code which is unnecessary or
> time consuming if that does not contain variable(s) declared as
> volatile. Right ?
>

That's partly right. The compiler can eliminate code that is
unnecessary. It has no concept of "time consuming", and even if it did,
it certainly couldn't eliminate code just because it took time to run!

The use of "volatile" in a case like the OP's, is to tell the compiler
that the code is /not/ unnecessary even though it looks that way. If
you make the index variable in a delay loop volatile, then the compiler
is required to generate reads and writes to a storage space for that
variable with the requested values, and in the requested order, with no
assumptions as to what might happen to that storage space outside the
compiler's control.

void Delay( int dcount ) {
volatile int iz;
for( iz = 0; iz < dcount; iz ++ ){}
}

The compiler is still free to do some sorts of optimisations and code
changes if it wants. For example, it can inline calls to Delay(), or
could partially unroll the loop. It could even decide to calculate the
first ten digits of pi inside the loop - as long as it didn't change the
reads and writes to the volatile data.

This is why such delay loops give only very rough timing - you have no
guarantees. Different compilers, different versions of the same
compiler, the same compiler with different flags, and the same compiler
with the same flags but different calling code, can compile this loop in
different ways and get different timings.

There are several ways to get reliable timing in a microcontroller:

1. You make use of the hardware timers in some way, such as setting up
an interrupt.

2. You poll hardware timers to check for elapsed time (this is often
quite simple and a good choice).

3. You write the critical part of the code in assembly (typically using
"asm volatile" statements - a good compiler can eliminate dead assembly
code under certain circumstances). That way you know how many clock
cycles each loop takes, and know exactly how it works.

4. You write the code in C, compile it with different flags and examine
the generated assembly code. You can then be confident of how your
Delay function works for that compiler - this can often be good enough
for a given project, as long as you are aware of the portability issues.

5. You write the code in C, and use compiler-specific __attribute__'s or
pragmas to force specific compilation or optimisation options for that
function. You must still check the assembly, and consider the code
non-portable, but you will have less risk of problems if you use
different compiler flags for the rest of the project.

6. You use a Delay function of some sort provided by your toolchain
vendor. These are often included as part of the library or as an
intrinsic function. Then it is the toolchain vendor's responsibility to
use assembly or C in a way that is independent of the compiler flags,
and with a timing that is properly specified.
Options 2 and 6 are typically the best choices. Option 4 sounds like a
quick and easy solution, but beware that you can get a lot of variation.
It might not matter if the time between your LED blinks is 400 ms or
600 ms, but it /will/ matter if it is 2000 ms.

mvh.,

David

> In the meantime, thank you very much for the detailed information.
>
> Best regards,
>
> Firat
>
> On 31.01.2011 01:52, David Brown wrote:
> >
> > On 30/01/11 23:47, FIRAT KOCAK wrote:
> > > Hi David,
> > >
> > > Honestly, after i read your message i said, "huh, that is it!" . thank
> > > you very much. The problem has now gone. It runs well. I had read about
> > > the issue "volatile" in the past messages but i have never thought it
> > > will happen to me :) I am still struggling to understand why compiler
> > > removes a local variable even though it is in use ?
> > >
> >
> > "volatile" is very simple to understand once you realise that a compiler
> > does not translate your source code into target code. A compiler reads
> > your source code, and generates target code that has the same effect as
> > though it had done a direct translation, but only in respect to reading
> > and writing of volatile data.
> >
> > Re-read the previous paragraph, as it is a big step for many people.
> >
> > The compiler can totally disregard everything to do with local
> > variables, global variables, functions, etc. All it is interested in is
> > that every time your source code says it should read some volatile data,
> > that data is read, and that every time your source code says it should
> > write some volatile data, the correct value is written out.
> >
> > Typically, it will do this using a rough translation as a starting point
> > - but only because that's the easiest way to handle the task.
> >
> > In particular, if the compiler can figure out that a piece of code has
> > no effect on any volatile accesses, then it can remove it entirely.
> >
> > Also note that that the compiler has absolutely no sense of timing - the
> > C language has no concept of time, only of the order of volatile
> > accesses. So the correctness of the compiler's job is unchanged if it
> > eliminates time-wasting code, or even if it adds time-wasting code
> > (though compilers that add extra time-wasting code don't stay on the
> > market very long).
> >
> > For hosted environments (such as on a desktop), calls to external
> > library code also count as "volatile" for compilation purposes.
> >
> > Another common misconception is what "optimisation" flags do. They do
> > /not/ enable or disable "optimisation", or command the compiler to
> > generate different types of code. They are just hints as to how much
> > effort the compiler should make in generating smaller and faster code,
> > and sometimes they inform the compiler about assumptions it can make
> > beyond the requirements of the C language (for example, you might
> > promise to avoid aliasing data, and let the compiler take advantage of
> > that promise to generate faster code).
> >
> > In particular, the compiler can remove useless code even with all
> > optimisations disabled.
> >
> > > I think i need some time to better learn and read much about the
> > > compiler behavior.
> > >
> > > Thank you and thanks to Bob and Dan.
> > >
> > > Firat
> > >
> > > On 31.01.2011 00:24, David Brown wrote:
> > > >
> > > > On 30/01/11 20:53, FIRAT KOCAK wrote:
> > > > > Hi,
> > > > >
> > > > > I am a newbie to Msp430 and CCS. I installed CCS 4 and trying to
> > learn
> > > > > some coding Msp430 Launchpad. I modified original Launchpad code
> > ( in
> > > > > fact removed almost all code just adding led pins update in a loop
> > > ). My
> > > > > code is so simple and includes a few line codes.
> > > > >
> > > >
> > > > If you are using CCS, I'd be sceptical of the compiler. Your code
> > > > doesn't yet make use of your global variables like "BitCnt", but be
> > > > aware that CCS does not initialise these to zero - in
> contradiction to
> > > > the C standards.
> > > >
> > > > > void Delay( int dcount )
> > > > > {
> > > > > int iz;
> > > > >
> > > > > for( iz = 0; iz < dcount; iz ++ ){}
> > > > > }
> > > > >
> > > >
> > > > You must declare "iz" to be volatile - if not, the compiler can
> remove
> > > > that loop entirely. It may well do so regardless of any optimisation
> > > > settings you may have picked.
> > > >
> > > >
> >
>

Beginning Microcontrollers with the MSP430

On 31/01/2011 02:50, Dan Bloomquist wrote:
> David Brown wrote:
> > On 31/01/11 00:10, Dan Bloomquist wrote:
> >
> >> Hi Firat,
> >>
> >> I just had to know. So I pasted your code into a default CCS project. It
> >> worked just as expected. I don't know why your build is optimized.
> >>
> >> Have your view/projects turned on. Right click the project and do 'Build
> >> properties'. Goto Compiler/optimizations and make sure nothing is
> >> checked. You don't need them to start with and it will keep your life
> >> simpler as you learn.
> >>
> >>
> > Absolutely wrong! You will /not/ make your life simpler by learning to
> > write bad code that only works with particular compiler flags - you will
> > make it simpler by learning to write correct code from the start. You
> > cannot do embedded programming until you understand "volatile".
> > "Learning" embedded programming with optimisations disabled "so that it
> > works" is like learning to drive a car in only first gear, because you
> > can't figure out how the brakes work, so you don't want to go too fast.
> >
> > Code whose correctness depends on the optimisation flags is broken code
> > (or a broken compiler), whether it works or not.
> >
> > Well, OK, if you say so...
>

I've been programming embedded systems for a /long/ time - using lots of
different tools of different qualities, and lots of different targets.
And perhaps more important than my own direct experience, I have
participated on mailing lists and newsgroups to help others since the
day our company got its first dial-up modem. I have seen a /lot/ of
examples of people having trouble because they don't understand about
"volatile" and optimisations, and find their code no longer works when
they enable optimisations, or when they move to a better compiler.

I have no doubt whatsoever that the best answer is to learn to use
volatile correctly from day 1 when you are doing embedded programming.
It will be of great benefit later.

I don't know anything about your background except your mention of
having programmed with MS VS for over a decade as your "day job". It is
important to realise that there is a big difference here between
bare-metal embedded programming, and programming for an operating
system. When using an OS, the program interacts with the outside world
using system calls (generally via a library). These system calls take
the place of "volatile" in almost all cases - the compiler will always
make these calls as requested by the source code. But for bare-metal
programming, you don't have that - you use "volatile" accesses to
interact with the world. Often this is done using the hardware register
definitions in include files - these are invariably defined as
"volatile". But if you want to have interaction outside of these, you
need to use "volatile" yourself.

And once you start working with interrupt routines, you have no choice
but to get "volatile" correct. Regardless of your optimisation flags,
"volatile" is what makes the difference between the system working all
the time, or the system working all the time except for impossible to
reproduce bugs that turn up occasionally on the customer's system.
I am a professional embedded programmer. I want my code to be correct -
it is not enough for me that something happens to work under certain
artificially constrained circumstances. I know that these circumstances
change, and fragile code breaks. And I also know there are a lot of
programmers who get these things wrong, even people with long experience
and/or education. So I have no qualms about giving people helpful
advice about how to write better code - many people need that advice.
Of course, please let me know if you think I am wrong - either that
there are circumstances when it is appropriate for code correctness to
depend on optimisation flags, or perhaps that it is not something to
emphasize for newbies to the field. I have plenty to learn here,
regarding both programming and helping other programmers. And even if
it turns out that we don't agree, the discussion can be informative to
both ourselves and others reading the thread.

mvh.,

David
Hi David,

I now understand what you mean. I was expecting an adequate answer but
your is really huge. Thank you very very much. I am now more confident
with "volatile" and i will follow your recommendations. since this is my
first entry to using Msp430, for sure, i am not so much careful about
writing a good code, thus the code was a bit dirty. Since i am in the
beginning of the first phase of my learning curve, my intention is to
learn Msp430 peripherals, registers, memory, interrupts, timers, clock
setting and so on. After then i go in deep for a better code writing.
But with not forgetting your recommendations of a better code writing.

Writing a precise delay function is a big matter. Because i am not
writing a critical piece of code (at least not in the near future ) i
am OK and a rough C code will be enough.

David, thank you very much for your efforts helping me understand the
issue "volatile" and the other things. You are such a great friend!.

Best regards,

Firat

On 31.01.2011 10:35, David Brown wrote:
>
> On 31/01/2011 07:59, Fırat Koçak wrote:
> > Hi David,
> >
> > What i understand from what you have written is that Compiler, while
> > targeting the code, eliminates the piece of code which is unnecessary or
> > time consuming if that does not contain variable(s) declared as
> > volatile. Right ?
> > That's partly right. The compiler can eliminate code that is
> unnecessary. It has no concept of "time consuming", and even if it did,
> it certainly couldn't eliminate code just because it took time to run!
>
> The use of "volatile" in a case like the OP's, is to tell the compiler
> that the code is /not/ unnecessary even though it looks that way. If
> you make the index variable in a delay loop volatile, then the compiler
> is required to generate reads and writes to a storage space for that
> variable with the requested values, and in the requested order, with no
> assumptions as to what might happen to that storage space outside the
> compiler's control.
>
> void Delay( int dcount ) {
> volatile int iz;
> for( iz = 0; iz < dcount; iz ++ ){}
> }
>
> The compiler is still free to do some sorts of optimisations and code
> changes if it wants. For example, it can inline calls to Delay(), or
> could partially unroll the loop. It could even decide to calculate the
> first ten digits of pi inside the loop - as long as it didn't change the
> reads and writes to the volatile data.
>
> This is why such delay loops give only very rough timing - you have no
> guarantees. Different compilers, different versions of the same
> compiler, the same compiler with different flags, and the same compiler
> with the same flags but different calling code, can compile this loop in
> different ways and get different timings.
>
> There are several ways to get reliable timing in a microcontroller:
>
> 1. You make use of the hardware timers in some way, such as setting up
> an interrupt.
>
> 2. You poll hardware timers to check for elapsed time (this is often
> quite simple and a good choice).
>
> 3. You write the critical part of the code in assembly (typically using
> "asm volatile" statements - a good compiler can eliminate dead assembly
> code under certain circumstances). That way you know how many clock
> cycles each loop takes, and know exactly how it works.
>
> 4. You write the code in C, compile it with different flags and examine
> the generated assembly code. You can then be confident of how your
> Delay function works for that compiler - this can often be good enough
> for a given project, as long as you are aware of the portability issues.
>
> 5. You write the code in C, and use compiler-specific __attribute__'s or
> pragmas to force specific compilation or optimisation options for that
> function. You must still check the assembly, and consider the code
> non-portable, but you will have less risk of problems if you use
> different compiler flags for the rest of the project.
>
> 6. You use a Delay function of some sort provided by your toolchain
> vendor. These are often included as part of the library or as an
> intrinsic function. Then it is the toolchain vendor's responsibility to
> use assembly or C in a way that is independent of the compiler flags,
> and with a timing that is properly specified.
>
> Options 2 and 6 are typically the best choices. Option 4 sounds like a
> quick and easy solution, but beware that you can get a lot of variation.
> It might not matter if the time between your LED blinks is 400 ms or
> 600 ms, but it /will/ matter if it is 2000 ms.
>
> mvh.,
>
> David
>
> > In the meantime, thank you very much for the detailed information.
> >
> > Best regards,
> >
> > Firat
> >
> > On 31.01.2011 01:52, David Brown wrote:
> > >
> > > On 30/01/11 23:47, FIRAT KOCAK wrote:
> > > > Hi David,
> > > >
> > > > Honestly, after i read your message i said, "huh, that is it!" .
> thank
> > > > you very much. The problem has now gone. It runs well. I had
> read about
> > > > the issue "volatile" in the past messages but i have never
> thought it
> > > > will happen to me :) I am still struggling to understand why
> compiler
> > > > removes a local variable even though it is in use ?
> > > >
> > >
> > > "volatile" is very simple to understand once you realise that a
> compiler
> > > does not translate your source code into target code. A compiler reads
> > > your source code, and generates target code that has the same
> effect as
> > > though it had done a direct translation, but only in respect to
> reading
> > > and writing of volatile data.
> > >
> > > Re-read the previous paragraph, as it is a big step for many people.
> > >
> > > The compiler can totally disregard everything to do with local
> > > variables, global variables, functions, etc. All it is interested
> in is
> > > that every time your source code says it should read some volatile
> data,
> > > that data is read, and that every time your source code says it should
> > > write some volatile data, the correct value is written out.
> > >
> > > Typically, it will do this using a rough translation as a starting
> point
> > > - but only because that's the easiest way to handle the task.
> > >
> > > In particular, if the compiler can figure out that a piece of code has
> > > no effect on any volatile accesses, then it can remove it entirely.
> > >
> > > Also note that that the compiler has absolutely no sense of timing
> - the
> > > C language has no concept of time, only of the order of volatile
> > > accesses. So the correctness of the compiler's job is unchanged if it
> > > eliminates time-wasting code, or even if it adds time-wasting code
> > > (though compilers that add extra time-wasting code don't stay on the
> > > market very long).
> > >
> > > For hosted environments (such as on a desktop), calls to external
> > > library code also count as "volatile" for compilation purposes.
> > >
> > > Another common misconception is what "optimisation" flags do. They do
> > > /not/ enable or disable "optimisation", or command the compiler to
> > > generate different types of code. They are just hints as to how much
> > > effort the compiler should make in generating smaller and faster code,
> > > and sometimes they inform the compiler about assumptions it can make
> > > beyond the requirements of the C language (for example, you might
> > > promise to avoid aliasing data, and let the compiler take advantage of
> > > that promise to generate faster code).
> > >
> > > In particular, the compiler can remove useless code even with all
> > > optimisations disabled.
> > >
> > > > I think i need some time to better learn and read much about the
> > > > compiler behavior.
> > > >
> > > > Thank you and thanks to Bob and Dan.
> > > >
> > > > Firat
> > > >
> > > > On 31.01.2011 00:24, David Brown wrote:
> > > > >
> > > > > On 30/01/11 20:53, FIRAT KOCAK wrote:
> > > > > > Hi,
> > > > > >
> > > > > > I am a newbie to Msp430 and CCS. I installed CCS 4 and trying to
> > > learn
> > > > > > some coding Msp430 Launchpad. I modified original Launchpad code
> > > ( in
> > > > > > fact removed almost all code just adding led pins update in
> a loop
> > > > ). My
> > > > > > code is so simple and includes a few line codes.
> > > > > >
> > > > >
> > > > > If you are using CCS, I'd be sceptical of the compiler. Your code
> > > > > doesn't yet make use of your global variables like "BitCnt",
> but be
> > > > > aware that CCS does not initialise these to zero - in
> > contradiction to
> > > > > the C standards.
> > > > >
> > > > > > void Delay( int dcount )
> > > > > > {
> > > > > > int iz;
> > > > > >
> > > > > > for( iz = 0; iz < dcount; iz ++ ){}
> > > > > > }
> > > > > >
> > > > >
> > > > > You must declare "iz" to be volatile - if not, the compiler can
> > remove
> > > > > that loop entirely. It may well do so regardless of any
> optimisation
> > > > > settings you may have picked.
> > > > >
> > > > >
> > >
> > >
> >