Zephyr设备树生成流程

Creative Commons
本作品采用知识共享署名

本文说明zephyr的dts生成C宏的主要流程。

Zephyr构建过程简述一文中我们知道zephyr对dts的使用是将dts其生成C宏,由代码来使用宏,当时大体提到过dts生成C宏的流程,本文更详细的介绍dts如何生成宏,但生成宏规则不在本文介绍范围内。

概览

下图说明了dts生成宏的详细过程,摘自zephyr官方文档,链接详见文末参考
dts.png
对于整个流程有4种输入文件:
sources (.dts) : 一般是board的dts文件
includes (.dtsi):被dts包含的dtsi文件,是soc或者驱动级的公用描述
overlays (.overlay):对于相同的应用采用不同的板子时,可以在应用下放不同overlay文件配置应用要用的设备
bindings (.yaml): binding文件,用于帮助dts生成宏
以上文件通常会出现在下面路径

1
2
3
4
5
boards/<ARCH>/<BOARD>/<BOARD>.dts
dts/common/skeleton.dtsi
dts/<ARCH>/.../<SOC>.dtsi
dts/bindings/.../binding.yaml
app/xxx.overlay

3种输出文件:
zephyr.dts:该文件是dts,dtsi,overlay合并为一个文件,作为调试参考用
devicetree.h:dts最后生成的宏,会被代码引用
devicetree.conf:dts生成的conf,会被Kconfig引用
下面我们分析详细流程

1 合并整个dts

合并生成dts

将dts和overlay合并为dts.pre.tmp文件
dts.cmake中取得board的dts, 该dts中会include其它dtsi部分

1
set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts)

加入到变量dts_files中

1
2
3
4
set(dts_files
${DTS_SOURCE}
${shield_dts_files}
)

将overlay放入dts_files中

1
2
3
4
5
string(REPLACE " " ";" DTC_OVERLAY_FILE_AS_LIST ${DTC_OVERLAY_FILE})
list(APPEND
dts_files
${DTC_OVERLAY_FILE_AS_LIST}
)

读出dts文件作为编译的-i选项DTC_INCLUDE_FLAG_FOR_DTS

1
2
3
foreach(dts_file ${dts_files})
list(APPEND DTC_INCLUDE_FLAG_FOR_DTS
-include ${dts_file})

通过预编译将所有的dts merge为一个dts文件${BOARD}.dts.pre.tmp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
execute_process(
COMMAND ${CMAKE_C_COMPILER}
-x assembler-with-cpp
-nostdinc
${DTS_ROOT_SYSTEM_INCLUDE_DIRS}
${DTC_INCLUDE_FLAG_FOR_DTS} # include the DTS source and overlays
${NOSYSDEF_CFLAG}
-D__DTS__
-P
-E # Stop after preprocessing
-MD # Generate a dependency file as a side-effect
-MF ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.d
-o ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp
${ZEPHYR_BASE}/misc/empty_file.c
WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
RESULT_VARIABLE ret
)

这里提一下empty_file.c,之前一直没搞清楚zephyr里面放个空文件搞什么,现在才知道是为了用来预编译帮助生成一些东西,除了在dts外,构建过程的其它地方也有使用。

检查dts语法

使用dtc工具检查merge后的dts.pre.tmp文件是否符合dts语法,在zephyr中dtc只用于检查合并后的dts.pre.tmp文件的正确性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
${BOARD}.dts.pre.tmp
OUTPUT_QUIET # Discard stdout
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
RESULT_VARIABLE ret
)

2.生devicetree_unfixed.h和devicetree.conf

获取dts的binding文件,也就是dts/binding下的yaml文件

1
2
3
4
5
6
7
8
9
foreach(dts_root ${DTS_ROOT})
set(full_path ${dts_root}/dts/bindings)
if(EXISTS ${full_path})
list(APPEND
DTS_ROOT_BINDINGS
${full_path}
)
endif()
endforeach()

使用gen_defines.py搭配yaml文件解析{BOARD}.dts.pre.tmp 生成devicetree.conf,devicetree_unfixed.h和zephyr.dts
zephyr.dts只是方便调试查看,不会对之后的构建有任何帮助。

1
2
3
4
5
6
7
8
set(CMD_NEW_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_defines.py
--dts ${BOARD}.dts.pre.tmp
--dtc-flags '${EXTRA_DTC_FLAGS}'
--bindings-dirs ${DTS_ROOT_BINDINGS}
--conf-out ${DEVICETREE_CONF}
--header-out ${DEVICETREE_UNFIXED_H}
--dts-out ${PROJECT_BINARY_DIR}/zephyr.dts # As a debugging aid
)

3. 生成devicetree_fixed.h

在soc目录下不同的架构有一些dts_fixup.h文件,该文件是为了将dts生成的宏转为一些代码里面固定的宏,例如

1
2
#define DT_RTC_0_NAME				DT_NXP_IMX_GPT_COUNTER_1_LABEL
#define DT_RTC_1_NAME DT_NXP_IMX_GPT_COUNTER_2_LABEL

这种方式将逐渐被废弃,但目前还在用,这里也说明一下生成过程:
dts_fixup.h合并成为devicetree_fixed.h,在根目录CMakeList.txt中处理。

取出各个地方的dts_fixup.h

1
2
3
set_ifndef(  DTS_BOARD_FIXUP_FILE                                   ${BOARD_DIR}/dts_fixup.h)
set_ifndef( DTS_SOC_FIXUP_FILE ${SOC_DIR}/${ARCH}/${SOC_PATH}/dts_fixup.h)
set( DTS_APP_FIXUP_FILE ${APPLICATION_SOURCE_DIR}/dts_fixup.h)

定义要生成文件devicetree_fixups.h路径DTS_CAT_OF_FIXUP_FILES

1
set_ifndef(DTS_CAT_OF_FIXUP_FILES ${ZEPHYR_BINARY_DIR}/include/generated/devicetree_fixups.h)

读出各个dts_fixup.h的内容将其写入devicetree_fixups.h

1
2
3
4
5
6
file(WRITE ${DTS_CAT_OF_FIXUP_FILES} "/* May only be included by devicetree.h */\n\n") foreach(fixup_file ${DTS_BOARD_FIXUP_FILE}
${DTS_SOC_FIXUP_FILE}
${DTS_APP_FIXUP_FILE}
${shield_dts_fixups}
) if(EXISTS ${fixup_file}) file(READ ${fixup_file} contents)
file(APPEND ${DTS_CAT_OF_FIXUP_FILES} "${contents}") endif() endforeach()

4. 生成devicetree.h

合并 devicetree_unfixed.h 和 devicetree_fixed.h 到devicetree.h,采用头文件直接include方式

1
2
#include <devicetree_unfixed.h>
#include <devicetree_fixups.h>

参考

https://docs.zephyrproject.org/latest/guides/dts/intro.html#input-and-output-files