Booting A Cortex M with NO IDE : like a caveman : Part 4 Compile,Link and Flash

 

So here we are at the end of this series now we get to put all the ones and zeros into the chip.

Compiling the main.c and myStartUp.c is straight forward. I will compile them separately and link it all in separate steps and that the end show the single command to do it all in one shot.

First lets make main.c have some thing to do, doesn't matter what it does.

 
#include "stdint.h"

// these will end up in the .data section
volatile uint32_t dummy1 = 0x12345678;
// these will end up in the .rodata section because they are constant
const uint32_t dummy2 = 0x87654321;
const float pi = 3.14159;
const char msg[] = "Hello, World!";

// these will end up in the .bss section because they are uninitialized
volatile uint32_t counter;
volatile uint32_t data[10];


// main function will end up in the .text section
void main(void)
{
char msgCopy[sizeof(msg)];
// Infinite loop
while(1){
counter++;
// Copy the message to another array
for (uint32_t i = 0; i < sizeof(msg); i++)
{
msgCopy[i] = msg[i];
}
}
}
 

Assuming you have openocd and ATM GNU tools on your system path per the Part 1 of the post.

The command is : arm-none-eabi-gcc -g -c -mthumb -mcpu=cortex-m0plus -std=gnu11 ./src/main.c -o ./outputs/main.o

  • arm-none-eabi-gcc: The GCC compiler for ARM Cortex-M processors (no OS or standard libraries).
  • -g: Includes debugging information in the compiled output for debugging tools. Generally removed for release firmware, otherwise present so your able to debug properly.
  • -c: Compiles the source code into an object file (.o), without linking.
  • -mthumb: Compiles the code in Thumb instruction set mode, optimized for ARM's reduced instruction set.
  • -mcpu=cortex-m0plus: Specifies the target CPU architecture as Cortex-M0+. This will be specific to your MCU, see the GCC manual -mcpu or -march
  • -std=gnu11: Sets the C standard to GNU C11, which is the C11 standard with GNU extensions. Some of you may still like C99, take your pick.
  • ./src/main.c: Path to the source file to compile.
  • -o ./outputs/main.o: Specifies the output file for the compiled object code (main.o).
 

We will do the exact same for myStartUp.c

The command is : arm-none-eabi-gcc -g -c -mthumb -mcpu=cortex-m0plus -std=gnu11 myStartUp.c -o ./outputs/myStartUp.o

Next we will link the object files like so: arm-none-eabi-gcc -g -nostdlib -T myLinkerScript.ld outputs/*.o  -o outputs/boot.elf -Wl,-Map=outputs/boot.map

  •  arm-none-eabi-gcc: GCC compiler for ARM Cortex-M processors (bare-metal, no OS).
  • -g: Includes debugging information in the output.
  • -nostdlib: Prevents linking with the standard C library, useful for embedded systems.
  • -T myLinkerScript.ld: Specifies the custom linker script to control memory layout and sections.
  • outputs/*.o: Specifies the object files (.o) to be linked. Here we use a wild card (*) to tell it to grab all .o files in that directory
  • -o outputs/boot.elf: Specifies the output file as the ELF executable (you can name it whatever you want).
  • -Wl,-Map=boot.map: Instructs the linker to generate a memory map file (boot.map) for debugging purposes. This is use full to verify our linker script sections and their content.

 Now lets analyze that map file and see where everything ended up.

.data 0x20000000 0x4 load address 0x0800019c
0x20000000 . = ALIGN (0x4)
0x20000000 _data = .
*(.data)
.data 0x20000000 0x4 outputs/main.o
0x20000000 dummy1
.data 0x20000004 0x0 outputs/myStartUp.o
*(.data*)
0x20000004 . = ALIGN (0x4)
0x20000004 _edata = .
0x0800019c _sidata = LOADADDR (.data)

 dummy1 ended up in the data section rightfully so, we can also see our symbols declared in the linker scripts are being resolved.

.bss 0x20000004 0x2c load address 0x080001a0
0x20000004 . = ALIGN (0x4)
0x20000004 _bss = .
*(.bss)
.bss 0x20000004 0x2c outputs/main.o
0x20000004 counter
0x20000008 data
.bss 0x20000030 0x0 outputs/myStartUp.o
*(.bss*)

Out uninitialized variables are in the .bss section.

*(.rodata)
.rodata 0x08000184 0x16 outputs/main.o
0x08000184 dummy2
0x08000188 pi
0x0800018c msg
*(.rodata*)

 Constants in the .rodata section.

LOAD outputs/main.o
LOAD outputs/myStartUp.o
0x20009000 _top_of_ram_stack_start = (ORIGIN (RAM) + LENGTH (RAM))
0x00000200 _Min_Heap_Size = 0x200
0x00000400 _Min_Stack_Size = 0x400

.isr_vector 0x08000000 0xbc
0x08000000 . = ALIGN (0x4)
*(.isr_vector)
.isr_vector 0x08000000 0xbc outputs/myStartUp.o
0x08000000 vector_table
0x080000bc . = ALIGN (0x4)

.text 0x080000bc 0xc8
0x080000bc . = ALIGN (0x4)
*(.text)
.text 0x080000bc 0x40 outputs/main.o
0x080000bc main
.text 0x080000fc 0x88 outputs/myStartUp.o
0x080000fc Reset_Handler
0x0800017c USART3_4_LPUART1_IRQHandler
0x0800017c DebugMon_Handler

We can see above our vector table which is supposed to start at 0x00000000 actually starts at 0x08000000 but we talked about why that is. Everything looks great. Now lets flash the device.

The following commands assume you have openocd, scripts, and arm gnu tools on path/ environment variables.

Start openocd with the following command (tailored to match your chip of course):

openocd -f interface/stlink.cfg -f target/stm32g0x.cfg

As you can see there is a local port listening at 3333

So we open up arm gdb and connect to that port, preferably just at the outputs directory when you do this:

the command is :

arm-none-eabi-gdb

Once ARM gdb is open run the following command to connect to localhost.

target remote localhost:3333


 
The GDB command to actually flash the device are as follow in this order:

  1. file boot.elf
  2. load

You can now set a break point at main and set through the code with these commands

  1. b main (set the break point)
  2. layoutr src (this command only works on linux)
  3. c (continue/start running code)
  4. n (n or s will go to next line, or step to next line)


 

And there you have it , you have booted the Cortex M core with no IDE or no magic being done for you in the background, aside form compiling, linking etc.

I plan a making a post on some useful GDB debugging features in conjunction with VSCode. 

My next post that is not part of this "Booting up" series, but it will be on build systems, I will use the same project as a starting base.



<< PREVIOUS

 

 

 

 

 

 

 

 

Comments

Share your comments with me

Archive

Contact Form

Send