mirror of
https://github.com/rfivet/stm32bringup.git
synced 2025-02-21 05:37:09 -05:00
289 lines
9.5 KiB
HTML
289 lines
9.5 KiB
HTML
<!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>.
|
||
|
||
<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-13.3.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-13.3.rel1-m
|
||
ingw-w64-i686-arm-none-eabi"/bin/arm-none-eabi-gcc --version
|
||
arm-none-eabi-gcc.exe (Arm GNU Toolchain 13.3.Rel1 (Build arm-13.24)) 13.3.1 202
|
||
40614
|
||
Copyright (C) 2023 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-13.3.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>Gmake</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-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
|
||
|
||
### 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 <i>gmake</i> may have
|
||
issues with <b>~</b> expansion (in this case <i>ld</i>). This way <i>gmake</i>
|
||
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>Gmake</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-13.3.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-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
|
||
|
||
LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld
|
||
|
||
### Build rules
|
||
|
||
%.elf: %.o
|
||
$(LD) -T$(LD_SCRIPT) -o $@ $<
|
||
</pre>
|
||
|
||
<pre>
|
||
$ make empty.elf
|
||
"D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-13.3.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-13.3.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>objcopy</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-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
|
||
|
||
LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld
|
||
|
||
### Build rules
|
||
|
||
%.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-13.3.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-13.3.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-13.3.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-13.3.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-13.3.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>gmake</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
|
||
<i>gmake</i> 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-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
|
||
|
||
LD_SCRIPT = $(GCCDIR)/share/gcc-arm-none-eabi/samples/ldscripts/mem.ld
|
||
|
||
### Build rules
|
||
|
||
.PHONY: clean
|
||
|
||
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-2024 Renaud Fivet
|
||
</body>
|
||
</html>
|