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#    - DTS_INCLUDE_FILES is set to a ;-list of all devicetree files
61#      used in this build, including transitive includes (the build
62#      system will be regenerated if any of those files change)
63#    - the devicetree extensions in the extensions.cmake module
64#      will be ready for use in other CMake list files that run
65#      after this module
66#
67# Required variables:
68# - BINARY_DIR_INCLUDE_GENERATED: where to put generated include files
69# - DTS_ROOT: a deduplicated list of places where devicetree
70#   implementation files (like bindings, vendor prefixes, etc.) are
71#   found
72# - DTS_ROOT_SYSTEM_INCLUDE_DIRS: set to "PATH1 PATH2 ...",
73#   with one path per potential location where C preprocessor #includes
74#   may be found for devicetree files
75# - KCONFIG_BINARY_DIR: where to put generated Kconfig files
76#
77# Optional variables:
78# - BOARD: board name to use when looking for DTS_SOURCE
79# - BOARD_DIRECTORIES: list of board directories to use when looking for DTS_SOURCE
80# - BOARD_REVISION_STRING: used when looking for a board revision's
81#   devicetree overlay file in one of the BOARD_DIRECTORIES
82# - CMAKE_DTS_PREPROCESSOR: the path to the preprocessor to use
83#   for devicetree files
84# - DTC_OVERLAY_FILE: list of devicetree overlay files which will be
85#   used to modify or extend the base devicetree.
86# - EXTRA_DTC_OVERLAY_FILE: list of extra devicetree overlay files.
87#   This variable is similar to DTC_OVERLAY_FILE but the files in
88#   EXTRA_DTC_OVERLAY_FILE will be applied after DTC_OVERLAY_FILE and
89#   thus files specified by EXTRA_DTC_OVERLAY_FILE have higher precedence.
90# - EXTRA_DTC_FLAGS: list of extra command line options to pass to
91#   dtc when using it to check for additional errors and warnings;
92#   invalid flags are automatically filtered out of the list
93# - DTS_EXTRA_CPPFLAGS: extra command line options to pass to the
94#   C preprocessor when generating the devicetree from DTS_SOURCE
95# - DTS_SOURCE: the devicetree source file to use may be pre-set
96#   with this variable; otherwise, it defaults to
97#   ${BOARD_DIRECTORIES}/<normalized_board_target>.dts
98#
99# Variables set by this module and not mentioned above are for internal
100# use only, and may be removed, renamed, or re-purposed without prior notice.
101
102# The directory containing devicetree related scripts.
103set(DT_SCRIPTS                  ${ZEPHYR_BASE}/scripts/dts)
104
105# This parses and collects the DT information
106set(GEN_EDT_SCRIPT              ${DT_SCRIPTS}/gen_edt.py)
107# This generates DT information needed by the C macro APIs,
108# along with a few other things.
109set(GEN_DEFINES_SCRIPT          ${DT_SCRIPTS}/gen_defines.py)
110# The edtlib.EDT object in pickle format.
111set(EDT_PICKLE                  ${PROJECT_BINARY_DIR}/edt.pickle)
112# The generated file containing the final DTS, for debugging.
113set(ZEPHYR_DTS                  ${PROJECT_BINARY_DIR}/zephyr.dts)
114# The generated C header needed by <zephyr/devicetree.h>
115set(DEVICETREE_GENERATED_H      ${BINARY_DIR_INCLUDE_GENERATED}/devicetree_generated.h)
116# Generated build system internals.
117set(DTS_POST_CPP                ${PROJECT_BINARY_DIR}/zephyr.dts.pre)
118set(DTS_DEPS                    ${PROJECT_BINARY_DIR}/zephyr.dts.d)
119
120# This generates DT information needed by the Kconfig APIs.
121set(GEN_DRIVER_KCONFIG_SCRIPT   ${DT_SCRIPTS}/gen_driver_kconfig_dts.py)
122# Generated Kconfig symbols go here.
123set(DTS_KCONFIG                 ${KCONFIG_BINARY_DIR}/Kconfig.dts)
124
125# The location of a file containing known vendor prefixes, relative to
126# each element of DTS_ROOT. Users can define their own in their own
127# modules.
128set(VENDOR_PREFIXES             dts/bindings/vendor-prefixes.txt)
129
130if(NOT DEFINED DTS_SOURCE)
131  zephyr_build_string(board_string SHORT shortened_board_string
132                      BOARD ${BOARD} BOARD_QUALIFIERS ${BOARD_QUALIFIERS}
133  )
134  foreach(dir ${BOARD_DIRECTORIES})
135    if(EXISTS ${dir}/${shortened_board_string}.dts AND NOT BOARD_${BOARD}_SINGLE_SOC)
136      message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name "
137              "(${shortened_board_string}.dts) not allowed, use '<board>_<soc>.dts' naming"
138      )
139    elseif(EXISTS ${dir}/${board_string}.dts AND EXISTS ${dir}/${shortened_board_string}.dts)
140      message(FATAL_ERROR "Conflicting file names discovered. Cannot use both "
141              "${board_string}.dts and ${shortened_board_string}.dts. "
142              "Please choose one naming style, ${board_string}.dts is recommended."
143      )
144    elseif(EXISTS ${dir}/${board_string}.dts)
145      set(DTS_SOURCE ${dir}/${board_string}.dts)
146    elseif(EXISTS ${dir}/${shortened_board_string}.dts)
147      set(DTS_SOURCE ${dir}/${shortened_board_string}.dts)
148    endif()
149  endforeach()
150endif()
151
152if(EXISTS ${DTS_SOURCE})
153  # We found a devicetree. Append all relevant dts overlays we can find...
154  zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DTS DTS_SOURCE)
155
156  zephyr_file(
157    CONF_FILES ${BOARD_DIRECTORIES}
158    DTS no_rev_suffix_dts_board_overlays
159    BOARD ${BOARD}
160    BOARD_QUALIFIERS ${BOARD_QUALIFIERS}
161  )
162
163  # ...but remove the ones that do not include the revision suffix
164  list(REMOVE_ITEM DTS_SOURCE ${no_rev_suffix_dts_board_overlays})
165else()
166  # If we don't have a devicetree, provide an empty stub
167  set(DTS_SOURCE ${ZEPHYR_BASE}/boards/common/stub.dts)
168endif()
169
170#
171# Find all the DTS files we need to concatenate and preprocess, as
172# well as all the devicetree bindings and vendor prefixes associated
173# with them.
174#
175
176zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} DTS board_extension_dts_files)
177
178set(dts_files
179  ${DTS_SOURCE}
180  ${board_extension_dts_files}
181  ${shield_dts_files}
182  )
183
184if(DTC_OVERLAY_FILE)
185  zephyr_list(TRANSFORM DTC_OVERLAY_FILE NORMALIZE_PATHS
186              OUTPUT_VARIABLE DTC_OVERLAY_FILE_AS_LIST)
187  build_info(devicetree user-files PATH ${DTC_OVERLAY_FILE_AS_LIST})
188  list(APPEND
189    dts_files
190    ${DTC_OVERLAY_FILE_AS_LIST}
191    )
192endif()
193
194if(EXTRA_DTC_OVERLAY_FILE)
195  zephyr_list(TRANSFORM EXTRA_DTC_OVERLAY_FILE NORMALIZE_PATHS
196              OUTPUT_VARIABLE EXTRA_DTC_OVERLAY_FILE_AS_LIST)
197  build_info(devicetree extra-user-files PATH ${EXTRA_DTC_OVERLAY_FILE_AS_LIST})
198  list(APPEND
199    dts_files
200    ${EXTRA_DTC_OVERLAY_FILE_AS_LIST}
201    )
202endif()
203
204set(i 0)
205foreach(dts_file ${dts_files})
206  if(i EQUAL 0)
207    message(STATUS "Found BOARD.dts: ${dts_file}")
208  else()
209    message(STATUS "Found devicetree overlay: ${dts_file}")
210  endif()
211
212  math(EXPR i "${i}+1")
213endforeach()
214
215unset(DTS_ROOT_BINDINGS)
216foreach(dts_root ${DTS_ROOT})
217  set(bindings_path ${dts_root}/dts/bindings)
218  if(EXISTS ${bindings_path})
219    list(APPEND
220      DTS_ROOT_BINDINGS
221      ${bindings_path}
222      )
223  endif()
224
225  set(vendor_prefixes ${dts_root}/${VENDOR_PREFIXES})
226  if(EXISTS ${vendor_prefixes})
227    list(APPEND EXTRA_GEN_EDT_ARGS --vendor-prefixes ${vendor_prefixes})
228  endif()
229endforeach()
230
231# Cache the location of the root bindings so they can be used by
232# scripts which use the build directory.
233set(CACHED_DTS_ROOT_BINDINGS ${DTS_ROOT_BINDINGS} CACHE INTERNAL
234  "DT bindings root directories")
235
236#
237# Run the C preprocessor on the devicetree source, so we can parse it
238# (using the Python devicetree package) in later steps.
239#
240
241# TODO: Cut down on CMake configuration time by avoiding
242# regeneration of devicetree_generated.h on every configure. How
243# challenging is this? Can we cache the dts dependencies?
244
245# Run the preprocessor on the DTS input files.
246if(DEFINED CMAKE_DTS_PREPROCESSOR)
247  set(dts_preprocessor ${CMAKE_DTS_PREPROCESSOR})
248else()
249  set(dts_preprocessor ${CMAKE_C_COMPILER})
250endif()
251zephyr_dt_preprocess(
252  CPP ${dts_preprocessor}
253  SOURCE_FILES ${dts_files}
254  OUT_FILE ${DTS_POST_CPP}
255  DEPS_FILE ${DTS_DEPS}
256  EXTRA_CPPFLAGS ${DTS_EXTRA_CPPFLAGS}
257  INCLUDE_DIRECTORIES ${DTS_ROOT_SYSTEM_INCLUDE_DIRS}
258  WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
259  )
260
261#
262# Make sure we re-run CMake if any devicetree sources or transitive
263# includes change.
264#
265
266# Parse the generated dependency file to find the DT sources that
267# were included, including any transitive includes.
268toolchain_parse_make_rule(${DTS_DEPS}
269  DTS_INCLUDE_FILES # Output parameter
270  )
271
272# Add the results to the list of files that, when change, force the
273# build system to re-run CMake.
274set_property(DIRECTORY APPEND PROPERTY
275  CMAKE_CONFIGURE_DEPENDS
276  ${DTS_INCLUDE_FILES}
277  ${GEN_EDT_SCRIPT}
278  ${GEN_DEFINES_SCRIPT}
279  ${GEN_DRIVER_KCONFIG_SCRIPT}
280  )
281
282#
283# Run GEN_EDT_SCRIPT.
284#
285
286if(WEST_TOPDIR)
287  set(GEN_EDT_WORKSPACE_DIR ${WEST_TOPDIR})
288else()
289  # If West is not available, define the parent directory of ZEPHYR_BASE as
290  # the workspace. This will create comments that reference the files in the
291  # Zephyr tree with a 'zephyr/' prefix.
292  set(GEN_EDT_WORKSPACE_DIR ${ZEPHYR_BASE}/..)
293endif()
294
295string(REPLACE ";" " " EXTRA_DTC_FLAGS_RAW "${EXTRA_DTC_FLAGS}")
296set(CMD_GEN_EDT ${PYTHON_EXECUTABLE} ${GEN_EDT_SCRIPT}
297--dts ${DTS_POST_CPP}
298--dtc-flags '${EXTRA_DTC_FLAGS_RAW}'
299--bindings-dirs ${DTS_ROOT_BINDINGS}
300--workspace-dir ${GEN_EDT_WORKSPACE_DIR}
301--dts-out ${ZEPHYR_DTS}.new # for debugging and dtc
302--edt-pickle-out ${EDT_PICKLE}.new
303${EXTRA_GEN_EDT_ARGS}
304)
305
306execute_process(
307  COMMAND ${CMD_GEN_EDT}
308  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
309  COMMAND_ERROR_IS_FATAL ANY
310  )
311zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT)
312zephyr_file_copy(${EDT_PICKLE}.new ${EDT_PICKLE} ONLY_IF_DIFFERENT)
313file(REMOVE ${ZEPHYR_DTS}.new ${EDT_PICKLE}.new)
314message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}")
315message(STATUS "Generated pickled edt: ${EDT_PICKLE}")
316
317#
318# Run GEN_DEFINES_SCRIPT.
319#
320
321set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT}
322--header-out ${DEVICETREE_GENERATED_H}.new
323--edt-pickle ${EDT_PICKLE}
324${EXTRA_GEN_DEFINES_ARGS}
325)
326
327execute_process(
328  COMMAND ${CMD_GEN_DEFINES}
329  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
330  COMMAND_ERROR_IS_FATAL ANY
331  )
332zephyr_file_copy(${DEVICETREE_GENERATED_H}.new ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT)
333file(REMOVE ${DEVICETREE_GENERATED_H}.new)
334message(STATUS "Generated devicetree_generated.h: ${DEVICETREE_GENERATED_H}")
335
336#
337# Run GEN_DRIVER_KCONFIG_SCRIPT.
338#
339
340execute_process(
341  COMMAND ${PYTHON_EXECUTABLE} ${GEN_DRIVER_KCONFIG_SCRIPT}
342  --kconfig-out ${DTS_KCONFIG}
343  --bindings-dirs ${DTS_ROOT_BINDINGS}
344  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
345  RESULT_VARIABLE ret
346  )
347if(NOT "${ret}" STREQUAL "0")
348  message(FATAL_ERROR "gen_driver_kconfig_dts.py failed with return code: ${ret}")
349endif()
350
351#
352# Import devicetree contents into CMake.
353# This enables the CMake dt_* API.
354#
355
356add_custom_target(devicetree_target)
357zephyr_dt_import(EDT_PICKLE_FILE ${EDT_PICKLE} TARGET devicetree_target)
358
359#
360# Run dtc if it was found.
361#
362# This is just to generate warnings and errors; we discard the output.
363#
364
365if(DTC)
366
367set(DTC_WARN_UNIT_ADDR_IF_ENABLED "")
368check_dtc_flag("-Wunique_unit_address_if_enabled" check)
369if (check)
370  set(DTC_WARN_UNIT_ADDR_IF_ENABLED "-Wunique_unit_address_if_enabled")
371endif()
372
373set(DTC_NO_WARN_UNIT_ADDR "")
374check_dtc_flag("-Wno-unique_unit_address" check)
375if (check)
376  set(DTC_NO_WARN_UNIT_ADDR "-Wno-unique_unit_address")
377endif()
378
379set(VALID_EXTRA_DTC_FLAGS "")
380foreach(extra_opt ${EXTRA_DTC_FLAGS})
381  check_dtc_flag(${extra_opt} check)
382  if (check)
383    list(APPEND VALID_EXTRA_DTC_FLAGS ${extra_opt})
384  endif()
385endforeach()
386set(EXTRA_DTC_FLAGS ${VALID_EXTRA_DTC_FLAGS})
387
388execute_process(
389  COMMAND ${DTC}
390  -O dts
391  -o - # Write output to stdout, which we discard below
392  -b 0
393  -E unit_address_vs_reg
394  ${DTC_NO_WARN_UNIT_ADDR}
395  ${DTC_WARN_UNIT_ADDR_IF_ENABLED}
396  ${EXTRA_DTC_FLAGS} # User settable
397  ${ZEPHYR_DTS}
398  OUTPUT_QUIET # Discard stdout
399  WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
400  COMMAND_ERROR_IS_FATAL ANY
401  )
402
403endif(DTC)
404
405build_info(devicetree files PATH ${dts_files})
406build_info(devicetree include-dirs PATH ${DTS_ROOT_SYSTEM_INCLUDE_DIRS})
407build_info(devicetree bindings-dirs PATH ${DTS_ROOT_BINDINGS})
408