I recently tried to make a bootable ISO using Buildroot, and
encountered some difficulty. In that process, I realized that I don’t
actually know what all goes into a bootable Linux ISO. So this is my
collection of notes that I made while trying to answer that question.
Starting out, the
ISOLINUX
documentation is going to be my guide, along with a
Core Linux
ISO. I was originally going to use an Ubuntu ISO, but I had a hard
time getting the Ubuntu ISO to boot in qemu without having a graphic
display attached to it (the box I’m running this all on is headless).
Loopback mount of the ISO
1234567891011121314
aja042@tellurium:~$ sudo mkdir /mnt/corelinux
[sudo] password for aja042:
aja042@tellurium:~$ sudo mount -o loop ~/Downloads/Core-current.iso /mnt/corelinux/
mount: /dev/loop1 is write-protected, mounting read-only
aja042@tellurium:~$ ls /mnt/corelinux/
boot
aja042@tellurium:~$ ls /mnt/corelinux/boot/
core.gz isolinux vmlinuz
aja042@tellurium:~$ ls /mnt/corelinux/boot/isolinux/
boot.cat boot.msg f2 f3 f4 isolinux.bin isolinux.cfg
$ qemu-system-x86_64 -curses -boot d -cdrom ~/Downloads/Core-current.iso -m 512
Note that I’m using the -curses option because I don’t have a
display on this machine. Drop that if you’re running from desktop
Linux. If you do run with -curses, note that you can press ESC 2
to get into the QEMU monitor mode and type quit to exit.
What else is on here?
12
aja042@tellurium:/mnt/corelinux/boot$ file vmlinuz
vmlinuz: Linux kernel x86 boot executable bzImage, version 4.2.9-tinycore (tc@box) #1999 SMP Mon Jan 18 19:42:12 UTC 2016, RO-rootFS, swap_dev 0x3, Normal VGA
So that’s a standard bzImage kernel, renamed to vmlinuz for some
reason. Good enough!
12
aja042@tellurium:/mnt/corelinux/boot$ file core.gz
core.gz: gzip compressed data, was "core.cpio", last modified: Mon Jul 4 08:08:02 2016, max compression, from Unix
That’s the initrd referenced above. Together, those are the only
real files that the isolinux.cfg references.
Can I repack this?
A good test to see if I get how an image like this works is to figure
out how to recreate it from the files. One important thing here is
that this is a bootable ISO:
12
aja042@tellurium:/mnt/corelinux/boot/isolinux$ file ~/Downloads/Core-current.iso
/home/aja042/Downloads/Core-current.iso: ISO 9660 CD-ROM filesystem data 'Core' (bootable)
Which is likely an extra wrinkle.
Going off the instructions, you can get a binary version of
syslinux from
kernel.org. I’m
going to use 6.03.
For a little bit of an added challenge, I’m going to make the
directory layout different - I’m just going to stick all the files in
the root of the new ISO.
123
aja042@tellurium:~/workspace/bootable-iso$ mkdir cd_root
aja042@tellurium:~/workspace/bootable-iso$ cd cd_root/
aja042@tellurium:~/workspace/bootable-iso/cd_root$ cp /mnt/corelinux/boot/{vmlinuz,core.gz} .
Next, the instructions say to copy isolinux.bin and ldlinux.c32:
One thing that isn’t clear to me in all of this is how isolinux
knows where to find these files. Anyway, I’ll keep following the
instructions for now. Next up is creating the actual ISO:
12345678910
aja042@tellurium:~/workspace/bootable-iso$ mkisofs -o output.iso -b isolinux.bin -c boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table cd_root/
I: -input-charset not specified, using utf-8 (detected in locale settings)
Size of boot image is 4 sectors -> No emulation
91.43% done, estimate finish Fri Mar 3 20:22:49 2017
Total translation table size: 2048
Total rockridge attributes bytes: 0
Total directory bytes: 0
Path table size(bytes): 10
Max brk space used 0
5483 extents written (10 MB)
Alright, that seemed to work. Let’s try booting it!
1
aja042@tellurium:~/workspace/bootable-iso$ qemu-system-x86_64 -curses -boot d -cdrom output.iso -m 512
This boots it successfully!
What do the -b and -c flags do in mkisofs?
The man page for mkisofs is actually called genisoimage, for some
reason. Doing mkisofs --help reveals this.
-b eltorito_boot_image
Specifies the path and filename of the boot image to be used when
making an El Torito bootable CD for x86 PCs. The pathname must be
relative to the source path specified to genisoimage. This option is
required to make an El Torito bootable CD. The boot image must be
exactly 1200 kB, 1440 kB or 2880 kB, and genisoimage will use this
size when creating the output ISO9660 filesystem. The PC BIOS will
use the image to emulate a floppy disk, so the first 512-byte sector
should contain PC boot code. This will work, for example, if the boot
image is a LILO-based boot floppy.
If the boot image is not an image of a floppy, you need to add either
-hard-disk-boot or -no-emul-boot. If the system should not boot off
the emulated disk, use -no-boot.
So this is useful! It says that the argument passed to -b is going
to be used as the boot image. So what the heck is
El Torito?
It is, apparently, a standard for how PCs should search for boot code
on a CD-ROM. If you’re really curious, you can have a look at the
specification from
Intel and Phoenix. Section
5.3 describes “No Emulation Booting”, which goes into some awesome x86
history describing interrupts that you can use to retrieve more boot
information.
One thing that the standard mentions is the “boot catalog”. The man
page confirms that -c is used to specify the filename to be used for
the boot catalog. What is the boot catalog? According to section 2.0,
it’s a “collection of [0x]20-byte entries, packed [0x]40 entries to
the sector”. Section 2.5 explains that these contain a Header ID, a
platform ID (x86/PowerPC/Mac), an ID string, a checksum, and two key
bytes. These also have a bit more information about what kind of boot
media it is, how many sectors should be loaded, etc. I’m personally
quite happy that mkisofs makes this for me and I don’t really have
to think much about what’s going on here.
So how does isolinux know where to find isolinux.cfg?
The file txt/syslinux.cfg.txt in the syslinux source directory
explains the answer to this:
*ISOLINUX* (before 4.02) used the configuration filename of
isolinux.cfg, searching /boot/isolinux (starting 2.00), then /isolinux
and /. As of 4.02, *ISOLINUX* will search for isolinux.cfg then
syslinux.cfg in /boot/isolinux before searching for the same files in
/isolinux, /boot/syslinux, /syslinux, and /.
So, basically, I got lucky that it searches for it in the root, since
I didn’t read those docs ahead of time.
Conclusion
Looks like the process of making a bootable Linux ISO is pretty
straight-forward. You take the isolinux.bin and make an
isolinux.cfg, put them in the right place, and tell mkisofs where
to find them. Awesome! Now I’m going to go back to figuring out why
the ISO I’m making using buildroot isn’t booting right.
What is an APU2? It’s a pretty awesome 64-bit single-board computer
from PC Engines. I have one here
that I want to turn into a router, and figured it’d be a good process
to turn into a tutorial.
The Fast Way
Want to see the completed project and build it yourself? Check out the
github repo and use
the bootable-iso branch. You should be able to run make in that
branch and get a completed ISO out at
output/images/rootfs.iso9660. This is a hybrid ISO, so you can write
it to either a CD (not all that useful) or a USB key (very useful!).
The Step-by-Step Way (Starting From Scratch)
I went through a lot of experimenting to get this process nailed
down. The bootable-iso repository linked to above has a series of
commits that are forked off of the 2017.02 tag that match up with
these steps.
Get a copy of the buildroot-2017.02 source.
You can either grab this as a
tarball or from
Github. If you’re going to
use git, make sure you’re starting from the 2017.02 tag, if you want
to be sure that the following steps work. Using something newer or
older? Let me know if it works!
Start with qemu_x86_64_defconfig.
This is a pre-made configuration that targets 64-bit x86 emulation in
qemu. If you want to see what’s in the file, it’s stored in
configs/qemu_x86_64_defconfig. We want to tell buildroot to use this
configuration:
1
aja042@tellurium:~/workspace/buildroot/buildroot$ make qemu_x86_64_defconfig
Do a build
To do a build with buildroot, you just run make. This takes will
take the configured settings, use them to download all of the
necessary sources, build them, and finally generate a kernel and root
filesystem for you.
1
aja042@tellurium:~/workspace/buildroot/buildroot$ make
This is going to take a while. Maybe go make a coffee…
The build filesystem images go into output/images. Once the build is
done, we can check out what we’ve got:
12
aja042@tellurium:~/workspace/buildroot/buildroot$ ls output/images/
bzImage rootfs.ext2
In the output, we’ve got a kernel and a root filesystem, but no ISO.
Configure buildroot to build an ISO
To change the types of filesystem images we want to build, we use the
buildroot menu-based configuration.
1
aja042@tellurium:~/workspace/buildroot/buildroot$ make menuconfig
This pops up the top-level menu:
From here, we go into Bootloaders and enable ISOLINUX (this is one
of the options for a bootable-ISO-compatible bootloader, and probably
the simplest):
Next, we go back to the top-level menu and choose Filesystem images
and enable “ISO image”. While you’re here, make sure you’ve checked
off Build hybrid image; a “hybrid image” is an ISO that can also be
written to a USB key, which will be useful when we go to run code on
the APU2.
Exit the menu system and re-run make to re-build with the new
options. This should be way quicker than the first build, because it
will re-use as much of the previous work as it can.
1234
aja042@tellurium:~/workspace/buildroot/buildroot$ make
...
aja042@tellurium:~/workspace/buildroot/buildroot$ ls output/images/
bzImage rootfs.cpio rootfs.ext2 rootfs.iso9660 syslinux
Hooray! We’ve got an ISO!
Run the built ISO in QEMU
QEMU is a pretty cool virtualization tool. It simulates an entire PC,
it’s free, and it’s easy to use from the command-line. You could
probably use VirtualBox, or VMware, or whatever else to do this step
too. One of the great perks of QEMU is that it can emulate a text
console to the target machine over SSH; handy, because I’m doing all
of this work on a headless Linux box.
12
aja042@tellurium:~/workspace/buildroot/buildroot$ qemu-system-x86_64 -curses -boot d -cdrom output
/images/rootfs.iso9660 -m 512
Unfortunately, the default kernel settings provided with the
qemu_x86_64_defconfig use a QEMU-emulated video card, and no matter
how many different kernel command-line options I tried, I couldn’t
figure out how to turn this off.
This is all it shows:
If you’re running the curses version of QEMU and need to quit, you can
press Alt-2 or ESC 2 to switch to the QEMU monitor console and
type quit. If you go to the monitor console and want to go back to
the console, press Alt-1.
Disable the QEMU video drivers in the Linux kernel
Buildroot provides the menuconfig for configuring what buildroot is
going build, and it also provides linux-menuconfig for configuring
which options are going to be built into the Linux kernel. We’ll use
that to find the video drivers and disable them.
1
aja042@tellurium:~/workspace/buildroot/buildroot$ make linux-menuconfig
In the Device Drivers, Graphics Drivers menu, you want to make
sure that QXL virtual GPU, DRM support for bochs..., and Virtio
GPU driver are all disabled.
Now, we’ll rebuild and try qemu again:
123
aja042@tellurium:~/workspace/buildroot/buildroot$ make
...
aja042@tellurium:~/workspace/buildroot/buildroot$ qemu-system-x86_64 -curses -boot d -cdrom output/images/rootfs.iso9660 -m 512
You can log in as root with no password now and poke around in our
super minimal Linux distribution:
When you’re ready to quit, press Alt-2 and type quit.
Saving our configuration
So far, we’ve modified both the buildroot configuration and the Linux
kernel configuration. Let’s export those configurations so that we can
get them back easily. First, we’ll change the name of the kernel
configuration in the main buildroot menuconfig.
123
aja042@tellurium:~/workspace/buildroot/buildroot$ make menuconfig
Kernel -> Configuration file path -> board/qemu/x86_64/apu2-linux-4.9.config
Exit out of the menuconfig, and tell buildroot to save the current
kernel config:
1
aja042@tellurium:~/workspace/buildroot/buildroot$ make linux-update-defconfig
This should create our apu2-linux-4.9.config for us:
12
aja042@tellurium:~/workspace/buildroot/buildroot$ ls board/qemu/x86_64/
apu2-linux-4.9.config linux-4.9.config readme.txt
Now that the kernel config is there, we can save the main buildroot
configuration. Since this has already been configured early on, we
want to override the BR2_DEFCONFIG variable with make to save to a
new one:
1234
aja042@tellurium:~/workspace/buildroot/buildroot$ grep DEFCONFIG .config
BR2_DEFCONFIG="/home/aja042/workspace/buildroot/buildroot/configs/qemu_x86_64_defconfig"
aja042@tellurium:~/workspace/buildroot/buildroot$ make savedefconfig BR2_DEFCONFIG=configs/apu2_x86_64_defconfig
Now, we can do make apu2_x86_64_defconfig to configure our system
using our own configuration instead of the qemu_x86_64_defconfig we
started with.
Getting it to work on serial
Right now, we’ve got the system booting successfully and displaying
everything on a text console. This is a good start, but the APU2
doesn’t have a display at all; it all just runs over the serial port.
In the command-line/curses version of QEMU, you can look at the serial
port by pressing Alt-3. If you do this on our existing image, you’ll
see that there isn’t anything interesting there yet.
There’s three things we need to configure for serial:
The ISOLINUX bootloader
The kernel console
The kernel login prompt
The bootloader configuration lives in
fs/iso9660/isolinux.cfg. Looking at the
docs
tells us that we need to add a serial directive as the first line of
the configuration file. At the same time, we’ll add a console=
directive to the append line (which configures the kernel
command-line).
Next, we’ll tell Buildroot to use the serial port for a login
prompt. To change this setting, you run make menuconfig, and
navigate to System configuration, Run a getty (login prompt) after
boot, and change the TTY Port to ttyS0.
Now, rebuild your ISO and run QEMU:
123
aja042@tellurium:~/workspace/buildroot/buildroot$ make
...
aja042@tellurium:~/workspace/buildroot/buildroot$ qemu-system-x86_64 -curses -boot d -cdrom output/images/rootfs.iso9660 -m 512
When you re-run QEMU, you will no longer get all of the kernel output on the main console; it should just show a bit of boot stuff:
If you press Alt-3, you will switch to the serial console and should see all of the kernel stuff you were expecting to see, along with a login prompt.
As usual, to quit QEMU, press Alt-2 and type quit.
The last step here is to save our modified buildroot configuration, so
that we don’t lose our changes to the default TTY. This time we don’t have to specify a BR2_DEFCONFIG, because we’ve already told it to use our apu2_x86_64_defconfig.
1
aja042@tellurium:~/workspace/buildroot/buildroot$ make savedefconfig
Copying the ISO onto a USB Key
Following the instructions in the
Syslinux
documentation, we should be able to use dd to copy the ISO image
onto a USB key. In the command below, replace xxxxx with the path to
your USB key. MAKE SURE YOU DON’T SCREW THIS UP!. If you put the
wrong device here, you could overwrite the start of your hard drive
with this ISO instead, and that’s likely not what you want…
I like to use dmesg to check which drive I just plugged in to the machine:
123456789101112
aja042@tellurium:~/workspace/buildroot/buildroot$ dmesg
[2067820.465956] usb-storage 3-10:1.0: USB Mass Storage device detected
[2067820.466252] scsi host19: usb-storage 3-10:1.0
[2067821.628122] scsi 19:0:0:0: Direct-Access PNY USB 2.0 FD 1100 PQ: 0 ANSI: 4
[2067821.628670] sd 19:0:0:0: Attached scsi generic sg2 type 0
[2067821.629397] sd 19:0:0:0: [sdb] 31195648 512-byte logical blocks: (16.0 GB/14.9 GiB)
[2067821.629965] sd 19:0:0:0: [sdb] Write Protect is off
[2067821.629967] sd 19:0:0:0: [sdb] Mode Sense: 43 00 00 00
[2067821.630513] sd 19:0:0:0: [sdb] No Caching mode page found
[2067821.630515] sd 19:0:0:0: [sdb] Assuming drive cache: write through
[2067821.633139] sdb: sdb1
[2067821.634613] sd 19:0:0:0: [sdb] Attached SCSI removable disk
That looks like the one. So I’m going to replace xxxxx with sdb,
but you need to make sure you’ve got that right.
12345
aja042@tellurium:~/workspace/buildroot/buildroot$ sudo dd if=output/images/rootfs.iso9660 of=/dev/xxxxx
[sudo] password for aja042:
12288+0 records in
12288+0 records out
6291456 bytes (6.3 MB, 6.0 MiB) copied, 1.07977 s, 5.8 MB/s
Giving the APU2 boot a shot!
My USB-serial adapter showed up as /dev/ttyUSB0 when I plugged it
in. Yours might show up as /dev/ttyUSB[x] or /dev/ttyACM[x] or
something else entirely. Replace as needed in the minicom command.
Minicom is my preferred serial terminal emulator. Before powering on the APU2, get the serial cable all hooked up and get minicom started:
Plug the power in on the APU2 and watch stuff start to scroll by. When
you get to the point where it’s asking which device to boot off of,
choose the number that corresponds with your USB key:
And TADA! It’s booted!
Unfortunately, it doesn’t know anything about the Ethernet controllers
on the APU2:
To quit minicom, press Ctrl-a z q and say yes to Leave without reset?
Rebuild the kernel with network driver support
We’re back to doing kernel configuration: make linux-menuconfig. In
here, we want to go to Device Drivers, Network device support,
Ethernet driver support, scroll down to Intel devices. The
APU2 spec page calls these NICs
“i210AT / i211AT”, but a bit of google searching reveals that these
are covered by the Intel(R) 82575/82576 PCI-Express Gigabit Ethernet
support driver. Enable that.
Now, rebuild your ISO and reflash your USB key. Once again, make sure you’re writing to the correct USB device!
123456
aja042@tellurium:~/workspace/buildroot/buildroot$ make
...
aja042@tellurium:~/workspace/buildroot/buildroot$ sudo dd if=output/images/rootfs.iso9660 of=/dev/xxxxx
12288+0 records in
12288+0 records out
6291456 bytes (6.3 MB, 6.0 MiB) copied, 4.0793 s, 1.5 MB/s
And try booting it again with minicom watching. Once it’s booted, verify that the NICs are detected:
Hooray! The last thing to do is to save the changed kernel config - we want to be able to reproduce this setup later!
1
aja042@tellurium:~/workspace/buildroot/buildroot$ make linux-update-defconfig
With that, I feel like this is a lot of content. There’ll be a
part 2 coming soon where this gets configured as a router.
Autoconf has always been a bit of a mystery to me. While working
through the MailCore 2/libetpan stuff, I once again got reacquainted
with Autotools and had to dive a bit deeper. John Calcote’s
Autotools is an awesome reference that I found on
O'Reilly Safari today.
This is mostly a set of rapid fire notes that I will probably refer
back to in the future for my own reference.
Tools
Automake - takes Makefile.am files and turns them into Makefile.in files.
Autoconf - takes configure.ac, aclocal.m4, acsite.m4 and uses
them to generate the configure script. Configure script takes
.in files and outputs them with the .in stripped
(e.g. Makefile.in -> Makefile)
So that’s the general flow of things here. Automake goes from .am to
.in, Autoconf takes .ac and .m4 to generate the configure
script (and config.h.in). Finally, ./configure takes the .in
files and generates the output with the .in removed (Makefile.in ->
Makefile, config.h.in -> config.h).
I’ve had an aversion to vendor tools for a long time. I’m going to
pick on Freescale here, but that’s just because I’m currently waiting
for their sample code to finish downloading. All 660MB of sample code
and… other stuff? My
previous post was
pointed squarely at ST. I love the hardware these companies put out, I
just can’t handle their software or the code that they write. I’ll
take emacs over Eclipse any day of the week, and most vendors these
days just rebrand Eclipse. Good for lots of people, just not good for
me.
Anyway, on to an example. Here’s some Freescale example
code
for the FRDM-KL25Z. It says this should be the content of your
main() function. I’ve fixed the formatting for them a little bit.
12345678910111213141516171819202122232425
volatile int delay;
// Configure board specific pin muxing
hardware_init();
// Initialize the UART terminal
dbg_uart_init();
PRINTF("\r\nRunning the myProject project.\n");
// Enable GPIO port for LED1
LED1_EN;
for (;;)
{
LED1_ON;
delay = 5000000;
while(delay--);
LED1_OFF;
delay = 5000000;
while(delay--);
}
Ok, so this starts out well. They have init functions for the board
and the debug UART. Nitpick: you can use vowels, debug is just find,
you don’t need to shorten that to dbg.
Next, we see PRINTF. The fact that the function name is in all caps
suggests to me that it’s some weird version of printf, perhaps
implemented as some kind of macro so that it outputs on the debug
UART. Either way, it’s a bit scary. Also, they can’t seem to decide on
whether to use Windows- or Unix-style newlines; the first one is
Windows-style, the second is Unix-style.
The next line is where I start getting grumpier. LED1_EN; is not a
function call. It looks to me like a side-effect free statement. (just
like x; is valid, but pointless C code). But, judging by the name,
this affects a GPIO register!
The body of the for-loop continues on the same way. The LED1_ON and
LED1_OFF macros are gross, and the delay loops are pretty
ridiculous.
I’ve ranted before about this, and I’ll do it again here: Sample code
is documentation, and possibly the only documentation your users are
ever going to read. By giving this as the very first example of what
the code can look like, you’re teaching new developers to write
garbage.
And that’s why I set up real toolchains that just use standard C
libraries and some very light (and idiomatic!) macros to do embedded
development.
Note: This is a snapshot of the README.md file on the git repository
for this project. Over the next few weeks, I plan on writing a series
of blog posts dissecting the different parts of it and explaining how
it all fits together.
If you’re looking at using an STM32F042K6 Nucleo board, this is a
decent alternative to using mbed, if you feel like rolling up your
sleeves a bit and getting dirty.
What is this?
This is a sample project for the STM32F042K6 Nucleo board that I’m
happy with. It has the following features:
CMake for builds
crosstool-ng for building a toolchain
Unity for unit testing on the host
OpenOCD and gdb for running code on the target
How do I get started?
Toolchain
First, you need to have crosstool-ng
installed. I’m using version
1.22.0. The
instructions are
pretty straight-forward. Once you’ve got it installed, don’t worry
about the rest of their instructions (i.e. stop after the export
PATH line).
Once it’s installed, you can use the configuration file in the
crosstool/ directory in this repository to build a compiler and
newlib (libc) for the microcontroller. To do this, you first tell
crosstool-ng to use the configuration provided, and then you tell it
to build it:
This will take a while. On my i7 desktop, it took about 16 minutes.
This will install the toolchain into ~/x-tools/arm-stm32f0-eabi.
OpenOCD
The crosstool-ng toolchain comes with an ARM-compatible gdb, but
without OpenOCD, there’s no way to connect to the target board.
I was lazy here and just installed it through apt: apt install -y openocd
To test that it’s working, plug your Nucleo board into a USB port and run this command. You should get similar output:
1234567891011121314151617
$ sudo openocd -f /usr/share/openocd/scripts/board/st_nucleo_f0.cfg
Open On-Chip Debugger 0.9.0 (2015-09-02-10:42)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v25 API v2 SWIM v14 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.243541
Info : stm32f0x.cpu: hardware has 4 breakpoints, 2 watchpoints
CMake
CMake is a build system. The two-second intro is that instead of
having a Makefile, you have files called CMakeLists.txt that are
used to generate Makefiles (and other things, in different
environments).
Here, too, I was lazy and just installed it via apt: sudo apt install -y cmake
Building and testing on the host
You should now have all the pieces in place necessary to do both
host-unit-test builds and target-binary builds!
First, let’s do a host build. For CMake projects, you generally make a
new directory to do the build in, which keeps all of the build objects
out of your source tree (makes for really easy cleanup!)
To do a host build, make a directory called build/ in the source
directory. The .gitignore file is already set up to ignore this
directory. In there, you run CMake to process the CMakeLists.txt files and generate a Makefile. Then you run make.
1234567891011121314151617
stm32f042-morse-example$ mkdir build
stm32f042-morse-example$ cd build
stm32f042-morse-example/build$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/aja042/workspace/crosstool/stm32f042-morse-example/build
stm32f042-morse-example/build$ make
... lots of output ...
Once this has finished, there’s a unit test runner stored in
build/test/testrunner. Executing this should result in output like
this:
1234567
stm32f042-morse-example/build$ test/testrunner
Unity test run 1 of 1
..............
-----------------------
14 Tests 0 Failures 0 Ignored
OK
Building and flashing onto the microcontroller
Now that you can see that the unit tests are passing, it’s time to try
running the code on the microcontroller!
We’re going to make a new build directory for this; this one will
contain all of the ARM-compiled pieces instead of the x86-compiled
pieces from above. The process here is pretty similar, but we’re going
to pass an additional parameter to CMake to tell it what to use for a
cross-compilation toolchain. We’ll also tell CMake to do a Debug
build, so that we can easily see what’s going on in GDB.
1234
stm32f042-morse-example$ mkdir build-target
stm32f042-morse-example$ cd build-target
stm32f042-morse-example/build-target$ cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-stm32f042.cmake -DCMAKE_BUILD_TYPE=Debug ..
stm32f042-morse-example/build-target$ make
The output from this is going to be target/target.elf. This file
contains all of the code that’s going to go onto the microcontroller,
plus additional things like debugging symbols.
To do these steps, make sure that you’ve got OpenOCD running (see
above). We’re going to use gdb to connect to OpenOCD and upload the
firmware. I’ll talk about what is happening with each step as we go
along.
Tell GDB to connect to OpenOCD. OpenOCD by defaults listens on port
4444 for general commands, and port 3333 for GDB commands:
123
(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x00000000 in ?? ()
Tell GDB which file has the firmware:
1234
(gdb) file target/target.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from target/target.elf...done.
And tell GDB to tell OpenOCD to flash that firmware onto the chip: