IC:

Overview

Concepts

  • SOC Project: Each SOC project root directory contains a CMakeLists.txt as the build entry point. All MCU projects under it are added here.

  • MCU Project: Each MCU project uses unified toolchain and configuration parameters (some configurations may vary due to firmware or ROM). Different MCU projects may use different toolchains. An MCU project includes builds of one or more firmwares.

  • Firmware: Mainly includes image1, image2, image3, with differences as follows:

    • image1: Defines the common infrastructure for bootloader and system flash layout in MCU systems, providing a secure bootloader with convenient software upgrade support.

    • image2: The main application firmware of the SOC, typically containing a real-time OS (FreeRTOS) and application tasks.

    • image3: Highly configurable, allowing users to include only required security services. Protected by Read Protection (RDP), decrypted in the secure bootloader, and loaded into TrustZone-protected SRAM.

  • Component: Components are organized under the component directory at the same level as SOC projects. Each subdirectory represents a functional module with its own CMakeLists.txt defining compilation and linking configurations.

  • Firmware Build: In each MCU project’s [asdk|vsdk]/make/[image1|image2|image3]/CMakeLists.txt, add required components and define generation rules.

SDK CMake Structure

The project’s CMakeLists.txt directory structure and invocation relationships are shown below:

../../_images/build_cmake_project_structure.svg

Attention

In the diagram above, the CMakeLists.txt in image1, image2, and image3 directories can independently add components for their respective firmwares.

Directory structure of cmake:

cmake
├── flags                              # Global compile and link options
│   ├── ca32
│   │   ├── compile_options.cmake
│   │   └── link_options.cmake
│   ├── common
│   │   ├── compile_options.cmake
│   │   └── link_options.cmake
│   ├── km0
│   │   ├── compile_options.cmake
│   │   └── link_options.cmake
│   ├── km4
│   │   ├── compile_options.cmake
│   │   └── link_options.cmake
│   └── kr4
│       ├── compile_options.cmake
│       └── link_options.cmake
├── CMakeLists-template.cmake        # CMakeLists.txt template for component
├── common.cmake                     # Project related APIs
├── global_define.cmake              # Global defined parameters
├── utility_base.cmake               # Utility APIs (lower level)
├── utility.cmake                    # Utility APIs (upper level)
└── toolchain                        # Toolchain defines
    ├── ameba-toolchain-asdk-10.3.1.cmake
    ├── ameba-toolchain-asdk-12.3.1.cmake
    ├── ameba-toolchain-check.cmake
    ├── ameba-toolchain-common.cmake
    └── ameba-toolchain-vsdk-10.3.1.cmake

Global Compilation Configuration

Refers to compilation settings that are ​**shared by all components** within a specified scope.

Compilation Configuration Contents

  • Sources

  • Include directories

  • Compile options

  • Definitions

  • Link options

  • Link libraries

Note

  • Sources, include directories, compile options, and definitions are used for component compilation.

  • Link options and link libraries are used for firmware linking.

Global Compilation Configuration Sources

Primarily consists of two sources:

Attention

  • The order of components’ appended configurations in the final global compilation settings (e.g., the order of header file paths) depends on the sequence in which components are added to the firmware

  • This ordering is unreliable. For order-sensitive configurations (like header file paths), it’s recommended to handle them through alternative approaches. For details see Component Private Part

Scope of Global Compilation Configuration

The global compilation configuration applies to each MCU project. For example, RTL8721Dx has separate configurations for km0 and km4, while RTL8730E has configurations for km0, km4, and ca32. These configurations are isolated. Components under the same MCU project use the same global compilation configuration. See the following diagram:

../../_images/build_cmake_project_structure_with_config.svg

Key points:

  • Each MCU project contains an independent compilation scope.

  • Different firmwares (image1, image2, image3) under the same MCU project share the same compilation config.

  • The same component is compiled with different global configurations across MCU projects (e.g., at_cmd in project_mcu1 vs. project_mcu2).

Component Compilation CMakeLists.txt

Consists of Component Public Part and Component Private Part:

Component Public Part

Appends the following to the Global Compilation Configuration:

  • Include directories: Added to global if other components need headers from this component.

  • Definitions: Global preprocessor macros added to global when this component is added (rarely used).

  • Link libraries: Required if the component is provided as a prebuilt library (not source). Use ameba_add_external_app_library to build the library.

Configuration variables: public_includes, public_definitions, public_libraries. Update these in the highlighted section of code block below. See examples in cmake/CMakeLists-template.cmake and APIs in List Operations.

 1##########################################################################################
 2## * This part defines public part of the component
 3## * Public part will be used as global build configures for all component
 4
 5set(public_includes)                #public include directories, NOTE: relative path is OK
 6set(public_definitions)             #public definitions
 7set(public_libraries)               #public libraries(files), NOTE: linked with whole-archive options
 8
 9#------------------------------------------------------------------#
10# Component public part, user config begin(DO NOT remove this line)
11
12# set public_includes, public_definitions, public_libraries in this section
13
14# Component public part, user config end(DO NOT remove this line)
15#------------------------------------------------------------------#
16
17#WARNING: Fixed section, DO NOT change!
18ameba_global_include(${public_includes})
19ameba_global_define(${public_defines})
20ameba_global_library(${public_libraries}) #default: whole-achived

Note

If there’s no need to update the three types of configurations mentioned above for the current component, the highlighted code section can be left blank.

Component Private Part

Defines the component library build:

  • Source files: Source files for current component

  • Private includes: Only for current component

  • Private definitions: Only for current component

  • Private compile options: Only for current component

Configuration variables: private_sources, private_includes, private_definitions, private_compile_options. Update these in the highlighted section of code block below. See examples in cmake/CMakeLists-template.cmake and APIs in List Operations.

Attention

The final compilation configuration of a component (particularly include directories, definitions and compile options) is composed of both Component Private Part and Component Public Part, with the former taking higher priority for include directories and compile options . For example, the actual header search paths for a component include:

  • Private include directories added by the current component.

  • Global include directories from Global Compilation Configuration, which includes paths appended by both the current component and other components.

Therefore, configurations already added in Component Public Part should not be duplicated in Component Private Part.

General recommendations:

  • Prefer placing include directories in the Component Private Part to avoid header file pollution.

  • Exceptions: For generic or low-level components used by many others, placing includes in Component Public Part is preferred to improve reusability.

 1##########################################################################################
 2## * This part defines private part of the component
 3## * Private part is used to build target of current component
 4## * NOTE: The build API guarantees the global build configures(mentioned above)
 5## *       applied to the target automatically. So if any configure was already added
 6## *       to public above, it's unnecessary to add again below.
 7
 8#NOTE: User defined section, add your private build configures here
 9# You may use if-else condition to set these predefined variable
10# They are only for ameba_add_internal_library/ameba_add_external_app_library/ameba_add_external_soc_library
11set(private_sources)                 #private source files, NOTE: relative path is OK
12set(private_includes)                #private include directories, NOTE: relative path is OK
13set(private_definitions)             #private definitions
14set(private_compile_options)         #private compile_options
15
16#------------------------------#
17# Component private part, user config begin
18
19 # set private_sources, private_includes, private_definitions, private_compile_options in this section
20
21# Component private part, user config end
22#------------------------------#
23
24#WARNING: Select right API based on your component's release/not-release/standalone
25
26###NOTE: For open-source component, always build from source
27ameba_add_internal_library(foo
28    p_SOURCES
29        ${private_sources}
30    p_INCLUDES
31        ${private_includes}
32    p_DEFINITIONS
33        ${private_definitions}
34    p_COMPILE_OPTIONS
35        ${private_compile_options}
36)

Note

Best Practices

Modifying Existing Component Build Config

Refer to the instructions in Component Compilation CMakeLists.txt, utilize Useful Types for Complex Logic Processing, and note that APIs from List Operations can be called multiple times. Below are some examples:

  • Add public header file paths. It is recommended to use relative paths:

    ameba_list_append(public_includes
        ${CMAKE_CURRENT_SOURCE_DIR}        # Access current dir by CMAKE_CURRENT_SOURCE_DIR, same as .
        ${CMAKE_CURRENT_SOURCE_DIR}/foo    # Access sub dir by CMAKE_CURRENT_SOURCE_DIR, same as ./foo
        foo                                # Access sub dir directly
    )
    
  • Add the link library paths. For the variables used here, you can refer to MCU Project-related Constants:

    ameba_list_append(public_libraries
        ${c_SDK_LIB_APP_DIR}/lib_foo.a
    )
    
  • Add private sources. It is recommended to use relative paths:

    ameba_list_append(private_sources
        foo/foo.c
        bar/bar.c
    )
    
  • Add private header file paths. It is recommended to use relative paths:

    ameba_list_append(public_includes
        ../common  # Access parent dir
        foo
        bar
    )
    
  • Add definitions:

    ameba_list_append(private_definitions
        __RTOS__
        MBEDTLS_CONFIG_FILE="mbedtls/config.h"
    )
    
  • Add compile options:

    ameba_list_append(private_compile_options
        -Wno-unused-parameter
    )
    

Integrating Code with Standalone Build System

For third-party code with independent build systems (e.g., CMake/Makefile-based components), non-intrusive integration solutions can be implemented to link them into the firmware. Specific adaptation strategies should be selected based on their build system types:

  1. Create a wrapper CMakeLists.txt in some directory using the template cmake/CMakeLists-template.cmake.

  2. Refer to Modifying Existing Component Build Config to config Component Public Part

  3. In Component Private Part, add the original CMake project via ameba_add_subdirectory() and use ameba_port_standalone_internal_library to link the target:

    ameba_add_subdirectory(path/to/your/cmakelists) # Add the real CMakeLists.txt dir of the wrapped component
    ameba_port_standalone_internal_library(foo)     # Add the real target of the wrapped component to link
    
  4. Refer to CMakeLists.txt invocation relationships add the component directory in the firmware’s CMakeLists.txt:

    ameba_add_subdirectory(path/to/your/wrap/cmakelists)
    
  5. Compile and test

Adjusting Submodule Organization Relationships in Components

When a component is complex and contains multiple subcomponents, the CMakeLists.txt in the component root directory should manage their organizational relationships. Pay attention to:

  • Configuration Checks: Since MCU projects generally don’t check switches and directly add components, configuration checks must be performed here. Example from component/bluetooth/CMakeLists.txt:

    if(NOT CONFIG_BT)
        ameba_info("CONFIG_BT is off, bluetooth will not be built")
        return()
    endif()
    
  • Subcomponent Addition Logic: When splitting subcomponents, handle the addition logic using Useful Types for Complex Logic Processing Example from component/bluetooth/CMakeLists.txt:

    ameba_add_subdirectory_ifnot(CONFIG_BT_INIC api)
    ameba_add_subdirectory_if(CONFIG_BT_AUDIO_CODEC_SUPPORT bt_audio)
    ameba_add_subdirectory(bt_mp)
    ameba_add_subdirectory(driver)
    ameba_add_subdirectory_ifnot(CONFIG_BT_INIC example)
    ameba_add_subdirectory(osif)
    ameba_add_subdirectory(rtk_coex)
    if(CONFIG_BT_ZEPHYR)
        ameba_add_subdirectory(zephyr)
    elseif(NOT CONFIG_BT_INIC)
        ameba_add_subdirectory(rtk_stack)  # refer to ble_mesh_stack
    endif()
    

Adding New Components in Component Directory

To add a new independent component:

  1. Add component path variables in cmake/global_define.cmake as below:

    ameba_set(c_CMPT_WIFI_DIR         ${c_COMPONENT_DIR}/wifi)
    ameba_set(c_CMPT_WPAN_DIR         ${c_COMPONENT_DIR}/wpan)
    
    ameba_set(c_CMPT_CRASHDUMP_DIR    ${c_COMPONENT_DIR}/soc/common/crashdump)
    ameba_set(c_CMPT_LZMA_DIR         ${c_COMPONENT_DIR}/soc/common/lzma)
    
    ameba_set(c_CMPT_FOO_DIR         ${c_COMPONENT_DIR}/foo)  # new component
    

    Attention

    For SOC/MCU-related components, add them in the ameba_reset_global_define() macro:

    macro(ameba_reset_global_define)
        ameba_set(c_CMPT_USRCFG_DIR ${c_COMPONENT_DIR}/soc/usrcfg/${c_SOC_TYPE})
        ameba_set(c_CMPT_BOOTLOADER_DIR ${c_CMPT_SOC_DIR}/bootloader)
    
        ameba_set(c_CMPT_FOO_DIR ${c_CMPT_SOC_DIR}/foo)  # new component
    endmacro()
    
  2. Create component directory under component and add CMakeLists.txt following Component Compilation CMakeLists.txt.

  3. Add in firmware’s CMakeLists.txt:

    ameba_add_subdirectory(${c_CMPT_FOO_DIR})
    

Commonly Used CMake Interfaces and Preset Constants

List Operations

ameba_list_append

ameba_list_append(<list_name> [<value> ...])

Appends elements to a list, supports appending multiple elements. Parameter description:

  • list_name: List variable name

  • value: Value(s) to append

ameba_list_append_if

ameba_list_append_if(<condition> <list_name> [<value> ...] [p_ELSE <else value> ...])

Appends elements to a list conditionally, supports appending multiple elements. Parameter description:

  • condition: Variable name representing the condition

  • list_name: List variable name

  • value: Value(s) to append when condition is true

  • p_ELSE: Optional keyword parameter, followed by value(s) to append when condition is false

  • else value: Value(s) to append when condition is false

Attention

If the variable represented by condition is undefined or defined with a bool value of FALSE, it will be considered as false.

ameba_list_append_ifnot

ameba_list_append_ifnot(<condition> <list_name> [<value> ...] [p_ELSE <else value> ...])

Appends elements to a list conditionally (opposite functionality to ameba_list_append_if()). Parameter description:

  • condition: Variable name representing the condition

  • list_name: List variable name

  • value: Value(s) to append when condition is false

  • p_ELSE: Optional keyword parameter, followed by value(s) to append when condition is true

  • else value: Value(s) to append when condition is true

Attention

If the variable represented by condition is undefined or defined with a bool value of FALSE, it will be considered as false.

ameba_list_append_ifdef

ameba_list_append_ifdef(<condition> <list_name> [<value> ...] [p_ELSE <else value> ...])

Appends elements to a list based on variable definition status. Parameter description:

  • condition: Variable name representing the condition

  • list_name: List variable name

  • value: Value(s) to append when condition is defined (Note: condition can be FALSE)

  • p_ELSE: Optional keyword parameter, followed by value(s) to append when condition is undefined

  • else value: Value(s) to append when condition is undefined

Add Library

The following APIs are used to compile code into static libraries or to perform further processing on existing targets.

Note

These APIs have the following characteristics:

  • The firmware that links the static library depends on which firmware’s CMakeLists.txt it is added in. For example, if added in image2/CMakeLists.txt, the static library will be linked to firmware image2.

  • The actual target name generated by the name parameter in these APIs is combined with other information such as c_MCU_PROJECT_NAME and c_CURRENT_IMAGE. Users can obtain the real target name using the variable c_CURRENT_TARGET_NAME after API calls.

  • These APIs internally apply Global Compilation Configuration to compile the current target.

ameba_add_internal_library

ameba_add_internal_library(<name>
                           [p_SOURCES <sources> ...]
                           [p_INCLUDES <include dirs> ...]
                           [p_COMPILE_OPTIONS <compile options> ...]
                           [p_DEFINITIONS <definitions> ...]
                           [p_DEPENDENCIES <dependencies> ...]
)

Adds a static library, compiles it, and automatically links the target to the current firmware. The library file is generated under the build directory. Parameter descriptions:

  • name: Target name. The actual library file is lib_${name}.a.

  • p_SOURCES: Source files for the target.

  • p_INCLUDES: Include directories for the target.

  • p_COMPILE_OPTIONS: Compile options for the target.

  • p_DEFINITIONS: Preprocessor definitions for the target.

  • p_DEPENDENCIES: Dependencies for the target.

Attention

The library file generated by this API resides in the build directory. It does not check CONFIG_AMEBA_RLS internally and is always active.

ameba_port_standalone_internal_library

ameba_port_standalone_internal_library(<name>)

Links an existing static library target to the current firmware:

  • name: Target name.

Tip

Particularly useful for non-intrusive adaptation of third-party library CMakeLists.txt refer to Integrating Code with Standalone Build System.

Attention

This API does not check CONFIG_AMEBA_RLS internally and is always active.

ameba_add_external_app_library

ameba_add_external_app_library(<name>
                               [p_SOURCES <sources> ...]
                               [p_INCLUDES <include dirs> ...]
                               [p_COMPILE_OPTIONS <compile options> ...]
                               [p_DEFINITIONS <definitions> ...]
                               [p_DEPENDENCIES <dependencies> ...]
)

Adds a static library, compiles it, but does not automatically link the target to the current firmware. It must be specified in Component Public Part. The library file is output to the ${c_SDK_LIB_APP_DIR} directory (see MCU Project-related Constants) and undergoes objcopy -g -D processing. Parameter descriptions:

  • name: Target name. The actual library file is lib_${name}.a.

  • p_SOURCES: Source files for the target.

  • p_INCLUDES: Include directories for the target.

  • p_COMPILE_OPTIONS: Compile options for the target.

  • p_DEFINITIONS: Preprocessor definitions for the target.

  • p_DEPENDENCIES: Dependencies for the target.

Attention

This API does nothing and returns immediately if CONFIG_AMEBA_RLS is TRUE.

Add Subdirectory

ameba_add_subdirectory

ameba_add_subdirectory(<dir>)

Add a directory for compilation. Refer to add_subdirectory. When dir is an external path, the last-level directory name will be used as binary_dir. Parameter description:

  • dir: Directory to be added for compilation

ameba_add_subdirectory_if

ameba_add_subdirectory_if(<condition> <dir>)

Add a directory for compilation based on specified condition. Other behaviors are same as ameba_add_subdirectory. Parameter description:

  • condition: Variable name representing the condition

  • dir: Directory to add for compilation when condition is met

ameba_add_subdirectory_ifnot

ameba_add_subdirectory_ifnot(<condition> <dir>)

Add a directory for compilation based on specified condition. Other behaviors are same as ameba_add_subdirectory. Parameter description:

  • condition: Variable name representing the condition

  • dir: Directory to add for compilation when condition is NOT met

Attention

Variables represented by condition that are undefined or defined with a bool value of FALSE will be considered unmet

ameba_add_subdirectory_if_exist

ameba_add_subdirectory_if_exist(<dir>)

When path doesn’t exist: silently return if CONFIG_AMEBA_RLS is TRUE, otherwise throw error. When path exists, behaves same as ameba_add_subdirectory. Parameter description:

  • dir: Directory to add for compilation when path exists

Constants Definition

The CMake script cmake/global_define.cmake defines several constants that can be directly used in script writing. Some examples are listed below:

General Constants

Variable Name

Value

c_BASEDIR

Root dir of the repo

c_CMAKE_FILES_DIR

${c_BASEDIR}/cmake

c_COMPONENT_DIR

${c_BASEDIR}/component

c_EMPTY_C_FILE

${c_CMAKE_FILES_DIR}/empty_file.c

Frequently Asked Questions & Suggestions

View Detailed Compilation Parameters of a Source File

Add the following code at the beginning of the target source file or introduce syntax errors, then compile. The compiler will report errors at the corresponding source file location and print detailed compilation parameters.

#error debug

Undefined Reference Error

This error typically occurs during the linking stage when generating axf files. Common causes include:

  • Cause 1: The library containing the symbol is not linked

  • Cause 2: The source file containing the symbol is not compiled

  • Cause 3: The code block containing the symbol is not compiled

The following uses RTL8721Dx as an example to demonstrate troubleshooting steps for the above causes. Below is a sample link error output (partial information retained for demonstration):

 1[5/42] Linking C executable project_km4/asdk/make/image2/target_img2_km4.axf
 2FAILED: project_km4/asdk/make/image2/target_img2_km4.axf
 3ccache /opt/rtk-toolchain/asdk-10.3.1-4354/linux/newlib/bin/arm-none-eabi-gcc
 4-O2
 5-o project_km4/asdk/make/image2/target_img2_km4.axf
 6
 7-Wl,--whole-archive
 8project_km4/asdk/make/image2/at_cmd/lib_at_cmd.a
 9project_km4/asdk/make/image2/swlib/lib_swlib.a
10project_km4/asdk/make/image2/file_system/fatfs/lib_fatfs.a
11project_km4/asdk/make/image2/file_system/littlefs/lib_littlefs.a
12project_km4/asdk/make/image2/file_system/kv/lib_kv.a
13project_km4/asdk/make/image2/file_system/vfs/lib_vfs.a
14project_km4/asdk/make/image2/fwlib/lib_fwlib.a
15project_km4/asdk/make/image2/hal/lib_hal.a
16project_km4/asdk/make/image2/misc/lib_misc.a
17project_km4/asdk/make/image2/lwip/lib_lwip.a
18
19-Wl,--no-whole-archive
20
21-lm
22-O2
23-o project_km4/asdk/make/image2/target_img2_km4.axf
24
25-Wl,--whole-archive
26project_km4/asdk/make/image2/at_cmd/lib_at_cmd.a
27project_km4/asdk/make/image2/cmsis-dsp/lib_cmsis_dsp.a
28
29-Wl,--no-whole-archive
30
31-lm
32-lstdc++
33ld: amebadplus_gcc_project/project_km4/asdk/lib/soc/lib_chipinfo.a(ameba_rom_patch.o): in function `io_assert_failed':
34(.text.io_assert_failed+0x12): undefined reference to `rtk_log_write_nano'

Key information from highlighted lines 1, 2, 33, 34:

  • MCU Project: km4

  • Firmware: image2, File: target_img2_km4.axf

  • Undefined symbol: rtk_log_write_nano, called in function io_assert_failed()

  • Source file: log.c

  • Component: swlib

  • Library: lib_swlib.a

Troubleshooting steps:

The library containing the symbol is not linked

  1. Verify if link parameters contain lib_swlib.a

  2. Line 9 shows project_km4/asdk/make/image2/swlib/lib_swlib.a is linked. If missing, check:

Useful Types for Complex Logic Processing

SOC Type

Identify SOC types (e.g., amebadplus, amebalite, amebasmart) using variables: CONFIG_AMEBADPLUS, CONFIG_AMEBALITE, CONFIG_AMEBASMART

if(CONFIG_AMEBADPLUS)
    # Add code for amebadplus here
elseif(CONFIG_AMEBALITE)
    # Add code for amebalite here
elseif(CONFIG_AMEBASMART)
    # Add code for amebasmart here
endif()

Attention

  1. SOC type distinctions should be minimized. Prefer feature-based configuration switches instead.

  2. If such usage is mandatory, adhere to the principle of upward compatibility to ensure future SOC type additions require no modification of this logic (e.g., avoid adding new elseif clauses). Example implementation in component/rtk_coex/CMakeLists.txt:

    if(NOT CONFIG_AMEBAD)
        if (CONFIG_COEXIST_HOST)
            include(rtk_coex_api.cmake)
        endif()
    endif()
    

MCU Type

Identify MCU types (e.g., km0, km4, kr4, ca32) using string variable c_MCU_TYPE:

if(${c_MCU_TYPE} STREQUAL "km0")
    # Add code for km0 here
elseif(${c_MCU_TYPE} STREQUAL "km4")
    # Add code for km4 here
elseif(${c_SOC_TYPE} STREUQAL "ca32")
    # Add code for ca32 here
endif()

Common CMake Debugging Methods

Logging

Use CMake’s built-in message() or more readable alternatives: ameba_debug(), ameba_info(), ameba_warning(), ameba_fatal().

Example usage to halt execution for analysis:

message(FATAL_ERROR "stop here")

Troubleshooting Common CMake Errors

Tip

Always check the first error in terminal output when troubleshooting CMake issues

External Command Parameter Error

Typically caused by incorrect parameters in COMMAND sections of add_custom_target() or add_custom_command(), often due to empty CMake variables. Sample error output showing CMake’s -E usage:

1FAILED: build/lib_atcmd.a
2/usr/bin/cmake -E copy build/lib_atcmd.a
3CMake Error: cmake version 3.30.2
4Usage: /usr/bin/cmake -E <command> [arguments...]
5Available commands:
6capabilities              - Report capabilities built into cmake in JSON format
7cat [--] <files>...       - concat the files and print them to the standard output
8chdir dir cmd [args...]   - run command in a given directory
9...

Corresponding CMake code example:

1add_custom_command(
2    OUTPUT lib/lib_atcmd.a
3    COMMAND ${CMAKE_COMMAND} -E copy build/lib_atcmd.a ${output_path}
4    DEPENDS build/lib_atcmd.a
5)

This error occurs when ${output_path} is empty, resulting in incomplete copy command as shown in line 2.

Copied and replaced a source file but did not trigger recompilation

The CMake build system uses incremental compilation by default, meaning it only recompiles when source code or header files change. Its mechanism for detecting changes is ​**based on whether the file’s modification timestamp is newer than the previous build**.

When replacing a file with an older version (via copy-paste) while keeping its original modification timestamp (default behavior in Windows Explorer), the incremental compilation ​**will not be triggered** because the timestamp remains unchanged.

Recommended solutions for Windows environments: 1. ​**Create a new file** and copy the content into it (generates a new timestamp) 2. Use the build.py -c command to clean the build directory

Advanced Learning Readings

Special Compilation Configurations

Set Compilation Configuration Per Source File

To set compilation configurations, such as predefined macros, for specific source files. For example, adding a predefined macro __FILE_Z_STR__ to each source file that expands to the filename string of the corresponding source file. Implementation is as follows:

ameba_list_append(private_sources
    foo.c
    bar.c
)

foreach(src ${private_sources})
    cmake_path(GET src FILENAME filename)
    set_source_files_properties(${src} PROPERTIES COMPILE_DEFINITIONS "__FILE_Z_STR__=\"${filename}\"")
endforeach()

Use the CMake built-in interface set_source_files_properties to precisely set compilation configurations for each source file.

Set Compile Options Per Source File Type

ameba_list_append(private_compile_options
    -Wno-multichar                              # for both asm/c/cpp language
    $<$<COMPILE_LANGUAGE:C>:-Wno-pointer-sign>  # for c language only
    $<$<COMPILE_LANGUAGE:CXX>:-Wno-narrowing>   # for cpp language only
    $<$<COMPILE_LANGUAGE:CXX>:-std=c++17>       # for cpp language only
)

Suppress Compilation Warnings as Errors

The global compilation configuration in cmake/flags/common/compile_options.cmake enables -Werror by default, which ​treats all compilation warnings as errors. To disable this behavior, use one of the following methods:

  • ​Method 1: Component-Specific Disable. Add compilation options in the target component’s CMakeLists.txt:

    ameba_list_append(private_compile_options
        -Wno-error  # Disable warning-as-error for this component
    )
    
  • ​Method 2: Global Disable. Modify the global configuration file cmake/flags/common/compile_options.cmake and ​comment out the -Werror line

Kconfig System

Kconfig Overview

Organization Structure

The configuration system uses Kconfig to organize. Top Kconfig file under amebaxxx_gcc_project references sub-Kconfig under each component, generating files for CMake. As following figure show:

../../_images/kconfig_tree.svg

Mutual references between Kconfig files can use following syntax:

  1. source :

    The source statement can reference a path relative to the top-level Kconfig. For example, a top-level Kconfig loacated at ${top_kconfig_path} can use the following statement:

    source "subdir/Kconfig"
    

    This will incorporate {top_kconfig_path}/subdir/Kconfig file into the top-level Kconfig. If {top_kconfig_path}/subdir/Kconfig file does not exist, a Kconfig error will pop.

  2. osource :

    The file path referenced after osource will not cause an error even if it does not exist.

  3. rsource :

    The path referenced by rsource is relative to the Kconfig file using the rsource statement, rather than the top-level Kconfig.

  4. orsource :

    This statement is based on rsource, allowing the referenced file path to not exist.

Below are some common usages and notice of Kconfig. For more detailed information about Kconfig, please refer to: https://docs.kernel.org/kbuild/kconfig-language.html

Defining Kconfig Symbols

Defining a Kconfig symbol follows the below format:

config FOO
   bool "choose FOO"
   default n

The above Kconfig structure defines a bool type config symbol, which appears in the visualization interface as:

[ ]  choose FOO
  • "choose FOO" is the prompt text, which will be displayed in the visualization configuration interface. If "choose FOO" is removed, the configuration item will not appear in the visualization interface. Besides bool type, other types such as string, hex, int, etc., can also be defined.

  • default n indicates “no”, meaning this item will not be selected by default. default n can be omitted. Note that the default value defined by default only takes effect if the user has never touched this config item.

  • Further, conditional expressions can expand Kconfig functionality. For example, the following writing means that when BAR is selected, the FOO item will be displayed in the menu, and when both BAR and TIZ are selected, the default value of FOO will be y.

    config FOO
       bool "choose FOO" if BAR
       default y if BAR && TIZ
    

Dependencies in Kconfig

  1. Adding dependencies with depends on

    Through depends on, all entries under this config item can be added with dependencies. The following two writings are equivalent:

    config FOO
       depends on BAR
       bool "choose FOO"
       default y
    
    ## equal to
    
    config FOO
       bool "choose FOO" if BAR
       default y if BAR
    
  2. Adding reverse dependencies with select

    select forces another config to be selected when a config is chosen, regardless of whether this config has other dependency relationships. For example, in the following example, when BUZZ is selected, FOO will definitely be selected regardless of the value of BAR, and it cannot be deselected through the visualization interface.

    config BUZZ
       bool "choose BUZZ"
       select FOO
    
    config FOO
       depends on BAR
       bool "choose FOO"
    

    Note

    • select can only select bool-type configs.

    • select is commonly used for invisible configs or those without dependencies.

Invisible Config Items

When no prompt text is defined under a config, this config item is invisible, meaning it cannot be manually selected or deselected by the user. Its value is generally selected through dependency items or defined by default values. For example:

config BAR
   bool "BAR"
   select FOO

config FOO
   bool
   default n

##equal to

config FOO
   bool
   default y if BAR

##also equal to

config FOO
   depends on BAR
   bool
   default y

If it is not a bool type:

config NUM
   int
   default 255 if BAR
   default 65535 if !BAR

Introduction to conf Files

conf File Format

Conf files replace the UI-based menuconfig approach by writing user-defined configuration items into them. Conf files consist of multiple configuration items, each following this format:

CONFIG_<name1>=<value>

CONFIG_<name2>=<value>

...

There should be no spaces around the = sign.

Writing conf Files

  • The writing style of conf files is similar to .config files, but note that .config files contain all config items, whether they are visible to the user or not. However, conf files can only set config items visible to the user.

  • Similar to UI menuconfig, this direct configuration method essentially selects/deselects some items defined in Kconfig files, replacing interactive input with parameter input.

For example, consider the following Kconfig:

config SUPPORT_ATCMD
   bool "Enable ATCMD"
   default n
if SUPPORT_ATCMD
   choice
      default ATCMD_MANUAL_TEST
      prompt "ATCMD Mode"
      config ATCMD_MANUAL_TEST
            bool "Manual Test Mode"
      config ATCMD_HOST_CONTROL
            bool "Host Control Mode"
   endchoice
   config ATCMD_NETWORK
      bool "Enable Network"
      default n
   config ATCMD_SOCKET
      bool "Enable Socket"
endif
[*] Enable ATCMD
   ATCMD Mode (Manual Test Mode)  --->
[ ]     Enable Longer CMD
[*]     Enable Network
[ ]     Enable Socket

The corresponding conf file should be written as:

CONFIG_SUPPORT_ATCMD=y

CONFIG_ATCMD_NETWORK=y

Since ATCMD_MANUAL_TEST is the default choice value, it can be omitted in the conf file as CONFIG_ATCMD_MANUAL_TEST=y.

In the {SDK}/amebaxxx_gcc_project/utils/confs_daily_build directory, various common configuration collection files are provided, which users can refer to for creating their own conf files.

Note

Users can save the current configurations as a conf file using menuconfig.py -s.

default.conf

  • In {SDK}/amebaxxx_gcc_project directory, there is a configuration file named default.conf, defining the initial configuration for building this SOC project.

  • Specifically, menuconfig.py -f implicitly includes the rule of menuconfig.py -f default.conf, so when users write conf files, they can omit config items already configured in the default.conf file, meaning they only need to write incremental configurations compared to default.conf. If certain options in default.conf need to be disabled, set the corresponding config items to n.

  • Default values in Kconfig only take effect if a specific config item is not touched, while default.conf files act as a series of default inputs, thus having higher priority than default values in Kconfig.

prj.conf

  • prj.conf is located under example or the user-created project path, recording the configuration items needed for this example or external project. Users can configure the project using menuconfig.py -f /.../prj.conf. Additionally, when users have not configured through the UI or specified other conf files, prj.conf will be used as the initial configuration.

  • The priority of prj.conf is higher than that of default.conf.

Kconfig Automatic Update Check

When the Kconfig file or default.conf file is updated (e.g., pulled from a remote repository), but the files in the menuconfig folder are still based on the previous Kconfig, running build.py directly can lead to unexpected behavior. To prevent this, a check for Kconfig updates is performed before each compilation.

The anchor file for this check is menuconfig/.config_default, which is generated based on default.conf before each compilation. It contains the default configuration values for the SOC. If the newly generated menuconfig/.config_default differs from an existing file in the menuconfig folder, the console will display the differences and prompt the user to decide:

  • If user determines that the Kconfig update can be ignored, press Enter to continue using the current .config configuration.

  • If user believes ignoring the Kconfig update may affect the compilation results, press Ctrl+C to exit. After exiting, the user can reconfigure using the visualization interface or via menuconfig.py -f, or clean the menuconfig folder using menuconfig.py -c or build.py -p, and then compile using the default configuration.