modm API documentation
Build System Generators

Modules

 CMake Build Script Generator
 
 CompilationDB Generator
 
 Makefile Build Script Generator
 
 SCons Build Script Generator
 

Detailed Description

lbuild module: modm:build

This parent module defines a common set of functionality that is independent of the specific build system generator implementation. This includes straight-forward options like project name and build path but also more complicated configuration for programming your target via AvrDude or OpenOCD and debugging via GDB.

Note that this module does not compile your project, you will need to choose the modm:build:scons or modm:build:cmake submodule for that, or provide your own build system.**

Compiler Options

We maintain a common set of compiler options for all build system generator, so that they all have feature parity. We currently only support compiling modm with GCC for AVR, ARM Cortex-M and x86/posix with the options mentioned in the offical GCC options documentation.

You can find all compiler options inside the generated build script for your project, the options presented here are only the most important ones.

Shared for C and C++

For release builds:

For debug builds:

Only C

Only C++

For exception and RTTI flags, see modm:stdc++ module.

Linker

For target specific flags, see the modm:platform:core and related modules.

Configuration

This module generates a common set of configuration files that are used by the common tooling. Please note that these files are the foundation of more extensive tooling available as Python scripts which are then again wrapped by your chosen build system for convenience.

OpenOCD

For accessing your ARM Cortex-M based device, we use OpenOCD by default and generate a modm/openocd.cfg file with the target specific configuration:

You need to start openocd with this configuration file:

openocd -f modm/openocd.cfg
Warning
Be careful attaching to a running target The OpenOCD implementation halts the target at least while the device's debug peripheral is initialized. Only connect to systems that cannot create any damage while being halted! For example halting motor controllers may damage motors!!

AvrDude

Unfortunately AvrDude does not support a project-specific configuration file like OpenOCD does (only a undocumented user config in ~/.avrduderc), so there is no convenient one-line command to issue. You have to use the wrapper support of the specific build system or simply call AvrDude yourself via its command line.

GDB

Two commands are provided for convenience via the modm/gdbinit configuration:

GDB continues running the target after attaching, but does not load an ELF file! Please pass the ELF file as a command line argument.

You can start your GDB session like so:

arm-none-eabi-gdb -x modm/gdbinit path/to/project.elf

Generic Python Tools

We have written a number of pure Python tools to provide common functionality that get wrapped by the build system.

Here is a selection of tools that have a command line interface, so you can call them even without build system support in case you have a special setup. Note that there are even more tools that can be called in Python only, so have a look in your generated modm/modm_tools folder.

AvrDude

This tool simply wraps the avrdude command to provide two features:

python3 modm/modm_tools/avrdude.py -p m328p -c arduino -P auto -b 57600 \
path/to/project.elf

Fuses stored in the ELF file can be programmed by passing --fuse arguments:

python3 modm/modm_tools/avrdude.py -p m328p -c stk500v2 path/to/project.elf \
--fuse hfuse --fuse lfuse --fuse efuse

(* only AVR targets)

OpenOCD

Simply wraps OpenOCD and issues the right command to program the target.

python3 modm/modm_tools/openocd.py -f modm/openocd.cfg path/to/project.elf

You can also reset the target:

python3 modm/modm_tools/openocd.py -f modm/openocd.cfg --reset

You can use a different OpenOCD binary by setting the MODM_OPENOCD_BINARY environment variable before calling this script. This can be useful when using a custom OpenOCD build for specific targets.

export MODM_OPENOCD_BINARY=/path/to/other/openocd

(* only ARM Cortex-M targets)

Black Magic Probe

This tool wraps GDB to program an ELF file onto a target connected to a BMP. You can explictly pass the serial port, or let the tool guess it.

python3 modm/modm_tools/bmp.py path/to/project.elf
# or choose the port explicitly
python3 modm/modm_tools/bmp.py path/to/project.elf -p /dev/tty.usbserial-123

You can also reset the target:

python3 modm/modm_tools/bmp.py --reset

(* only ARM Cortex-M targets)

GDB

For debugging your program on ARM Cortex-M device, this Python tool wraps arm-none-eabi-gdb and connects it to a number of programmers running in the background or remotely.

The tool can be called from the command line. Here is a typical use-case using the openocd backend with the common configuration files:

python3 modm/modm_tools/gdb.py path/to/project.elf --ui=tui
-x modm/gdbinit -x modm/openocd_gdbinit
openocd -f modm/openocd.cfg

Or you can call the Python API directly:

import sys
sys.path.append("modm")
from modm_tools import gdb
from modm_tools.openocd import OpenOcdBackend
backend = OpenOcdBackend(config="modm/openocd.cfg")
gdb.call(source="path/to/project.elf", backend=backend,
config=["modm/gdbinit", "modm/openocd_gdbinit"], ui="tui")

This configuration starts the OpenOCD process in the background for you, however, if you want to connect to an already running process, you can use the remote backend with the --host={ip or hostname} via the command line:

# Extended-Remote running remotely
python3 modm/modm_tools/gdb.py path/to/project.elf -x modm/gdbinit --ui=tui
remote --host 123.45.67.89

Note that you can use different programmer backends to GDB:

# Black Magic Probe
python3 modm/modm_tools/gdb.py path/to/project.elf -x modm/gdbinit --ui=tui
bmp --port /dev/tty.usbserial-123
# CrashDebug for Post-Mortem debugging
python3 modm/modm_tools/gdb.py path/to/project.elf -x modm/gdbinit --ui=tui
crashdebug --binary-path modm/ext/crashcatcher/bins --dump coredump.txt

(* only ARM Cortex-M targets)

Currently two UIs are implemented for debugging:

Text UI

This UI is builtin to GDB and is therefore always available.

┌——main.cpp———————————————————————————————————————————————————————┐
>│194 DRAW(x+1, y+3); │
│195 DRAW(x+2, y+3); │
│196 #else
│197 DRAW(x , y ); │
│198 #endif │
│199 #undef DRAW │
│200 } │
│201 │
│202 static inline void drawScreen(framebuffer_t before, frame│
└—————————————————————————————————————————————————————————————————┘
>│0x80017a0 <game_of_life()+1692> strh.w r3, [r4, r12, lsl #1] │
│0x80017a4 <game_of_life()+1696> add r0, lr │
│0x80017a6 <game_of_life()+1698> ldr r2, [r2, #0] │
│0x80017a8 <game_of_life()+1700> strh.w r3, [r2, r0, lsl #1] │
│0x80017ac <game_of_life()+1704> ldr r3, [sp, #12] │
│0x80017ae <game_of_life()+1706> ldr r2, [sp, #0] │
│0x80017b0 <game_of_life()+1708> add r2, r3 │
│0x80017b2 <game_of_life()+1710> ldrb r3, [r7, r1] │
│0x80017b4 <game_of_life()+1712> strb r3, [r2, r1] │
└—————————————————————————————————————————————————————————————————┘
extended-r Remote target In: game_of_life L194 PC: 0x80017a0
Program received signal SIGINT, Interrupt.
0x080017a0 in drawPixel (color=<optimized out>, y=42, x=578) at main.c
(gdb)

Web UI

This UI simply uses the gdbgui project and works very well as an advanced IDE-independent debugging solution.

Size Report

Inspects the ELF file and generates a size report of the static usage of the device's memories. You must pass the available memory segments as a Python dictionary:

python3 modm/modm_tools/size.py path/to/project.elf \
"[{'name': 'flash', 'access': 'rx', 'start': 134217728, 'size': 65536}, \
{'name': 'sram1', 'access': 'rwx', 'start': 536870912, 'size': 20480}]"
Program: 1.4 KiB / 64.0 KiB (2.2% used)
(.build_id + .fastcode + .fastdata + .hardware_init + .rodata +
.table.copy.intern + .table.heap + .table.zero.intern + .text + .vector_rom)
Data: 3.0 KiB / 20.0 KiB (15.1% used) = 20 B static (0.1%) + 3072 B stack (15.0%)
(.bss + .fastdata + .stack)
Heap: 17.0 KiB / 20.0 KiB (84.9% available)
(.heap1)

(* only ARM Cortex-M targets)

Information Tool

This tool generates a set of source files containing information about the repository state.

You can use the --check-rebuild flag to only write the output file when the information changed. This prevents unnecessary rebuilding and relinking.

Warning
Respect developers privacy This information is placed into the firmware in cleartext, so it will be trivial to extract from a memory dump. Consider this information public as soon as it is uploaded to your target. Make sure you only use the information you absolutely need!

Git Information

python3 modm/modm_tools/info.py -o git_info.c -t git [--check-rebuild]
python3 modm/modm_tools/info.py -o git_info.c -t git_status [--check-rebuild]

The git_info(directory) function returns a dictionary with these values:

The git_info(directory, with_status=True) function returns these additional values:

This example project is showing an unclean repository state with uncommitted changes. This can give you a few hints as to where a firmware came from and help you pinpoint the source of a bug or feature.

Local Git User:
Name: Name Surname
Email: name.surname@example.com
Last Commit:
SHA: 1b5a9a642857182161a615039c92907e59881614
Abbreviated SHA: 1b5a9a642
Subject: wip
Author:
Name: Name Surname
Email: name.surname@example.com
Date: Tue Jul 17 22:23:20 2018 +0200
Timestamp: 1531859000
Committer:
Name: Name Surname
Email: name.surname@example.com
Date: Tue Jul 17 22:23:20 2018 +0200
Timestamp: 1531859000
File Status:
Modified: 10
Added: 0
Deleted: 0
Renamed: 0
Copied: 0
Untracked: 6

Build Information

python3 modm/modm_tools/info.py -o build_info.c -t build --compiler=gcc [--check-rebuild]

Generates a files with these values defined as const char * strings:

Example output on macOS:

Project: build_info
Machine: name.local
User: user
OS: macOS 10.14.6 (x86_64)
Compiler: g++-10 10.2.0

Unittest

This tools scans a directory for files ending in _test.hpp, extracts their test cases and generates a source file containing the test runner.

python3 modm/modm_tools/unit_test.py path/containing/tests \
path/to/generated_runner.cpp

Note that the files containing unittests must contain one class that inherits from the unittest::TestSuite class, and test case names must begin with test:

class TestClass : public unittest::TestSuite
{
public:
void testCase1();
}

Logging

Usually logging is done simply via a serial port and only a USB-Serial adapter is necessary to read the device output. However, some forms of logging require host support or additional processing steps.

ITM

Logging via the SWO trace pin is supported via OpenOCD and the modm:platform:itm module:

python3 modm/modm_tools/log.py itm openocd -f modm/openocd.cfg --fcpu 64000000

RTT

Logging via the RTT protocol is supported via OpenOCD and the modm:platform:rtt module:

python3 modm/modm_tools/log.py rtt openocd -f modm/openocd.cfg --channel 0

(* only ARM Cortex-M targets)

GNU Build-ID

To extract the build ID from an ELF file:

python3 modm/modm_tools/build_id.py path/to/project.elf
fa8918e6971ed783b25bff0ad11a0541be47a933

To copy the ELF file to a artifact cache:

python3 modm/modm_tools/build_id.py path/to/project.elf --cache path/to/folder
# copies to `path/to/folder/fa8918e6971ed783b25bff0ad11a0541be47a933.elf`

(* only ARM Cortex-M targets)

Bitmap

This tool can convert P1 .pbm files into C++ source code.

python3 modm/modm_tools/bitmap.py image.pbm --outpath .
# creates `image.hpp` and `image.cpp` in the CWD

UF2 Converter

UF2 is a Microsoft file format to pass to a on-device bootloader.

python3 modm/modm_tools/elf2uf2.py firmware.elf -o firmware.uf2 --target rp2040
--range 0x10000000:0x15000000:CONTENTS
--range 0x20000000:0x20042000:NO_CONTENTS

(* only ARM Cortex-M targets)

Module Options

modm:build:project.name: Project Name

The project name defaults to the folder name you're calling lbuild from.

It's used by your build system to name the executable and it may also be passed to your application via a string constant or CPP define.

Generated with: tmpwb176ud9 in [String]

modm:build:build.path: Build Path

The build path is defaulted to build/{modm:build:project.name}.

If you have a lot of embedded projects, you may want to change the build path to a common directory so that you don't have build/ folders everywhere. Remember to add your build path to your .gitignore.

You should use a relative path instead of an absolute one, so that this option still works for other developers.

Generated with: build/tmpwb176ud9 in [Path]

modm:build:unittest.source: Path to directory containing unittests

When this path is declared, the generated build script will compile only the unittests, not your application source code! You must use separate project configurations for compiling your unittest and application!

Generated with: [] in [Path]

modm:build:image.source: Path to directory containing .pbm files

Generated with: [] in [Path]

modm:build:info.git: Generate git repository state information

Generated with: Disabled in [Disabled, Info, Info+Status]

modm:build:info.build: Generate build state information

Generated with: no in [yes, no]

modm:build:avrdude.programmer: AvrDude programmer

Generated with: "" in [String]

modm:build:avrdude.port: AvrDude programmer port

Generated with: "" in [String]

modm:build:avrdude.baudrate: AvrDude programmer baudrate

Generated with: 0 in [0 ... +Inf]

modm:build:avrdude.options: AvrDude programmer options

Generated with: "" in [String]