<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>1.1 GNU Arm Embedded Toolchain</title> <link type="text/css" rel="stylesheet" href="style.css"> </head> <body> <h1>1.1 GNU Arm Embedded Toolchain</h1> Arm maintains a GCC cross-compiler available as binaries that run on Linux and Windows.<br>It is available on their <a href="https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm"> arm developer site</a>. <p> I want to develop for <b>AArch32 bare-metal target (arm-none-eabi)</b>. <h2>Installation on Windows</h2> So far there are only win32 versions available for download either as <b>.exe</b> installers or <b>.zip</b> archives. File naming convention helps to identify the type of release (preview, major or update). <p> By default each release installs to its own directory instead of upgrading the previous one. This way several releases can coexist and you can select which one you use for a specific project. One downside to this is that the directory and filename convention is heavy. For practical use, you need to configure an IDE or encapsulate those paths and names in <b>Makefile</b> variables. <pre> ### Build environment selection GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" BINPFX = $(GCCDIR)/bin/arm-none-eabi- CC = $(BINPFX)gcc ### Build rules .PHONY: version version: $(CC) --version </pre> <ul> <li> <b>GCCDIR</b> holds the path to the folder where the toolchain is installed. When I install a new release, I need to update this path. <li> All the commands to build are located in one <b>bin</b> subfolder and they share the same name prefix <b>arm-none-eabi-</b>. So I have created a <b>BINPFX</b> to easily identify the commands. </ul> <pre> $ make "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-gcc --version arm-none-eabi-gcc.exe (Arm GNU Toolchain 14.2.Rel1 (Build arm-14.52)) 14.2.1 202 41119 Copyright (C) 2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. </pre> <h2>Installation on Linux</h2> Installation on Linux means downloading the Linux x86_64 tarball and extracting it. I use the <b>~/Packages</b> folder for this type of distribution. This means that the <b>Makefile</b> on Linux will be the same as the Windows one except for the value of the <b>GCCDIR</b> variable. <pre> GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi </pre> By selecting the path based on the development environment, there is no need to make changes while switching between OS. <b><i>gmake</i></b> has the built-in variable <b>MAKE_HOST</b> that can be tested for this. <pre> ### Build environment selection ifeq (linux, $(findstring linux, $(MAKE_HOST))) GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi else GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" endif BINPFX = $(GCCDIR)/bin/arm-none-eabi- CC = $(BINPFX)gcc ### Build rules .PHONY: version version: $(CC) --version </pre> I use the path prefix <b>$(HOME)/Packages</b> instead of <b>~/Packages</b> when defining <b>GCCDIR</b> as some sub-processes called by <b><i>gmake</i></b> may have issues with <b>~</b> expansion (in this case <i>ld</i>). This way <b><i>gmake</i></b> will handle the expansion before calling the sub-processes. <h2>Toolchain’s chain of events</h2> In order to generate a file that can be loaded in the micro-controller, I need to sketch the chain of events that will take place. <ol> <li> <b>Compile</b> source codes (<b>.c</b>) to object modules (<b>.o</b>) <li> <b>Link</b> all the object modules (<b>.o</b>) together into an executable (<b>.elf</b>) <li> <b>Convert</b> the executable (<b>.elf</b>) into a format suitable for loading or flashing (<b>.bin</b> or <b>.hex</b>). </ol> <h3>1. Compile</h3> <b><i>gmake</i></b> has default rules to built <b>.o</b> files out of <b>.c</b> files. As I have already defined with <b>CC</b> the command to compile, I can make a simple test of this step by creating an empty <b>.c</b> file and checking what happens when I try to compile it. <pre> $ touch empty.c $ make empty.o "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-gcc -c -o empty.o empty.c </pre> Compilation is succesful and <b>empty.o</b> file is generated. <h3>2. Link</h3> To link the object module generated in the first step, I need to <ul> <li> specify the command to link (<b>ld</b>) <li> provide the name of a link script <li> create a rule that calls the linker to generate an executable <b>.elf</b> file from the object module <b>.o</b> file. </ul> <p> There are sample link scripts that come with the toolchain, they are located in the subfolder <b>share/gcc-arm-none-eabi/samples/ldscripts</b>. For now I can use the simplest script: <b>mem.ld</b>. <pre> ### Build environment selection ifeq (linux, $(findstring linux, $(MAKE_HOST))) GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi else GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" endif BINPFX = $(GCCDIR)/bin/arm-none-eabi- CC = $(BINPFX)gcc LD = $(BINPFX)ld LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld ### Build rules .PHONY: version version: $(CC) --version %.elf: %.o $(LD) -T$(LD_SCRIPT) -o $@ $< </pre> <pre> $ make empty.elf "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-ld -T"D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" /share/gcc-arm-none-eabi/samples/ldscripts/mem.ld -o empty.elf empty.o </pre> Link terminates successfully and creates <b>empty.elf</b>. <h3>3. Convert</h3> Finally, I use the command <b><i>objcopy</i></b> to convert the executable <b>.elf</b> file into binary or intel hex format suitable to load in the micro-controller. <pre> ### Build environment selection ifeq (linux, $(findstring linux, $(MAKE_HOST))) GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi else GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" endif BINPFX = $(GCCDIR)/bin/arm-none-eabi- CC = $(BINPFX)gcc LD = $(BINPFX)ld OBJCOPY = $(BINPFX)objcopy LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld ### Build rules .PHONY: version version: $(CC) --version %.elf: %.o $(LD) -T$(LD_SCRIPT) -o $@ $< %.bin: %.elf $(OBJCOPY) -O binary $< $@ %.hex: %.elf $(OBJCOPY) -O ihex $< $@ </pre> Now, if I start in a directory that contains only this <b>Makefile</b> and an empty <b>empty.c</b> file, I can successfully build. <pre> $ make empty.bin empty.hex "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-gcc -c -o empty.o empty.c "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-ld -T"D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" /share/gcc-arm-none-eabi/samples/ldscripts/mem.ld -o empty.elf empty.o "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-objcopy -O binary empty.elf empty .bin "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-m ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-objcopy -O ihex empty.elf empty.h ex rm empty.o empty.elf </pre> Notice that <b><i>gmake</i></b> automatically removes the intermediary <b>.o</b> and <b>.elf</b> files on completion. <p> The generated <b>empty.bin</b> is empty. <h2>Cleaning up</h2> I want to keep the output of the build easy to understand without the clutter of the long command names. Also I need a way to clean the working directory back to its initial state. <ul> <li>By prefixing <b>BINPFX</b> with <i>@</i>, commands will not be displayed by <b><i>gmake</i></b> when they are executed. Adding an <b>echo</b> of the command target in the rules helps to keep track of the build progression. <li>A new <b>clean</b> rule will remove all generated files. </ul> <pre> ### Build environment selection ifeq (linux, $(findstring linux, $(MAKE_HOST))) GCCDIR = $(HOME)/Packages/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi else GCCDIR = "D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi" endif BINPFX = @$(GCCDIR)/bin/arm-none-eabi- CC = $(BINPFX)gcc LD = $(BINPFX)ld OBJCOPY = $(BINPFX)objcopy LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld ### Build rules .PHONY: clean version version: $(CC) --version clean: @echo CLEAN @rm -f *.o *.elf *.bin *.hex %.elf: %.o @echo $@ $(LD) -T$(LD_SCRIPT) -o $@ $< %.bin: %.elf @echo $@ $(OBJCOPY) -O binary $< $@ %.hex: %.elf @echo $@ $(OBJCOPY) -O ihex $< $@ </pre> <pre> $ make clean CLEAN $ make empty.bin empty.hex empty.elf empty.bin empty.hex rm empty.o empty.elf </pre> <h2>Checkpoint</h2> At this stage, I have a working toolchain and I am able to build from an empty source file (<b>empty.c</b>) to an empty binary file (<b>empty.bin</b>). <p> <a href="12_bootstrap.html">Next</a>, I will select a micro-controller from the STM32 family and generate a binary file that it can execute. <hr>© 2020-2025 Renaud Fivet </body> </html>