advertisement

Listen Print

Choosing a Compiler: The Little Things
Pages: 1, 2

Assembly Language Generation

Can your current cross compiler generate an assembly-language listing? Mine can. A command-line argument causes this compiler to produce assembly-language listings as part of the compilation process. Each input C/C++ source file results in a single assembly-language file being created.



The assembly language files contain the results of each compilation, exactly as the target processor will execute it. But this listing is in a human-readable form. The original C/C++ code is provided in comments that are interspersed with the assembly. Each line of source code is followed by the compiled result.

I find this feature very helpful for manual code optimization. This is because you can easily see what code is produced for each line of your high-level language program. And if a particular function is too slow for a given application, you'll be able to easily select the best part of the function to rewrite in assembly.

Related Articles:

Manipulating Fixed-Width Integer Data Types

Introduction to Closed-Loop Control

Introduction to Pulse Width Modulation (PWM)

Standard Libraries

When you're developing application software for a general-purpose computer, you expect that your compiler will include a set of standard C libraries, math libraries, and C++ classes. These include various routines like memcpy(), sin(), and cout, respectively. But because the functions in these libraries are not strictly part of the C or C++ language standards (the library standards are separate), a compiler vendor may choose to omit them. Such omissions are more common among vendors of the cross compilers used by embedded systems programmers. So in many cases, you've got to fight for your right to the standard libraries.

Just think about how much time you'd spend rewriting all of those functions yourself. Then spend some of that time insisting that standard libraries be included with any compiler that you buy. Of course, it's unlikely you'll be using printf() in the majority of embedded systems applications. But you may not realize just how many of the other functions you will need until it's already too late.

If standard libraries are provided, be sure that they are reentrant. In other words, that each of the functions within those libraries can be executed multiple times simultaneously. Reentrant functions can be called recursively or from within multiple threads of execution. What this means in the case of a library routine is that it may not use global variables. All of its internal data must be on the stack.

Fortunately, if you absolutely must buy a compiler that does not come with standard libraries, there is an alternative. Cygnus has put together a standard C library and math library specifically for embedded systems. All of the functions include source code and are designed with reentrancy in mind. And porting these libraries to new platforms is made easier by a design that puts all board and processor-specific interfaces into a single directory. The package is called newlib and the latest version is available for download at ftp://ftp.cygnus.com/pub/newlib/.

Startup Code

Another thing that non-embedded software development tools usually do for you automatically is to include startup code. Startup code is an extra piece of software that executes prior to main(). The startup code is generally written in assembly language and linked with any executable that you build. It prepares the way for the execution of programs written in a high-level language. Each such language has its own set of expectations about the run-time environment in which programs are executed. For example, many languages utilize a stack. Space for the stack must be allocated and some registers or data structures initialized before software written in the high-level language can be properly executed.

Startup code for an embedded system should be provided with the cross compiler. If the compiler is designed to be used for embedded software development, it generally will be. But it is also important to consider whether this code and its proper use are well documented. The startup code will likely be written in the assembly language of your target processor and should, ideally, be provided to you in source code form. If properly implemented, you shouldn't ever need to make any changes, but it's still helpful to look at it and understand what it does.

Startup code for C/C++ usually performs the following actions:

  1. Disable interrupts
  2. Copy any initialized data from ROM to RAM
  3. Zero the uninitialized data area
  4. Allocate space for and initialize the stack
  5. Create and initialize the heap
  6. Execute the constructors and initializers for all global variables (C++ only)
  7. Enable interrupts
  8. Call main()

To properly utilize the compiler-supplied startup code, you'll need to know how it should be linked with your program. You'll also need to know where and how to place the initialized data in ROM (so they can be copied to RAM) and how to set the size of the stack and heap. In the best case, these steps are documented in the literature provided by the compiler vendor. But I have seen many cases in which they were not.

Table 1. Checklist for Cross Compiler Selection
Target Processor
The first step in selecting a cross compiler is finding one that will produce code for your target processor.
Host Platform
The next step is to decide on a development platform. If there are several platforms available, you may want to check some of the other items in this list before making a decision about the host.
RTOS Support
If you're planning to use an RTOS, does the compiler vendor have a working relationship with your RTOS vendor? This is important because part or all of the RTOS may be provided in object files or libraries. For compatibility, your compiler and linker must support that same object file format.
Integration with Other Tools
Is the compiler compatible with any debugging environments? Is a make utility included? If the compiler is shipped with an IDE, is it extensible so that you can integrate your version control tool?
Standard Libraries
Will you need functions from the standard C library, math library, or C++ classes? If so, are they provided with the compiler? Are all of the functions in those libraries reentrant?
Startup Code
Is startup code for embedded systems provided? Are the code and its use well documented? If you can't find any mention of startup code in the user's manuals for a potential cross compiler, consider that a bad sign.
Execution Speed Optimizations
If your program is too slow, you'll want the compiler to try to speed it up. Will the compiler do this? If so, what specific optimizations are supported? Can they be individually enabled or disabled?
Program Size Optimizations
If your program is too big for your target memory, you'll want the compiler to attempt to reduce the amount of code space used. Will the compiler do this? If so, what specific optimizations are supported?
Support for Embedded C++
The Embedded C++ (EC++) standard is a proper subset of the C++ language and libraries that reduces run-time overhead. In order to restrict yourself to EC++ functionality, you'll need a cross compiler that knows what features of the language it is not allowed to use.

Details, Details, Details

Obviously, a compiler that lacks some of the features I've mentioned above may still be a good compiler. And there will undoubtedly be a few people who would argue that non-standard features like the 'asm' and 'interrupt' keywords reduce code portability. But if you've got a choice between two or more cross compilers that are otherwise equivalent, you may want to look for these things. Even if they aren't strictly required to get the job done, they may just make your work easier. And they will certainly reduce programmer frustration.

Michael Barr is a leading authority on the design of embedded computer systems. He has provided expert testimony in court, appeared on the PBS show "American Business Review", and been quoted in newspaper articles. Barr is also the author of more than forty technical articles, co-author of the "Embedded Systems Dictionary", and founder of Embedipedia.net.

This article was originally published in the May 1999 issue of Embedded Systems Programming.


O'Reilly & Associates published Programming Embedded Systems in C and C++ in January 1999.


Return to the O'Reilly Network.