Zephyr Devicetree详解 生成过程和结果
前文提到过Zephyr Devicetree会在构建过程中被转化为C宏,本文将详细描述这一过程。
Devicetree的输入和输出 链接到标题
输入文件 链接到标题
Zephyr中Devicetree文件分为4类
- source文件:.dts
- board dts
- 各个厂商的开发板Devicetree, 描述了板级硬件信息,存放在zephyr/boards下各个厂商的开发板子目录下,例如
./zephyr/boards/madmachine/mm_feather/mm_feather.dts
- 各个厂商的开发板Devicetree, 描述了板级硬件信息,存放在zephyr/boards下各个厂商的开发板子目录下,例如
- board dts
- include文件:.dtsi
- .dtsi文件会被.dts文件include
- soc dtsi
- 各个厂商提供的soc级dtsi文件,描述soc上的硬件信息,集中放在zephyr/dts下的各个厂商的子目录下,例如
./zephyr/dts/arm/nxp/nxp_rt1024.dtsi
- 各个厂商提供的soc级dtsi文件,描述soc上的硬件信息,集中放在zephyr/dts下的各个厂商的子目录下,例如
- pinctrl dtsi
- 各个厂商soc的pin mux的可选配置值,分散存放在
- modules/hal下各个厂商的子目录,例如,
modules/hal/nxp/dts/nxp/nxp_imx/mimx8mn6dvtjz-pinctrl.dtsi
- zephyr/dts下各厂商的子目录,例如
zephyr/dts/arm/adi/max32/max32680-pinctrl.dtsi
- modules/hal下各个厂商的子目录,例如,
- 各个开发板的pin mux配置信息,将引用可选配置值配置pin,存放在zephyr/boards下各个厂商的开发板子目录下,例如
zephyr/boards/madmachine/mm_feather/mm_feather-pinctrl.dtsi
- 各个厂商soc的pin mux的可选配置值,分散存放在
- soc dtsi
- .dtsi文件会被.dts文件include
- overlay文件: .overlay
- 针对板级dts,需要新增/修改/禁用一些和硬件相关的功能时,通过overlay文件将原来dts内的配置覆盖达到这一目的. 通常zephyr/boards/下各个厂商与shields的子目录下和zephyr/sample下会放,例如
- ./zephyr/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm7_B.overlay
- ./zephyr/boards/nxp/mimxrt1170_evk/mimxrt1170_evk_mimxrt1176_cm4_B.overlay
- ./zephyr/boards/shields/esp_8266/esp_8266_arduino.overlay
- ./zephyr/samples/subsys/zbus/uart_bridge/boards/hifive1_fe310_B.overlay
- ./zephyr/samples/subsys/zbus/uart_bridge/boards/nrf52840dk_nrf52840.overlay
- 针对板级dts,需要新增/修改/禁用一些和硬件相关的功能时,通过overlay文件将原来dts内的配置覆盖达到这一目的. 通常zephyr/boards/下各个厂商与shields的子目录下和zephyr/sample下会放,例如
-
绑定文件: .yaml - 描述 dts、dtsi和overlay需要遵守的规则。 zephyr/dts/bindings 目录包含所有的绑定文件 链接到标题
输出文件 链接到标题
Zephyr中Devicetree文件的输出 - build/zephyr/zephyr.dts.pre:预处理的后的dts。是一个中间输出文件,它是 gen_defines.py 的输入并用于创建 zephyr.dts 和 devicetree_generated.h - build/zephyr/include/generated/zephyr/devicetree_generated.h 生成的宏,会被devicetree.h include,Zephyr的source code中都是以C宏来访问Devicetree - build/zephyr/zephyr.dts 由dts,dtsi,overlay合并而成的最终dts。由 gen_defines.py 输出。对于调试很有用。 - build/zephyr/edt.pickle, 由 gen_defines.py 输出,可被python识别,用于生成dts.cmake
Devicetree的脚本: 链接到标题
脚本完成Zephyr从Devicetree输入到Devicetree输出的转换,这些脚本存放在zephyr/script/dts下, 主要文件如下
.
├── gen_defines.py
├── gen_driver_kconfig_dts.py
├── gen_dts_cmake.py
├── python-devicetree
│ ├── requirements.txt
│ ├── setup.py
│ ├── src
│ │ └── devicetree
│ │ ├── dtlib.py
│ │ ├── edtlib.py
│ │ ├── grutils.py
python-devicetree/
下是python解析Devicetree的脚本,可以用到任何Devicetree上gen_defines.py
使用 edtlib 从Devicetree和绑定生成 C 预处理器宏gen_driver_kconfig_dts.py
从Devicetree生成Kconfig选项gen_dts_cmake.py
从Devicetree生成cmake配置信息
下图描述了Zephyr的转化过程 图片摘自Zephyr官方文档
Zephyr Devicetree构建过程 链接到标题
在Zerphyr中Devicetree在构建过程中被转化为C宏,整个构建过程由CMake控制,体现到Devicetree的CMake处理流程如下:
- app/CMakeLists.txt
- zephyr/share/zephyr-package/cmake/ZephyrConfigVersion.cmake
- zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake
- zephyr/cmake/modules/zephyr_default.cmake
- zephyr/cmake/modules/dts.cmake
- zephyr/cmake/modules/zephyr_default.cmake
- zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake
- zephyr/share/zephyr-package/cmake/ZephyrConfigVersion.cmake
从应用的CMakeLists.txt开始,最后进入到zephyr/cmake/modules/dts.cmake
, dts.cmake 处理Zephyr的Devicetree, 它让Zephyr Devicetree中的信息能够被各个构建阶段和其他的Python脚本所访问和使用。
Devicetree生成结果的使用 链接到标题
各个阶段使用Devicetree的手段和内容:
- 对于Zephyr和应用程序源代码文件:调用zephyr/devicetree.h
中定义的C宏API访问Devicetree
- 访问生成内容build/zephyr/include/generated/zephyr/devicetree_generated.h
- 对于其他任意的Python脚本(如twister),使用Python pickle格式序列化的edtlib.EDT对象
- 访问生成内容build/zephyr/edt.pickle
- 对于开发者,可以阅读Devicetree合成后的完整.dts文件,可用于调试
- 访问生成内容build/zephyr/zephyr.dts
- 对于CMake文件, 使用cmake/modules/extensions.cmake中定义的Devicetree扩展访问
- 对于Kconfig文件,既使用生成的一些Kconfig符号,也使用scripts/kconfig/kconfigfunctions.py中定义的扩展函数
Devicetree生成依赖的工具 链接到标题
dts.cmake 转换Zephyr的Devicetree,依赖下面工具
- 使用pre_dt.cmake进行预先处理
- 遍历app,board,shield,arch,dts,dts/common,include, include/zephyr目录生成DTS_ROOT
和DTS_ROOT_SYSTEM_INCLUDE_DIRS
变量
- C预处理器
- devicetree python
- scripts/dts脚本
- dtc工具
Devicetree生成过程 链接到标题
Devicetree的生成过程由zephyr/cmake/modules/dts.cmake
控制,具体步骤如下
- 获取dts/overlay文件列表:
-
找到board下的dts文件,结果放在
DTS_SOURCE
中 链接到标题``` set(DTS_SOURCE ${BOARD_DIR}/${board_string}.dts) zephyr_file(CONF_FILES ${BOARD_DIR} DTS DTS_SOURCE) ```
-
找board/extension下的dts文件,结果放在
board_extension_dts_files
中 链接到标题``` zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} DTS board_extension_dts_files) ```
-
将要处理的dts文件列表放到
dts_files
中,下面的shield_dts_files
变量由shield.cmake输出 链接到标题``` set(dts_files ${DTS_SOURCE} ${board_extension_dts_files} ${shield_dts_files} ) ```
-
将overlay dts和extension下的overlay dts文件列表放到
dts_files
中,DTC_OVERLAY_FILE
和EXTRA_DTC_OVERLAY_FILE
变量由 - ``` if(DTC_OVERLAY_FILE) zephyr_list(TRANSFORM DTC_OVERLAY_FILE NORMALIZE_PATHS OUTPUT_VARIABLE DTC_OVERLAY_FILE_AS_LIST) list(APPEND dts_files ${DTC_OVERLAY_FILE_AS_LIST} ) endif()if(EXTRA_DTC_OVERLAY_FILE) zephyr_list(TRANSFORM EXTRA_DTC_OVERLAY_FILE NORMALIZE_PATHS OUTPUT_VARIABLE EXTRA_DTC_OVERLAY_FILE_AS_LIST) list(APPEND dts_files ${EXTRA_DTC_OVERLAY_FILE_AS_LIST} ) endif() ```
-
- 获取绑定文件列表:
- 从
dts_root
中获取绑定文件的列表放在变量DTS_ROOT_BINDINGS
中 -
unset(DTS_ROOT_BINDINGS) foreach(dts_root ${DTS_ROOT}) set(bindings_path ${dts_root}/dts/bindings) if(EXISTS ${bindings_path}) list(APPEND DTS_ROOT_BINDINGS ${bindings_path} ) endif() set(vendor_prefixes ${dts_root}/${VENDOR_PREFIXES}) if(EXISTS ${vendor_prefixes}) list(APPEND EXTRA_GEN_DEFINES_ARGS --vendor-prefixes ${vendor_prefixes}) endif() endforeach()
- 从
- 预处理dts:
-
设置预处理器
dts_preprocessor
, 一般情况都是C预处理器 链接到标题``` set(dts_preprocessor ${CMAKE_C_COMPILER}) ```
-
对
dts_files
进行预处理,预处理相当于是将dts_files
列出的各种文件整合为一个文件zephyr.dts.pre(DTS_POST_CPP
)并将其依赖的dtsi和头文件路径列表在zephyr.dts.d(DTS_DEPS
)内 链接到标题``` zephyr_dt_preprocess( CPP ${dts_preprocessor} SOURCE_FILES ${dts_files} OUT_FILE ${DTS_POST_CPP} DEPS_FILE ${DTS_DEPS} EXTRA_CPPFLAGS ${DTS_EXTRA_CPPFLAGS} INCLUDE_DIRECTORIES ${DTS_ROOT_SYSTEM_INCLUDE_DIRS} WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR} ) ```
-
- 使用gen_defines.py转换得到C和Python可用的文件:
- 输入:
- 预处理产生的zephyr.dts.pre
- Devicetree绑定文件
- 输出:
- 并转化生成Devicetree的C宏头文件devicetree_generated.h
- 合成后的Devicetree文件zephyr.dts
- python可用的edt.pickle
-
#设置命令变量 set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT} --dts ${DTS_POST_CPP} --dtc-flags '${EXTRA_DTC_FLAGS_RAW}' --bindings-dirs ${DTS_ROOT_BINDINGS} --header-out ${DEVICETREE_GENERATED_H}.new --dts-out ${ZEPHYR_DTS}.new # for debugging and dtc --edt-pickle-out ${EDT_PICKLE} ${EXTRA_GEN_DEFINES_ARGS} ) #执行命令 execute_process( COMMAND ${CMD_GEN_DEFINES} WORKING_DIRECTORY ${PROJECT_BINARY_DIR} RESULT_VARIABLE ret ) #移除过程文件 #命令生成的是devicetree.h.new和zephyr.dts.new,将起拷贝为devicetree.h和zephyr.dts zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT) zephyr_file_copy(${DEVICETREE_GENERATED_H}.new ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT) file(REMOVE ${ZEPHYR_DTS}.new ${DEVICETREE_GENERATED_H}.new)
- 输入:
- 使用gen_driver_kconfig_dts.py转换得到Kconfig可用的文件:
- 输入:
- Devicetree绑定文件
- 输出:
- build/Kconfig/Kconfig.dts, 从Devicetree转换成Kconfig的配置项
-
-
execute_process( COMMAND ${PYTHON_EXECUTABLE} ${GEN_DRIVER_KCONFIG_SCRIPT} --kconfig-out ${DTS_KCONFIG} --bindings-dirs ${DTS_ROOT_BINDINGS} WORKING_DIRECTORY ${PROJECT_BINARY_DIR} RESULT_VARIABLE ret )
-
- 输入:
- 使用gen_dts_cmake.py转换得到CMake可用的文件:
- 输入
- edt.pickle
- 输出
- build/zephyr/dts.cmake,该文件包含了的Devicetree中各节点的属性,方便被CMake系统访问判断节点属性
-
execute_process( COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_CMAKE_SCRIPT} --edt-pickle ${EDT_PICKLE} --cmake-out ${DTS_CMAKE} WORKING_DIRECTORY ${PROJECT_BINARY_DIR} RESULT_VARIABLE ret ) #使用前面生成的dts.cmake include(${DTS_CMAKE})
- 输入
-
使用dtc检查前面生成的zephyr.dts是否合法: 链接到标题
``` execute_process( COMMAND ${DTC} -O dts -o - # Write output to stdout, which we discard below -b 0 -E unit_address_vs_reg ${DTC_NO_WARN_UNIT_ADDR} ${DTC_WARN_UNIT_ADDR_IF_ENABLED} ${EXTRA_DTC_FLAGS} # User settable ${ZEPHYR_DTS} OUTPUT_QUIET # Discard stdout WORKING_DIRECTORY ${PROJECT_BINARY_DIR} RESULT_VARIABLE ret ) ```
参考 链接到标题
https://docs.zephyrproject.org/latest/build/dts/intro-input-output.html