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 bjarki-andreasen committed Oct 1, 2024
1 parent 2b210c4 commit e82f43f
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 @@ -3611,6 +3611,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 Down Expand Up @@ -3638,6 +3641,10 @@ endfunction()
# NODELABEL <label> : Node label
# REQUIRED : Generate a fatal error if the node-label is not found
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(options "REQUIRED")
set(req_single_args "NODELABEL")
cmake_parse_arguments(DT_LABEL "${options}" "${req_single_args}" "" ${ARGN})
Expand All @@ -3654,7 +3661,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)
if(DT_LABEL_REQUIRED)
message(FATAL_ERROR "required nodelabel not found: ${DT_LABEL_NODELABEL}")
Expand Down Expand Up @@ -3686,6 +3693,10 @@ endfunction()
# PROPERTY <prop> : The alias to check
# REQUIRED : Generate a fatal error if the alias is not found
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(options "REQUIRED")
set(req_single_args "PROPERTY")
cmake_parse_arguments(DT_ALIAS "${options}" "${req_single_args}" "" ${ARGN})
Expand All @@ -3702,7 +3713,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)
if(DT_ALIAS_REQUIRED)
message(FATAL_ERROR "required alias not found: ${DT_ALIAS_PROPERTY}")
Expand Down Expand Up @@ -3730,6 +3741,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 @@ -3775,6 +3790,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 @@ -3861,6 +3880,10 @@ endfunction()
# INDEX <idx> : Optional index when retrieving a value in an array property
# REQUIRED : Generate a fatal error if the property is not found
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(options "REQUIRED")
set(req_single_args "PATH;PROPERTY")
set(single_args "INDEX")
Expand All @@ -3879,7 +3902,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 @@ -3892,7 +3915,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 @@ -3921,6 +3944,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 @@ -3937,7 +3964,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 @@ -3947,7 +3974,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 @@ -3976,6 +4003,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 @@ -3992,7 +4023,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 @@ -4022,6 +4053,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 @@ -4051,7 +4086,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 @@ -4082,6 +4117,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 @@ -4111,7 +4150,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 @@ -4159,6 +4198,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 @@ -4174,7 +4217,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 @@ -4194,6 +4237,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 @@ -4209,7 +4256,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 @@ -4281,7 +4328,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 @@ -503,6 +503,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 @@ -750,3 +752,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 e82f43f

Please sign in to comment.