Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: use codegen for device instantiation #10888

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,13 @@ add_subdirectory(subsys)
add_subdirectory(drivers)
add_subdirectory(tests)

# Add all generated zephyr sources in the same context as the zephyr library
# is created. Assure all sub directories that might invoke code generation
# are processed before.
get_property(zephyr_generated_sources GLOBAL PROPERTY zephyr_generated_sources_property)
set_source_files_properties(${zephyr_generated_sources} PROPERTIES GENERATED 1)
target_sources(zephyr PRIVATE ${zephyr_generated_sources})

set(syscall_macros_h ${ZEPHYR_BINARY_DIR}/include/generated/syscall_macros.h)

add_custom_target(syscall_macros_h_target DEPENDS ${syscall_macros_h})
Expand Down
151 changes: 151 additions & 0 deletions cmake/extensions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,13 @@ macro(zephyr_library_named name)
# This is a macro because we need add_library() to be executed
# within the scope of the caller.
set(ZEPHYR_CURRENT_LIBRARY ${name})

if("${name}" STREQUAL "zephyr")
# We have to mark all the generated files "GENERATED" in this context
get_property(zephyr_generated_files GLOBAL PROPERTY zephyr_generated_files_property)
set_source_files_properties(${zephyr_generated_files} PROPERTIES GENERATED 1)
endif()

add_library(${name} STATIC "")

zephyr_append_cmake_library(${name})
Expand Down Expand Up @@ -516,6 +523,150 @@ function(generate_inc_file_for_target
generate_inc_file_for_gen_target(${target} ${source_file} ${generated_file} ${generated_target_name} ${ARGN})
endfunction()

function(target_sources_codegen
target # The cmake target that depends on the generated file
)

set(options)
set(oneValueArgs)
set(multiValueArgs CODEGEN_DEFINES DEPENDS)
cmake_parse_arguments(CODEGEN "${options}" "${oneValueArgs}"
"${multiValueArgs}" ${ARGN})
# prepend -D to all defines
string(REGEX REPLACE "([^;]+)" "-D;\\1"
CODEGEN_CODEGEN_DEFINES "${CODEGEN_CODEGEN_DEFINES}")
# Get all the files that make up cogeno for dependency reasons.
file(GLOB CODEGEN_SOURCES
${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/*.py
${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/*.py
${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/extractors/*.py
${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/devicetree/*.py
${ZEPHYR_BASE}/scripts/cogeno/cogeno/modules/edtsdb/bindings/*.yaml
${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/*.c
${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/*.jinja2
${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/zephyr/*.c
${ZEPHYR_BASE}/scripts/cogeno/cogeno/templates/zephyr/*.jinja2
${ZEPHYR_BASE}/scripts/cogeno/cogeno/*.py)

message(STATUS "Will generate for target ${target}")
# Generated file must be generated to the current binary directory.
# Otherwise this would trigger CMake issue #14633:
# https://gitlab.kitware.com/cmake/cmake/issues/14633
foreach(arg ${CODEGEN_UNPARSED_ARGUMENTS})
if(IS_ABSOLUTE ${arg})
set(template_file ${arg})
get_filename_component(generated_file_name ${arg} NAME)
set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${generated_file_name})
else()
set(template_file ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${arg})
endif()
get_filename_component(template_dir ${template_file} DIRECTORY)
get_filename_component(generated_dir ${generated_file} DIRECTORY)

if(IS_DIRECTORY ${template_file})
message(FATAL_ERROR "target_sources_codegen() was called on a directory")
endif()

# Generate file from template
message(STATUS " from '${template_file}'")
message(STATUS " to '${generated_file}'")
add_custom_command(
COMMENT "codegen ${generated_file}"
OUTPUT ${generated_file}
MAIN_DEPENDENCY ${template_file}
DEPENDS
${CODEGEN_DEPENDS}
${CODEGEN_SOURCES}
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/gen_code.py
${CODEGEN_CODEGEN_DEFINES}
-D "\"BOARD=${BOARD}\""
-D "\"APPLICATION_SOURCE_DIR=${APPLICATION_SOURCE_DIR}\""
-D "\"APPLICATION_BINARY_DIR=${APPLICATION_BINARY_DIR}\""
-D "\"PROJECT_NAME=${PROJECT_NAME}\""
-D "\"PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}\""
-D "\"PROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}\""
-D "\"CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}\""
-D "\"CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}\""
-D "\"CMAKE_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}\""
-D "\"CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}\""
-D "\"CMAKE_CURRENT_LIST_DIR=${CMAKE_CURRENT_LIST_DIR}\""
-D "\"CMAKE_FILES_DIRECTORY=${CMAKE_FILES_DIRECTORY}\""
-D "\"CMAKE_PROJECT_NAME=${CMAKE_PROJECT_NAME}\""
-D "\"CMAKE_SYSTEM=${CMAKE_SYSTEM}\""
-D "\"CMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}\""
-D "\"CMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION}\""
-D "\"CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}\""
-D "\"CMAKE_C_COMPILER=${CMAKE_C_COMPILER}\""
-D "\"CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\""
-D "\"CMAKE_COMPILER_IS_GNUCC=${CMAKE_COMPILER_IS_GNUCC}\""
-D "\"CMAKE_COMPILER_IS_GNUCXX=${CMAKE_COMPILER_IS_GNUCXX}\""
-D "\"GENERATED_DTS_BOARD_H=${GENERATED_DTS_BOARD_H}\""
-D "\"GENERATED_DTS_BOARD_CONF=${GENERATED_DTS_BOARD_CONF}\""
--config "${PROJECT_BINARY_DIR}/.config"
--cmakecache "${CMAKE_BINARY_DIR}/CMakeCache.txt"
--dts "${PROJECT_BINARY_DIR}/${BOARD}.dts_compiled"
--bindings "${DTS_APP_BINDINGS}" "${PROJECT_SOURCE_DIR}/dts/bindings"
--edts "${PROJECT_BINARY_DIR}/edts.json"
--modules "${APPLICATION_SOURCE_DIR}/templates" "${PROJECT_SOURCE_DIR}/templates"
--templates "${APPLICATION_SOURCE_DIR}/templates" "${PROJECT_SOURCE_DIR}/templates"
--input "${template_file}"
--output "${generated_file}"
--log "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/codegen.log"
--lock "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/codegen.lock"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
if("${target}" STREQUAL "zephyr")
# zephyr is special
generate_unique_target_name_from_filename(${generated_file} generated_target_name)
add_custom_target(${generated_target_name} DEPENDS ${generated_file})
add_dependencies(zephyr ${generated_target_name})
# Remember all the files that are generated for zephyr.
# target_sources(zephyr PRIVATE ${zephyr_generated_sources})
# is executed in the top level CMakeFile.txt context.
get_property(zephyr_generated_sources GLOBAL PROPERTY zephyr_generated_sources_property)
list(APPEND zephyr_generated_sources ${generated_file})
set_property(GLOBAL PROPERTY zephyr_generated_sources_property "${zephyr_generated_sources}")
# Add template directory to include path to allow includes with
# relative path in generated file to work
zephyr_include_directories(${template_dir})
# Add directory of generated file to include path to allow includes
# of generated header file with relative path.
zephyr_include_directories(${generated_dir})
else()
target_sources(${target} PRIVATE ${generated_file})
# Add template directory to include path to allow includes with
# relative path in generated file to work
target_include_directories(${target} PRIVATE ${template_dir})
# Add directory of generated file to include path to allow includes
# of generated header file with relative path.
target_include_directories(${target} PRIVATE ${generated_dir})
endif()
endforeach()
endfunction()

function(zephyr_sources_codegen)
target_sources_codegen(zephyr ${ARGN})
endfunction()

function(zephyr_sources_codegen_ifdef feature_toggle)
if(${${feature_toggle}})
zephyr_sources_codegen(${ARGN})
endif()
endfunction()

function(zephyr_library_sources_codegen)
target_sources_codegen(${ZEPHYR_CURRENT_LIBRARY} ${ARGN})
endfunction()

function(zephyr_library_sources_codegen_ifdef feature_toggle)
if(${${feature_toggle}})
zephyr_library_sources_codegen(${ARGN})
endif()
endfunction()

# 1.4. board_*
#
# This section is for extensions which control Zephyr's board runners
Expand Down
2 changes: 1 addition & 1 deletion drivers/can/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
zephyr_sources_ifdef(CONFIG_CAN_STM32 stm32_can.c)
zephyr_sources_codegen_ifdef(CONFIG_CAN_STM32 stm32_can.c)
zephyr_sources_ifdef(CONFIG_USERSPACE can_handlers.c)
111 changes: 63 additions & 48 deletions drivers/can/stm32_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -841,51 +841,66 @@ static const struct can_driver_api can_api_funcs = {
.detach = can_stm32_detach
};

#ifdef CONFIG_CAN_1

static void config_can_1_irq(CAN_TypeDef *can);

static const struct can_stm32_config can_stm32_cfg_1 = {
.can = (CAN_TypeDef *)DT_CAN_1_BASE_ADDRESS,
.bus_speed = DT_CAN_1_BUS_SPEED,
.swj = DT_CAN_1_SJW,
.prop_bs1 = DT_CAN_1_PROP_SEG_PHASE_SEG1,
.bs2 = DT_CAN_1_PHASE_SEG2,
.pclken = {
.enr = DT_CAN_1_CLOCK_BITS,
.bus = DT_CAN_1_CLOCK_BUS,
},
.config_irq = config_can_1_irq
};

static struct can_stm32_data can_stm32_dev_data_1;

DEVICE_AND_API_INIT(can_stm32_1, DT_CAN_1_NAME, &can_stm32_init,
&can_stm32_dev_data_1, &can_stm32_cfg_1,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&can_api_funcs);

static void config_can_1_irq(CAN_TypeDef *can)
{
LOG_DBG("Enable CAN1 IRQ");
#ifdef CONFIG_SOC_SERIES_STM32F0X
IRQ_CONNECT(DT_CAN_1_IRQ, DT_CAN_1_IRQ_PRIORITY, can_stm32_isr,
DEVICE_GET(can_stm32_1), 0);
irq_enable(DT_CAN_1_IRQ);
#else
IRQ_CONNECT(DT_CAN_1_IRQ_RX0, DT_CAN_1_IRQ_PRIORITY,
can_stm32_rx_isr, DEVICE_GET(can_stm32_1), 0);
irq_enable(DT_CAN_1_IRQ_RX0);

IRQ_CONNECT(DT_CAN_1_IRQ_TX, DT_CAN_1_IRQ_PRIORITY,
can_stm32_tx_isr, DEVICE_GET(can_stm32_1), 0);
irq_enable(DT_CAN_1_IRQ_TX);

IRQ_CONNECT(DT_CAN_1_IRQ_SCE, DT_CAN_1_IRQ_PRIORITY,
can_stm32_tx_isr, DEVICE_GET(can_stm32_1), 0);
irq_enable(DT_CAN_1_IRQ_SCE);
#endif
can->IER |= CAN_IT_TME | CAN_IT_ERR | CAN_IT_FMP0 | CAN_IT_FMP1;
}

#endif /*CONFIG_CAN_1*/
/**
* @code{.cogeno.py}
* cogeno.import_module('devicedeclare')
*
* device_configs = ['CONFIG_CAN_1', ]
* driver_names = ['CAN_1', ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned several times, this should not be present in API.
This information should be extracted from device tree and drivers <> EDTS binding should be done by 'compatible' property. This is literally its main and only use.
cf #8561

Copy link
Collaborator Author

@b0661 b0661 Oct 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentionend several times to identify a specific driver instance the compatible is not enough. There are usual several driver instantiations for one compatible. So you need something more to identify a single one and use the 'label' property for that. But if you use the 'label' property in addition to the 'compatible' you find out that the 'label' alone is unique und you really do not need the 'compatible'. Taking the 'compatible' and the 'label' is just a waste of computing.

If you would look into the code of your device_declare variant you could easily identify that in the end only the 'label' is used.

* device_inits = 'can_stm32_init'
* device_levels = 'POST_KERNEL'
* device_prios = 'CONFIG_KERNEL_INIT_PRIORITY_DEVICE'
* device_api = 'can_api_funcs'
* device_info = \
* """
* DEVICE_DECLARE(${device-name});
* static void ${device-config-irq}(CAN_TypeDef *can)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole piece of code, IRQ configuration functions, should be extracted by API.
API should be able to detect if node specifies IRQs, and generated this code if needed, cf #8561

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know your opinion. This is just to show the principle. You can use your device_declare variant here also. Your variant will fail with STM32F0 UART drivers. A lot of shared interrupts where you definitely need a more sofisticated way to generate interrupt routines. The driver is OOT but I can tell it definitely did not work with the driver_declare interrupt configuration - your one and also not the simple one that I have here.

* {
* LOG_DBG("Enable ${driver-name} IRQ");
* #ifdef CONFIG_SOC_SERIES_STM32F0X
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'compatible' should be used to identify and provide variations of the same driver.
#ifdef CONFIG_SOC_SERIES_STM32F0X should not be present in templated code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey this is a proof of concept and not a full blown driver rework, I expect the CAN driver author to do the rework in the end when there is a code generation facility. My focus was the extraction of device tree data not to make that work without some ifdefs.

* IRQ_CONNECT(${interrupts/0/irq}, ${interrupts/0/priority},\\
* can_stm32_isr,
* DEVICE_GET(${device-name}), 0);
* irq_enable(${interrupts/0/irq});
* #else
* IRQ_CONNECT(${interrupts/rx0/irq}, ${interrupts/rx0/priority}, \\
* can_stm32_rx_isr,
* DEVICE_GET(${device-name}), 0);
* irq_enable(${interrupts/rx0/irq});
* IRQ_CONNECT(${interrupts/tx/irq}, ${interrupts/tx/priority}, \\
* can_stm32_tx_isr,
* DEVICE_GET(${device-name}), 0);
* irq_enable(${interrupts/tx/irq});
* IRQ_CONNECT(${interrupts/sce/irq}, ${interrupts/sce/priority}, \\
* can_stm32_tx_isr,
* DEVICE_GET(${device-name}), 0);
* irq_enable(${interrupts/sce/irq});
* #endif
* can->IER |= CAN_IT_TME | CAN_IT_ERR | CAN_IT_FMP0 | CAN_IT_FMP1;
* }
* static const struct can_stm32_config ${device-config-info} = {
* .can = (CAN_TypeDef *)${reg/0/address/0},
* .bus_speed = ${bus-speed},
* .swj = ${sjw},
* .prop_bs1 = ${prop_seg_phase_seg1},
* .bs2 = ${phase_seg2},
* .pclken = {
* .enr = ${clocks/0/bits},
* .bus = ${clocks/0/bus},
* },
* .config_irq = ${device-config-irq},
* };
* static struct can_stm32_data ${device-data};
* """
*
* devicedeclare.device_declare_multi( \
* device_configs,
* driver_names,
* device_inits,
* device_levels,
* device_prios,
* device_api,
* device_info)
* @endcode{.cogeno.py}
*/
/** @code{.cogeno.ins}@endcode */
13 changes: 5 additions & 8 deletions drivers/i2c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@ zephyr_library_sources_ifdef(CONFIG_I2C_SBCON i2c_sbcon.c)
zephyr_library_sources_ifdef(CONFIG_I2C_NIOS2 i2c_nios2.c)
zephyr_library_sources_ifdef(CONFIG_I2C_GECKO i2c_gecko.c)

zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1
i2c_ll_stm32_v1.c
i2c_ll_stm32.c
)
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2
i2c_ll_stm32_v2.c
i2c_ll_stm32.c
)
zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32_v1.c)
zephyr_library_sources_codegen_ifdef(CONFIG_I2C_STM32_V1 i2c_ll_stm32.c)

zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32_v2.c)
zephyr_library_sources_codegen_ifdef(CONFIG_I2C_STM32_V2 i2c_ll_stm32.c)

zephyr_library_sources_ifdef(CONFIG_USERSPACE i2c_handlers.c)

Expand Down
Loading