433 lines
15 KiB
CMake
433 lines
15 KiB
CMake
# 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)
|
|
|
|
# This generates DT information needed by the CMake APIs.
|
|
set(GEN_DTS_CMAKE_SCRIPT ${DT_SCRIPTS}/gen_dts_cmake.py)
|
|
# The generated information itself, which we include() after
|
|
# creating it.
|
|
set(DTS_CMAKE ${PROJECT_BINARY_DIR}/dts.cmake)
|
|
|
|
# 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}
|
|
${GEN_DTS_CMAKE_SCRIPT}
|
|
)
|
|
|
|
#
|
|
# Run GEN_EDT_SCRIPT.
|
|
#
|
|
|
|
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}
|
|
--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 ${ZEPHYR_DTS}.new ${DEVICETREE_GENERATED_H}.new)
|
|
message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}")
|
|
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()
|
|
|
|
#
|
|
# Run GEN_DTS_CMAKE_SCRIPT.
|
|
#
|
|
# A temporary file is copied to the original file if it differs. This prevents issue such as a
|
|
# cycle when sysbuild is used of configuring and building multiple times due to the dts.cmake file
|
|
# of images having a newer modification time than the sysbuild build.ninja file, despite the
|
|
# output having not changed
|
|
#
|
|
set(dts_cmake_tmp ${DTS_CMAKE}.new)
|
|
|
|
execute_process(
|
|
COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_CMAKE_SCRIPT}
|
|
--edt-pickle ${EDT_PICKLE}
|
|
--cmake-out ${dts_cmake_tmp}
|
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
|
RESULT_VARIABLE ret
|
|
)
|
|
if(NOT "${ret}" STREQUAL "0")
|
|
message(FATAL_ERROR "gen_dts_cmake.py failed with return code: ${ret}")
|
|
else()
|
|
zephyr_file_copy(${dts_cmake_tmp} ${DTS_CMAKE} ONLY_IF_DIFFERENT)
|
|
file(REMOVE ${dts_cmake_tmp})
|
|
set(dts_cmake_tmp)
|
|
message(STATUS "Including generated dts.cmake file: ${DTS_CMAKE}")
|
|
include(${DTS_CMAKE})
|
|
endif()
|
|
|
|
#
|
|
# 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}
|
|
RESULT_VARIABLE ret
|
|
ERROR_VARIABLE stderr
|
|
)
|
|
|
|
if(NOT "${ret}" STREQUAL "0")
|
|
message(FATAL_ERROR "dtc failed with return code: ${ret}")
|
|
elseif(stderr)
|
|
# dtc printed warnings on stderr but did not fail.
|
|
# Display them as CMake warnings to draw attention.
|
|
message(WARNING "dtc raised one or more warnings:\n${stderr}")
|
|
endif()
|
|
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})
|