zephyr/cmake/modules/dts.cmake

369 lines
12 KiB
CMake
Raw Normal View History

# 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)
# Zephyr code is usually configured using devicetree, but this is
# still technically optional (see e.g. CONFIG_HAS_DTS).
#
# 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
# - the build system will be regenerated if any devicetree files
# used in this build change, including transitive includes
# - 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
# - KCONFIG_BINARY_DIR: where to put generated Kconfig files
#
# Optional variables:
# - BOARD: board name to use when looking for DTS_SOURCE
# - BOARD_DIR: board directory to use when looking for DTS_SOURCE
# - BOARD_REVISION_STRING: used when looking for a board revision's
# devicetree overlay file in BOARD_DIR
# - 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 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_DIR}/${BOARD}.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 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)
#
# Halt execution early if there is no devicetree.
#
# TODO: What to do about non-posix platforms where NOT CONFIG_HAS_DTS (xtensa)?
# Drop support for NOT CONFIG_HAS_DTS perhaps?
set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts)
if(EXISTS ${DTS_SOURCE})
# We found a devicetree. Check for a board revision overlay.
if(BOARD_REVISION AND EXISTS ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.overlay)
list(APPEND DTS_SOURCE ${BOARD_DIR}/${BOARD}_${BOARD_REVISION_STRING}.overlay)
endif()
else()
# If we don't have a devicetree after all, there's not much to do.
set(header_template ${ZEPHYR_BASE}/misc/generated/generated_header.template)
zephyr_file_copy(${header_template} ${DEVICETREE_GENERATED_H} ONLY_IF_DIFFERENT)
return()
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)
cmake: support versioning of board This commit introduces support for versioning of boards. The existing board handling is limited in such a way that it is not possible to support a specific board in multiple variants. This commit introduces versioning of board revisions so that it is possible to support minor variations to a board without having to defining a completely new board. This can be done by adding a revision.cmake file in the board folder: boards/<arch>/<board-dir>/revision.cmake Depending on the revision format chosen, additional configuration files for each revision available must also be added, those have the form: boards/<arch>/<board-dir>/<board>_<revision>.conf Examples: boards/<arch>/<board-dir>/<board>_defconfig: Common board settings Revision format: MAJOR.MINOR.PATCH boards/<arch>/<board-dir>/<board>_0_5_0.conf: Revision 0.5.0 boards/<arch>/<board-dir>/<board>_1_0_0.conf: Revision 1.0.0 boards/<arch>/<board-dir>/<board>_1_5_0.conf: Revision 1.5.0 Revision format: LETTER boards/<arch>/<board-dir>/<board>_A.conf: Revision A boards/<arch>/<board-dir>/<board>_B.conf: Revision B The `board_check_revision` function is available in `extensions.cmake` to facilitate board revision handling in `revision.cmake`. User select the board revision using: `-DBOARD=<board>@<revision>`, as example `-DBOARD=plank@0.5.0`. If a shield, test, sample, or application needs to specify DTS overlay or Kconfig fragments, this can be done by adding revision specific configuration files in the sample/test/shield folder, like this: <shield/sample-path>/boards/<board>.conf <shield/sample-path>/boards/<board>_<revision>.conf or if there is there is only a need for adjusting on a given board revision: <shield/sample-path>/boards/<board>_<revision>.conf Similar for DTS overlay files: <shield-path>/boards/<board>.overlay <shield-path>/boards/<board>_<revision>.overlay or: <shield-path>/boards/<board>_<revision>.conf For test/samples/apps: <sample-path>/<board>.overlay <sample-path>/<board>_<revision>.overlay or: <sample-path>/<board>_<revision>.overlay Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no> Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-11-11 10:10:41 +01:00
set(dts_files
${DTS_SOURCE}
${board_extension_dts_files}
cmake: support versioning of board This commit introduces support for versioning of boards. The existing board handling is limited in such a way that it is not possible to support a specific board in multiple variants. This commit introduces versioning of board revisions so that it is possible to support minor variations to a board without having to defining a completely new board. This can be done by adding a revision.cmake file in the board folder: boards/<arch>/<board-dir>/revision.cmake Depending on the revision format chosen, additional configuration files for each revision available must also be added, those have the form: boards/<arch>/<board-dir>/<board>_<revision>.conf Examples: boards/<arch>/<board-dir>/<board>_defconfig: Common board settings Revision format: MAJOR.MINOR.PATCH boards/<arch>/<board-dir>/<board>_0_5_0.conf: Revision 0.5.0 boards/<arch>/<board-dir>/<board>_1_0_0.conf: Revision 1.0.0 boards/<arch>/<board-dir>/<board>_1_5_0.conf: Revision 1.5.0 Revision format: LETTER boards/<arch>/<board-dir>/<board>_A.conf: Revision A boards/<arch>/<board-dir>/<board>_B.conf: Revision B The `board_check_revision` function is available in `extensions.cmake` to facilitate board revision handling in `revision.cmake`. User select the board revision using: `-DBOARD=<board>@<revision>`, as example `-DBOARD=plank@0.5.0`. If a shield, test, sample, or application needs to specify DTS overlay or Kconfig fragments, this can be done by adding revision specific configuration files in the sample/test/shield folder, like this: <shield/sample-path>/boards/<board>.conf <shield/sample-path>/boards/<board>_<revision>.conf or if there is there is only a need for adjusting on a given board revision: <shield/sample-path>/boards/<board>_<revision>.conf Similar for DTS overlay files: <shield-path>/boards/<board>.overlay <shield-path>/boards/<board>_<revision>.overlay or: <shield-path>/boards/<board>_<revision>.conf For test/samples/apps: <sample-path>/<board>.overlay <sample-path>/<board>_<revision>.overlay or: <sample-path>/<board>_<revision>.overlay Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no> Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-11-11 10:10:41 +01:00
${shield_dts_files}
)
if(DTC_OVERLAY_FILE)
zephyr_list(TRANSFORM DTC_OVERLAY_FILE NORMALIZE_PATHS
OUTPUT_VARIABLE 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)
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_DEFINES_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}
)
dts: Add new DTS/binding parser Add a new DTS/binding parser to scripts/dts/ for generating generated_dts_board.conf and generated_dts_board_unfixed.h. The old code is kept to generate some deprecated defines, using the --deprecated-only flag. It will be removed later. The new parser is implemented in three files in scripts/dts/: dtlib.py: A low-level .dts parsing library. This is similar to devicetree.py in the old code, but is a general robust DTS parser that doesn't rely on preprocessing. edtlib.py (e for extended): A library built on top of dtlib.py that brings together data from DTS files and bindings and creates Device instances with all the data for a device. gen_defines.py: A script that uses edtlib.py to generate generated_dts_board.conf and generated_dts_board_unfixed.h. Corresponds to extract_dts_includes.py and the files in extract/ in the old code. testdtlib.py: Test suite for dtlib.py. Can be run directly as a script. testedtlib.py (uses test.dts and test-bindings/): Test suite for edtlib.py. Can be run directly as a script. The test suites will be run automatically in CI. The new code turns some things that were warnings (or not checked) in the old code into errors, like missing properties that are specified with 'category: required' in the binding for the node. The code includes lots of documentation and tries to give helpful error messages instead of Python errors. Co-authored-by: Kumar Gala <kumar.gala@linaro.org> Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
2018-12-17 20:09:47 +01:00
#
# 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}
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
${include_files}
${GEN_DEFINES_SCRIPT}
${GEN_DRIVER_KCONFIG_SCRIPT}
${GEN_DTS_CMAKE_SCRIPT}
)
#
# Run GEN_DEFINES_SCRIPT.
#
string(REPLACE ";" " " EXTRA_DTC_FLAGS_RAW "${EXTRA_DTC_FLAGS}")
set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT}
--dts ${DTS_POST_CPP}
--dtc-flags '${EXTRA_DTC_FLAGS_RAW}'
--bindings-dirs ${DTS_ROOT_BINDINGS}
--header-out ${DEVICETREE_GENERATED_H}.new
--dts-out ${ZEPHYR_DTS}.new # for debugging and dtc
--edt-pickle-out ${EDT_PICKLE}
${EXTRA_GEN_DEFINES_ARGS}
)
execute_process(
COMMAND ${CMD_GEN_DEFINES}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
RESULT_VARIABLE ret
)
if(NOT "${ret}" STREQUAL "0")
message(STATUS "In: ${PROJECT_BINARY_DIR}, command: ${CMD_GEN_DEFINES}")
message(FATAL_ERROR "gen_defines.py failed with return code: ${ret}")
else()
zephyr_file_copy(${ZEPHYR_DTS}.new ${ZEPHYR_DTS} ONLY_IF_DIFFERENT)
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}")
endif()
#
# 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.
#
execute_process(
COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_CMAKE_SCRIPT}
--edt-pickle ${EDT_PICKLE}
--cmake-out ${DTS_CMAKE}
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()
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
)
if(NOT "${ret}" STREQUAL "0")
message(FATAL_ERROR "command failed with return code: ${ret}")
endif()
endif(DTC)