Programming embedded systems: Startup code and the world before main() – Embedded

Posted under Programming, Technology On By James Steward

Advertisement

Advertisement
Embedded.com
Advertisement
“Normal” programmers can get away with the assumption that their C programs start executing from the main() function. But embedded developers need to know what happens before that. Lesson 13 explores the strange world before main(), also known as the “startup code”:
Lesson 13 – What is startup code, and how the CPU gets from reset to main?

The video shows that the CPU begins after the reset by executing some library code (from IAR in this case) that initializes certain “data sections” in RAM. But what are these sections? The best way to find out is to generate the linker map file. This file tells you how big the various modules are, where precisely in memory they reside, and much more. I highly recommend that you get into the habit of generating the linker map file for all your projects and learn how to read it. This is a treasure trove of fascinating information, much like an actual map for your code.
A program section is a contiguous chunk of memory with a symbolic name. For historical reasons, the main program sections are named: .text (dot-text) for the code in ROM, .bss for uninitialized read-write data in RAM, .rodata for read-only data in ROM, and .data for initialized read-write data. The.data section in RAM also has a matching section with the initial values in ROM. This ROM section has no common name. The IAR linker calls it “Initializer bytes,” but other linkers might call it differently.
The main job of the startup code is to correctly initialize the RAM sections. The C Standard [1] requires the following initializations to be completed before calling main():
Even before initializing the RAM sections, the CPU executes some processor-specific instructions. For example, the ARM Cortex-M CPU is hardwired to load the Main Stack Pointer (the SP register) from address 0x0 and the Program Counter (PC) from address 0x4. These two words must be in ROM and are part of the Cortex-M vector table [2]. The second word (loaded into the PC) is also known as the reset vector.
The generic vector table and the code corresponding to the reset vector (__iar_program_start in this case) are both provided in the library accompanying the compiler. But to use interrupts (a subject of future lessons), you need to replace the generic vector table with your own, as the video explains.
More than once in my career, I’ve encountered non-compliant startup code that failed to clear the .bss sections, and consequently, the static variables had random initial values. This was a surprisingly hard problem to diagnose, so I recommend you always check your startup code. If you find incomplete initialization, you should fix the startup code (e.g., to clear the .bss section) instead of modifying the source code to explicitly initialize variables. Explicit initialization pushes variables from .bss into .data sections, which comes at a high cost of additional “Initializer bytes” in ROM. (So you waste both ROM for a bunch of zeros and CPU cycles to copy those zeros into RAM). Also, it’s easy to miss initializations, so some variables (especially those from third-party libraries) might remain uninitialized. These can be the “ticking bombs” in your code.
[1] ISO/IEC 9899:TC3 Standard (C99)
[2] Joseph Yiu, “The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors, 3rd Edition”, ISBN:‎ 978-0124080829
Related Contents:
For more Embedded, subscribe to Embedded’s weekly email newsletter.

Advertisement
You must or to post a comment.
This site uses Akismet to reduce spam. Learn how your comment data is processed.
Advertisement
Advertisement
Advertisement

source

Note that any programming tips and code writing requires some knowledge of computer programming. Please, be careful if you do not know what you are doing…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.