alfin K

I plan to use this place to document my work, keep short notes for easy reference and write about things I might forget.

Bare Metal C

Notes on working with C compilers and linkers

published » updated
written by Alfin · category embedded ·

These are some notes for this topic based on these references:

  • Bare Metal C by Stephen Oualline published by No Starch Press.

Basics#

Arrays and Pointers#

They are very simmilar but differ in terms of how they are declared:

int array[5] = {1,2,3,4,5};
int* arrayPtr = array; // note we used array and not &array
                       // array is already a ptr
int i = array[1];
// is same as
int i = *(arrayPtr + 1);

Compilation and Linking#

Ideal C Model#

There are 3 standard sections: text, data, bss. text is where read-only data go to. data is the place for initialized data. bss is for uninitialized data. Use size to view each sections.

$ size example.o
    text    data    bss     dec     hex     filename
     481       4      4     489     1e9     example.o

dec is the total number and hex is the total in hexadecimal. Those sections are allocated by the compiler.

Non Standard Sections#

There can also be custom defined sections such as .isr_vector, which needs to be flashed into a particular address for the hardware to work.

Linking Process#

All these sections produced at compile time either wants to go to flash or RAM. For example your program code, read only data, ISR table these need to live inside your flash memory. All the variables that are initialized and uninitialized data must be sent to RAM. But RAM is volatile and usually gets written at runtime. How does the linker deal with this?

Code, Read-only Data#

These are straightforward. The linker script defines the starting address of flash and its size. This tells the linker where to put the different sections of code and read-only data. The linker finally assigns them an absolute address and it gets flashed into that exact location.

ISR Vector#

Usually there is a file called startup script given along with a toolchain. This is just a program written by the manufacture which sets up everything, and as the name suggests runs first before handing over control to your main. Inside this progam you have hardcoded addresses of ISR vectors which works for your chip. This file is also compiled and linked with your main program.

RAM Data#

In the linker script you also define the starting address of RAM and its size. With this information linker decides where to put your initialized and unitialized data. The startup script also does a few more things. It fills the sections allocated by the linker for initialized data and zero the memory for unitialized addresses in the RAM, essentially sending over your variables to RAM before your main program executes.

GCC flags#

  • -Wall -Wextra * : To print warning messages for code that is correct but questionable.

  • -Wa, <ASM_FLAGS> * : Pass flags that follow to assembler

  • -Wl, <LD_FLAGS> * : Pass flags that follow to the linker

Bit Twidling#

PORTD = (1 << 3) // sets the 3rd bit to 1
PORTD |= (1 << 3) // sets the 3rd bit to 1 keeping rest of them same

PORTD |= (1 << 3) | (1 << 5) // set multiple bits;
                             // keeping the rest of them same

PORTD &= ~(1 << 3) // unset a bit; keeping others same
PORTD &= ~((1 << 3) | (1 << 5)) // unset multiple bits;
                                // keeps rest of them same

Useful Snippets#

For print formating 32bit registers in hex use %08lx. Breakdown:

  • % start formatting
  • 0 leading zeros
  • 8 field width; bcoz 32 bit fits inside 8 fields perfectly.
  • l long integer type; is atleast 32 bit long
  • x hexadecimal

Select Problems#

Chapter 4 : Extracting bits 2 and 3 from an 8 bit register

uint8_t parity = (IO_REG >> 2) & 0x03