编译源码
下载配置工具链
访问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
文档。
初始化流程分析
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_MODULE
为 test
。
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.in
和 fwk_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
文件,这个文件中会包含所有模块的索引,如下: