# ##############################################################################
# cmake/nuttx_generate_dts.cmake
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################
# KCONFIG_CONFIG, NUTTX_DIR, and CMAKE_BINARY_DIR are set by -D flags
include(cmake/nuttx_kconfig.cmake)
nuttx_export_kconfig(${KCONFIG_CONFIG})
# The directory containing devicetree related scripts.
set(DT_SCRIPTS_DIR ${NUTTX_DIR}/tools/devicetree)
# This parses and collects the DT information
set(GEN_EDT_SCRIPT ${DT_SCRIPTS_DIR}/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_DIR}/gen_defines.py)
set(GEN_MULTIPLE_DEFINED_SCRIPT ${DT_SCRIPTS_DIR}/gen_dynamic_api.py)
# The generated file containing the final DTS, for debugging.
set(DTS_FILE ${CMAKE_BINARY_DIR}/dt/nuttx.dts)
# The edtlib.EDT object in pickle format.
set(EDT_PICKLE_FILE ${CMAKE_BINARY_DIR}/dt/edt.pickle)
# The generated C header needed by <nuttx/devicetree.h>
set(GEN_DEFINES ${CMAKE_BINARY_DIR}/include/nuttx/devicetree_generated.h)
# Generated build system internals.
set(DTS_PRE_FILE ${CMAKE_BINARY_DIR}/dt/nuttx.dts.pre)
set(DTS_DEPS_FILE ${CMAKE_BINARY_DIR}/dt/nuttx.dts.pre.d)
# Create a directory for the devicetree output files.
if(NOT EXISTS ${CMAKE_BINARY_DIR}/dt)
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/dt)
endif()
set(BOARD_NUM)
set(CURRENT_BOARD_ID 0)
function(parse_dts_config idx)
math(EXPR idx "${idx} - 1")
# Get current board devicetree configs argumennts
list(LENGTH CONFIG_DTS_SOURCE_DTR list_length)
if(idx LESS list_length)
list(GET CONFIG_DTS_SOURCE_DTR ${idx} dts_source_dir)
else()
set(dts_source_dir "")
endif()
list(LENGTH CONFIG_DTS_OVERLAY_DIR list_length)
if(idx LESS list_length)
list(GET CONFIG_DTS_OVERLAY_DIR ${idx} dts_overlay_dir)
else()
set(dts_overlay_dir "")
endif()
list(LENGTH CONFIG_DTS_CUSTOM_BINDINGS_DIR list_length)
if(idx LESS list_length)
list(GET CONFIG_DTS_CUSTOM_BINDINGS_DIR ${idx} dts_custom_bindings_dir)
else()
set(dts_custom_bindings_dir "")
endif()
list(LENGTH CONFIG_DTS_CUSTOM_DT_BINDINGS_INCLUDE_DIR list_length)
if(idx LESS list_length)
list(GET CONFIG_DTS_CUSTOM_DT_BINDINGS_INCLUDE_DIR ${idx}
dts_custom_dt_bindings_include_dir)
else()
set(dts_custom_dt_bindings_include_dir "")
endif()
set(DTS_SRCS "")
set(CUSTOM_OVERLAY_DTS_FILE "")
# The source files to be preprocessed.
set(DT_PREPROCESS_SOURCE_FILES "")
# The root directory of the devicetree bindings files.
set(DTS_ROOT_BINDINGS ${NUTTX_DIR}/devicetree/bindings)
# The include directories for the devicetree preprocessor.
set(DTS_PREPROCESS_INCLUDE_DIRECTORIES
${NUTTX_DIR}/include ${NUTTX_DIR}/devicetree
${NUTTX_DIR}/devicetree/common)
# Get .dts files from defconfig
string(REGEX REPLACE "," ";" dirlist "${dts_source_dir}")
foreach(dir ${dirlist})
get_filename_component(abs_dir ${dir} ABSOLUTE)
if(IS_DIRECTORY ${abs_dir})
file(GLOB_RECURSE dts_files ${abs_dir}/*.dts)
list(APPEND DTS_SRCS ${dts_files})
endif()
endforeach()
# Get .overlay files from defconfig
string(REGEX REPLACE "," ";" dirlist "${dts_overlay_dir}")
foreach(dir ${dirlist})
get_filename_component(abs_dir ${dir} ABSOLUTE)
if(IS_DIRECTORY ${abs_dir})
file(GLOB_RECURSE overlay_files ${abs_dir}/*.overlay)
list(APPEND CUSTOM_OVERLAY_DTS_FILE ${overlay_files})
endif()
endforeach()
# Get .dts file by CONFIG_ARCH_BOARD name if it can not get from defconfig.
if("${DTS_SRCS}" STREQUAL "")
foreach(dir ${NUTTX_BOARD_ABS_DIR})
if(EXISTS ${dir}/src/${CONFIG_ARCH_BOARD}.dts)
list(APPEND DTS_SRCS ${dir}/src/${CONFIG_ARCH_BOARD}.dts)
endif()
endforeach()
endif()
list(APPEND DT_PREPROCESS_SOURCE_FILES ${DTS_SRCS} ${CUSTOM_OVERLAY_DTS_FILE})
# Get custom bindings directory from defconfig
string(REGEX REPLACE "," ";" dirlist "${dts_custom_bindings_dir}")
set(abs_dirlist "")
foreach(dir ${dirlist})
get_filename_component(abs_dir ${dir} ABSOLUTE)
list(APPEND abs_dirlist ${abs_dir})
endforeach()
list(APPEND DTS_ROOT_BINDINGS ${abs_dirlist})
# Get custom dt-bindings directory from defconfig
# CONFIG_DTS_CUSTOM_DT_BINDINGS_INCLUDE_DIR
string(REGEX REPLACE "," ";" dirlist "${dts_custom_dt_bindings_include_dir}")
set(abs_dirlist "")
foreach(dir ${dirlist})
get_filename_component(abs_dir ${dir} ABSOLUTE)
list(APPEND abs_dirlist ${abs_dir})
endforeach()
list(APPEND DTS_PREPROCESS_INCLUDE_DIRECTORIES ${abs_dirlist})
set(DTS_SRCS
"${DTS_SRCS}"
PARENT_SCOPE)
set(DT_PREPROCESS_SOURCE_FILES
"${DT_PREPROCESS_SOURCE_FILES}"
PARENT_SCOPE)
set(DTS_ROOT_BINDINGS
"${DTS_ROOT_BINDINGS}"
PARENT_SCOPE)
set(DTS_PREPROCESS_INCLUDE_DIRECTORIES
"${DTS_PREPROCESS_INCLUDE_DIRECTORIES}"
PARENT_SCOPE)
endfunction()
# Preprocess device tree source files using the C preprocessor
function(dts_preprocess)
set(include_opts)
foreach(dir ${DTS_PREPROCESS_INCLUDE_DIRECTORIES})
list(APPEND include_opts -isystem ${dir})
endforeach()
set(source_opts)
foreach(file ${DT_PREPROCESS_SOURCE_FILES})
list(APPEND source_opts -include ${file})
endforeach()
set(CMD_PREPROCESS
cpp
-x
assembler-with-cpp
-nostdinc
${include_opts}
${source_opts}
-D__DTS__
${DT_PREPROCESS_EXTRA_CPPFLAGS}
-E # Stop after preprocessing
-MD
-MF
${DTS_DEPS_FILE}
-o
${DTS_PRE_FILE}
${NUTTX_DIR}/devicetree/common/empty_file.c)
execute_process(COMMAND ${CMD_PREPROCESS} RESULT_VARIABLE PREPROCESS_RESULT)
if(NOT "${PREPROCESS_RESULT}" STREQUAL "0")
message(FATAL_ERROR "failed to preprocess devicetree files
(error code ${ret}): ${DT_PREPROCESS_SOURCE_FILES}")
endif()
message(STATUS "Generated nuttx.dts.pre: ${DTS_PRE_FILE}")
message(STATUS "CMD_PREPROCESS: ${CMD_PREPROCESS}")
message(STATUS "CURRENT_BOARD_ID: ${CURRENT_BOARD_ID}")
message(STATUS "BOARD_NUM: ${BOARD_NUM}")
string(REGEX REPLACE "nuttx.dts.pre" "nuttx_${CURRENT_BOARD_ID}.dts.pre"
new_dts_pre_file "${DTS_PRE_FILE}")
file(COPY_FILE ${DTS_PRE_FILE} ${new_dts_pre_file} ONLY_IF_DIFFERENT)
string(REGEX REPLACE "nuttx.dts.pre.d" "nuttx_${CURRENT_BOARD_ID}.dts.pre.d"
new_dts_deps_file "${DTS_DEPS_FILE}")
file(COPY_FILE ${DTS_DEPS_FILE} ${new_dts_deps_file} ONLY_IF_DIFFERENT)
endfunction()
# Generate edt pickle file using gen_edt.py
function(gen_edt)
set(CMD_GEN_EDT
${PYTHON_EXECUTABLE}
${GEN_EDT_SCRIPT}
--dts
${DTS_PRE_FILE}
--dtc-flags
'${EXTRA_DTC_FLAGS_RAW}'
--bindings-dirs
${DTS_ROOT_BINDINGS}
--dts-out
${DTS_FILE}.new
--edt-pickle-out
${EDT_PICKLE_FILE}.new)
execute_process(
COMMAND ${CMD_GEN_EDT}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE GEN_EDT_RESULT)
if(NOT "${GEN_EDT_RESULT}" STREQUAL "0")
message(FATAL_ERROR "failed to generate edt pickle file
(error code ${GEN_EDT_RESULT})")
endif()
file(COPY_FILE ${DTS_FILE}.new ${DTS_FILE} ONLY_IF_DIFFERENT)
file(COPY_FILE ${EDT_PICKLE_FILE}.new ${EDT_PICKLE_FILE} ONLY_IF_DIFFERENT)
file(REMOVE ${DTS_FILE}.new ${EDT_PICKLE_FILE}.new)
message(STATUS "Generated nuttx.dts: ${DTS_FILE}")
message(STATUS "Generated pickled edt: ${EDT_PICKLE_FILE}")
message(STATUS "CMD_GEN_EDT: ${CMD_GEN_EDT}")
string(REGEX REPLACE "nuttx.dts" "nuttx_${CURRENT_BOARD_ID}.dts" new_dts_file
"${DTS_FILE}")
file(COPY_FILE ${DTS_FILE} ${new_dts_file} ONLY_IF_DIFFERENT)
string(REGEX REPLACE "edt.pickle" "edt_${CURRENT_BOARD_ID}.pickle"
new_edt_pickle_file "${EDT_PICKLE_FILE}")
file(COPY_FILE ${EDT_PICKLE_FILE} ${new_edt_pickle_file} ONLY_IF_DIFFERENT)
endfunction(gen_edt)
# Generate devicetree_generated.h using gen_defines.py
function(gen_devicetree_header)
set(CMD_GEN_DEFINES ${PYTHON_EXECUTABLE} ${GEN_DEFINES_SCRIPT} --edt-pickle
${EDT_PICKLE_FILE} --header-out ${GEN_DEFINES}.new)
if(BOARD_NUM EQUAL 1)
execute_process(
COMMAND ${CMD_GEN_DEFINES}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE GEN_DEFINES_RESULT)
file(COPY_FILE ${GEN_DEFINES}.new ${GEN_DEFINES} ONLY_IF_DIFFERENT)
file(REMOVE ${GEN_DEFINES}.new)
endif()
if(CURRENT_BOARD_ID EQUAL 1)
execute_process(
COMMAND ${CMD_GEN_DEFINES}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE GEN_DEFINES_RESULT)
string(REGEX REPLACE "devicetree_generated.h" "devicetree_generated_base.h"
gen_defines_base "${GEN_DEFINES}")
file(COPY_FILE ${GEN_DEFINES}.new ${gen_defines_base} ONLY_IF_DIFFERENT)
file(REMOVE ${GEN_DEFINES}.new)
message(STATUS "Generated devicetree_generated_base.h: ${gen_defines_base}")
endif()
string(
REGEX
REPLACE "devicetree_generated.h"
"devicetree_generated_${CURRENT_BOARD_ID}.h" current_gen_defines
"${GEN_DEFINES}")
set(CMD_GEN_DEFINES
${PYTHON_EXECUTABLE}
${GEN_DEFINES_SCRIPT}
--edt-pickle
${EDT_PICKLE_FILE}
--header-out
${GEN_DEFINES}.new
--board-id
"B${CURRENT_BOARD_ID}")
execute_process(
COMMAND ${CMD_GEN_DEFINES}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE GEN_DEFINES_RESULT)
message(STATUS "CMD_GEN_DEFINES: ${CMD_GEN_DEFINES}")
file(COPY_FILE ${GEN_DEFINES}.new ${current_gen_defines} ONLY_IF_DIFFERENT)
file(REMOVE ${GEN_DEFINES}.new)
message(STATUS "Generated devicetree_generated.h: ${GEN_DEFINES}")
endfunction()
function(gen_multi_devicetree_header)
set(CMD_GEN_MULTIPLE_DEFINES
${PYTHON_EXECUTABLE} ${GEN_MULTIPLE_DEFINED_SCRIPT} --header-out
"${GEN_DEFINES}" --board-nums ${BOARD_NUM})
message(STATUS "CMD_GEN_MULTIPLE_DEFINES: ${CMD_GEN_MULTIPLE_DEFINES}")
execute_process(
COMMAND ${CMD_GEN_MULTIPLE_DEFINES}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE GEN_DEFINES_RESULT)
endfunction()
# ~~~
# nuttx_generate_dts
#
# Description:
# Function to generate the devicetree_generated.h header and nuttx.dts files.
#
# ~~~
function(nuttx_generate_dts)
if(NOT "${CONFIG_DEVICETREE_HEADER_GENERATION}" STREQUAL "y")
return()
endif()
list(LENGTH CONFIG_DTS_SOURCE_DTR dts_count)
set(BOARD_NUM ${dts_count})
foreach(i RANGE 1 ${BOARD_NUM})
message(STATUS "Current board id: ${i}")
parse_dts_config(${i})
# Check if DTS_SRCS is empty
if("${DTS_SRCS}" STREQUAL "")
message(STATUS "no device tree source files specified (DTS_SRCS),
skipped device tree generation")
else()
math(EXPR CURRENT_BOARD_ID "${CURRENT_BOARD_ID} + 1")
# Preprocess device tree source files
dts_preprocess()
# Generate edt pickle file using gen_edt.py
gen_edt()
# Generate devicetree_generated.h using gen_defines.py
gen_devicetree_header()
endif()
endforeach()
# Generate dynamic device tree API macros, even if there is only one device
# tree source file.
gen_multi_devicetree_header()
endfunction()
nuttx_generate_dts()