Makefiles

A Makefile is a file, read by the Make program, which executes all the commands and rules in the Makefile.

A simple makefile

Remember:

  • the last operation we made was to create an executable file from an object file with the followinf command:

arm-none-eabi-ld -Ttext 0x8000000 -Tdata=0x20000000 fhc.o -o fhc.elf

  • but, first, we created an objet file names fhc.o from an assembly file fhc.s:

    arm-none-eabi-as -g fhc.s -o fhc.o

A makefile is composed of source files (here a .s file) and some rules.

The syntax of a makefile command is

target file: prerequisite (source file)

rules or recipes

First, let’s try:

fhc.elf: fhc.o
arm-none-eabi-ld -Ttext 0x8000000 -Tdata=0x20000000 fhc.o -o fhc.elf

Tip

When we mention `fhc.o’, make will automatically look for an implicit rule and use the ‘as’ assembler.

In order to disable the built-in implicit rules and the built-in variable settings, use “make -r -R”:

$ make -R -r
make: *** No rule to make target 'fhc.o', needed by 'fhc.elf'.  Stop.

In order to build the elf file, fhc.o (line 1) must be present. We say that fhc.o is prerequisited.

But fhc.o does not exist yet, since it was not assembled.

We need to add a second rule:

fhc.elf: fhc.o
   arm-none-eabi-ld -Ttext 0x8000000 -Tdata=0x20000000 fhc.o -o fhc.elf

fhc.o: fhc.s
   arm-none-eabi-as -mthumb -o fhc.o fhc.s

The file fhc.s can be assembled thanks to the new rule.

With fhc.o, the elf file can be built, thanks to the first rule.

Use a linker script (.ld file)

In the linker command “arm-none-eabi-ld -Ttext 0x8000000 -Tdata=0x20000000 fhc.o -o fhc.elf”, the memory directives can be put in a file.

The script is called “stm32l476x.ld”.

To suppress the warning “cannot find entry symbol _start; defaulting to 08000000”, we create an Entry Point.

When the MCU is powered on or reset, the Program Counter (PC) must point to the address of the first instruction to execute. The ENTRY directive ensures that the Reset_Handler function address is used as the initial value for the PC.

MEMORY will be a description of the L476 memory map. The memory attributes are r (read), w (write) and x (execute). ORIGIN specifies the start of a memory section.

1st linker script
1  ENTRY(Reset_Handler)
2
3  MEMORY
4  {
5     FLASH (rx)   : ORIGIN = 0x08000000, LENGTH = 1024K
6     SRAM  (rwx)  : ORIGIN = 0x20000000, LENGTH = 96K
7     SRAM2 (rwx)  : ORIGIN = 0x10000000, LENGTH = 32K
8  }

At power-up, the processor loads the Stack Pointer (SP), typically the Main Stack Pointer (MSP), with the value stored at address 0x00000000.

However, flash memory start at address 0x0800 0000. The MCU will remap the address 0x0800 0000 so that it appears to be 0x0000 0000.

At address 0x00000000, the MCU will load the value (highest address of RAM) into the stack pointer.

Sections

We’ll use:

.text : this section contains the program code, is read-only and resides in Flash memory.

.rodata : this section stores constants, string literals, and other data that should not change during program execution. It is placed in Flash memory.

.data : this section is used for initialized global and static variables. During startup, these values are copied from Flash to RAM so they can be modified during execution.

1st linker script
 1  ENTRY(Reset_Handler)
 2
 3  MEMORY
 4  {
 5     FLASH (rx)   : ORIGIN = 0x08000000, LENGTH = 1024K
 6     SRAM  (rwx)  : ORIGIN = 0x20000000, LENGTH = 96K
 7     SRAM2 (rwx)  : ORIGIN = 0x10000000, LENGTH = 32K
 8  }
 9
10  SECTIONS {
11 .text : {
12   *.o(.text)    /* fhc.o(.text) */
13    *(.rodata*)
14 } >FLASH
15
16 .data : {
17
18       fhc.o(.data)
19
20 } >SRAM2

}