编译源码

下载配置工具链

访问ARM 官网下载工具链下载自己系统的版本的工具链。如果你也是 x86_64 的 Linux 系统,直接点击该链接下载 arm-gnu-toolchain-13.3

解压工具链:

xz -d arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
tar -xvf arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar

将工具链路径添加到环境变量中。

编译源码

源码使用 CMake 进行编译,CMake 版本需要 3.18.4 以上。如果你是 Ubuntu 20.04,直接安装即可。

sudo apt install cmake

如果你是 Ubuntu 18.04,需要升级 CMAKE。可以通过下面的方式下载源码编译安装 CMAKE。

wget https://github.com/Kitware/CMake/releases/download/v3.22.0/cmake-3.22.0-linux-x86_64.tar.gz
tar -zxvf cmake-3.22.0-linux-x86_64.tar.gz
mv cmake-3.22.0-linux-x86_64 /usr/local/cmake
export PATH=/usr/local/cmake/bin:$PATH

源码编译:

make -f Makefile.cmake PRODUCT=totalcompute/tc2 MODE=debug firmware-scp_ramfw

源码编译成功后,会在build/tc2/GNU/debug/firmware-scp_ramfw/bin目录下生成二进制文件。具体编译参数,可以参考源码根目录下的user_guide文档。

初始化流程分析

Responsive Image

fwk_module_init 初始时,module_config_table 是在什么时候被初始化的?

module_config_table 无法在源码中直接搜索到,因为它是在编译过程中生成的,当你编译一次后,就会在 output/build/mpw/GNU/debug/firmware-scp_romfw/framework/src/fwk_module_list.c 找到这个变量。具体它是如何生成的,注意通过 framework/CMakeLists.txt 文件中的这段代码完成,我们逐行分析这段代码。

list(LENGTH SCP_MODULES SCP_MODULE_IDX_MAX)

获取模块列表的长度,保存在 SCP_MODULE_IDX_MAX 变量中。在每个 Firmware.cmake 文件中,都会有一个 SCP_MODULES 变量,这个变量保存了该产品所包含的模块。

# product/juno/scp_romfw/Firmware.cmake
list(APPEND SCP_MODULES "test")
list(APPEND SCP_MODULES "uart")

当前我们加入了两个模块,所以 SCP_MODULE_IDX_MAX 的值为 2。

foreach(idx RANGE ${SCP_MODULE_IDX_MAX})
    if(idx EQUAL SCP_MODULE_IDX_MAX)
        string(APPEND SCP_MODULE_IDX_GEN "    FWK_MODULE_IDX_COUNT = ${idx},\n")

        break()
    endif()

遍历模块列表,生成模块索引的定义,如下:

enum fwk_module_idx {
    FWK_MODULE_IDX_COUNT = 0,
};
    list(GET SCP_MODULES ${idx} SCP_MODULE)

SCP_MODULES 列表中索引为 idx 的元素赋值给变量 SCP_MODULE,以便在后续的代码中使用。当前 idx 为 0,所以 SCP_MODULEtest

    string(MAKE_C_IDENTIFIER ${SCP_MODULE} SCP_MODULE)
    string(TOUPPER ${SCP_MODULE} SCP_MODULE_UPPER)

SCP_MODULE 转换为 C 标识符。将 SCP_MODULE 转为大写并保存在 SCP_MODULE_UPPER 中,即 SCP_MODULE_UPPER="TEST"

    string(APPEND SCP_MODULE_IDX_GEN "    FWK_MODULE_IDX_${SCP_MODULE_UPPER} = ${idx},\n")
    string(APPEND SCP_MODULE_ID_INIT_GEN "#define FWK_MODULE_ID_${SCP_MODULE_UPPER}_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_${SCP_MODULE_UPPER})\n")
    string(APPEND SCP_MODULE_ID_GEN "#define FWK_MODULE_ID_${SCP_MODULE_UPPER} FWK_ID_MODULE(FWK_MODULE_IDX_${SCP_MODULE_UPPER})\n")
    string(APPEND SCP_MODULE_ID_CONST_GEN "static const fwk_id_t fwk_module_id_${SCP_MODULE} = FWK_MODULE_ID_${SCP_MODULE_UPPER}_INIT;\n")
    string(APPEND SCP_MODULE_EXTERN_GEN "extern const struct fwk_module module_${SCP_MODULE};\n")
    string(APPEND SCP_MODULE_EXTERN_CONFIG_GEN "extern const struct fwk_module_config config_${SCP_MODULE};\n")
    string(APPEND SCP_MODULE_GEN "    &module_${SCP_MODULE},\n")
    string(APPEND SCP_MODULE_CONFIG_GEN "    &config_${SCP_MODULE},\n")

进行一些宏替换,替换后的结果如下:

SCP_MODULE_IDX_GEN: "   FWK_MODULE_IDX_TEST = 0,"
SCP_MODULE_ID_INIT_GEN: "#define FWK_MODULE_ID_TEST_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST)"
SCP_MODULE_ID_GEN: "#define FWK_MODULE_ID_TEST FWK_ID_MODULE(FWK_MODULE_IDX_TEST)"
SCP_MODULE_ID_CONST_GEN: "static const fwk_id_t fwk_module_id_test = FWK_MODULE_ID_TEST_INIT;"
SCP_MODULE_EXTERN_GEN: "extern const struct fwk_module module_test;"
SCP_MODULE_EXTERN_CONFIG_GEN: "extern const struct fwk_module_config config_test;"
SCP_MODULE_GEN: "&module_test,"
SCP_MODULE_CONFIG_GEN: "&config_test,"
    target_compile_definitions(framework
                               PUBLIC "BUILD_HAS_MOD_${SCP_MODULE_UPPER}=1")

BUILD_HAS_MOD_TEST 定义为 1,以便在后续的代码中使用。

在源码文件中有两个模板文件,分别为 fwk_module_list.c.infwk_module_idx.h.in,这两个文件中包含了一些宏定义,这些宏定义在编译过程中会被替换为上面生成的宏定义。

#include <stddef.h>

@SCP_MODULE_EXTERN_GEN@

const struct fwk_module *module_table[FWK_MODULE_IDX_COUNT] = {
@SCP_MODULE_GEN@
};

@SCP_MODULE_EXTERN_CONFIG_GEN@

const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = {
@SCP_MODULE_CONFIG_GEN@
};
#include <fwk_id.h>

@SCP_MODULE_ID_INIT_GEN@
@SCP_MODULE_ID_GEN@

enum fwk_module_idx {
@SCP_MODULE_IDX_GEN@
};

@SCP_MODULE_ID_CONST_GEN@

在编译过程中就会生成下面两个文件,当使用module_config_table时,就会引用这两个文件,找到对应的配置。

// build/mpw/GNU/debug/firmware-scp_romfw/framework/include/fwk_module_idx.h
#define FWK_MODULE_ID_TEST_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TEST)
#define FWK_MODULE_ID_UART_INIT FWK_ID_MODULE_INIT(FWK_MODULE_IDX_UART)


#define FWK_MODULE_ID_TEST FWK_ID_MODULE(FWK_MODULE_IDX_TEST)
#define FWK_MODULE_ID_UART FWK_ID_MODULE(FWK_MODULE_IDX_UART)


enum fwk_module_idx {
    FWK_MODULE_IDX_TEST = 0,
    FWK_MODULE_IDX_UART = 1,
    FWK_MODULE_IDX_COUNT = 2,

};

static const fwk_id_t fwk_module_id_test = FWK_MODULE_ID_TEST_INIT;
static const fwk_id_t fwk_module_id_uart = FWK_MODULE_ID_UART_INIT;
// build/mpw/GNU/debug/firmware-scp_romfw/framework/src/fwk_module_list.c
extern const struct fwk_module module_test;
extern const struct fwk_module module_uart;


const struct fwk_module *module_table[FWK_MODULE_IDX_COUNT] = {
    &module_test,
    &module_uart,

};

extern const struct fwk_module_config config_test;
extern const struct fwk_module_config config_uart;


const struct fwk_module_config *module_config_table[FWK_MODULE_IDX_COUNT] = {
    &config_test,
    &config_uart,
};

如何确定各个模块在初始化过程中的执行顺序?

在每个产品目录 mscp/product/juno/scp_romfw/Firmware.cmake 中,都会有一个 Firmware.cmake 文件,这个文件中会包含该产品所包含的模块:

list(APPEND SCP_MODULES "juno-ppu")
list(APPEND SCP_MODULES "juno-rom")
list(APPEND SCP_MODULES "gtimer")
list(APPEND SCP_MODULES "sds")
list(APPEND SCP_MODULES "bootloader")
list(APPEND SCP_MODULES "juno-soc-clock")

编译后会在 output 文件夹中生成fwk_module_idx.h文件,这个文件中会包含所有模块的索引,如下:

Responsive Image