The pw_stm32cube_build module provides helper utilities for building a target with the stm32cube HAL and/or the stm32cube initialization code.

The actual GN build files and headers live in third_party/stm32cube but are documented here. The rationale for keeping the build files in third_party is that code depending on stm32cube can clearly see that their dependency is on third party, not pigweed code.

STM32Cube MCU Components#

Each stm32 product family (ex. F4, L5, etc.) has its own stm32cube libraries. This integration depends on ST’s 3 core MCU Components instead of their monolithic MCU Package. The components are the hal_driver, cmsis_core, and cmsis_device. All of these repos exist on ST’s GitHub page. Compatible version tags are specified on the of each MCU component.

To use Pigweed’s STM32Cube integration, you will need to acquire the three components. The details are build-system dependent.

GN build#

The primary pw_source_set for this integration is $dir_pw_third_party/stm32cube:stm32cube. This source set includes all of the HAL, init code, and templates, depending on the value of the GN args.

Directory setup#

Within a single directory, the following directory/file names are required.

Dir/File Name



checkout of stm32{family}xx_hal_driver


checkout of cmsis_device_{family}


checkout of cmsis_core


list of files generated by gen_file_list


The stm32cube directory can alternatively be setup using pw_package. This will automatically download compatible repos into the expected folders and generate the files.txt.

pw package install stm32cube_{family}


$dir_pw_third_party/stm32cube:stm32cube contains the following primary headers that external targets / applications would care about.


ex. stm32f4xx.h, stm32l5xx.h

This is the primary HAL header provided by stm32cube. It includes the entire HAL and all product specific defines.


This is a convenience define provided by this integration. It simply includes {family}.h.

This is useful because there is a lot of commonality between the HAL’s of the different stm32 families. Although the API’s are not guaranteed to be compatible, many basic API’s often are (ex. GPIO, UART, etc.). This common header allows for stm32 family agnostic modules (ex. pw_sys_io_stm32, which could work with most, if not all families).


As described in the inject_init section, if you decide to use the built in init functionality, a pre main init function call, pw_stm32cube_Init(), is injected into ST’s startup scripts.

This header contains the pw_stm32cube_Init() function declaration. It should be included and implemented by target init code.

GN args#

The stm32cube GN build arguments are defined in $dir_pw_third_party/stm32cube/stm32cube.gni.


These should be set to point to the stm32cube directory for each family that you need to build for. These are optional to set and are only provided for convenience if you need to build for multiple families in the same project.


This needs to point to the stm32cube directory for the current build.

For multi target projects, the standard practice to set this for each target:

dir_pw_third_party_stm32cube = dir_pw_third_party_stm32cube_f4


The product specified in as much detail as possible. ex. stm32f429zit, stm32l552ze, stm32f207zg, etc.


The pw_source_set that provides stm32{family}xx_hal_conf.h. The default uses the in-tree stm32{family}xx_hal_conf_template.h.


The pw_source_set containing the timebase. The default uses the in-tree stm32{family}xx_hal_timebase_tim_template.c.


The pw_source_set containing the cmsis init logic. The default uses the in-tree system_stm32{family}xx.c.


pw_source_set containing the core initialization logic. This normally includes a startup_stm32{...}.s + a dependent pw_linker_script. The default core_init_template uses the upstream startup and linker script matching pw_third_party_stm32cube_PRODUCT. If set to “”, you must provide your own linker/startup logic somewhere else in the build.


stm32cube_builder is utility that contains the backend scripts used by pw_package/stm32cube and the GN build scripts in third_party/stm32cube to interact with the stm32cube repos. You should only need to interact with stm32cube_builder directly if you are doing something custom, like using git submodules instead of pw_package, forking the stm32cube libraries, interfacing with a different build system, or using your own init.


Build systems like GN are unable to depend on arbitrary directories. Instead, they must have dependencies on specific files. The HAL for each stm32 product family has different filenames, so files.txt was created as a workaround. files.txt is a basic list of all the files in the stm32cube directory with relavent file extensions. The build system only directly depends on this list, which must be updated everytime the underlying repos are updated.

This command will generate files.txt for correctly structured stm32cube directories.

stm32cube_builder gen_file_list /path/to/stm32cube_dir


Within each stm32 family, there are specific products. Although most of the HAL is common between products, the init code is almost always different. find_files looks for all of the files relevant to a particular product within a stm32cube directory.

The product string should be specified in as much detail as possible because there are sometimes different defines or init code for submembers of products.

Ex. stm32f412cx, stm32f412rx, stm32f412vx, and stm32f412zx all have different init logic, while all stm32f439xx have the same init.

find_files only ever looks for init (linker + startup scripts) if the --init flag is provided.

The output is currently only provided in the GN ‘scope’ format to stdout. The following variables are output: family, product_define, sources, headers, include_dirs, and the following three if --init is specified: startup, gcc_linker, iar_linker.

stm32cube_builder find_files /path/to/stm32cube_dir stm32{family}{product} [--init]


ST provides init assembly files for every product in cmsis_device. This is helpful for getting up and running quickly, but they directly call into main() before initializing the hardware / peripherals. This is because they expect to do that initialization in main(), then call into the user application. Upstream Pigweed unit tests expect at least sys_io to be initialized before main() is called.

This command injects a call to pw_stm32cube_Init() immediately before the call to main(). This function should be implemented by the target to do whatever init is necessary (hal init, sys_io init, clock configuration, etc.)

inject_init takes in an ST assembly script and outputs the same script with the pre main init call. The output is printed to stdout, or to the specified --out-startup-path.

stm32cube_builder inject_init /path/to/startup.s [--out-startup-path /path/to/new_startup.s]


Pigweed primarily uses GCC for its Cortex-M builds. However, ST only provides IAR linker scripts in cmsis_device for most product families. This script converts from ST’s IAR linker script format (.icf) to a basic GCC linker script (.ld). This is a very basic converter that only works with exactly how ST currently formats their .icf files.

The output .ld files only contain RAM and FLASH sections. Anything more complicated will require hand customized .ld scripts. Output is printed to stdout or the specified --ld-path.

stm32cube_builder inject_init /path/to/iar_linker.icf [--ld-path /path/to/gcc_linker.ld]

Bazel build#

External dependencies#

As discussed above in STM32Cube MCU Components, you need the three STM32Cube Components for your MCU family to use Pigweed’s STM32Cube integration. You need to add the following git repositories to your workspace:

  • stm32{family}xx_hal_driver (e.g., HAL driver repo for the F4 family). We provide a Bazel build file which works for any family at @pigweed//third_party/stm32cube/stm32_hal_driver.BUILD.bazel. By default, we assume this repository will be named @hal_driver, but this can be overriden with a label flag (discussed below).

  • cmsis_device_{family} (e.g., CMSIS device repo for the F4 family). We provide a Bazel build file which works for any family at @pigweed//third_party/stm32cube/cmsis_device.BUILD.bazel. By default, we assume this repository will be named @cmsis_device, but this can be overriden with a label flag (discussed below).

  • cmsis_core, at We provide a Bazel build file for it at @pigweed//third_party/stm32cube/cmsis_core.BUILD.bazel. By default, we assume this repository will be named @cmsis_core, but this can be overriden with a label flag (discussed below).

Building for more than one MCU family#

Different MCU families require different HAL driver and CMSIS device packages from STM. So, if your project builds firmware for more than one MCU family, you will need to configure separate sets of the three [1] STM repositories for each MCU family. To do so,

  1. Add the appropriate repositories to your WORKSPACE under different names, eg. @stm32f4xx_hal_driver and @stm32h7xx_hal_driver.

  2. Set the corresponding Label flags as part of the platform configuration for your embedded target platforms. Currently, the best way to do this is via a bazelrc config, which would look like this:

    build:stm32f429i --platforms=//targets/stm32f429i_disc1_stm32cube:platform
    build:stm32f429i --@pigweed//third_party/stm32cube:stm32_hal_driver=@stm32f4xx_hal_driver//:hal_driver
    build:stm32f429i --@stm32f4xx_hal_driver//:cmsis_device=@cmsis_device_f4//:cmsis_device
    build:stm32f429i --@stm32f4xx_hal_driver//:cmsis_init=@cmsis_device_f4//:default_cmsis_init

    However, once platform-based flags are implemented in Bazel, it will be possible to set these flags directly in the platform definition.



Upstream Pigweed modules that depend on the STM32Cube HAL, like pw_sys_io_stm32cube, include the HAL through the family-agnostic header stm32cube/stm32cube.h. This header expects the family to be set through a define of STM32CUBE_HEADER. So, to use these Pigweed modules, you need to set that define to the correct value (e.g., \"stm32f4xx.h\"; note the backslashes) as part of your build. This is most conveniently done through copts associated with the target platform.

Label flags#



Points to the cc_library target providing a header with the HAL configuration. Note that this header needs an appropriate, family-specific name (e.g., stm32f4xx_hal_conf.h for the F4 family).


These label flags can be used to further customize the behavior of STM32Cube.


This label_flag introduces a layer of indirection useful when building a project that requires more than one STM32Cube package (see Building for more than one MCU family). It should point to the repository containing the HAL driver.

The default value is @hal_driver.


This label flag should point to the repository containing the CMSIS core build target.

The default value is @cmsis_core.


This label flag should point to the repository containing the CMSIS device build target.

The default value is @cmsis_device.


This label flag should point to the CMSIS initialization code. By default it points to the system_{family}.c template provided in the CMSIS device repository.


This label flag should point to a cc_library providing a timebase implementation. By default it points to the template included with STM’s HAL repository.