Skip to content

Commit

Permalink
[nrf fromlist] sysbuild: dts: Introduce sysbuild_dt_* API
Browse files Browse the repository at this point in the history
Upstream PR: zephyrproject-rtos/zephyr#73903

Add new functions to `sysbuild_extensions.cmake`, which will mirror the
familiar dt_* API from Zephyr `extensions.cmake`. For example:

   dt_nodelabel(<var> NODELABEL <label>)

gets the following sysbuild counterpart, with one extra argument:

   sysbuild_dt_nodelabel(<var> IMAGE <image> NODELABEL <label>)

This API allows sysbuild to retrieve devicetree information for a given
<image>, only after its respective `ExternalZephyrProject_Cmake()` call.

This works by importing the generated `dts.cmake` files from each
image's build directory, and creating multiple CMake targets to hold the
generated properties - much like how the `CMakeCache.txt` and `.config`
are also imported to be used by the related `sysbuild_get()` function.

The dt_* API itself also has to be updated, in order to read properties
from a variable `DEVICETREE_TARGET` set in the parent scope.

Signed-off-by: Grzegorz Swiderski <[email protected]>
(cherry picked from commit 3bf9a41a20ae52adc3ccadc2ff79425160f8ad73)
  • Loading branch information
57300 authored and rlubos committed Jul 16, 2024
1 parent d53b51e commit 1e0d975
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 12 deletions.
71 changes: 59 additions & 12 deletions cmake/modules/extensions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3553,6 +3553,9 @@ endfunction()
# alias at the beginning of a path interchangeably with the full
# path to the aliased node in these functions. The usage comments
# will make this clear in each case.
#
# - Each of these methods also has a sysbuild_dt_* counterpart.
# See share/sysbuild/cmake/modules/sysbuild_extensions.cmake.

# Usage:
# dt_nodelabel(<var> NODELABEL <label>)
Expand All @@ -3579,6 +3582,10 @@ endfunction()
# <var> : Return variable where the node path will be stored
# NODELABEL <label> : Node label
function(dt_nodelabel var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_nodelabel(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "NODELABEL")
cmake_parse_arguments(DT_LABEL "" "${req_single_args}" "" ${ARGN})

Expand All @@ -3594,7 +3601,7 @@ function(dt_nodelabel var)
endif()
endforeach()

get_target_property(${var} devicetree_target "DT_NODELABEL|${DT_LABEL_NODELABEL}")
get_target_property(${var} "${DEVICETREE_TARGET}" "DT_NODELABEL|${DT_LABEL_NODELABEL}")
if(${${var}} STREQUAL ${var}-NOTFOUND)
set(${var})
endif()
Expand Down Expand Up @@ -3622,6 +3629,10 @@ endfunction()
# <var> : Return variable where the node path will be stored
# PROPERTY <prop> : The alias to check
function(dt_alias var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_alias(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PROPERTY")
cmake_parse_arguments(DT_ALIAS "" "${req_single_args}" "" ${ARGN})

Expand All @@ -3637,7 +3648,7 @@ function(dt_alias var)
endif()
endforeach()

get_target_property(${var} devicetree_target "DT_ALIAS|${DT_ALIAS_PROPERTY}")
get_target_property(${var} "${DEVICETREE_TARGET}" "DT_ALIAS|${DT_ALIAS_PROPERTY}")
if(${${var}} STREQUAL ${var}-NOTFOUND)
set(${var})
endif()
Expand All @@ -3662,6 +3673,10 @@ endfunction()
# <var> : Return variable where the check result will be returned
# PATH <path> : Node path
function(dt_node_exists var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_node_exists(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PATH")
cmake_parse_arguments(DT_NODE "" "${req_single_args}" "" ${ARGN})

Expand Down Expand Up @@ -3707,6 +3722,10 @@ endfunction()
# PATH <path> : Node path
# STATUS <status> : Status to check
function(dt_node_has_status var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_node_has_status(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PATH;STATUS")
cmake_parse_arguments(DT_NODE "" "${req_single_args}" "" ${ARGN})

Expand Down Expand Up @@ -3792,6 +3811,10 @@ endfunction()
# appears in the DTS source
# INDEX <idx> : Optional index when retrieving a value in an array property
function(dt_prop var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_prop(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PATH;PROPERTY")
set(single_args "INDEX")
cmake_parse_arguments(DT_PROP "" "${req_single_args};${single_args}" "" ${ARGN})
Expand All @@ -3809,7 +3832,7 @@ function(dt_prop var)
endforeach()

dt_path_internal(canonical "${DT_PROP_PATH}")
get_property(exists TARGET devicetree_target
get_property(exists TARGET "${DEVICETREE_TARGET}"
PROPERTY "DT_PROP|${canonical}|${DT_PROP_PROPERTY}"
SET
)
Expand All @@ -3819,7 +3842,7 @@ function(dt_prop var)
return()
endif()

get_target_property(val devicetree_target
get_target_property(val "${DEVICETREE_TARGET}"
"DT_PROP|${canonical}|${DT_PROP_PROPERTY}"
)

Expand Down Expand Up @@ -3848,6 +3871,10 @@ endfunction()
# INDEX <idx> : Optional index when retrieving a value in an array property

function(dt_comp_path var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_comp_path(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "COMPATIBLE")
set(single_args "INDEX")
cmake_parse_arguments(DT_COMP "" "${req_single_args};${single_args}" "" ${ARGN})
Expand All @@ -3864,7 +3891,7 @@ function(dt_comp_path var)
endif()
endforeach()

get_property(exists TARGET devicetree_target
get_property(exists TARGET "${DEVICETREE_TARGET}"
PROPERTY "DT_COMP|${DT_COMP_COMPATIBLE}"
SET
)
Expand All @@ -3874,7 +3901,7 @@ function(dt_comp_path var)
return()
endif()

get_target_property(val devicetree_target
get_target_property(val "${DEVICETREE_TARGET}"
"DT_COMP|${DT_COMP_COMPATIBLE}"
)

Expand Down Expand Up @@ -3903,6 +3930,10 @@ endfunction()
# <var> : Return variable where the property value will be stored
# PATH <path> : Node path
function(dt_num_regs var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_num_regs(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PATH")
cmake_parse_arguments(DT_REG "" "${req_single_args}" "" ${ARGN})

Expand All @@ -3919,7 +3950,7 @@ function(dt_num_regs var)
endforeach()

dt_path_internal(canonical "${DT_REG_PATH}")
get_target_property(${var} devicetree_target "DT_REG|${canonical}|NUM")
get_target_property(${var} "${DEVICETREE_TARGET}" "DT_REG|${canonical}|NUM")

set(${var} ${${var}} PARENT_SCOPE)
endfunction()
Expand Down Expand Up @@ -3949,6 +3980,10 @@ endfunction()
# INDEX <idx> : Register block index number
# NAME <name> : Register block name
function(dt_reg_addr var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PATH")
set(single_args "INDEX;NAME")
cmake_parse_arguments(DT_REG "" "${req_single_args};${single_args}" "" ${ARGN})
Expand Down Expand Up @@ -3978,7 +4013,7 @@ function(dt_reg_addr var)
endif()

dt_path_internal(canonical "${DT_REG_PATH}")
get_target_property(${var}_list devicetree_target "DT_REG|${canonical}|ADDR")
get_target_property(${var}_list "${DEVICETREE_TARGET}" "DT_REG|${canonical}|ADDR")

list(GET ${var}_list ${DT_REG_INDEX} ${var})

Expand Down Expand Up @@ -4009,6 +4044,10 @@ endfunction()
# INDEX <idx> : Register block index number
# NAME <name> : Register block name
function(dt_reg_size var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_reg_size(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PATH")
set(single_args "INDEX;NAME")
cmake_parse_arguments(DT_REG "" "${req_single_args};${single_args}" "" ${ARGN})
Expand Down Expand Up @@ -4038,7 +4077,7 @@ function(dt_reg_size var)
endif()

dt_path_internal(canonical "${DT_REG_PATH}")
get_target_property(${var}_list devicetree_target "DT_REG|${canonical}|SIZE")
get_target_property(${var}_list "${DEVICETREE_TARGET}" "DT_REG|${canonical}|SIZE")

list(GET ${var}_list ${DT_REG_INDEX} ${var})

Expand Down Expand Up @@ -4086,6 +4125,10 @@ endfunction()
# <var> : Return variable
# PROPERTY <prop> : Chosen property
function(dt_has_chosen var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_has_chosen(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PROPERTY")
cmake_parse_arguments(DT_CHOSEN "" "${req_single_args}" "" ${ARGN})

Expand All @@ -4101,7 +4144,7 @@ function(dt_has_chosen var)
endif()
endforeach()

get_target_property(exists devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")
get_target_property(exists "${DEVICETREE_TARGET}" "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")

if(${exists} STREQUAL exists-NOTFOUND)
set(${var} FALSE PARENT_SCOPE)
Expand All @@ -4121,6 +4164,10 @@ endfunction()
# <var> : Return variable where the node path will be stored
# PROPERTY <prop> : Chosen property
function(dt_chosen var)
if(NOT DEFINED DEVICETREE_TARGET OR NOT TARGET "${DEVICETREE_TARGET}")
message(FATAL_ERROR "dt_chosen(${ARGV0} ...) devicetree is not available.")
endif()

set(req_single_args "PROPERTY")
cmake_parse_arguments(DT_CHOSEN "" "${req_single_args}" "" ${ARGN})

Expand All @@ -4136,7 +4183,7 @@ function(dt_chosen var)
endif()
endforeach()

get_target_property(${var} devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")
get_target_property(${var} "${DEVICETREE_TARGET}" "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")

if(${${var}} STREQUAL ${var}-NOTFOUND)
set(${var} PARENT_SCOPE)
Expand Down Expand Up @@ -4208,7 +4255,7 @@ endfunction()
# to an existing node. Set it to FALSE otherwise. See
# dt_path_internal for a definition and examples of 'canonical' paths.
function(dt_path_internal_exists var path)
get_target_property(path_prop devicetree_target "DT_NODE|${path}")
get_target_property(path_prop "${DEVICETREE_TARGET}" "DT_NODE|${path}")
if (path_prop)
set(${var} TRUE PARENT_SCOPE)
else()
Expand Down
85 changes: 85 additions & 0 deletions share/sysbuild/cmake/modules/sysbuild_extensions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ function(ExternalZephyrProject_Cmake)
endif()
load_cache(IMAGE ${ZCMAKE_APPLICATION} BINARY_DIR ${BINARY_DIR})
import_kconfig(CONFIG_ ${BINARY_DIR}/zephyr/.config TARGET ${ZCMAKE_APPLICATION})
set(DEVICETREE_TARGET ${ZCMAKE_APPLICATION}_devicetree_target)
include(${BINARY_DIR}/zephyr/dts.cmake)

# This custom target informs CMake how the BYPRODUCTS are generated if a target
# depends directly on the BYPRODUCT instead of depending on the image target.
Expand Down Expand Up @@ -737,3 +739,86 @@ function(sysbuild_images_order variable dependency_type)
topological_sort(TARGETS ${SIS_IMAGES} PROPERTY_NAME ${property_name} RESULT sorted)
set(${variable} ${sorted} PARENT_SCOPE)
endfunction()

# Internal macro for defining the sysbuild_dt_* CMake extensions, used to
# retrieve devicetree information from a Zephyr based build system.
#
# It takes a dt_* function <name> and adds common wrapper code, which
# adds an `IMAGE` argument to select the devicetree of a given <image>,
# and forwards all other arguments to the original function, including
# the sole return variable <var> - a common trait of the dt_* API.
#
# For additional documentation of each dt_* CMake extension,
# see section 4.1. of cmake/modules/extensions.cmake.
#
macro(sysbuild_dt_function name)
function(sysbuild_${name} var)
cmake_parse_arguments(PARSE_ARGV 1 SBDT "" "IMAGE" "")
zephyr_check_arguments_required_all("sysbuild_${name}" SBDT IMAGE)

set(DEVICETREE_TARGET ${SBDT_IMAGE}_devicetree_target)
if(NOT TARGET ${SBDT_IMAGE} OR NOT TARGET ${DEVICETREE_TARGET})
message(FATAL_ERROR "sysbuild_${name}(...) image '${SBDT_IMAGE}' "
"does not exist or its devicetree is not loaded yet"
)
endif()

cmake_language(CALL ${name} ${var} ${SBDT_UNPARSED_ARGUMENTS})
set(${var} "${${var}}" PARENT_SCOPE)
endfunction()
endmacro()

# Usage:
# sysbuild_dt_nodelabel(<var> IMAGE <image> NODELABEL <label>)
#
sysbuild_dt_function(dt_nodelabel)

# Usage:
# sysbuild_dt_alias(<var> IMAGE <image> PROPERTY <prop>)
#
sysbuild_dt_function(dt_alias)

# Usage:
# sysbuild_dt_node_exists(<var> IMAGE <image> PATH <path>)
#
sysbuild_dt_function(dt_node_exists)

# Usage:
# sysbuild_dt_node_has_status(<var> IMAGE <image> PATH <path> STATUS <status>)
#
sysbuild_dt_function(dt_node_has_status)

# Usage:
# sysbuild_dt_prop(<var> IMAGE <image> PATH <path> PROPERTY <prop> [INDEX <idx>])
#
sysbuild_dt_function(dt_prop)

# Usage:
# sysbuild_dt_comp_path(<var> IMAGE <image> COMPATIBLE <compatible> [INDEX <idx>])
#
sysbuild_dt_function(dt_comp_path)

# Usage:
# sysbuild_dt_num_regs(<var> IMAGE <image> PATH <path>)
#
sysbuild_dt_function(dt_num_regs)

# Usage:
# sysbuild_dt_reg_addr(<var> IMAGE <image> PATH <path> [INDEX <idx>] [NAME <name>])
#
sysbuild_dt_function(dt_reg_addr)

# Usage:
# sysbuild_dt_reg_size(<var> IMAGE <image> PATH <path> [INDEX <idx>] [NAME <name>])
#
sysbuild_dt_function(dt_reg_size)

# Usage:
# sysbuild_dt_has_chosen(<var> IMAGE <image> PROPERTY <prop>)
#
sysbuild_dt_function(dt_has_chosen)

# Usage:
# sysbuild_dt_chosen(<var> IMAGE <image> PROPERTY <prop>)
#
sysbuild_dt_function(dt_chosen)

0 comments on commit 1e0d975

Please sign in to comment.