Skip to content

Set up an embedded / firmware project

Embedded projects break the assumptions a desktop project makes: the compiler is a cross-compiler inside a proprietary IDE, there may be no compile_commands.json, headers come from a vendor SDK and define hardware registers, and some source is hand-written assembly. This guide gets Understand to an accurate analysis anyway.

What's different about embedded

  • Cross-compiler + vendor IDE — the build runs under an embedded toolchain, not plain gcc.
  • Vendor / register headers — correct include paths and predefined macros matter more, because the wrong #ifdef branch leaves whole drivers as inactive code.
  • Often no standard build export — many embedded IDEs don't emit a compilation database.
  • Assembly — startup code, ISRs, and hot loops are frequently .asm/.s.

1. Give Understand the real include & macro set

Accuracy in Strict (Clang) mode depends on matching how the firmware is compiled. In order of preference:

  1. Import a compilation database if your toolchain can emit one — und add -cmake compile_commands.json.
  2. Capture the build with Buildspy/Build Watcher — it works with any gcc-like compiler, and you can point the wrappers at a cross-compiler with -cc/-cxx or the UND_PBCCCOMPILER / UND_PBCXXCOMPILER environment variables. See Build Watcher vs Buildspy.
  3. Set includes and macros by hand when neither is possible — add the vendor SDK include directories and the toolchain's predefined macros with the -C++Includes, -C++SystemIncludes, and -C++Macros settings (or Project → Improve Project Accuracy). Your compiler can usually dump its predefined macros (e.g. gcc-like: -dM -E), which you can feed in directly.

One target per project

Firmware often builds several images from shared sources with different macro sets. Analyze one target/configuration per project so the active #ifdef branches match a real build — mixing configurations produces contradictory parsing.

2. Know what happens to the assembly

Understand parses assembly for two assemblers only: Coldfire 68K and IBM (System/370 mainframe) — selected per project under the Assembly language options. If your startup code and ISRs are written for one of those targets, add the .asm/.s files and calls into and out of them appear; see the Assembly notes in Per-language accuracy notes.

For any other architecture (ARM Cortex-M, RISC-V, Xtensa, DSP assembly, …) Understand does not parse the assembly, so references between C/C++ and those files won't resolve. The practical approach is to analyze the C/C++ side and treat the assembly boundary (vector table, startup, ISR entry points) as landmarks you locate by search instead.

3. Verify accuracy, then find the landmarks

Check Parse Accuracy on the Home dashboard and clear analysis errors first. Then orient yourself in the firmware the way most embedded teams do — locate the main loop, the state machine, and the interrupt handlers, and trace outward from them with the 10-minute tour and call graphs.

Where to next