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# This generates DT information needed by the CMake APIs. 126set(GEN_DTS_CMAKE_SCRIPT ${DT_SCRIPTS}/gen_dts_cmake.py) 127# The generated information itself, which we include() after 128# creating it. 129set(DTS_CMAKE ${PROJECT_BINARY_DIR}/dts.cmake) 130 131# The location of a file containing known vendor prefixes, relative to 132# each element of DTS_ROOT. Users can define their own in their own 133# modules. 134set(VENDOR_PREFIXES dts/bindings/vendor-prefixes.txt) 135 136if(NOT DEFINED DTS_SOURCE) 137 zephyr_build_string(board_string SHORT shortened_board_string 138 BOARD ${BOARD} BOARD_QUALIFIERS ${BOARD_QUALIFIERS} 139 ) 140 foreach(dir ${BOARD_DIRECTORIES}) 141 if(EXISTS ${dir}/${shortened_board_string}.dts AND NOT BOARD_${BOARD}_SINGLE_SOC) 142 message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name " 143 "(${shortened_board_string}.dts) not allowed, use '<board>_<soc>.dts' naming" 144 ) 145 elseif(EXISTS ${dir}/${board_string}.dts AND EXISTS ${dir}/${shortened_board_string}.dts) 146 message(FATAL_ERROR "Conflicting file names discovered. Cannot use both " 147 "${board_string}.dts and ${shortened_board_string}.dts. " 148 "Please choose one naming style, ${board_string}.dts is recommended." 149 ) 150 elseif(EXISTS ${dir}/${board_string}.dts) 151 set(DTS_SOURCE ${dir}/${board_string}.dts) 152 elseif(EXISTS ${dir}/${shortened_board_string}.dts) 153 set(DTS_SOURCE ${dir}/${shortened_board_string}.dts) 154 endif() 155 endforeach() 156endif() 157 158if(EXISTS ${DTS_SOURCE}) 159 # We found a devicetree. Append all relevant dts overlays we can find... 160 zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DTS DTS_SOURCE) 161 162 zephyr_file( 163 CONF_FILES ${BOARD_DIRECTORIES} 164 DTS no_rev_suffix_dts_board_overlays 165 BOARD ${BOARD} 166 BOARD_QUALIFIERS ${BOARD_QUALIFIERS} 167 ) 168 169 # ...but remove the ones that do not include the revision suffix 170 list(REMOVE_ITEM DTS_SOURCE ${no_rev_suffix_dts_board_overlays}) 171else() 172 # If we don't have a devicetree, provide an empty stub 173 set(DTS_SOURCE ${ZEPHYR_BASE}/boards/common/stub.dts) 174endif() 175 176# 177# Find all the DTS files we need to concatenate and preprocess, as 178# well as all the devicetree bindings and vendor prefixes associated 179# with them. 180# 181 182zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} DTS board_extension_dts_files) 183 184set(dts_files 185 ${DTS_SOURCE} 186 ${board_extension_dts_files} 187 ${shield_dts_files} 188 ) 189 190if(DTC_OVERLAY_FILE) 191 zephyr_list(TRANSFORM DTC_OVERLAY_FILE NORMALIZE_PATHS 192 OUTPUT_VARIABLE DTC_OVERLAY_FILE_AS_LIST) 193 build_info(devicetree user-files PATH ${DTC_OVERLAY_FILE_AS_LIST}) 194 list(APPEND 195 dts_files 196 ${DTC_OVERLAY_FILE_AS_LIST} 197 ) 198endif() 199 200if(EXTRA_DTC_OVERLAY_FILE) 201 zephyr_list(TRANSFORM EXTRA_DTC_OVERLAY_FILE NORMALIZE_PATHS 202 OUTPUT_VARIABLE EXTRA_DTC_OVERLAY_FILE_AS_LIST) 203 build_info(devicetree extra-user-files PATH ${EXTRA_DTC_OVERLAY_FILE_AS_LIST}) 204 list(APPEND 205 dts_files 206 ${EXTRA_DTC_OVERLAY_FILE_AS_LIST} 207 ) 208endif() 209 210set(i 0) 211foreach(dts_file ${dts_files}) 212 if(i EQUAL 0) 213 message(STATUS "Found BOARD.dts: ${dts_file}") 214 else() 215 message(STATUS "Found devicetree overlay: ${dts_file}") 216 endif() 217 218 math(EXPR i "${i}+1") 219endforeach() 220 221unset(DTS_ROOT_BINDINGS) 222foreach(dts_root ${DTS_ROOT}) 223 set(bindings_path ${dts_root}/dts/bindings) 224 if(EXISTS ${bindings_path}) 225 list(APPEND 226 DTS_ROOT_BINDINGS 227 ${bindings_path} 228 ) 229 endif() 230 231 set(vendor_prefixes ${dts_root}/${VENDOR_PREFIXES}) 232 if(EXISTS ${vendor_prefixes}) 233 list(APPEND EXTRA_GEN_EDT_ARGS --vendor-prefixes ${vendor_prefixes}) 234 endif() 235endforeach() 236 237# Cache the location of the root bindings so they can be used by 238# scripts which use the build directory. 239set(CACHED_DTS_ROOT_BINDINGS ${DTS_ROOT_BINDINGS} CACHE INTERNAL 240 "DT bindings root directories") 241 242# 243# Run the C preprocessor on the devicetree source, so we can parse it 244# (using the Python devicetree package) in later steps. 245# 246 247# TODO: Cut down on CMake configuration time by avoiding 248# regeneration of devicetree_generated.h on every configure. How 249# challenging is this? Can we cache the dts dependencies? 250 251# Run the preprocessor on the DTS input files. 252if(DEFINED CMAKE_DTS_PREPROCESSOR) 253 set(dts_preprocessor ${CMAKE_DTS_PREPROCESSOR}) 254else() 255 set(dts_preprocessor ${CMAKE_C_COMPILER}) 256endif() 257zephyr_dt_preprocess( 258 CPP ${dts_preprocessor} 259 SOURCE_FILES ${dts_files} 260 OUT_FILE ${DTS_POST_CPP} 261 DEPS_FILE ${DTS_DEPS} 262 EXTRA_CPPFLAGS ${DTS_EXTRA_CPPFLAGS} 263 INCLUDE_DIRECTORIES ${DTS_ROOT_SYSTEM_INCLUDE_DIRS} 264 WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR} 265 ) 266 267# 268# Make sure we re-run CMake if any devicetree sources or transitive 269# includes change. 270# 271 272# Parse the generated dependency file to find the DT sources that 273# were included, including any transitive includes. 274toolchain_parse_make_rule(${DTS_DEPS} 275 DTS_INCLUDE_FILES # Output parameter 276 ) 277 278# Add the results to the list of files that, when change, force the 279# build system to re-run CMake. 280set_property(DIRECTORY APPEND PROPERTY 281 CMAKE_CONFIGURE_DEPENDS 282 ${DTS_INCLUDE_FILES} 283 ${GEN_EDT_SCRIPT} 284 ${GEN_DEFINES_SCRIPT} 285 ${GEN_DRIVER_KCONFIG_SCRIPT} 286 ${GEN_DTS_CMAKE_SCRIPT} 287 ) 288 289# 290# Run GEN_EDT_SCRIPT. 291# 292 293string(REPLACE ";" " " EXTRA_DTC_FLAGS_RAW "${EXTRA_DTC_FLAGS}") 294set(CMD_GEN_EDT ${PYTHON_EXECUTABLE} ${GEN_EDT_SCRIPT} 295--dts ${DTS_POST_CPP} 296--dtc-flags '${EXTRA_DTC_FLAGS_RAW}' 297--bindings-dirs ${DTS_ROOT_BINDINGS} 298--dts-out ${ZEPHYR_DTS}.new # for debugging and dtc 299--edt-pickle-out ${EDT_PICKLE}.new 300${EXTRA_GEN_EDT_ARGS} 301) 302 303execute_process( 304 COMMAND ${CMD_GEN_EDT} 305 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 306 COMMAND_ERROR_IS_FATAL ANY 307 ) 308zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT) 309zephyr_file_copy(${EDT_PICKLE}.new ${EDT_PICKLE} ONLY_IF_DIFFERENT) 310file(REMOVE ${ZEPHYR_DTS}.new ${EDT_PICKLE}.new) 311message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}") 312message(STATUS "Generated pickled edt: ${EDT_PICKLE}") 313 314# 315# Run GEN_DEFINES_SCRIPT. 316# 317 318set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT} 319--header-out ${DEVICETREE_GENERATED_H}.new 320--edt-pickle ${EDT_PICKLE} 321${EXTRA_GEN_DEFINES_ARGS} 322) 323 324execute_process( 325 COMMAND ${CMD_GEN_DEFINES} 326 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 327 COMMAND_ERROR_IS_FATAL ANY 328 ) 329zephyr_file_copy(${DEVICETREE_GENERATED_H}.new ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT) 330file(REMOVE ${ZEPHYR_DTS}.new ${DEVICETREE_GENERATED_H}.new) 331message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}") 332message(STATUS "Generated devicetree_generated.h: ${DEVICETREE_GENERATED_H}") 333 334# 335# Run GEN_DRIVER_KCONFIG_SCRIPT. 336# 337 338execute_process( 339 COMMAND ${PYTHON_EXECUTABLE} ${GEN_DRIVER_KCONFIG_SCRIPT} 340 --kconfig-out ${DTS_KCONFIG} 341 --bindings-dirs ${DTS_ROOT_BINDINGS} 342 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 343 RESULT_VARIABLE ret 344 ) 345if(NOT "${ret}" STREQUAL "0") 346 message(FATAL_ERROR "gen_driver_kconfig_dts.py failed with return code: ${ret}") 347endif() 348 349# 350# Run GEN_DTS_CMAKE_SCRIPT. 351# 352# A temporary file is copied to the original file if it differs. This prevents issue such as a 353# cycle when sysbuild is used of configuring and building multiple times due to the dts.cmake file 354# of images having a newer modification time than the sysbuild build.ninja file, despite the 355# output having not changed 356# 357set(dts_cmake_tmp ${DTS_CMAKE}.new) 358 359execute_process( 360 COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_CMAKE_SCRIPT} 361 --edt-pickle ${EDT_PICKLE} 362 --cmake-out ${dts_cmake_tmp} 363 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 364 RESULT_VARIABLE ret 365 ) 366if(NOT "${ret}" STREQUAL "0") 367 message(FATAL_ERROR "gen_dts_cmake.py failed with return code: ${ret}") 368else() 369 zephyr_file_copy(${dts_cmake_tmp} ${DTS_CMAKE} ONLY_IF_DIFFERENT) 370 file(REMOVE ${dts_cmake_tmp}) 371 set(dts_cmake_tmp) 372 message(STATUS "Including generated dts.cmake file: ${DTS_CMAKE}") 373 include(${DTS_CMAKE}) 374endif() 375 376# 377# Run dtc if it was found. 378# 379# This is just to generate warnings and errors; we discard the output. 380# 381 382if(DTC) 383 384set(DTC_WARN_UNIT_ADDR_IF_ENABLED "") 385check_dtc_flag("-Wunique_unit_address_if_enabled" check) 386if (check) 387 set(DTC_WARN_UNIT_ADDR_IF_ENABLED "-Wunique_unit_address_if_enabled") 388endif() 389 390set(DTC_NO_WARN_UNIT_ADDR "") 391check_dtc_flag("-Wno-unique_unit_address" check) 392if (check) 393 set(DTC_NO_WARN_UNIT_ADDR "-Wno-unique_unit_address") 394endif() 395 396set(VALID_EXTRA_DTC_FLAGS "") 397foreach(extra_opt ${EXTRA_DTC_FLAGS}) 398 check_dtc_flag(${extra_opt} check) 399 if (check) 400 list(APPEND VALID_EXTRA_DTC_FLAGS ${extra_opt}) 401 endif() 402endforeach() 403set(EXTRA_DTC_FLAGS ${VALID_EXTRA_DTC_FLAGS}) 404 405execute_process( 406 COMMAND ${DTC} 407 -O dts 408 -o - # Write output to stdout, which we discard below 409 -b 0 410 -E unit_address_vs_reg 411 ${DTC_NO_WARN_UNIT_ADDR} 412 ${DTC_WARN_UNIT_ADDR_IF_ENABLED} 413 ${EXTRA_DTC_FLAGS} # User settable 414 ${ZEPHYR_DTS} 415 OUTPUT_QUIET # Discard stdout 416 WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 417 RESULT_VARIABLE ret 418 ERROR_VARIABLE stderr 419 ) 420 421if(NOT "${ret}" STREQUAL "0") 422 message(FATAL_ERROR "dtc failed with return code: ${ret}") 423elseif(stderr) 424 # dtc printed warnings on stderr but did not fail. 425 # Display them as CMake warnings to draw attention. 426 message(WARNING "dtc raised one or more warnings:\n${stderr}") 427endif() 428endif(DTC) 429 430build_info(devicetree files PATH ${dts_files}) 431build_info(devicetree include-dirs PATH ${DTS_ROOT_SYSTEM_INCLUDE_DIRS}) 432build_info(devicetree bindings-dirs PATH ${DTS_ROOT_BINDINGS}) 433