The Makefile provided with the example designs allows developers to highly customize the build environment.
The following key configurations can be modified:
Developers can choose to use either the NANO or NOSTD C standard library implementations by setting the NEWLIB variable in the Makefile.
| Aspect | NANO (newlib-nano + nosys) | NOSTD (No Standard Library) |
|---|---|---|
| Overview | Uses newlib-nano and nosys.specs. | Completely removes libc and runtime support (-nostdlib). |
| Library Size | Small footprint. | Minimal (zero hidden overhead). |
| Binary Control | Moderate (nano handles the reductions). | Absolute control over the binary. Strips away all unused libc functions. |
| Libc Support | Uses an optimized, size-reduced C standard library. | Uses the legacy bsp_printf API. Does not support scanf natively. |
| Ease of Use | Easy → works out-of-the-box. | Hard → full control, full responsibility. |
Using newlib-nano (NANO)
In this configuration, the compiler builds using both --specs=nano.specs and --specs=nosys.specs.
Using nostdlib (NOSTD)
If the NANO configuration is used, the compiler automatically includes the C standard library (libc), the math library (libm), and the GCC runtime (libgcc). These do not typically need to be manually included.
However, if using the NOSTD configuration (-nostdlib), the compiler strips these libraries out completely. In this bare-metal mode, developers might still want to perform specific operations—such as printing floating-point numbers using the legacy bsp_printf—without pulling in the full overhead of the standard library.
To support this hybrid approach, the BSP allows developers to explicitly toggle the inclusion of specific compiler libraries back into the Makefile:
The Makefile allows you to control how aggressively the GCC compiler optimizes the resulting binary. Higher optimization levels can significantly increase execution speed, but may make debugging difficult or increase the final binary size.
You can set the optimization level by modifying the standard GCC flags (e.g., -O0, -O2, -Os) in the Makefile:
| Flag | Name | Description |
|---|---|---|
| -O0 | None | No optimization. This is the default for debugging, as the compiled code perfectly matches the written C code. Compiles the fastest. |
| -O1 | Moderate | Basic optimizations that do not require significant compilation time. Balances speed and code size. |
| -O2 | High | Aggressive optimizations for maximum execution speed. Highly recommended for Release builds. Does not perform loop unrolling to avoid bloating binary size. |
| -O3 | Maximum | Enables all -O2 optimizations plus loop unrolling and function inlining. Maximizes speed, but can significantly increase RAM/Flash usage. |
| -Os | Size | Optimizes specifically to reduce binary size. Highly recommended for embedded systems with strict memory constraints. |
The debug level dictates how much symbolic debugging information (like variable names, line numbers, and macros) the compiler embeds into the final .elf file. This information is critical when using a debugger like GDB or OpenOCD.
You can adjust the debug detail by setting the -g flag in the Makefile:
| Flag | Level | Description |
|---|---|---|
| -g0 | None | Strips all debug information. Use this for final production builds to keep file sizes as small as possible. |
| -g1 | Minimal | Produces minimal debug info (line numbers and external variables). Sufficient for basic backtraces, but local variables cannot be inspected. |
| -g / -g2 | Standard | The default debug level. Includes comprehensive symbolic debug information (local variables, line numbers) required for standard step-by-step GDB debugging. |
| -g3 | Maximum | Includes everything in -g2, plus extra information like macro definitions (#define). Extremely useful if you need to debug complex hardware register macros. |
Below is the standard Makefile template used across the BSP example projects: