/* Linker script to configure memory regions. * Need modifying for a specific board. * FLASH.ORIGIN: starting address of flash * FLASH.LENGTH: length of flash * RAM.ORIGIN: starting address of RAM bank 0 * RAM.LENGTH: length of RAM bank 0 */ MEMORY { FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x20000 /* 128K */ RAM (rwx) : ORIGIN = 0x10000000, LENGTH = 0x2000 /* 8K */ }It needs to be modified with actual flash and ram locations and sizes.
Also this link script does not contain any information for the linker to know how to locate the output of the C compiler. Code, constant data and initial value of variables need to be located in flash, variables and stack need to be located in ram. I need a better link script that specify all of that. nokeep.ld in the sample scripts folder is the one I need.
/* Linker script to configure memory regions. * Need modifying for a specific board. * FLASH.ORIGIN: starting address of flash * FLASH.LENGTH: length of flash * RAM.ORIGIN: starting address of RAM bank 0 * RAM.LENGTH: length of RAM bank 0 */ MEMORY { FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x20000 /* 128K */ RAM (rwx) : ORIGIN = 0x10000000, LENGTH = 0x2000 /* 8K */ } /* Linker script to place sections and symbol values. Should be used together * with other linker script that defines memory regions FLASH and RAM. * It references following symbols, which must be defined in code: * Reset_Handler : Entry of reset handler * * It defines following symbols, which code can use without definition: * __exidx_start * __exidx_end ... ... * __StackTop * __stack */ ENTRY(Reset_Handler) SECTIONS { ... ...From this snippet I can see that not only flash and ram parameters but also the entry point for code execution, Reset_Handler, needs to be provided.
As a check, let’s change the link script to nokeep.ld in Makefile and generate an executable .elf from the empty source code file empty.c:
LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/nokeep.ld
$ make empty.elf empty.elf D:\Program Files (x86)\GNU Arm Embedded Toolchain\arm-gnu-toolchain-13.3.rel1-mi ngw-w64-i686-arm-none-eabi\bin\arm-none-eabi-ld.exe: warning: cannot find entry symbol Reset_Handler; defaulting to 00000000 rm empty.oThe linker gives a warning and fallback on a default address as entry point.
So let’s create boot.c with an idle loop as Reset_Handler:
void Reset_Handler( void) { for( ;;) ; }In order to better understand the output of the link phase, I make the following changes to the Makefile:
OBJDUMP = $(BINPFX)objdump SIZE = $(BINPFX)size CFLAGS = -g clean: @echo CLEAN @rm -f *.o *.elf *.map *.lst *.bin *.hex %.elf: %.o @echo $@ $(LD) -T$(LD_SCRIPT) -Map=$*.map -cref -o $@ $< $(SIZE) $@ $(OBJDUMP) -hS $@ > $*.lst
$ make boot.elf boot.elf text data bss dec hex filename 12 0 0 12 c boot.elf rm boot.oI can see that this build results in 12 bytes of code in the text section. More details can be found in the boot.map and boot.lst files.
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4K }The Makefile needs the following changes:
CPU = -mthumb -mcpu=cortex-m0 CFLAGS = $(CPU) -g -Wall -Wextra -Os LD_SCRIPT = f030f4.ldAt boot time, the Arm core fetches the initial address of the stack pointer and the address where to start execution from the first two entries of its interrupt routine table. I have to modify boot.c to initialize such a table in accord with the symbols defined in the link script.
/* Memory locations defined by linker script */ extern long __StackTop ; /* &__StackTop points after end of stack */ void Reset_Handler( void) ; /* Entry point for execution */ /* Interrupt vector table: * 1 Stack Pointer reset value * 15 System Exceptions * NN Device specific Interrupts */ typedef void (*isr_p)( void) ; isr_p const isr_vector[ 2] __attribute__((section(".isr_vector"))) = { (isr_p) &__StackTop, /* System Exceptions */ Reset_Handler } ; void Reset_Handler( void) { for( ;;) ; }__StackTop is defined by the linker script and is located after the end of the RAM. I use the GNU C extension __attribute__() to name the section where I want the interrupt vector to be included. If you check the linker script you will see that it places .isr_vector at the start of the text area which is located at the beginning of the flash memory. I chose to name the interrupt vector table isr_vector to match the section name .isr_vector, but it is really the section name that matters here.
$ make boot.hex boot.elf text data bss dec hex filename 10 0 0 10 a boot.elf boot.hex rm boot.elf boot.oA build produces 10 bytes of code, I can check the disassembly in the boot.lst file.
Disassembly of section .text: 08000000 <isr_vector>: 8000000: 00 10 00 20 09 00 00 08 ... .... 08000008 <Reset_Handler>: /* System Exceptions */ Reset_Handler } ; void Reset_Handler( void) { for( ;;) ; 8000008: e7fe b.n 8000008 <Reset_Handler>
Next, I will take a board with a STM32F030F4P6 and check if the code generated behaves as expected.
Below is the Makefile for reference. If you happen to cut&paste from this web page to a file, remember that gmake expects rules to be tab indented.
### Build environment selection ifeq (linux, $(findstring linux, $(MAKE_HOST))) GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi else GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-13.3.rel1-mingw-w64-i686-arm-none-eabi" endif BINPFX = @$(GCCDIR)/bin/arm-none-eabi- CC = $(BINPFX)gcc LD = $(BINPFX)ld OBJCOPY = $(BINPFX)objcopy OBJDUMP = $(BINPFX)objdump SIZE = $(BINPFX)size CPU = -mthumb -mcpu=cortex-m0 CFLAGS = $(CPU) -g -Wall -Wextra -Os LD_SCRIPT = f030f4.ld ### Build rules .PHONY: clean clean: @echo CLEAN @rm -f *.o *.elf *.map *.lst *.bin *.hex %.elf: %.o @echo $@ $(LD) -T$(LD_SCRIPT) -Map=$*.map -cref -o $@ $< $(SIZE) $@ $(OBJDUMP) -hS $@ > $*.lst %.bin: %.elf @echo $@ $(OBJCOPY) -O binary $< $@ %.hex: %.elf @echo $@ $(OBJCOPY) -O ihex $< $@