Skip to the content.

3.6: Toolchain Update

While I was experimenting with the internal temperature sensor, a major release of GNU ARM Embedded Toolchain came out and I had to do some adaptations when switching to it.

To switch from release 9 update to release 10 major, I made three changes to Makefile.

● Update the Linux base directory location:

#GCCDIR = $(HOME)/Packages/gcc-arm-none-eabi-9-2020-q2-update
GCCDIR = $(HOME)/Packages/gcc-arm-none-eabi-10-2020-q4-major

● Update the Windows base directory location:

#GCCDIR = D:/Program Files (x86)/GNU Arm Embedded Toolchain/9 2020-q2-update
GCCDIR = D:/Program Files (x86)/GNU Arm Embedded Toolchain/10 2020-q4-major

● Update the library subdirectory location:

#LIBDIR  = $(GCCDIR)/lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp
LIBDIR  = $(GCCDIR)/lib/gcc/arm-none-eabi/10.2.1/thumb/v6-m/nofp

Unfortunately when I did some regression testing by recompiling the projects so far, I found that the new release optimizes further the C startup clearing of BSS data by calling memset() from the distribution libraries. So I had to add libc.a on top of libgcc.a to the list of libraries.

As I don’t want to turn off size optimization and I am not willing to always pay the full 180 bytes for a production ready memset() when it is called only once at startup to clear a few bytes, I ended up adding my own version of memset() to my local library.

#include <string.h>

void *memset( void *s, int c, size_t n) {
    char *p = s ;
    while( n--)
        *p++ = c ;

    return s ;

As I was investigating the compilation flags to find if there was a better way to solve this issue, I figure out I could let gcc handle the distribution libraries selection and their location based on the CPU type. So I changed the linker invocation accordingly and got rid of LD, LIBDIR and LIB_PATHS definitions.

#   $(LD) -T$(LD_SCRIPT) $(LIB_PATHS) -Map=$(PROJECT).map -cref -o $@ $(OBJS) $(LIBS)
    $(CC) $(CPU) -T$(LD_SCRIPT) -L. -Wl,-Map=$(PROJECT).map,-cref \
        -nostartfiles -o $@ $(OBJS) $(LIBS)

Finally, I revised the way I specify the commands location by updating the PATH environment variable instead of giving the full path of each command. On Windows, I make sure that drive specification matches the development environment in use (Cygwin, MSYS2 and other).

### Build environment selection

ifeq (linux, $(findstring linux, $(MAKE_HOST)))
 INSTALLDIR = $(HOME)/Packages
#REVDIR = gcc-arm-none-eabi-9-2019-q4-major
#REVDIR = gcc-arm-none-eabi-9-2020-q2-update
 REVDIR = gcc-arm-none-eabi-10-2020-q4-major
 DRIVE = d
ifeq (cygwin, $(findstring cygwin, $(MAKE_HOST)))
 OSDRIVE = /cygdrive/$(DRIVE)
else ifeq (msys, $(findstring msys, $(MAKE_HOST)))
 INSTALLDIR = $(OSDRIVE)/Program Files (x86)
#REVDIR = GNU Tools ARM Embedded/5.4 2016q2
#REVDIR = GNU Tools ARM Embedded/6 2017-q2-update
#REVDIR = GNU Tools ARM Embedded/7 2017-q4-major
#REVDIR = GNU Tools ARM Embedded/7 2018-q2-update
#REVDIR = GNU Tools ARM Embedded/9 2019-q4-major
#REVDIR = GNU Arm Embedded Toolchain/9 2020-q2-update
 REVDIR = GNU Arm Embedded Toolchain/10 2020-q4-major

export PATH := $(GCCDIR)/bin:$(PATH)

BINPFX  = @arm-none-eabi-
AR      = $(BINPFX)ar
CC      = $(BINPFX)gcc
OBJCOPY = $(BINPFX)objcopy
OBJDUMP = $(BINPFX)objdump
SIZE    = $(BINPFX)size


Invoking the compiler instead of the linker gives more flexibility in case the toolchain directory structure changes or if I target a different core. The compiler is aware of the location of the toolchain libraries while the linker need explicit parameters to handle those changes.

Next I will (re)build to execute code in RAM instead of FLASH.

© 2020-2021 Renaud Fivet