Understanding the C/C++ SDK architecture for the Raspberry Pi Pico

Everything in this file comes from the Raspberry Pi Pico for C/C++ SDK and Prof. Hunter Adams's SDK Architecture page. This file contains all of the same content, just organized into a different manner.



INTERFACE Libraries

All libraries within the SDK (with the exception of the C/C++ standard libraries provided by the compiler) are INTERFACE libraries. A CMake INTERFACE library is a collection of:

  • Source files
  • Include paths
  • Compiler definitions (visible to code as #defines)
  • Compile and link options
  • Dependencies on other INTERFACE libraries

All of these INTERFACE libraries form a tree of dependencies, each contributing source files, include paths, compiler definitions, and compile/link options to the build. Collection of all of these dependencies is done recursively. They are collected based on the libraries you have listed in your CMakeLists.txt file, and by the libraries depended on by those libraries, and so on.

A single project may contain many executables, as is the case for the pico-examples project. All of the code for each executable, including the SDK libraries, is (re)compiled for each executable from source. This allows you to specify customised settings for those libraries on a per-application basis, at compile-time.


SDK library structure

High-level API's

The SDK libraries are arranged heirarchically. There are high-level libraries (pico_xxxx) that generally enable the user to do things that have cross-cutting concerns between various pieces of hardware. For example, the sleep_ functions in pico_time must be aware of both the RP2040's timer hardware and with the way that the RP2040 enters and exits low-power states.

Generally speaking, these libraries are build upon one or more lower-level hardware_ libraries, and often depend on one another. Section 4.2 of the SDK guide lists all these high-level libraries.

Runtime support libraries

As per wikipedia, a runtime library is a set of low-level routines used by a compiler to invoke some of the behaviors of a runtime environment, by inserting calls to the runtime library into compiled executable binary. Section 4.4 of the SDK guide provides a description of all runtime libraries that bundle functionality which is common to most RP2040-based applications.

Hardware support libraries

Hardware support libraries are individual libraries (hardware_xxx) that provide actual API's for interacting with each piece of physical hardware/peripheral. They are lightweight and provide only thin abstractions. They generally provide functions for configuring or interacting with the peripheral hardware at a functional level, rather than accessing registers directly. These have been described quite extensively in section 4.1 of the SDK.

These libraries are intended to have very minimal runtime cost. They generally do not require any or much RAM, and do not rely on other runtime infrastructure. In general, their only dpendencies are the hardware_structs and hardware_regs libraries that contain definitions of memory-mapped register layout on the RP2040. Many of them are implemented as static inline functions, the idea being that you sacrifice no performance by using these functions as compared with using preprocessor macros with the hardware_regs definitions.

Hardware structs library

The hardware_structs library provides a set of C structures which represent the memory mapped layout of the RP2040 registers in the system address space. The struct headers are named consistently with both the hardware libraries and the hardware_regs register headers. So, for example, if you access the hardware_pio library's functionality through hardware/pio.h, the hardware_structs library (a dependee of hardware_pio) contains a header you can include as hardware/structs/pio.h if you need to access a register directly, and this itself pulls in hardware/regs/pio.h for register field definitions.

Hardware registers library

These are the lowest level libraries. The hardware_regs library is a complete set of include files for all RP2040 registers, autogenerated from the hardware itself. These are heavily commented, and they define the offset of every register and the layout of the fields in those registers, as well as the access type of the field (e.g. read-only). Note that these contain only comments and #define statements, so they can be included from both assembly files and C files.


Adding SDK libraries to your project

The build system

The Pico SDK uses CMake to manage builds. The project files titled CMakeLists.txt specify how your application or project should be built. To quote the SDK guide, "CMake is fundamental to the way the SDK is structured, and how applications are configured and built."

Some of the most commonly used syntax and ideas are as follows:

  • The add_executable(programName fileName.c) function in this file declares that a program programName should be built from the C source file fileName.c. This will also be the target name used to build the program, allowing the user to say something like make programName in the build directory to build this particular application.

  • target_link_libraries(programName library1 library2 ... libraryN) is pulling in the SDK functionality that the program needs. If you don't ask for a library, it doesn't appear in your program binary.

  • pico_add_extra_outputs(programName) creates UF2 files for loading onto the Pico via USB. If we didn't include this, the system would build an ELF file (executable linkable format) that could be loaded onto the Pico through the Serial Wire Debug port, with a debugger setup like gdb and openocd. This also creates .hex, .bin, .map, and .dis files.