SDK CMake User Guide
This guide provides comprehensive information on how to use the HPM SDK’s CMake extensions for building embedded applications. It focuses on workflow, best practices, and practical examples rather than API reference (which is covered in cmake_intro).
Overview
This section provides a high-level introduction to the HPM SDK CMake build system and its key concepts.
Design Philosophy and Architecture
Design Philosophy of SDK CMake Build System
The HPM SDK CMake build system is designed with a fundamental principle: decoupling user applications from the underlying SDK. This design philosophy addresses several key challenges in embedded development:
1. SDK Version Independence
Users can switch between different SDK versions within the same major version by simply changing the SDK_BASE path
No need to modify application code when switching SDK versions
Maintains application compatibility across SDK updates
2. Reduced Communication Overhead in Team Development
Multiple developers can collaborate on applications based on the same SDK version
Only application-related data needs to be exchanged, not the entire SDK
Eliminates the need to share large SDK repositories between team members
Reduces version synchronization issues and merge conflicts
3. Complete Decoupling Through Custom Board Support
Users can define custom board configurations without modifying SDK core files
Applications become completely independent of SDK internals
Enables rapid prototyping and customization for specific hardware requirements
Improves development efficiency by isolating application logic from SDK complexity
4. Modular Architecture Benefits
Clear separation of concerns between application and SDK layers
Easier maintenance and debugging of application-specific issues
Simplified dependency management and build configuration
Enhanced portability across different development environments
5. Cross-Platform Build System
Windows, macOS, and Linux Support: Full compatibility across major operating systems
Unified Build Commands: Same CMake commands work identically on all platforms
Automatic Toolchain Detection: Automatically detects and configures platform-specific toolchains
IDE Integration: Generates platform-appropriate project files for various IDEs
Path Handling: Automatic path conversion between different operating system conventions
Dependency Resolution: Platform-agnostic dependency management and resolution
6. SOC-Agnostic Application Development
Dependency Abstraction: Build system shields users from complex SOC-specific dependency details
Unified API Interface: Consistent development experience across different HPM SOC families
Automatic Configuration: SOC-specific settings, memory layouts, and peripheral configurations are handled automatically
Focus on Application Logic: Developers can concentrate on application development without worrying about underlying SOC complexities
Architecture Overview
hpm_sdk’s build system creates a layered architecture that separates user applicat with rom SDK internals while maintaincodeflexibility through custom board support:
┌─────────────────────────────────────────────────────────────────┐
│ User Application Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │
│ │ Application │ │ Application │ │ Application │ │
│ │ Source Code │ │ Configuration │ │ Build Files │ │
│ │ (main.c, etc) │ │ (app.yaml) │ │ (CMakeLists.txt) │ │
│ └─────────────────┘ └─────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ depends on
▼
┌──────────────────────────────────────────────────────────────────┐
│ Custom Board Configuration │
│ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────────┐ │
│ │ Board YAML │ │ Custom Board │ │ Board-Specific │ │
│ │ Configuration │ │ Definitions │ │ Pin Mappings │ │
│ │ (board.yaml) │ │(board.h/board.c)│ │(pinmux.h/pinmux.c)│ │
│ └─────────────────┘ └─────────────────┘ └───────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
│ references
▼
┌────────────────────────────────────────────────────────────────┐
│ SDK Core Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ SOC │ │ Drivers │ │ Middleware │ │
│ │ Definitions │ │ (GPIO, UART, │ │ (FreeRTOS, │ │
│ │ (registers, │ │ SPI, I2C, │ │ USB, etc.) │ │
│ │ memory map) │ │ etc.) │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ CMake │ │ Toolchain │ │ IDE │ │
│ │ Build System │ │ Integration │ │ Integration │ │
│ │ (localization, │ │ (GCC, SEGGER, │ │ (SES, IAR) │ │
│ │ optimization) │ │ Andes, etc.) │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└────────────────────────────────────────────────────────────────┘
SDK Version Flexibility:
The same application can work with different SDK versions by simply changing the HPM_SDK_BASE path:
Application A (main.c, app.yaml, CMakeLists.txt)
├── SDK Version 1.0.0 (HPM_SDK_BASE=/path/to/sdk_v1.0.0)
├── SDK Version 1.1.0 (HPM_SDK_BASE=/path/to/sdk_v1.1.0)
├── SDK Version 1.2.0 (HPM_SDK_BASE=/path/to/sdk_v1.2.0)
└── Custom SDK Build (HPM_SDK_BASE=/path/to/custom_sdk)
Application B (main.c, app.yaml, CMakeLists.txt)
├── SDK Version 1.0.0 (HPM_SDK_BASE=/path/to/sdk_v1.0.0)
├── SDK Version 1.1.0 (HPM_SDK_BASE=/path/to/sdk_v1.1.0)
└── SDK Version 1.2.0 (HPM_SDK_BASE=/path/to/sdk_v1.2.0)
Key Benefits of This Architecture:
Separation of Concerns: User applications are completely independent of SDK internals
Custom Board Support: Users can define custom boards without modifying SDK core files
Version Flexibility: Easy switching between SDK versions by changing HPM_SDK_BASE
Team Collaboration: Share only application and board files, not the entire SDK
Maintainability: Clear boundaries make debugging and maintenance easier
Build System Overview:
The SDK build system is a comprehensive solution that integrates multiple components to provide a seamless development experience. It combines CMake-based project management with automated toolchain detection, intelligent dependency resolution, and cross-platform compatibility. The system is designed to handle the complexity of embedded development while maintaining simplicity for application developers.
Core Components:
The build system handles:
Toolchain Management: Automatic detection and configuration of supported toolchains
Board Configuration: Board-specific settings and peripheral configurations
Component Integration: Modular component system for drivers and middleware
Multi-core Support: Support for dual-core applications with linked projects
IDE Integration: Generation of project files for various IDEs
Build Optimization: Configurable build types and optimization levels
Basic Workflow
Setting Up Your Project
Create Project Structure
my_project/ ├── CMakeLists.txt ├── app.yaml └── src/ └── main.c
Configure CMakeLists.txt
cmake_minimum_required(VERSION 3.13) # Find and configure the HPM SDK find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE}) # Define your project project(my_application) # Add source files sdk_app_src(src/main.c) # Generate IDE projects (optional) generate_ide_projects()
Configure app.yaml
debug: ses: auto_start_gdb_server: true gdb_server_port: 3333
Building Your Project
Basic Build Commands:
# Configure and build
cmake -B build -S . -DBOARD=hpm6750evkmini
cmake --build build
# Or in one step
cmake -B build -S . -DBOARD=hpm6750evkmini && cmake --build build
Build Configuration:
The SDK uses two different build type concepts:
CMAKE_BUILD_TYPE - Compilation optimization level:
debug: Debug build with symbols and minimal optimizationrelease: Release build with full optimization
HPM_BUILD_TYPE - Linker configuration for memory layout:
ram: Build for RAM execution (default)flash_xip: Build for flash execution with XIPflash_sdram_xip: Build for flash execution with SDRAM XIPflash_hybrid_xip: Build for hybrid execution (code in ram-mapped flash content, data in RAM)sec_core_img: Build for secondary core image
Setting Build Types:
Compilation Type (CMAKE_BUILD_TYPE):
# Debug compilation cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=debug # Release compilation cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=release
Linker Type (HPM_BUILD_TYPE):
In CMakeLists.txt (Recommended for project-specific builds):
cmake_minimum_required(VERSION 3.13) # Set linker configuration for this project set(HPM_BUILD_TYPE "flash_xip") find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE}) project(my_application) sdk_app_src(src/main.c) generate_ide_projects()
Via Command Line (Override CMakeLists.txt setting):
# Flash XIP execution cmake -B build -S . -DBOARD=hpm6750evkmini -DHPM_BUILD_TYPE=flash_xip
Combined Example:
# Release compilation with flash XIP execution cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=release -DHPM_BUILD_TYPE=flash_xip
Advanced Configuration
Customize Output File Names
Output File Name (APP_NAME):
The APP_NAME variable controls the output file names. If not specified, it defaults to “demo”. The output file names are:
${APP_NAME}.elf - Executable file
${APP_NAME}.bin - Binary file for flashing
${APP_NAME}.map - Memory map file
${APP_NAME}.asm - Assembly listing
In CMakeLists.txt:
cmake_minimum_required(VERSION 3.13)
# Set custom application name
set(APP_NAME "my_custom_app")
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(my_application)
sdk_app_src(src/main.c)
generate_ide_projects()
Via Command Line:
# Set custom application name
cmake -B build -S . -DBOARD=hpm6750evkmini -DAPP_NAME=my_custom_app
Output Files:
The APP_NAME affects the following output files:
${APP_NAME}.elf - Executable file
${APP_NAME}.bin - Binary file for flashing
${APP_NAME}.map - Memory map file
${APP_NAME}.asm - Assembly listing
Combined Advanced Example:
# Release compilation with flash XIP execution and custom app name
cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=release -DHPM_BUILD_TYPE=flash_xip -DAPP_NAME=my_custom_app
Segger Embedded Studio (SES) Advanced Customization
The SDK provides extensive customization options for Segger Embedded Studio projects through CMake variables and specialized functions.
Toolchain and Compiler Variants:
# In your CMakeLists.txt file:
# Set toolchain variant (Standard, Andes)
set(SES_TOOLCHAIN_VARIANT "Standard")
# Configure compiler variant (gcc, SEGGER)
set(SES_COMPILER_VARIANT "gcc")
# Set assembler variant (gcc, SEGGER)
set(SES_ASSEMBLER_VARIANT "SEGGER")
# Configure linker variant (gnu, SEGGER)
set(SES_LINKER_VARIANT "SEGGER")
Library I/O Configuration:
# Set library I/O type for SES
sdk_ses_opt_lib_io_type(STD) # Options: STD, SEGGER, Custom
Advanced Compilation Control:
# Set SES-specific compile options
sdk_ses_compile_options(-O2 -g -Wall)
# Set SES-specific compile definitions
sdk_ses_compile_definitions(-DDEBUG -DVERSION=1)
# Link SES-specific libraries
sdk_ses_link_libraries(libm libc)
Per-File Optimization Control:
# Set optimization level for specific files
sdk_ses_set_optimization_level("src/critical.c" "Level 2 for speed")
sdk_ses_set_optimization_level("src/debug.c" "None")
# Configuration-specific optimization
sdk_ses_set_optimization_level_debug("src/test.c" "Level 1")
sdk_ses_set_optimization_level_release("src/test.c" "Level 3 for more speed")
Section Placement Control:
# Control section placement for specific files
sdk_ses_set_code_placement("src/vector.c" "vector_section")
sdk_ses_set_data_placement("src/constants.c" "const_section")
sdk_ses_set_isr_placement("src/interrupts.c" "isr_section")
# Configuration-specific section placement
sdk_ses_set_code_placement_debug("src/debug.c" "debug_code")
sdk_ses_set_code_placement_release("src/debug.c" "release_code")
Linker and Memory Configuration:
# Set SES-specific linker options
sdk_ses_ld_options(-Wl,--start-group -Wl,--end-group)
# Link SES-specific input libraries
sdk_ses_ld_lib(custom_lib.a)
# Configure memory layout
set(SES_MEMORY_LAYOUT "Custom")
Debug and Target Configuration:
# Configure debug target connection
set(SES_DEBUG_TARGET_CONNECTION "GDB Server") # Options: GDB Server, J-Link
# GDB Server configuration
set(SES_GDB_SERVER_TYPE "Custom")
set(SES_GDB_SERVER_COMMAND "openocd")
set(SES_GDB_SERVER_ARGS "-f board/hpm6750evkmini.cfg")
set(SES_GDB_SERVER_PORT "3333")
# J-Link configuration
set(SES_JLINK_SPEED "4000")
set(SES_JLINK_DEVICE "HPM6750xVMx")
Build Configuration:
# Set SES build configuration
set(SES_BUILD_CONFIGURATION "Release") # Options: Debug, Release, RelWithDebInfo
# Configure optimization level
set(SES_OPTIMIZATION_LEVEL "2") # 0=None, 1=Basic, 2=Full, 3=Size
# Set warning level
set(SES_WARNING_LEVEL "All") # Options: None, Basic, All, Extra
Command Line Configuration:
# Configure SES via command line
cmake -B build -S . -DBOARD=hpm6750evkmini \
-DSES_TOOLCHAIN_VARIANT="Standard" \
-DSES_COMPILER_VARIANT="gcc" \
-DSES_ASSEMBLER_VARIANT="SEGGER" \
-DSES_LINKER_VARIANT="SEGGER" \
-DSES_DEBUG_TARGET_CONNECTION="GDB Server" \
-DSES_GDB_SERVER_COMMAND="openocd" \
-DSES_GDB_SERVER_ARGS="-f board/hpm6750evkmini.cfg"
Generated SES Project Structure:
After building, the following SES project files will be generated:
build/segger_embedded_studio/
├── ${CMAKE_PROJECT_NAME}.emProject # Main project file
└── ${CMAKE_PROJECT_NAME}.emSession # Session configuration
Integration with app.yaml:
You can also configure some SES debug settings in your app.yaml file:
# app.yaml
debug:
ses:
auto_start_gdb_server: true # Auto-start GDB server (Yes/No)
gdb_server_port: 3333 # GDB server port number
gdb_server_reset_command: "reset halt" # GDB server reset command
Note: Only these three SES debug settings are supported in app.yaml. For other SES configurations (toolchain variants, compiler options, section placement, etc.), you must use CMake variables and functions as described above.
Advanced SES Functions Reference:
The SDK provides several specialized functions for SES customization:
# Set SES project options
sdk_ses_options("LIBRARY_IO_TYPE=STD")
sdk_ses_options("debug_target_connection=GDB Server")
sdk_ses_options("linker_printf_fp_enabled=1")
sdk_ses_options("linker_scanf_fp_enabled=1")
sdk_ses_options("linker_printf_fmt_level=F4000"
Debug Connection Types:
- "GDB Server", "J-Link# Note: If using Andes extensions or DSP features, you MUST use Andes toolchain
Andes")
# When using Andes toolchain, the following variants are automatically set:
# - ES_COMPILER_VARIANT defauls to "gcc"
# - SES_ASSEMBLER_VARIANT defults to "gcc"
# - SES_LINKER_VARIANT defaults to "gnu"
# Aes toolchain is required for:
# - Andes RISC-V extensions (custom instructions)
# - DSP features and optimizations
# - Andes-specific compiler gnu - Standard GNU GCC toolchain
- - Andes RISC-V toolchain (required for Andes extensions and DSP features))Important Note about sdk_ses_options():**
The sdk_ses_options() function accepts key-value pairs that will be saved to the SES project file (.emProject). To discover available configuration options:
Generate the SES project using the SDK
Open the project in Segger Embedded Studio
Modify settings in the SES GUI interface (Project Options, Build Options, etc.)
Save the project - this will update the .emProject file
Examine the `.emProject` file to see the key-value pairs that were saved
This way you can identify which configuration options are available and their valid values for use with sdk_ses_options().
SDK Localization
The SDK provides a localization mechanism designed to address two specific needs:
SES Project Settings Persistence: Enable users to save and transfer SES project settings that cannot be preserved in the CMake build system
Project Simplification for Version Control: Allow users to create minimal, self-contained projects for efficient version management
Localization Process:
The localization process creates a self-contained project by copying only the necessary SDK files:
Dependency Analysis: Analyzes your project’s build dependencies
File Collection: Identifies required SOC, board, middleware, and source files
Copy Operation: Copies necessary files to a local directory
Path Updates: Updates project files to use local paths
Using LOCALIZE_SDK with Ninja:
# Localize the SDK using ninja
ninja -C build localize_sdk
# Unlocalize the SDK (restore original paths)
ninja -C build unlocalize_sdk
# Force localization (overwrite existing localized files)
ninja -C build localize_sdk -- -f
Localization Directory Structure:
After localization, a new directory structure is created:
your_project/
├── CMakeLists.txt # Original project file
├── CMakeLists.txt.localized.bak # Backup of original file
├── src/ # Your source files
├── hpm_sdk_localized/ # Localized SDK files
│ ├── soc/ # Required SOC files
│ ├── boards/ # Required board files
│ ├── middleware/ # Required middleware files
│ └── cmake/ # Required CMake files
└── build/ # Build directory
Key Benefits:
SES Settings Persistence: Preserve SES project configurations that CMake cannot maintain
Minimal Version Control: Include only essential SDK files in your repository
Self-contained Projects: Build projects without external SDK dependencies
Easier Distribution: Share projects with others without SDK installation requirements
Use Cases:
SES Project Customization: When you need to preserve complex SES project settings
Version Control Optimization: Create minimal repositories with only necessary SDK components
Project Distribution: Share self-contained projects with team members or external users
Build Environment Consistency: Ensure consistent builds across different development environments
Multi-core Applications
For dual-core applications, the SDK supports linked projects through a hierarchical directory structure that reflects the multi-core architecture:
Multi-core Project Organization
Directory Structure Philosophy:
The multi-core project structure is designed to:
Separate Core Responsibilities: Each core has its own directory with independent build configuration
Maintain Build Independence: Each core can be built independently or as part of the complete system
Support Different Build Types: Core0 typically runs the main application, while Core1 runs specialized tasks
Enable Parallel Development: Teams can work on different cores simultaneously
Standard Directory Layout:
multicore_app/ ├── core0/ # Primary core (main application) │ ├── CMakeLists.txt # Core0 build configuration │ ├── app.yaml # Core0 application configuration │ ├── src/ # Core0 source files │ │ ├── main.c # Core0 main entry point │ │ ├── app_init.c # Core0 initialization │ │ └── sec_core_img.c # Generated Core1 image (auto-generated) │ └── include/ # Core0 header files │ └── app_config.h ├── core1/ # Secondary core (specialized tasks) │ ├── CMakeLists.txt # Core1 build configuration │ ├── src/ # Core1 source files │ │ ├── demo.c # Core1 main entry point │ │ └── core1_task.c # Core1 specific tasks │ └── include/ # Core1 header files │ └── core1_config.h ├── shared/ # Shared resources (optional) │ ├── include/ # Shared header files │ │ ├── common.h # Common definitions │ │ └── ipc.h # Inter-processor communication │ └── src/ # Shared source files │ └── ipc_common.c └── README.md # Project documentation
Multi-core Application Architecture:
From Image Perspective:
The multi-core application works by converting Core1’s binary into a C array that is embedded within Core0’s project:
Core1 Binary Generation: Core1 is compiled as a standalone binary with sec_core_img build type
Binary to C Array Conversion: The Core1 binary is converted to a C array format using specialized tools
Embedding Process: The C array is written to sec_core_img.c and included in Core0’s source files
Single ELF Generation: Core0 is compiled with the embedded Core1 image, resulting in a single ELF file containing both cores
From Runtime Perspective:
At runtime, Core0 manages the Core1 execution lifecycle:
Image Loading: Core0 copies the Core1 image from the embedded C array to the memory location specified in the linker script
Entry Point Update: Core0 updates Core1’s entry point to point to the correct memory location
Core1 Release: Core0 releases Core1 from reset, allowing it to execute its code independently
Independent Execution: Both cores now run independently, with Core0 managing the system and Core1 handling specialized tasks
Multi-core Program Organization
Core Roles and Responsibilities:
Core0 (Primary Core):
Runs the main application logic
Handles system initialization and configuration
Manages the secondary core lifecycle
Contains the generated secondary core image
Typically uses flash_xip build type
Core1 (Secondary Core):
Runs specialized tasks (real-time processing, communication, etc.)
Operates independently after initialization
Communicates with Core0 through IPC mechanisms
Uses sec_core_img build type to generate a binary image
Build Process Flow:
Core1 Compilation: Core1 is compiled first as a standalone binary
Binary to C Array: Core1 binary is converted to a C array format
File Generation: The C array is written to sec_core_img.c in Core0’s source directory
Core0 Compilation: Core0 is compiled with the embedded Core1 image
Final Binary: Single binary containing both cores
Core0 Configuration
Core0 CMakeLists.txt:
cmake_minimum_required(VERSION 3.13) # Set linker configuration for flash XIP (recommended for primary core) set(HPM_BUILD_TYPE "flash_xip") # Set custom application name (optional) set(APP_NAME "multicore_demo") find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE}) project(multicore_core0) # Add Core0 source files sdk_app_src( src/main.c src/app_init.c src/sec_core_img.c # it will be auto-generated by build system ) # Add header file directories sdk_app_inc(include) # Generate IDE projects generate_ide_projects()
Core0 app.yaml:
linked_project: project_name: multicore_app/core1 project_path: ../core1 build_type: sec_core_img
Core1 Configuration (CMakeLists.txt)
Key Configuration Elements:
cmake_minimum_required(VERSION 3.13) # Essential for secondary core image generation set(HPM_BUILD_TYPE "sec_core_img") # Specify where the generated C array file will be placed # This file contains Core1's binary as a C array for embedding in Core0 set(SEC_CORE_IMG_C_ARRAY_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/../core0/src/sec_core_img.c) find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE}) project(multicore_core1) # Add Core1 source files sdk_app_src(src/demo.c) # Generate IDE projects (optional for Core1) generate_ide_projects()
Configuration Explanation:
`HPM_BUILD_TYPE=”sec_core_img”`: Tells the build system to compile Core1 as a secondary core image
`SEC_CORE_IMG_C_ARRAY_OUTPUT`: Specifies where the converted C array file will be placed
Relative Path: Uses ../core0/src/ to place the file in Core0’s source directory
File Naming: Conventionally named sec_core_img.c for clarity
Building Multi-core Applications
Complete Build Process:
# Navigate to Core0 directory (main application) cd core0 # Build both cores (Core1 image + Core0 with embedded Core1) cmake -B build -S . -DBOARD=hpm6750evkmini cmake --build build
Build Output Structure:
core0/ ├── src/ │ ├── main.c # Core0 main source │ └── sec_core_img.c # Core1 binary converted to C array (generated) └── build/ ├── output/ │ ├── ${APP_NAME}.elf # Final binary with both cores (default: demo.elf) │ ├── ${APP_NAME}.bin # Binary format for flashing (default: demo.bin) │ ├── ${APP_NAME}.map # Memory map file (default: demo.map) │ └── ${APP_NAME}.asm # Assembly listing (default: demo.asm) ├── segger_embedded_studio/ # Segger IDE project files └── iar_embedded_workbench/ # IAR IDE project filesBuild Process Details:
The sec_core_img.c file is generated through the following process:
Core1 Compilation: Core1 is compiled as a standalone binary
Binary Conversion: The Core1 binary is converted to a C array format
File Generation: The C array is written to sec_core_img.c in Core0’s src/ directory
Core0 Compilation: Core0 is compiled with the embedded Core1 image from sec_core_img.c
Individual Core Development:
# Build Core0 only (Core1 generates empty placeholder) cmake -B build -S . -DBOARD=hpm6750evkmini -DDISABLE_LINKED_PROJECT_BUILD=ON cmake --build build # Build Core1 standalone (for testing) cd ../core1 cmake -B build -S . -DBOARD=hpm6750evkmini -DHPM_BUILD_TYPE=sec_core_img cmake --build build
Linked Project Build Control
The DISABLE_LINKED_PROJECT_BUILD option provides flexibility in multi-core development:
Purpose:
Avoid Build Failures: Prevents linked project compilation failures from causing the current project build to fail
Development Workflow: Allows developers to focus on one core while the other core is still under development
Incremental Development: Enables building and testing individual cores independently
How it Works:
When DISABLE_LINKED_PROJECT_BUILD=ON, the build system:
Skips the actual compilation of the linked project
Extracts the output file path from the linked project’s CMakeLists.txt
Generates a placeholder file with C error macro at that location
Continues with the current project build
Placeholder File Details:
The generated placeholder file contains:
A warning comment explaining that this is an automatically generated file
A C preprocessor #error directive that will cause compilation to fail
Clear instructions on how to obtain the actual file
This ensures that:
Users cannot accidentally use the placeholder file in their builds
Clear error messages guide users to complete the linked project build
The build system can continue without the actual linked project output
Use Cases:
Core Development: When developing core1, you can build core0 without waiting for core1 to be complete
Debugging: Focus on debugging one core while the other is temporarily broken
CI/CD: In automated builds where linked projects might not be available
Testing: Test individual core functionality independently
Example Workflow:
# Normal build (both cores) cmake -B build -S . -DBOARD=hpm6750evkmini cmake --build build # Build core0 only (core1 generates placeholder file with error macro) cmake -B build -S . -DBOARD=hpm6750evkmini -DDISABLE_LINKED_PROJECT_BUILD=ON cmake --build build # Later, when core1 is ready, build normally cmake -B build -S . -DBOARD=hpm6750evkmini cmake --build build
Example Placeholder File Content:
When DISABLE_LINKED_PROJECT_BUILD=ON is used, the generated placeholder file will contain:
/* * WARNING: This file is automatically generated by build_linked_project.py * This is NOT the actual SEC_CORE_IMG file! * * To get the actual SEC_CORE_IMG, you need to complete the compilation * of the linked project by setting build_linked=true */ #error "SEC_CORE_IMG not available. Please compile the linked project to generate the actual SEC_CORE_IMG file."
Custom Board Configuration
For custom boards or development kits:
Create Board Directory
boards/ └── my_custom_board/ ├── my_custom_board.yaml └── my_custom_board.cfg
Important: The directory name, YAML file name, and CFG file name must all be exactly the same (e.g., my_custom_board). Any mismatch will cause the build system to fail to locate your custom board configuration.
Board YAML Configuration
The board YAML file defines the hardware configuration and build settings:
board: soc: HPM6750 # the name of SOC used on this board device: HPM6750xVMx # device name used for jlink connection openocd-soc: hpm6750-dual-core # used for openocd soc config file name: hpm6750-dual-core.cfg openocd-probe: ft2232 # used for openocd probe config file name: ft2232.cfg on-board-ram: type: sdram size: 16M # the size of on-board ram will be used in linker script as external ram size width: 16bit on-board-flash: type: qspi-nor-flash size: 8M # the size of on-board flash which will be used in linker script # Optional: SDK dependency maintenance (not required for custom boards) feature: - board_audio_in # PDM - board_audio_out # DAO - board_gpt_pin - board_motor_control - board_gpio_led - board_gpio_key - board_pwm_rgb_led - board_puart_pin # Optional: SDK dependency maintenance (not required for custom boards) excluded_samples: - samples/motor_ctrl/bldc_hfi - samples/trace_recorder/rtthread-nano
Note: Both feature and excluded_samples fields are used by the SDK for dependency maintenance and are not required for custom board configurations. The feature field specifies board-specific hardware features for sample application compatibility, while excluded_samples specifies which sample applications should not be built for this board due to hardware limitations or compatibility issues.
Using Custom Board
When using a custom board, you need to specify the BOARD_SEARCH_PATH to tell CMake where to find your custom board configuration:
# Specify the path to your custom board directory cmake -B build -S . -DBOARD=my_custom_board -DBOARD_SEARCH_PATH=/path/to/your/boards
Example:
If your custom board is located at /home/user/my_boards/my_custom_board/, use:
cmake -B build -S . -DBOARD=my_custom_board -DBOARD_SEARCH_PATH=/home/user/my_boards
Component Management
The SDK uses a component-based architecture:
Available Components:
Drivers: gpio, uart, spi, i2c, adc, pwm, etc.
Middleware: freertos, lwip, fatfs, etc.
Components: ppi, plb, i2c, spi, i2s_over_spi, pmbus, smbus, serial_nor, uart_lin, dma_mgr, tsw_phy, touch, sccb, segment_led, panel, log, jpeg, ipc_event_mgr, enet_phy, eeprom_emulation, debug_console, codec, camera, usb, etc.
Utilities: sbrk, ffssi, swap, crc32, size_utils, etc.
Adding Components:
Components are reusable software modules that provide specific functionality. They are located in the components/ directory and include:
Communication: i2c, spi, uart_lin, smbus, pmbus, serial_nor
Display & Interface: panel, segment_led, camera, codec
System Services: dma_mgr, ipc_event_mgr, log, debug_console
Specialized Hardware: tsw_phy, enet_phy, eeprom_emulation
Protocol Support: ppi, plb, i2s_over_spi, sccb, touch, jpeg
Utilities:
The utils/ directory contains utility functions and tools:
Memory Management: sbrk (dynamic memory allocation)
Data Processing: swap (byte order conversion), crc32 (checksum calculation)
File System: ffssi (flash file system)
Build Tools: size_utils (binary size analysis)
These utilities are automatically available when using the SDK and don’t need to be explicitly added to dependencies.
Build Optimization
Optimization Levels:
# Debug build (default)
cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=debug
# Release build
cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=release
Custom Compiler Flags:
# Using SDK CMake extensions
sdk_compile_options("-O2")
sdk_compile_definitions("NDEBUG=1")
Compiler Flags Configuration
The SDK provides CMake functions to set compiler flags in a cross-platform way:
Setting Compiler Options:
cmake_minimum_required(VERSION 3.13)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(my_application)
# Add compiler options for all toolchains
sdk_compile_options("-O2")
sdk_compile_options("-DNDEBUG")
sdk_compile_options("-Wall")
# Add conditional compiler options
sdk_compile_options_ifdef(CONFIG_DEBUG "-g")
sdk_compile_options_ifdef(CONFIG_RELEASE "-O3")
sdk_app_src(src/main.c)
generate_ide_projects()
Setting Compiler Definitions:
# Add compiler definitions for all toolchains
sdk_compile_definitions("DEBUG_MODE=1")
sdk_compile_definitions("VERSION_MAJOR=1")
sdk_compile_definitions("VERSION_MINOR=0")
# Add conditional compiler definitions
sdk_compile_definitions_ifdef(CONFIG_FEATURE_X "FEATURE_X_ENABLED=1")
sdk_compile_definitions_ifndef(CONFIG_FEATURE_Y "FEATURE_Y_DISABLED=1")
Available Functions:
sdk_compile_options(OPTIONS…): Add compiler options for all toolchains
sdk_compile_options_ifdef(CONDITION OPTIONS…): Add compiler options if condition is defined
sdk_compile_options_ifndef(CONDITION OPTIONS…): Add compiler options if condition is not defined
sdk_compile_definitions(DEFINITIONS…): Add compiler definitions for all toolchains
sdk_compile_definitions_ifdef(CONDITION DEFINITIONS…): Add compiler definitions if condition is defined
sdk_compile_definitions_ifndef(CONDITION DEFINITIONS…): Add compiler definitions if condition is not defined
Example with Conditional Compilation:
cmake_minimum_required(VERSION 3.13)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(my_application)
# Set optimization level based on build type
if(CMAKE_BUILD_TYPE STREQUAL "release")
sdk_compile_options("-O3")
sdk_compile_definitions("NDEBUG=1")
else()
sdk_compile_options("-O0", "-g")
sdk_compile_definitions("DEBUG=1")
endif()
# Add feature-specific flags
sdk_compile_definitions_ifdef(CONFIG_USB "USB_ENABLED=1")
sdk_compile_definitions_ifdef(CONFIG_NETWORK "NETWORK_ENABLED=1")
Using EXTRA Flags:
The SDK also supports additional compiler, linker, and assembler flags through CMake variables:
# Set extra C compiler flags
set(EXTRA_C_FLAGS "-fstack-protector-strong -fno-strict-aliasing")
# Set extra linker flags
set(EXTRA_LD_FLAGS "-Wl,--gc-sections -Wl,--as-needed")
# Set extra assembler flags
set(EXTRA_AS_FLAGS "-Wa,--no-warn")
# Set extra linker symbols
set(EXTRA_LD_SYMBOLS "--defsym=__heap_size__=0x8000")
Memory Configuration:
The SDK provides built-in support for configuring heap and stack sizes through CMake variables:
# In your project's CMakeLists.txt file:
# Set heap size (default: 0x4000 = 16KB)
set(HEAP_SIZE 0x8000) # 32KB heap
# Set stack size (default: 0x4000 = 16KB)
set(STACK_SIZE 0x2000) # 8KB stack
Default Values and How It Works:
The SDK automatically sets these variables if not specified:
HEAP_SIZE: Defaults to 0x4000 (16KB) - Segger default heap size
STACK_SIZE: Defaults to 0x4000 (16KB) - Segger default stack size
The SDK automatically converts these CMake variables into linker symbols:
- HEAP_SIZE → _heap_size symbol in linker script
- STACK_SIZE → _stack_size symbol in linker script
Size Format Support:
You can specify sizes in different formats:
# Hexadecimal (recommended)
set(HEAP_SIZE 0x8000)
# Decimal
set(HEAP_SIZE 32768)
# With K/M suffix
set(HEAP_SIZE 32K)
set(HEAP_SIZE 1M)
RISC-V Architecture Configuration:
Configure RISC-V architecture and ABI settings:
# In your project's CMakeLists.txt file:
# Set RISC-V architecture (default: rv32imac)
set(RV_ARCH "rv32imac_zicsr_zifencei")
# Set RISC-V ABI (default: ilp32)
set(RV_ABI "ilp32")
Default Values and How It Works:
The SDK automatically sets these variables if not specified:
RV_ARCH: Defaults to rv32imac (32-bit RISC-V with integer multiply/divide, atomic, and compressed instructions)
RV_ABI: Defaults to ilp32 (32-bit integer, long, and pointer)
The SDK automatically appends required extensions:
CSR Support: Automatically adds _zicsr if not present and needed
Fence.i Support: Automatically adds _zifencei if not present and needed
Common RV_ARCH Values:
# Basic 32-bit RISC-V
set(RV_ARCH "rv32imac")
# With floating point support
set(RV_ARCH "rv32imafc")
# With vector support
set(RV_ARCH "rv32imafdcv")
# Custom extensions
set(RV_ARCH "rv32imac_zicsr_zifencei_zba_zbb")
Common RV_ABI Values:
# 32-bit integer, long, and pointer (most common)
set(RV_ABI "ilp32")
# 32-bit integer, 64-bit long, 32-bit pointer
set(RV_ABI "ilp32f")
# 32-bit integer, 64-bit long, 32-bit pointer with float
set(RV_ABI "ilp32d")
Command Line Usage:
# Pass extra flags via command line
cmake -B build -S . -DBOARD=hpm6750evkmini -DEXTRA_C_FLAGS="-fstack-protector-strong" -DEXTRA_LD_FLAGS="-Wl,--gc-sections" -DEXTRA_AS_FLAGS="-Wa,--no-warn" -DEXTRA_LD_SYMBOLS="--defsym=__heap_size__=0x8000"
# Configure memory sizes via command line
cmake -B build -S . -DBOARD=hpm6750evkmini -DHEAP_SIZE=0x8000 -DSTACK_SIZE=0x2000
# Configure RISC-V architecture and ABI via command line
cmake -B build -S . -DBOARD=hpm6750evkmini -DRV_ARCH="rv32imac_zicsr_zifencei" -DRV_ABI="ilp32"
Nano Specs Configuration:
The HPM_SDK_LD_NO_NANO_SPECS variable controls whether to use the nano.specs linker specification file, which provides optimized implementations of standard library functions for embedded systems.
Default Behavior:
By default, the SDK automatically includes nano.specs for GCC toolchain builds, which provides:
Optimized printf/scanf: Smaller, faster implementations of printf and scanf functions
Reduced memory footprint: Uses less RAM and flash compared to full standard library
Float support: Enables floating-point support for printf and scanf operations
Disabling Nano Specs:
To disable nano.specs and use the full standard library implementations:
# In your CMakeLists.txt file:
# Disable nano.specs (use full standard library)
set(HPM_SDK_LD_NO_NANO_SPECS 1)
When to Disable Nano Specs:
Full printf/scanf functionality: When you need complete printf/scanf features not available in nano.specs
Custom library implementations: When using custom or third-party standard library implementations
Debugging purposes: When you need full standard library debugging capabilities
Specialized applications: For applications that require specific standard library behavior
Example Usage:
cmake_minimum_required(VERSION 3.13)
# Disable nano.specs for this project
set(HPM_SDK_LD_NO_NANO_SPECS 1)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(my_application)
sdk_app_src(src/main.c)
generate_ide_projects()
Command Line Usage:
# Disable nano.specs via command line
cmake -B build -S . -DBOARD=hpm6750evkmini -DHPM_SDK_LD_NO_NANO_SPECS=1
Custom Target Triplet Configuration:
The CUSTOM_TARGET_TRIPLET variable allows you to override the default target triplet used by the SDK’s cross-compilation toolchain. This is useful when you have a toolchain with a different naming convention than the SDK’s default.
Default Behavior:
By default, the SDK uses riscv32-unknown-elf as the target triplet, which means:
Architecture: RISC-V 32-bit
Vendor: unknown (generic)
Operating System: elf (bare metal/embedded)
Using Custom Target Triplet:
To use a different target triplet (e.g., for different toolchain installations):
# In your CMakeLists.txt file:
# Set custom target triplet
set(CUSTOM_TARGET_TRIPLET "riscv32-elf")
Common Target Triplet Formats:
# Standard RISC-V bare metal (default)
set(CUSTOM_TARGET_TRIPLET "riscv32-unknown-elf")
How It Works:
When you set CUSTOM_TARGET_TRIPLET, the SDK uses this value to construct the cross-compilation toolchain prefix instead of the default riscv32-unknown-elf:
Compiler: ${CUSTOM_TARGET_TRIPLET}-gcc
Linker: ${CUSTOM_TARGET_TRIPLET}-ld
Binutils: ${CUSTOM_TARGET_TRIPLET}-objcopy, ${CUSTOM_TARGET_TRIPLET}-objdump, etc.
Example Usage:
cmake_minimum_required(VERSION 3.13)
# Set custom target triplet for specific toolchain
set(CUSTOM_TARGET_TRIPLET "riscv32-elf")
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(my_application)
sdk_app_src(src/main.c)
generate_ide_projects()
Command Line Usage:
# Set custom target triplet via command line
cmake -B build -S . -DBOARD=hpm6750evkmini -DCUSTOM_TARGET_TRIPLET="riscv32-hpmicro-elf"
Toolchain Compatibility:
The target triplet must match your installed toolchain. Common scenarios:
Standard RISC-V GCC: Use riscv32-unknown-elf
Vendor-specific toolchain: Use vendor-specific triplet
Custom toolchain: Use the prefix format of your custom toolchain
Verification:
To verify your toolchain uses the correct target triplet:
# Check if the toolchain exists
ls $GNURISCV_TOOLCHAIN_PATH/bin/
# Look for files matching your CUSTOM_TARGET_TRIPLET
# Example: if CUSTOM_TARGET_TRIPLET="riscv32-elf"
# Look for: riscv32-elf-gcc, riscv32-elf-ld, etc.
IDE Integration
The SDK supports multiple IDEs through the generate_ide_projects() function in CMakeLists.txt:
CMakeLists.txt Configuration:
cmake_minimum_required(VERSION 3.13)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(my_application)
sdk_app_src(src/main.c)
# Generate IDE projects for all supported IDEs
generate_ide_projects()
Build and Generate Projects:
# Configure and build the project
cmake -B build -S . -DBOARD=hpm6750evkmini
cmake --build build
Generated IDE Projects:
After building, the following IDE project files will be generated in the build directory:
Segger Embedded Studio: build/segger_embedded_studio/
IAR Embedded Workbench: build/iar_embedded_workbench/
Opening in IDEs:
Segger Embedded Studio: Open the .emProject file in the segger directory
IAR Embedded Workbench: Open the .ewp file in the iar directory
Best Practices
Project Organization
Use Clear Directory Structure
project/ ├── CMakeLists.txt # Main build configuration ├── app.yaml # Application configuration ├── src/ # Source files │ ├── main.c │ └── app/ ├── include/ # Header files │ └── app/ ├── config/ # Configuration files └── docs/ # Documentation
Modular Source Organization
# CMakeLists.txt sdk_app_src( src/main.c src/app/gpio_handler.c src/app/uart_handler.c ) sdk_app_inc(include/app)
Configuration Management
Environment-specific Configurations
# Development cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=debug # Production cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=release
Debugging
Enable Debug Information
cmake -B build -S . -DBOARD=hpm6750evkmini -DCMAKE_BUILD_TYPE=debug
Use IDE Debugging
Open generated IDE project
Set breakpoints
Start debugging session
Performance Optimization
Choose Appropriate Build Type
# For development cmake -B build -S . -DCMAKE_BUILD_TYPE=debug # For production cmake -B build -S . -DCMAKE_BUILD_TYPE=release
Troubleshooting
Common Issues and Solutions
Build Fails with “Board not found”
First, check these fundamental issues:
# 1. Verify board path is correct
ls boards/ | grep your_board_name
# 2. Verify board YAML file exists and name is correct
ls boards/your_board_name/your_board_name.yaml
# 3. Check if board name matches exactly (case-sensitive)
cmake -B build -S . -DBOARD=your_board_name
There are two scenarios to consider:
Scenario 1: Using Standard SDK Boards
# Check available boards in SDK
ls boards/
# Use correct board name from SDK
cmake -B build -S . -DBOARD=hpm6750evkmini
Scenario 2: Using Custom Boards
# Ensure custom board directory structure is correct
# Directory name must match YAML and CFG file names exactly
ls /path/to/your/custom/boards/
# Specify BOARD_SEARCH_PATH to locate custom board
cmake -B build -S . -DBOARD=my_custom_board -DBOARD_SEARCH_PATH=/path/to/your/custom/boards
Missing Toolchain
# Set toolchain path
export PATH=$PATH:/path/to/toolchain/bin
# Or specify in CMake
cmake -B build -S . -DBOARD=hpm6750evkmini -DTOOLCHAIN_PATH=/path/to/toolchain
Multi-core Build Issues
By default, multi-core Core0 projects depend on successful compilation of Core1 projects using GNU GCC. However, GNU GCC on Windows cannot handle long file paths, which may cause Core1 project compilation to fail, leading to Core0 build failure.
# Check linked project configuration
# Ensure SEC_CORE_IMG_C_ARRAY_OUTPUT is set correctly
# Use DISABLE_LINKED_PROJECT_BUILD to bypass Core1 compilation on Windows
cmake -B build -S . -DBOARD=hpm6750evkmini -DDISABLE_LINKED_PROJECT_BUILD=ON
IDE Project Generation Fails
# Use supported generator
cmake -B build -S . -DBOARD=hpm6750evkmini -G "Unix Makefiles"
Debugging Build Issues
Enable Verbose Output
cmake -B build -S . -DBOARD=hpm6750evkmini --debug-output cmake --build build --verbose
Check CMake Cache
cat build/CMakeCache.txt | grep -i board cat build/CMakeCache.txt | grep -i toolchain
Validate Configuration
cmake -B build -S . -DBOARD=hpm6750evkmini -L
Examples
Basic GPIO Application
Project Structure:
gpio_example/
├── CMakeLists.txt
└── src/
└── main.c
CMakeLists.txt:
cmake_minimum_required(VERSION 3.13)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(gpio_example)
sdk_app_src(src/main.c)
generate_ide_projects()
main.c:
#include "board.h"
#include "hpm_gpio_drv.h"
int main(void)
{
/* Initialize GPIO */
gpio_set_pin_output(HPM_GPIO0, GPIO_DI_GPIOA, 0);
while (1) {
/* Toggle LED */
gpio_toggle_pin(HPM_GPIO0, GPIO_DI_GPIOA, 0);
board_delay_ms(500);
}
}
Build Command:
cmake -B build -S . -DBOARD=hpm6750evkmini
cmake --build build
UART Communication Example
main.c:
#include "board.h"
#include "hpm_uart_drv.h"
int main(void)
{
uart_config_t config;
uart_default_config(HPM_UART0, &config);
config.baudrate = 115200;
uart_init(HPM_UART0, &config);
while (1) {
uart_send_byte(HPM_UART0, 'H');
board_delay_ms(1000);
}
}
Multi-core Example
Core0 (app.yaml):
linked_project:
project_name: multicore_example/core1
project_path: ../core1
build_type: sec_core_img
Core1 (CMakeLists.txt):
cmake_minimum_required(VERSION 3.13)
set(HPM_BUILD_TYPE "sec_core_img")
set(SEC_CORE_IMG_C_ARRAY_OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/../core0/src/sec_core_img.c)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(multicore_core1)
sdk_app_src(src/core1_main.c)
Build Command:
cd core0
cmake -B build -S . -DBOARD=hpm6750evkmini
cmake --build build
Conclusion
The HPM SDK CMake build system provides a powerful and flexible foundation for embedded application development. By following the workflows and best practices outlined in this guide, you can:
Accelerate Development: Use pre-configured components and board support
Ensure Consistency: Standardized build process across projects
Simplify Debugging: Integrated IDE support and debugging tools
Optimize Performance: Configurable build types and optimization levels
Support Multi-core: Seamless dual-core application development
For detailed API reference, see cmake_intro. For additional examples and advanced usage, refer to the samples directory in the SDK.