|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | include_guard(GLOBAL) | 
|  |  | 
|  | include(extensions) | 
|  | include(python) | 
|  | include(boards) | 
|  | include(pre_dt) | 
|  | find_package(HostTools) | 
|  | find_package(Dtc 1.4.6) | 
|  |  | 
|  | # This module makes information from the devicetree available to | 
|  | # various build stages, as well as to other arbitrary Python scripts: | 
|  | # | 
|  | #   - To Zephyr and application source code files, as a C macro API | 
|  | #     defined in <zephyr/devicetree.h> | 
|  | # | 
|  | #   - To other arbitrary Python scripts (like twister) using a | 
|  | #     serialized edtlib.EDT object in Python's pickle format | 
|  | #     (https://docs.python.org/3/library/pickle.html) | 
|  | # | 
|  | #   - To users as a final devicetree source (DTS) file which can | 
|  | #     be used for debugging | 
|  | # | 
|  | #   - To CMake files, after this module has finished running, using | 
|  | #     devicetree extensions defined in cmake/modules/extensions.cmake | 
|  | # | 
|  | #   - To Kconfig files, both using some Kconfig symbols we generate | 
|  | #     here as well as the extension functions defined in | 
|  | #     scripts/kconfig/kconfigfunctions.py | 
|  | # | 
|  | # See the specific API documentation for each of these cases for more | 
|  | # information on what is currently available to you. | 
|  | # | 
|  | # We rely on the C preprocessor, the devicetree python package, and | 
|  | # files in scripts/dts to make all this work. We also optionally will | 
|  | # run the dtc tool if it is found, in order to catch any additional | 
|  | # warnings or errors it generates. | 
|  | # | 
|  | # Outcome: | 
|  | # | 
|  | # 1. The following has happened: | 
|  | # | 
|  | #    - The pre_dt module has been included; refer to its outcome | 
|  | #      section for more information on the consequences | 
|  | #    - DTS_SOURCE: set to the path to the devicetree file which | 
|  | #      was used, if one was provided or found | 
|  | #    - ${BINARY_DIR_INCLUDE_GENERATED}/devicetree_generated.h exists | 
|  | # | 
|  | # 2. The following has happened if a devicetree was found and | 
|  | #    no errors occurred: | 
|  | # | 
|  | #    - CACHED_DTS_ROOT_BINDINGS is set in the cache to the | 
|  | #      value of DTS_ROOT_BINDINGS | 
|  | #    - DTS_ROOT_BINDINGS is set to a ;-list of locations where DT | 
|  | #      bindings were found | 
|  | #    - ${PROJECT_BINARY_DIR}/zephyr.dts exists | 
|  | #    - ${PROJECT_BINARY_DIR}/edt.pickle exists | 
|  | #    - ${KCONFIG_BINARY_DIR}/Kconfig.dts exists | 
|  | #    - DTS_INCLUDE_FILES is set to a ;-list of all devicetree files | 
|  | #      used in this build, including transitive includes (the build | 
|  | #      system will be regenerated if any of those files change) | 
|  | #    - the devicetree extensions in the extensions.cmake module | 
|  | #      will be ready for use in other CMake list files that run | 
|  | #      after this module | 
|  | # | 
|  | # Required variables: | 
|  | # - BINARY_DIR_INCLUDE_GENERATED: where to put generated include files | 
|  | # - DTS_ROOT: a deduplicated list of places where devicetree | 
|  | #   implementation files (like bindings, vendor prefixes, etc.) are | 
|  | #   found | 
|  | # - DTS_ROOT_SYSTEM_INCLUDE_DIRS: set to "PATH1 PATH2 ...", | 
|  | #   with one path per potential location where C preprocessor #includes | 
|  | #   may be found for devicetree files | 
|  | # - KCONFIG_BINARY_DIR: where to put generated Kconfig files | 
|  | # | 
|  | # Optional variables: | 
|  | # - BOARD: board name to use when looking for DTS_SOURCE | 
|  | # - BOARD_DIRECTORIES: list of board directories to use when looking for DTS_SOURCE | 
|  | # - BOARD_REVISION_STRING: used when looking for a board revision's | 
|  | #   devicetree overlay file in one of the BOARD_DIRECTORIES | 
|  | # - CMAKE_DTS_PREPROCESSOR: the path to the preprocessor to use | 
|  | #   for devicetree files | 
|  | # - DTC_OVERLAY_FILE: list of devicetree overlay files which will be | 
|  | #   used to modify or extend the base devicetree. | 
|  | # - EXTRA_DTC_OVERLAY_FILE: list of extra devicetree overlay files. | 
|  | #   This variable is similar to DTC_OVERLAY_FILE but the files in | 
|  | #   EXTRA_DTC_OVERLAY_FILE will be applied after DTC_OVERLAY_FILE and | 
|  | #   thus files specified by EXTRA_DTC_OVERLAY_FILE have higher precedence. | 
|  | # - EXTRA_DTC_FLAGS: list of extra command line options to pass to | 
|  | #   dtc when using it to check for additional errors and warnings; | 
|  | #   invalid flags are automatically filtered out of the list | 
|  | # - DTS_EXTRA_CPPFLAGS: extra command line options to pass to the | 
|  | #   C preprocessor when generating the devicetree from DTS_SOURCE | 
|  | # - DTS_SOURCE: the devicetree source file to use may be pre-set | 
|  | #   with this variable; otherwise, it defaults to | 
|  | #   ${BOARD_DIRECTORIES}/<normalized_board_target>.dts | 
|  | # | 
|  | # Variables set by this module and not mentioned above are for internal | 
|  | # use only, and may be removed, renamed, or re-purposed without prior notice. | 
|  |  | 
|  | # The directory containing devicetree related scripts. | 
|  | set(DT_SCRIPTS                  ${ZEPHYR_BASE}/scripts/dts) | 
|  |  | 
|  | # This parses and collects the DT information | 
|  | set(GEN_EDT_SCRIPT              ${DT_SCRIPTS}/gen_edt.py) | 
|  | # This generates DT information needed by the C macro APIs, | 
|  | # along with a few other things. | 
|  | set(GEN_DEFINES_SCRIPT          ${DT_SCRIPTS}/gen_defines.py) | 
|  | # The edtlib.EDT object in pickle format. | 
|  | set(EDT_PICKLE                  ${PROJECT_BINARY_DIR}/edt.pickle) | 
|  | # The generated file containing the final DTS, for debugging. | 
|  | set(ZEPHYR_DTS                  ${PROJECT_BINARY_DIR}/zephyr.dts) | 
|  | # The generated C header needed by <zephyr/devicetree.h> | 
|  | set(DEVICETREE_GENERATED_H      ${BINARY_DIR_INCLUDE_GENERATED}/devicetree_generated.h) | 
|  | # Generated build system internals. | 
|  | set(DTS_POST_CPP                ${PROJECT_BINARY_DIR}/zephyr.dts.pre) | 
|  | set(DTS_DEPS                    ${PROJECT_BINARY_DIR}/zephyr.dts.d) | 
|  |  | 
|  | # This generates DT information needed by the Kconfig APIs. | 
|  | set(GEN_DRIVER_KCONFIG_SCRIPT   ${DT_SCRIPTS}/gen_driver_kconfig_dts.py) | 
|  | # Generated Kconfig symbols go here. | 
|  | set(DTS_KCONFIG                 ${KCONFIG_BINARY_DIR}/Kconfig.dts) | 
|  |  | 
|  | # The location of a file containing known vendor prefixes, relative to | 
|  | # each element of DTS_ROOT. Users can define their own in their own | 
|  | # modules. | 
|  | set(VENDOR_PREFIXES             dts/bindings/vendor-prefixes.txt) | 
|  |  | 
|  | if(NOT DEFINED DTS_SOURCE) | 
|  | zephyr_build_string(board_string SHORT shortened_board_string | 
|  | BOARD ${BOARD} BOARD_QUALIFIERS ${BOARD_QUALIFIERS} | 
|  | ) | 
|  | foreach(dir ${BOARD_DIRECTORIES}) | 
|  | if(EXISTS ${dir}/${shortened_board_string}.dts AND NOT BOARD_${BOARD}_SINGLE_SOC) | 
|  | message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name " | 
|  | "(${shortened_board_string}.dts) not allowed, use '<board>_<soc>.dts' naming" | 
|  | ) | 
|  | elseif(EXISTS ${dir}/${board_string}.dts AND EXISTS ${dir}/${shortened_board_string}.dts) | 
|  | message(FATAL_ERROR "Conflicting file names discovered. Cannot use both " | 
|  | "${board_string}.dts and ${shortened_board_string}.dts. " | 
|  | "Please choose one naming style, ${board_string}.dts is recommended." | 
|  | ) | 
|  | elseif(EXISTS ${dir}/${board_string}.dts) | 
|  | set(DTS_SOURCE ${dir}/${board_string}.dts) | 
|  | elseif(EXISTS ${dir}/${shortened_board_string}.dts) | 
|  | set(DTS_SOURCE ${dir}/${shortened_board_string}.dts) | 
|  | endif() | 
|  | endforeach() | 
|  | endif() | 
|  |  | 
|  | if(EXISTS ${DTS_SOURCE}) | 
|  | # We found a devicetree. Append all relevant dts overlays we can find... | 
|  | zephyr_file(CONF_FILES ${BOARD_DIRECTORIES} DTS DTS_SOURCE) | 
|  |  | 
|  | zephyr_file( | 
|  | CONF_FILES ${BOARD_DIRECTORIES} | 
|  | DTS no_rev_suffix_dts_board_overlays | 
|  | BOARD ${BOARD} | 
|  | BOARD_QUALIFIERS ${BOARD_QUALIFIERS} | 
|  | ) | 
|  |  | 
|  | # ...but remove the ones that do not include the revision suffix | 
|  | list(REMOVE_ITEM DTS_SOURCE ${no_rev_suffix_dts_board_overlays}) | 
|  | else() | 
|  | # If we don't have a devicetree, provide an empty stub | 
|  | set(DTS_SOURCE ${ZEPHYR_BASE}/boards/common/stub.dts) | 
|  | endif() | 
|  |  | 
|  | # | 
|  | # Find all the DTS files we need to concatenate and preprocess, as | 
|  | # well as all the devicetree bindings and vendor prefixes associated | 
|  | # with them. | 
|  | # | 
|  |  | 
|  | zephyr_file(CONF_FILES ${BOARD_EXTENSION_DIRS} DTS board_extension_dts_files) | 
|  |  | 
|  | set(dts_files | 
|  | ${DTS_SOURCE} | 
|  | ${board_extension_dts_files} | 
|  | ${shield_dts_files} | 
|  | ) | 
|  |  | 
|  | if(DTC_OVERLAY_FILE) | 
|  | zephyr_list(TRANSFORM DTC_OVERLAY_FILE NORMALIZE_PATHS | 
|  | OUTPUT_VARIABLE DTC_OVERLAY_FILE_AS_LIST) | 
|  | build_info(devicetree user-files PATH ${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) | 
|  | build_info(devicetree extra-user-files PATH ${EXTRA_DTC_OVERLAY_FILE_AS_LIST}) | 
|  | list(APPEND | 
|  | dts_files | 
|  | ${EXTRA_DTC_OVERLAY_FILE_AS_LIST} | 
|  | ) | 
|  | endif() | 
|  |  | 
|  | set(i 0) | 
|  | foreach(dts_file ${dts_files}) | 
|  | if(i EQUAL 0) | 
|  | message(STATUS "Found BOARD.dts: ${dts_file}") | 
|  | else() | 
|  | message(STATUS "Found devicetree overlay: ${dts_file}") | 
|  | endif() | 
|  |  | 
|  | math(EXPR i "${i}+1") | 
|  | endforeach() | 
|  |  | 
|  | 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_EDT_ARGS --vendor-prefixes ${vendor_prefixes}) | 
|  | endif() | 
|  | endforeach() | 
|  |  | 
|  | # Cache the location of the root bindings so they can be used by | 
|  | # scripts which use the build directory. | 
|  | set(CACHED_DTS_ROOT_BINDINGS ${DTS_ROOT_BINDINGS} CACHE INTERNAL | 
|  | "DT bindings root directories") | 
|  |  | 
|  | # | 
|  | # Run the C preprocessor on the devicetree source, so we can parse it | 
|  | # (using the Python devicetree package) in later steps. | 
|  | # | 
|  |  | 
|  | # TODO: Cut down on CMake configuration time by avoiding | 
|  | # regeneration of devicetree_generated.h on every configure. How | 
|  | # challenging is this? Can we cache the dts dependencies? | 
|  |  | 
|  | # Run the preprocessor on the DTS input files. | 
|  | if(DEFINED CMAKE_DTS_PREPROCESSOR) | 
|  | set(dts_preprocessor ${CMAKE_DTS_PREPROCESSOR}) | 
|  | else() | 
|  | set(dts_preprocessor ${CMAKE_C_COMPILER}) | 
|  | endif() | 
|  | 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} | 
|  | ) | 
|  |  | 
|  | # | 
|  | # Make sure we re-run CMake if any devicetree sources or transitive | 
|  | # includes change. | 
|  | # | 
|  |  | 
|  | # Parse the generated dependency file to find the DT sources that | 
|  | # were included, including any transitive includes. | 
|  | toolchain_parse_make_rule(${DTS_DEPS} | 
|  | DTS_INCLUDE_FILES # Output parameter | 
|  | ) | 
|  |  | 
|  | # Add the results to the list of files that, when change, force the | 
|  | # build system to re-run CMake. | 
|  | set_property(DIRECTORY APPEND PROPERTY | 
|  | CMAKE_CONFIGURE_DEPENDS | 
|  | ${DTS_INCLUDE_FILES} | 
|  | ${GEN_EDT_SCRIPT} | 
|  | ${GEN_DEFINES_SCRIPT} | 
|  | ${GEN_DRIVER_KCONFIG_SCRIPT} | 
|  | ) | 
|  |  | 
|  | # | 
|  | # Run GEN_EDT_SCRIPT. | 
|  | # | 
|  |  | 
|  | if(WEST_TOPDIR) | 
|  | set(GEN_EDT_WORKSPACE_DIR ${WEST_TOPDIR}) | 
|  | else() | 
|  | # If West is not available, define the parent directory of ZEPHYR_BASE as | 
|  | # the workspace. This will create comments that reference the files in the | 
|  | # Zephyr tree with a 'zephyr/' prefix. | 
|  | set(GEN_EDT_WORKSPACE_DIR ${ZEPHYR_BASE}/..) | 
|  | endif() | 
|  |  | 
|  | string(REPLACE ";" " " EXTRA_DTC_FLAGS_RAW "${EXTRA_DTC_FLAGS}") | 
|  | set(CMD_GEN_EDT ${PYTHON_EXECUTABLE} ${GEN_EDT_SCRIPT} | 
|  | --dts ${DTS_POST_CPP} | 
|  | --dtc-flags '${EXTRA_DTC_FLAGS_RAW}' | 
|  | --bindings-dirs ${DTS_ROOT_BINDINGS} | 
|  | --workspace-dir ${GEN_EDT_WORKSPACE_DIR} | 
|  | --dts-out ${ZEPHYR_DTS}.new # for debugging and dtc | 
|  | --edt-pickle-out ${EDT_PICKLE}.new | 
|  | ${EXTRA_GEN_EDT_ARGS} | 
|  | ) | 
|  |  | 
|  | execute_process( | 
|  | COMMAND ${CMD_GEN_EDT} | 
|  | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} | 
|  | COMMAND_ERROR_IS_FATAL ANY | 
|  | ) | 
|  | zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT) | 
|  | zephyr_file_copy(${EDT_PICKLE}.new ${EDT_PICKLE} ONLY_IF_DIFFERENT) | 
|  | file(REMOVE ${ZEPHYR_DTS}.new ${EDT_PICKLE}.new) | 
|  | message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}") | 
|  | message(STATUS "Generated pickled edt: ${EDT_PICKLE}") | 
|  |  | 
|  | # | 
|  | # Run GEN_DEFINES_SCRIPT. | 
|  | # | 
|  |  | 
|  | set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT} | 
|  | --header-out ${DEVICETREE_GENERATED_H}.new | 
|  | --edt-pickle ${EDT_PICKLE} | 
|  | ${EXTRA_GEN_DEFINES_ARGS} | 
|  | ) | 
|  |  | 
|  | execute_process( | 
|  | COMMAND ${CMD_GEN_DEFINES} | 
|  | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} | 
|  | COMMAND_ERROR_IS_FATAL ANY | 
|  | ) | 
|  | zephyr_file_copy(${DEVICETREE_GENERATED_H}.new ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT) | 
|  | file(REMOVE ${DEVICETREE_GENERATED_H}.new) | 
|  | message(STATUS "Generated devicetree_generated.h: ${DEVICETREE_GENERATED_H}") | 
|  |  | 
|  | # | 
|  | # Run GEN_DRIVER_KCONFIG_SCRIPT. | 
|  | # | 
|  |  | 
|  | 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 | 
|  | ) | 
|  | if(NOT "${ret}" STREQUAL "0") | 
|  | message(FATAL_ERROR "gen_driver_kconfig_dts.py failed with return code: ${ret}") | 
|  | endif() | 
|  |  | 
|  | # | 
|  | # Import devicetree contents into CMake. | 
|  | # This enables the CMake dt_* API. | 
|  | # | 
|  |  | 
|  | add_custom_target(devicetree_target) | 
|  | zephyr_dt_import(EDT_PICKLE_FILE ${EDT_PICKLE} TARGET devicetree_target) | 
|  |  | 
|  | # | 
|  | # Run dtc if it was found. | 
|  | # | 
|  | # This is just to generate warnings and errors; we discard the output. | 
|  | # | 
|  |  | 
|  | if(DTC) | 
|  |  | 
|  | set(DTC_WARN_UNIT_ADDR_IF_ENABLED "") | 
|  | check_dtc_flag("-Wunique_unit_address_if_enabled" check) | 
|  | if (check) | 
|  | set(DTC_WARN_UNIT_ADDR_IF_ENABLED "-Wunique_unit_address_if_enabled") | 
|  | endif() | 
|  |  | 
|  | set(DTC_NO_WARN_UNIT_ADDR "") | 
|  | check_dtc_flag("-Wno-unique_unit_address" check) | 
|  | if (check) | 
|  | set(DTC_NO_WARN_UNIT_ADDR "-Wno-unique_unit_address") | 
|  | endif() | 
|  |  | 
|  | set(VALID_EXTRA_DTC_FLAGS "") | 
|  | foreach(extra_opt ${EXTRA_DTC_FLAGS}) | 
|  | check_dtc_flag(${extra_opt} check) | 
|  | if (check) | 
|  | list(APPEND VALID_EXTRA_DTC_FLAGS ${extra_opt}) | 
|  | endif() | 
|  | endforeach() | 
|  | set(EXTRA_DTC_FLAGS ${VALID_EXTRA_DTC_FLAGS}) | 
|  |  | 
|  | 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} | 
|  | COMMAND_ERROR_IS_FATAL ANY | 
|  | ) | 
|  |  | 
|  | endif(DTC) | 
|  |  | 
|  | build_info(devicetree files PATH ${dts_files}) | 
|  | build_info(devicetree include-dirs PATH ${DTS_ROOT_SYSTEM_INCLUDE_DIRS}) | 
|  | build_info(devicetree bindings-dirs PATH ${DTS_ROOT_BINDINGS}) |