1# SPDX-License-Identifier: Apache-2.0
2
3include_guard(GLOBAL)
4
5include(extensions)
6include(python)
7include(boards)
8include(pre_dt)
9find_package(HostTools)
10find_package(Dtc 1.4.6)
11
12# This module makes information from the devicetree available to
13# various build stages, as well as to other arbitrary Python scripts:
14#
15#   - To Zephyr and application source code files, as a C macro API
16#     defined in <zephyr/devicetree.h>
17#
18#   - To other arbitrary Python scripts (like twister) using a
19#     serialized edtlib.EDT object in Python's pickle format
20#     (https://docs.python.org/3/library/pickle.html)
21#
22#   - To users as a final devicetree source (DTS) file which can
23#     be used for debugging
24#
25#   - To CMake files, after this module has finished running, using
26#     devicetree extensions defined in cmake/modules/extensions.cmake
27#
28#   - To Kconfig files, both using some Kconfig symbols we generate
29#     here as well as the extension functions defined in
30#     scripts/kconfig/kconfigfunctions.py
31#
32# See the specific API documentation for each of these cases for more
33# information on what is currently available to you.
34#
35# We rely on the C preprocessor, the devicetree python package, and
36# files in scripts/dts to make all this work. We also optionally will
37# run the dtc tool if it is found, in order to catch any additional
38# warnings or errors it generates.
39#
40# Outcome:
41#
42# 1. The following has happened:
43#
44#    - The pre_dt module has been included; refer to its outcome
45#      section for more information on the consequences
46#    - DTS_SOURCE: set to the path to the devicetree file which
47#      was used, if one was provided or found
48#    - ${BINARY_DIR_INCLUDE_GENERATED}/devicetree_generated.h exists
49#
50# 2. The following has happened if a devicetree was found and
51#    no errors occurred:
52#
53#    - CACHED_DTS_ROOT_BINDINGS is set in the cache to the
54#      value of DTS_ROOT_BINDINGS
55#    - DTS_ROOT_BINDINGS is set to a ;-list of locations where DT
56#      bindings were found
57#    - ${PROJECT_BINARY_DIR}/zephyr.dts exists
58#    - ${PROJECT_BINARY_DIR}/edt.pickle exists
59#    - ${KCONFIG_BINARY_DIR}/Kconfig.dts exists
60#    - the build system will be regenerated if any devicetree files
61#      used in this build change, including transitive includes
62#    - the devicetree extensions in the extensions.cmake module
63#      will be ready for use in other CMake list files that run
64#      after this module
65#
66# Required variables:
67# - BINARY_DIR_INCLUDE_GENERATED: where to put generated include files
68# - KCONFIG_BINARY_DIR: where to put generated Kconfig files
69#
70# Optional variables:
71# - BOARD: board name to use when looking for DTS_SOURCE
72# - BOARD_DIR: board directory to use when looking for DTS_SOURCE
73# - BOARD_REVISION_STRING: used when looking for a board revision's
74#   devicetree overlay file in BOARD_DIR
75# - DTC_OVERLAY_FILE: list of devicetree overlay files which will be
76#   used to modify or extend the base devicetree.
77# - EXTRA_DTC_OVERLAY_FILE: list of extra devicetree overlay files.
78#   This variable is is similar to DTC_OVERLAY_FILE but the files in
79#   EXTRA_DTC_OVERLAY_FILE will be applied after DTC_OVERLAY_FILE and
80#   thus files specified by EXTRA_DTC_OVERLAY_FILE have higher precedence.
81# - EXTRA_DTC_FLAGS: list of extra command line options to pass to
82#   dtc when using it to check for additional errors and warnings;
83#   invalid flags are automatically filtered out of the list
84# - DTS_EXTRA_CPPFLAGS: extra command line options to pass to the
85#   C preprocessor when generating the devicetree from DTS_SOURCE
86# - DTS_SOURCE: the devicetree source file to use may be pre-set
87#   with this variable; otherwise, it defaults to
88#   ${BOARD_DIR}/${BOARD}.dts
89#
90# Variables set by this module and not mentioned above are for internal
91# use only, and may be removed, renamed, or re-purposed without prior notice.
92
93# The directory containing devicetree related scripts.
94set(DT_SCRIPTS                  ${ZEPHYR_BASE}/scripts/dts)
95
96# This generates DT information needed by the C macro APIs,
97# along with a few other things.
98set(GEN_DEFINES_SCRIPT          ${DT_SCRIPTS}/gen_defines.py)
99# The edtlib.EDT object in pickle format.
100set(EDT_PICKLE                  ${PROJECT_BINARY_DIR}/edt.pickle)
101# The generated file containing the final DTS, for debugging.
102set(ZEPHYR_DTS                  ${PROJECT_BINARY_DIR}/zephyr.dts)
103# The generated C header needed by <zephyr/devicetree.h>
104set(DEVICETREE_GENERATED_H      ${BINARY_DIR_INCLUDE_GENERATED}/devicetree_generated.h)
105# Generated build system internals.
106set(DTS_POST_CPP                ${PROJECT_BINARY_DIR}/zephyr.dts.pre)
107set(DTS_DEPS                    ${PROJECT_BINARY_DIR}/zephyr.dts.d)
108
109# This generates DT information needed by the Kconfig APIs.
110set(GEN_DRIVER_KCONFIG_SCRIPT   ${DT_SCRIPTS}/gen_driver_kconfig_dts.py)
111# Generated Kconfig symbols go here.
112set(DTS_KCONFIG                 ${KCONFIG_BINARY_DIR}/Kconfig.dts)
113
114# This generates DT information needed by the CMake APIs.
115set(GEN_DTS_CMAKE_SCRIPT        ${DT_SCRIPTS}/gen_dts_cmake.py)
116# The generated information itself, which we include() after
117# creating it.
118set(DTS_CMAKE                   ${PROJECT_BINARY_DIR}/dts.cmake)
119
120# The location of a file containing known vendor prefixes, relative to
121# each element of DTS_ROOT. Users can define their own in their own
122# modules.
123set(VENDOR_PREFIXES             dts/bindings/vendor-prefixes.txt)
124
125set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts)
126if(EXISTS ${DTS_SOURCE})
127  # We found a devicetree. Check for a board revision overlay.
128  if(DEFINED BOARD_REVISION AND EXISTS ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.overlay)
129    list(APPEND DTS_SOURCE ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.overlay)
130  endif()
131else()
132  # If we don't have a devicetree, provide an empty stub
133  set(DTS_SOURCE ${ZEPHYR_BASE}/boards/common/stub.dts)
134endif()
135
136#
137# Find all the DTS files we need to concatenate and preprocess, as
138# well as all the devicetree bindings and vendor prefixes associated
139# with them.
140#
141
142zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} DTS board_extension_dts_files)
143
144set(dts_files
145  ${DTS_SOURCE}
146  ${board_extension_dts_files}
147  ${shield_dts_files}
148  )
149
150if(DTC_OVERLAY_FILE)
151  zephyr_list(TRANSFORM DTC_OVERLAY_FILE NORMALIZE_PATHS
152              OUTPUT_VARIABLE DTC_OVERLAY_FILE_AS_LIST)
153  list(APPEND
154    dts_files
155    ${DTC_OVERLAY_FILE_AS_LIST}
156    )
157endif()
158
159if(EXTRA_DTC_OVERLAY_FILE)
160  zephyr_list(TRANSFORM EXTRA_DTC_OVERLAY_FILE NORMALIZE_PATHS
161              OUTPUT_VARIABLE EXTRA_DTC_OVERLAY_FILE_AS_LIST)
162  list(APPEND
163    dts_files
164    ${EXTRA_DTC_OVERLAY_FILE_AS_LIST}
165    )
166endif()
167
168set(i 0)
169foreach(dts_file ${dts_files})
170  if(i EQUAL 0)
171    message(STATUS "Found BOARD.dts: ${dts_file}")
172  else()
173    message(STATUS "Found devicetree overlay: ${dts_file}")
174  endif()
175
176  math(EXPR i "${i}+1")
177endforeach()
178
179unset(DTS_ROOT_BINDINGS)
180foreach(dts_root ${DTS_ROOT})
181  set(bindings_path ${dts_root}/dts/bindings)
182  if(EXISTS ${bindings_path})
183    list(APPEND
184      DTS_ROOT_BINDINGS
185      ${bindings_path}
186      )
187  endif()
188
189  set(vendor_prefixes ${dts_root}/${VENDOR_PREFIXES})
190  if(EXISTS ${vendor_prefixes})
191    list(APPEND EXTRA_GEN_DEFINES_ARGS --vendor-prefixes ${vendor_prefixes})
192  endif()
193endforeach()
194
195# Cache the location of the root bindings so they can be used by
196# scripts which use the build directory.
197set(CACHED_DTS_ROOT_BINDINGS ${DTS_ROOT_BINDINGS} CACHE INTERNAL
198  "DT bindings root directories")
199
200#
201# Run the C preprocessor on the devicetree source, so we can parse it
202# (using the Python devicetree package) in later steps.
203#
204
205# TODO: Cut down on CMake configuration time by avoiding
206# regeneration of devicetree_generated.h on every configure. How
207# challenging is this? Can we cache the dts dependencies?
208
209# Run the preprocessor on the DTS input files.
210if(DEFINED CMAKE_DTS_PREPROCESSOR)
211  set(dts_preprocessor ${CMAKE_DTS_PREPROCESSOR})
212else()
213  set(dts_preprocessor ${CMAKE_C_COMPILER})
214endif()
215zephyr_dt_preprocess(
216  CPP ${dts_preprocessor}
217  SOURCE_FILES ${dts_files}
218  OUT_FILE ${DTS_POST_CPP}
219  DEPS_FILE ${DTS_DEPS}
220  EXTRA_CPPFLAGS ${DTS_EXTRA_CPPFLAGS}
221  INCLUDE_DIRECTORIES ${DTS_ROOT_SYSTEM_INCLUDE_DIRS}
222  WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
223  )
224
225#
226# Make sure we re-run CMake if any devicetree sources or transitive
227# includes change.
228#
229
230# Parse the generated dependency file to find the DT sources that
231# were included, including any transitive includes.
232toolchain_parse_make_rule(${DTS_DEPS}
233  include_files # Output parameter
234  )
235
236# Add the results to the list of files that, when change, force the
237# build system to re-run CMake.
238set_property(DIRECTORY APPEND PROPERTY
239  CMAKE_CONFIGURE_DEPENDS
240  ${include_files}
241  ${GEN_DEFINES_SCRIPT}
242  ${GEN_DRIVER_KCONFIG_SCRIPT}
243  ${GEN_DTS_CMAKE_SCRIPT}
244  )
245
246#
247# Run GEN_DEFINES_SCRIPT.
248#
249
250string(REPLACE ";" " " EXTRA_DTC_FLAGS_RAW "${EXTRA_DTC_FLAGS}")
251set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT}
252--dts ${DTS_POST_CPP}
253--dtc-flags '${EXTRA_DTC_FLAGS_RAW}'
254--bindings-dirs ${DTS_ROOT_BINDINGS}
255--header-out ${DEVICETREE_GENERATED_H}.new
256--dts-out ${ZEPHYR_DTS}.new # for debugging and dtc
257--edt-pickle-out ${EDT_PICKLE}
258${EXTRA_GEN_DEFINES_ARGS}
259)
260
261execute_process(
262  COMMAND ${CMD_GEN_DEFINES}
263  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
264  RESULT_VARIABLE ret
265  )
266if(NOT "${ret}" STREQUAL "0")
267  message(STATUS "In: ${PROJECT_BINARY_DIR}, command: ${CMD_GEN_DEFINES}")
268  message(FATAL_ERROR "gen_defines.py failed with return code: ${ret}")
269else()
270  zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT)
271  zephyr_file_copy(${DEVICETREE_GENERATED_H}.new ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT)
272  file(REMOVE ${ZEPHYR_DTS}.new ${DEVICETREE_GENERATED_H}.new)
273  message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}")
274  message(STATUS "Generated devicetree_generated.h: ${DEVICETREE_GENERATED_H}")
275endif()
276
277#
278# Run GEN_DRIVER_KCONFIG_SCRIPT.
279#
280
281execute_process(
282  COMMAND ${PYTHON_EXECUTABLE} ${GEN_DRIVER_KCONFIG_SCRIPT}
283  --kconfig-out ${DTS_KCONFIG}
284  --bindings-dirs ${DTS_ROOT_BINDINGS}
285  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
286  RESULT_VARIABLE ret
287  )
288if(NOT "${ret}" STREQUAL "0")
289  message(FATAL_ERROR "gen_driver_kconfig_dts.py failed with return code: ${ret}")
290endif()
291
292#
293# Run GEN_DTS_CMAKE_SCRIPT.
294#
295
296execute_process(
297  COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_CMAKE_SCRIPT}
298  --edt-pickle ${EDT_PICKLE}
299  --cmake-out ${DTS_CMAKE}
300  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
301  RESULT_VARIABLE ret
302  )
303if(NOT "${ret}" STREQUAL "0")
304  message(FATAL_ERROR "gen_dts_cmake.py failed with return code: ${ret}")
305else()
306  message(STATUS "Including generated dts.cmake file: ${DTS_CMAKE}")
307  include(${DTS_CMAKE})
308endif()
309
310#
311# Run dtc if it was found.
312#
313# This is just to generate warnings and errors; we discard the output.
314#
315
316if(DTC)
317
318set(DTC_WARN_UNIT_ADDR_IF_ENABLED "")
319check_dtc_flag("-Wunique_unit_address_if_enabled" check)
320if (check)
321  set(DTC_WARN_UNIT_ADDR_IF_ENABLED "-Wunique_unit_address_if_enabled")
322endif()
323
324set(DTC_NO_WARN_UNIT_ADDR "")
325check_dtc_flag("-Wno-unique_unit_address" check)
326if (check)
327  set(DTC_NO_WARN_UNIT_ADDR "-Wno-unique_unit_address")
328endif()
329
330set(VALID_EXTRA_DTC_FLAGS "")
331foreach(extra_opt ${EXTRA_DTC_FLAGS})
332  check_dtc_flag(${extra_opt} check)
333  if (check)
334    list(APPEND VALID_EXTRA_DTC_FLAGS ${extra_opt})
335  endif()
336endforeach()
337set(EXTRA_DTC_FLAGS ${VALID_EXTRA_DTC_FLAGS})
338
339execute_process(
340  COMMAND ${DTC}
341  -O dts
342  -o - # Write output to stdout, which we discard below
343  -b 0
344  -E unit_address_vs_reg
345  ${DTC_NO_WARN_UNIT_ADDR}
346  ${DTC_WARN_UNIT_ADDR_IF_ENABLED}
347  ${EXTRA_DTC_FLAGS} # User settable
348  ${ZEPHYR_DTS}
349  OUTPUT_QUIET # Discard stdout
350  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
351  RESULT_VARIABLE ret
352  )
353
354if(NOT "${ret}" STREQUAL "0")
355  message(FATAL_ERROR "command failed with return code: ${ret}")
356endif()
357endif(DTC)
358