cmake: dts: import devicetree symbols into CMake

This commit introduces devicetree API in CMake so that devicetree
properties and register block can be used in the CMake build system.

The script scripts/dts/gen_dts_cmake.py processes the edt.pickle and
generates a corresponding devicetree property map in a devicetree_target
that is then used in CMake.

The following devicetree API has been made available in Zephyr CMake:
- dt_nodelabel(<var> NODELABEL <label>)
- dt_node_exists(<var> PATH <path>)
- dt_node_has_status(<var> PATH <path> STATUS <status>)
- dt_prop(<var> PATH <path> PROPERTY <prop>)
- dt_prop(<var> PATH <path> INDEX <idx> PROPERTY <prop>)
- dt_num_regs(<var> PATH <path>)
- dt_reg_addr(<var> PATH <path> [INDEX <idx>])
- dt_reg_size(<var> PATH <path> [INDEX <idx>])
- dt_has_chosen(<var> PROPERTY <prop>)
- dt_chosen(<var> PROPERTY <prop>)

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
This commit is contained in:
Torsten Rasmussen 2021-08-11 13:48:25 +02:00 committed by Anas Nashif
commit fcf720941f
3 changed files with 596 additions and 0 deletions

View file

@ -19,6 +19,8 @@
# 3.3.1 Toolchain integration
# 3.4. Debugging CMake
# 3.5. File system management
# 4. Devicetree extensions
# 4.1 dt_*
########################################################
# 1. Zephyr-aware extensions
@ -2267,3 +2269,435 @@ function(target_byproducts)
COMMENT "Logical command for additional byproducts on target: ${TB_TARGET}"
)
endfunction()
########################################################
# 4. Zephyr devicetree function
########################################################
# 4.1. dt_*
#
# The following methods are for retrieving devicetree information in CMake.
#
# Note: In CMake we refer to the nodes using the node's path, therefore there
# is no dt_path(...) function for obtaining a node identifier.
# Usage:
# dt_nodelabel(<var> NODELABEL <label>)
#
# Function for retrieving the node path for the node having nodelabel
# <label>.
#
# Example devicetree fragment:
#
# / {
# soc {
# nvic: interrupt-controller@e000e100 { ... };
# };
# };
#
# Example usage:
#
# # Sets 'nvic_path' to "/soc/interrupt-controller@e000e100"
# dt_nodelabel(nvic_path NODELABEL "nvic")
#
# The node's path will be returned in the <var> parameter.
# <var> will be undefined if node does not exist.
#
# <var> : Return variable where the node path will be stored
# NODELABEL <label> : Node label
function(dt_nodelabel var)
set(req_single_args "NODELABEL")
cmake_parse_arguments(DT_LABEL "" "${req_single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_nodelabel(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_LABEL_${arg})
message(FATAL_ERROR "dt_nodelabel(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
get_target_property(${var} devicetree_target "DT_NODELABEL|${DT_LABEL_NODELABEL}")
if(${${var}} STREQUAL ${var}-NOTFOUND)
set(${var})
endif()
set(${var} ${${var}} PARENT_SCOPE)
endfunction()
# Usage:
# dt_node_exists(<var> PATH <path>)
#
# Tests whether a node with path <path> exists in the devicetree.
#
# The result of the check, either TRUE or FALSE, will be returned in
# the <var> parameter.
#
# <var> : Return variable where the check result will be returned
# PATH <path> : Node path
function(dt_node_exists var)
set(req_single_args "PATH")
cmake_parse_arguments(DT_NODE "" "${req_single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_node_existsl(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_NODE_${arg})
message(FATAL_ERROR "dt_node_exists(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
get_target_property(${var} devicetree_target "DT_NODE|${DT_NODE_PATH}")
if(${var})
set(${var} ${${var}} PARENT_SCOPE)
else()
set(${var} FALSE PARENT_SCOPE)
endif()
endfunction()
# Usage:
# dt_node_has_status(<var> PATH <path> STATUS <status>)
#
# Tests whether <path> refers to a node which:
# - exists in the devicetree, and
# - has a status property matching the <status> argument
# (a missing status or an ok status is treated as if it
# were okay instead)
#
# The result of the check, either TRUE or FALSE, will be returned in
# the <var> parameter.
#
# <var> : Return variable where the check result will be returned
# PATH <path> : Node path
# STATUS <status> : Status to check
function(dt_node_has_status var)
set(req_single_args "PATH;STATUS")
cmake_parse_arguments(DT_NODE "" "${req_single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_node_has_status(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_NODE_${arg})
message(FATAL_ERROR "dt_node_has_status(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
dt_node_exists(${var} PATH ${DT_NODE_PATH})
if(NOT ${${var}})
set(${var} FALSE PARENT_SCOPE)
endif()
dt_prop(${var} PATH ${DT_NODE_PATH} PROPERTY status)
if(NOT DEFINED ${var} OR "${${var}}" STREQUAL ok)
set(${var} okay)
endif()
if(${var} STREQUAL ${DT_NODE_STATUS})
set(${var} TRUE PARENT_SCOPE)
else()
set(${var} FALSE PARENT_SCOPE)
endif()
endfunction()
# Usage:
#
# dt_prop(<var> PATH <path> PROPERTY <prop> [INDEX <idx>])
#
# Get a devicetree property value. The value will be returned in the
# <var> parameter.
#
# This function currently only supports properties with the following
# devicetree binding types: string, int, boolean, array, uint8-array,
# string-array, path.
#
# For array valued properties (including uint8-array and
# string-array), the entire array is returned as a CMake list unless
# INDEX is given. If INDEX is given, just the array element at index
# <idx> is returned.
#
# The property value will be returned in the <var> parameter if the
# node exists and has a property <prop> with one of the above types.
# <var> will be undefined otherwise.
#
# To test if the property is defined before using it, use DEFINED on
# the return <var>, like this:
#
# dt_prop(reserved_ranges PATH "/soc/gpio@deadbeef" PROPERTY "gpio-reserved-ranges")
# if(DEFINED reserved_ranges)
# # Node exists and has the "gpio-reserved-ranges" property.
# endif()
#
# To distinguish a missing node from a missing property, combine
# dt_prop() and dt_node_exists(), like this:
#
# dt_node_exists(node_exists PATH "/soc/gpio@deadbeef")
# dt_prop(reserved_ranges PATH "/soc/gpio@deadbeef" PROPERTY "gpio-reserved-ranges")
# if(DEFINED reserved_ranges)
# # Node "/soc/gpio@deadbeef" exists and has the "gpio-reserved-ranges" property
# elseif(node_exists)
# # Node exists, but doesn't have the property, or the property has an unsupported type.
# endif()
#
# <var> : Return variable where the property value will be stored
# PATH <path> : Node path
# PROPERTY <prop>: Property for which a value should be returned, as it
# appears in the DTS source
# INDEX <idx> : Optional index when retrieving a value in an array property
function(dt_prop var)
set(req_single_args "PATH;PROPERTY")
set(single_args "INDEX")
cmake_parse_arguments(DT_PROP "" "${req_single_args};${single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_prop(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_PROP_${arg})
message(FATAL_ERROR "dt_prop(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
get_property(exists TARGET devicetree_target
PROPERTY "DT_PROP|${DT_PROP_PATH}|${DT_PROP_PROPERTY}"
SET
)
if(NOT exists)
set(${var} PARENT_SCOPE)
return()
endif()
get_target_property(val devicetree_target
"DT_PROP|${DT_PROP_PATH}|${DT_PROP_PROPERTY}"
)
if(DEFINED DT_PROP_INDEX)
list(GET val ${DT_PROP_INDEX} element)
set(${var} "${element}" PARENT_SCOPE)
else()
set(${var} "${val}" PARENT_SCOPE)
endif()
endfunction()
# Usage:
# dt_num_regs(<var> PATH <path>)
#
# Get the number of register blocks in the node's reg property;
# this may be zero.
#
# The value will be returned in the <var> parameter.
#
# <var> : Return variable where the property value will be stored
# PATH <path> : Node path
function(dt_num_regs var)
set(req_single_args "PATH")
cmake_parse_arguments(DT_REG "" "${req_single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_num_regs(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_REG_${arg})
message(FATAL_ERROR "dt_num_regs(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
get_target_property(${var} devicetree_target "DT_REG|${DT_REG_PATH}|NUM")
set(${var} ${${var}} PARENT_SCOPE)
endfunction()
# Usage:
# dt_reg_addr(<var> PATH <path> [INDEX <idx>])
#
# Get the base address of the register block at index <idx>.
# If <idx> is omitted, then the value at index 0 will be returned.
#
# The value will be returned in the <var> parameter.
#
# Results can be:
# - The base address of the register block
# - <var> will be undefined if node does not exists or does not have a register
# block at the requested index.
#
# <var> : Return variable where the address value will be stored
# PATH <path> : Node path
# INDEX <idx> : Index number
function(dt_reg_addr var)
set(req_single_args "PATH")
set(single_args "INDEX")
cmake_parse_arguments(DT_REG "" "${req_single_args};${single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_REG_${arg})
message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
if(NOT DEFINED DT_REG_INDEX)
set(DT_REG_INDEX 0)
endif()
get_target_property(${var}_list devicetree_target "DT_REG|${DT_REG_PATH}|ADDR")
list(GET ${var}_list ${DT_REG_INDEX} ${var})
if("${var}" STREQUAL NONE)
set(${var})
endif()
set(${var} ${${var}} PARENT_SCOPE)
endfunction()
# Usage:
# dt_reg_size(<var> PATH <path> [INDEX <idx>])
#
# Get the size of the register block at index <idx>.
# If INDEX is omitted, then the value at index 0 will be returned.
#
# The value will be returned in the <value> parameter.
#
# <var> : Return variable where the size value will be stored
# PATH <path> : Node path
# INDEX <idx> : Index number
function(dt_reg_size var)
set(req_single_args "PATH")
set(single_args "INDEX")
cmake_parse_arguments(DT_REG "" "${req_single_args};${single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_REG_${arg})
message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
if(NOT DEFINED DT_REG_INDEX)
set(DT_REG_INDEX 0)
endif()
get_target_property(${var}_list devicetree_target "DT_REG|${DT_REG_PATH}|SIZE")
list(GET ${var}_list ${DT_REG_INDEX} ${var})
if("${var}" STREQUAL NONE)
set(${var})
endif()
set(${var} ${${var}} PARENT_SCOPE)
endfunction()
# Usage:
# dt_has_chosen(<var> PROPERTY <prop>)
#
# Test if the devicetree's /chosen node has a given property
# <prop> which contains the path to a node.
#
# Example devicetree fragment:
#
# chosen {
# foo = &bar;
# };
#
# Example usage:
#
# # Sets 'result' to TRUE
# dt_has_chosen(result PROPERTY "foo")
#
# # Sets 'result' to FALSE
# dt_has_chosen(result PROPERTY "baz")
#
# The result of the check, either TRUE or FALSE, will be stored in the
# <var> parameter.
#
# <var> : Return variable
# PROPERTY <prop> : Chosen property
function(dt_has_chosen var)
set(req_single_args "PROPERTY")
cmake_parse_arguments(DT_CHOSEN "" "${req_single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_has_chosen(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_CHOSEN_${arg})
message(FATAL_ERROR "dt_has_chosen(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
get_target_property(exists devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")
if(${exists} STREQUAL exists-NOTFOUND)
set(${var} FALSE PARENT_SCOPE)
else()
set(${var} TRUE PARENT_SCOPE)
endif()
endfunction()
# Usage:
# dt_chosen(<var> PROPERTY <prop>)
#
# Get a node path for a /chosen node property.
#
# the node path will be returned in the <value> parameter.
#
# <var> : Return variable where the node path will be stored
# PROPERTY <prop> : Chosen property
function(dt_chosen var)
set(req_single_args "PROPERTY")
cmake_parse_arguments(DT_CHOSEN "" "${req_single_args}" "" ${ARGN})
if(${ARGV0} IN_LIST req_single_args)
message(FATAL_ERROR "dt_has_chosen(${ARGV0} ...) missing return parameter.")
endif()
foreach(arg ${req_single_args})
if(NOT DEFINED DT_CHOSEN_${arg})
message(FATAL_ERROR "dt_chosen(${ARGV0} ...) "
"missing required argument: ${arg}"
)
endif()
endforeach()
get_target_property(${var} devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")
if(${${var}} STREQUAL ${var}-NOTFOUND)
set(${var} PARENT_SCOPE)
else()
set(${var} ${${var}} PARENT_SCOPE)
endif()
endfunction()