diff --git a/arch/Kconfig b/arch/Kconfig index d059e2a2a221..859e9a4c3b04 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -472,6 +472,7 @@ config ISR_TABLES_LOCAL_DECLARATION config DYNAMIC_INTERRUPTS bool "Installation of IRQs at runtime" + select SRAM_SW_ISR_TABLE help Enable installation of interrupts at runtime, which will move some interrupt-related data structures to RAM instead of ROM, and @@ -598,6 +599,12 @@ config SRAM_VECTOR_TABLE When XiP is enabled, this option will result in the vector table being relocated from Flash to SRAM. +config SRAM_SW_ISR_TABLE + bool "Place the software ISR table in SRAM instead of flash" + help + The option specifies that the software interrupts vector table will be + placed inside SRAM instead of the flash. + config IRQ_OFFLOAD_NESTED bool "irq_offload() supports nested IRQs" depends on IRQ_OFFLOAD diff --git a/arch/arm/core/CMakeLists.txt b/arch/arm/core/CMakeLists.txt index 922dab2ddba5..ccfa5b6c777c 100644 --- a/arch/arm/core/CMakeLists.txt +++ b/arch/arm/core/CMakeLists.txt @@ -37,7 +37,7 @@ zephyr_linker_sources(ROM_START SORT_KEY 0x1vectors cortex_m/vector_table_pad.ld endif() if(CONFIG_GEN_SW_ISR_TABLE) - if(CONFIG_DYNAMIC_INTERRUPTS) + if(CONFIG_SRAM_SW_ISR_TABLE) zephyr_linker_sources(RWDATA swi_tables.ld) else() zephyr_linker_sources(RODATA swi_tables.ld) diff --git a/arch/arm64/core/CMakeLists.txt b/arch/arm64/core/CMakeLists.txt index 9112e55f302e..1c1070416327 100644 --- a/arch/arm64/core/CMakeLists.txt +++ b/arch/arm64/core/CMakeLists.txt @@ -60,7 +60,7 @@ endif() add_subdirectory_ifdef(CONFIG_XEN xen) if(CONFIG_GEN_SW_ISR_TABLE) - if(CONFIG_DYNAMIC_INTERRUPTS) + if(CONFIG_SRAM_SW_ISR_TABLE) zephyr_linker_sources(RWDATA swi_tables.ld) else() zephyr_linker_sources(RODATA swi_tables.ld) diff --git a/cmake/linker_script/common/common-ram.cmake b/cmake/linker_script/common/common-ram.cmake index 23fae8c69484..210ab4be1af1 100644 --- a/cmake/linker_script/common/common-ram.cmake +++ b/cmake/linker_script/common/common-ram.cmake @@ -2,7 +2,7 @@ # The contents of this file is based on include/zephyr/linker/common-ram.ld # Please keep in sync -if(CONFIG_GEN_SW_ISR_TABLE AND CONFIG_DYNAMIC_INTERRUPTS) +if(CONFIG_GEN_SW_ISR_TABLE AND CONFIG_SRAM_SW_ISR_TABLE) # ld align has been changed to subalign to provide identical behavior scatter vs. ld. zephyr_linker_section(NAME sw_isr_table GROUP DATA_REGION diff --git a/cmake/linker_script/common/common-rom.cmake b/cmake/linker_script/common/common-rom.cmake index c56ccd3670be..bdd76b8e97ff 100644 --- a/cmake/linker_script/common/common-rom.cmake +++ b/cmake/linker_script/common/common-rom.cmake @@ -14,7 +14,7 @@ zephyr_linker_section_obj_level(SECTION init LEVEL SMP) zephyr_iterable_section(NAME device NUMERIC KVMA RAM_REGION GROUP RODATA_REGION) zephyr_iterable_section(NAME service NUMERIC KVMA RAM_REGION GROUP RODATA_REGION) -if(CONFIG_GEN_SW_ISR_TABLE AND NOT CONFIG_DYNAMIC_INTERRUPTS) +if(CONFIG_GEN_SW_ISR_TABLE AND NOT CONFIG_SRAM_SW_ISR_TABLE) # ld align has been changed to subalign to provide identical behavior scatter vs. ld. zephyr_linker_section(NAME sw_isr_table KVMA FLASH GROUP RODATA_REGION SUBALIGN ${CONFIG_ARCH_SW_ISR_TABLE_ALIGN} NOINPUT) zephyr_linker_section_configure( diff --git a/doc/releases/migration-guide-4.3.rst b/doc/releases/migration-guide-4.3.rst index f668d9ef6f56..34071417b65e 100644 --- a/doc/releases/migration-guide-4.3.rst +++ b/doc/releases/migration-guide-4.3.rst @@ -46,3 +46,6 @@ Modules Architectures ************* + +* The :kconfig:option:`CONFIG_DYNAMIC_INTERRUPTS` option has a new dependency on + :kconfig:option:`CONFIG_SRAM_SW_ISR_TABLE`. diff --git a/doc/releases/release-notes-4.3.rst b/doc/releases/release-notes-4.3.rst index 71aea3a12018..efd50244cf1e 100644 --- a/doc/releases/release-notes-4.3.rst +++ b/doc/releases/release-notes-4.3.rst @@ -63,6 +63,10 @@ New APIs and options like you need to add more details, add them in the API documentation code instead. +* Architectures + + * :kconfig:option:`CONFIG_SRAM_SW_ISR_TABLE` + New Boards ********** diff --git a/include/zephyr/linker/common-ram.ld b/include/zephyr/linker/common-ram.ld index 78016f9572f4..792574c9d37a 100644 --- a/include/zephyr/linker/common-ram.ld +++ b/include/zephyr/linker/common-ram.ld @@ -17,7 +17,7 @@ ITERABLE_SECTION_RAM(scmi_protocol, Z_LINK_ITERABLE_SUBALIGN) #endif /* CONFIG_ARM_SCMI */ -#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_DYNAMIC_INTERRUPTS) +#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_SRAM_SW_ISR_TABLE) SECTION_DATA_PROLOGUE(sw_isr_table,,) { /* diff --git a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld index 2bb7d2c509ce..2cad86b76df4 100644 --- a/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld +++ b/include/zephyr/linker/common-rom/common-rom-kernel-devices.ld @@ -44,7 +44,7 @@ } GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) #endif -#if defined(CONFIG_GEN_SW_ISR_TABLE) && !defined(CONFIG_DYNAMIC_INTERRUPTS) +#if defined(CONFIG_GEN_SW_ISR_TABLE) && !defined(CONFIG_SRAM_SW_ISR_TABLE) SECTION_PROLOGUE(sw_isr_table,,) { /* diff --git a/tests/application_development/ram_context_for_isr/CMakeLists.txt b/tests/application_development/ram_context_for_isr/CMakeLists.txt new file mode 100644 index 000000000000..abbe49f04513 --- /dev/null +++ b/tests/application_development/ram_context_for_isr/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2025 Silicon Laboratories Inc. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(ram_context_for_isr) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + +if(CONFIG_CPU_CORTEX_M_HAS_VTOR) + zephyr_code_relocate(FILES ${ZEPHYR_BASE}/arch/arm/core/cortex_m/isr_wrapper.c LOCATION RAM) + zephyr_code_relocate(FILES ${ZEPHYR_BASE}/arch/arm/core/cortex_m/exc_exit.c LOCATION RAM) +endif() + +zephyr_code_relocate(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c FILTER ".test_irq_callback" LOCATION RAM ) +zephyr_code_relocate(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/fake_driver.c FILTER ".fake_driver_isr" LOCATION RAM ) + +# Only needed because the fake driver is defined in tests folder +zephyr_linker_sources(SECTIONS sections-rom.ld) diff --git a/tests/application_development/ram_context_for_isr/README.rst b/tests/application_development/ram_context_for_isr/README.rst new file mode 100644 index 000000000000..c8dc83d7d379 --- /dev/null +++ b/tests/application_development/ram_context_for_isr/README.rst @@ -0,0 +1,94 @@ +.. _vector_table_relocation: + +RAM Context for ISR +################### + +Overview +******** +A test that verifies the ISR vector table relocation and +full IRQ execution under RAM context. + + +Test Description +**************** + +This test verifies that interrupt service routines (ISRs), their callbacks, and the driver code +can be fully relocated to RAM, ensuring that all interrupt handling occurs from SRAM rather than +flash. This is important for real-time applications where minimizing interrupt latency is critical. + +The test uses a simple "fake driver" that registers an IRQ callback. The following aspects are +validated: + +- The vector table and ISR wrapper are relocated to SRAM. +- The fake driver's ISR and the registered callback are also located in SRAM. +- When the interrupt is triggered, the callback and all relevant code execute from RAM, not flash. +- The test asserts at runtime that the addresses of the callback, driver ISR, and ISR wrapper are + within the SRAM address range. + +Test Steps +********** + +1. The test registers a callback with the fake driver. +2. It triggers the interrupt (IRQ 27). +3. The callback checks (using assertions) that: + - Its own address, + - The driver's ISR address, + - The ISR wrapper address, + - And the device structure, + are all within the SRAM region. +4. The test passes if all assertions succeed and the callback is executed. + +Configuration +************* + +The test is configured with the following options in prj.conf: + +- ``CONFIG_SRAM_VECTOR_TABLE=y``: Relocate the vector table to SRAM. +- ``CONFIG_SRAM_SW_ISR_TABLE=y``: Relocate the software ISR table to SRAM. +- ``CONFIG_CODE_DATA_RELOCATION=y``: Enable code/data relocation to SRAM. +- ``CONFIG_DEVICE_MUTABLE=y``: Allow device structures to be mutable (in RAM). +- ``CONFIG_ZTEST=y``: Enable Zephyr's test framework. + +The test is only supported on ARM Cortex-M platforms with VTOR (Vector Table Offset Register) support. + +Advanced Configuration and Limitations +************************************** + +Configuration Options in testcase.yaml +====================================== + +By default, the test disables several options in testcase.yaml: + +- ``CONFIG_TRACING_ISR=n``: Disables ISR tracing. + **If enabled:** Enabling ISR tracing adds hooks to trace ISR entry/exit, which may introduce + additional code paths not relocated to RAM. This can increase interrupt latency and may cause + some tracing code to execute from flash, partially defeating the purpose of full RAM relocation. + +- ``CONFIG_STACK_SENTINEL=n``: Disables stack overflow detection sentinels. + **If enabled:** Enabling stack sentinels adds extra checks to detect stack overflows. These + checks may reside in flash and be called during interrupt handling, potentially increasing + latency and causing some code to execute from flash. + +- ``CONFIG_PM=n``: Disables power management. + **If enabled:** Enabling power management may cause the system to enter low-power states. Some + wake-up interrupts may not use the relocated vector table in RAM, and the system may revert to + using the flash-based vector table to exit idle states. This can result in some ISRs or their + wrappers executing from flash, especially during wake-up from deep sleep. + +**Summary:** +If you enable any of these options, ensure all code paths executed during interrupt handling are +also relocated to RAM to maintain the lowest possible interrupt latency. Otherwise, some code may +still execute from flash, increasing latency and partially defeating the test's purpose. + +Driver API Location and Limitations +=================================== + +**Important Note:** +While the test ensures ISR, callback, and device structures are relocated to RAM, the driver API +structure (``struct fake_driver_api``)—containing function pointers for driver methods—is not +relocated to RAM by default. This structure typically resides in flash (ROM) as device constant data. + +As long as you don't call the driver API during ISR execution, the current configuration is sufficient. +However, if you need to call driver API functions from within an ISR, you must relocate the driver API +structure to RAM. The simplest approach is to relocate the entire driver file to RAM rather than just +the ISR symbol. diff --git a/tests/application_development/ram_context_for_isr/app.overlay b/tests/application_development/ram_context_for_isr/app.overlay new file mode 100644 index 000000000000..15899c033044 --- /dev/null +++ b/tests/application_development/ram_context_for_isr/app.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + #address-cells = <1>; + #size-cells = <1>; + + fakedriver: fakedriver@E0000000 { + compatible = "fakedriver"; + reg = <0xE0000000 0x2000>; + zephyr,mutable; + status = "okay"; + }; +}; diff --git a/tests/application_development/ram_context_for_isr/dts/bindings/fakedriver.yml b/tests/application_development/ram_context_for_isr/dts/bindings/fakedriver.yml new file mode 100644 index 000000000000..80821fcbc4fa --- /dev/null +++ b/tests/application_development/ram_context_for_isr/dts/bindings/fakedriver.yml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 Silicon Laboratories +# SPDX-License-Identifier: Apache-2.0 + +description: Properties for fake driver. + +compatible: "fakedriver" + +include: [base.yaml, mutable.yaml] diff --git a/tests/application_development/ram_context_for_isr/include/fake_driver.h b/tests/application_development/ram_context_for_isr/include/fake_driver.h new file mode 100644 index 000000000000..198f0123976a --- /dev/null +++ b/tests/application_development/ram_context_for_isr/include/fake_driver.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_FAKE_DRIVER_H_ +#define ZEPHYR_INCLUDE_DRIVERS_FAKE_DRIVER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*fake_driver_irq_callback_t)(const struct device *dev, void *user_data); + +struct fake_driver_config { + void (*irq_config_func)(void); + uint8_t irq_num; + uint8_t irq_priority; +}; + +struct fake_driver_data { + fake_driver_irq_callback_t irq_callback; + void *user_data; +}; + +__subsystem struct fake_driver_api { + int (*configure)(const struct device *dev, int config); + int (*register_irq_callback)(const struct device *dev, fake_driver_irq_callback_t cb, + void *user_data); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_FAKE_DRIVER_H_ */ diff --git a/tests/application_development/ram_context_for_isr/prj.conf b/tests/application_development/ram_context_for_isr/prj.conf new file mode 100644 index 000000000000..a6e8cd2353db --- /dev/null +++ b/tests/application_development/ram_context_for_isr/prj.conf @@ -0,0 +1,6 @@ +CONFIG_COVERAGE=n +CONFIG_ZTEST=y +CONFIG_SRAM_VECTOR_TABLE=y +CONFIG_SRAM_SW_ISR_TABLE=y +CONFIG_CODE_DATA_RELOCATION=y +CONFIG_DEVICE_MUTABLE=y diff --git a/tests/application_development/ram_context_for_isr/sections-rom.ld b/tests/application_development/ram_context_for_isr/sections-rom.ld new file mode 100644 index 000000000000..eb4193b3433b --- /dev/null +++ b/tests/application_development/ram_context_for_isr/sections-rom.ld @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* Only needed because the driver is defined in tests folder */ +ITERABLE_SECTION_ROM(fake_driver_api, Z_LINK_ITERABLE_SUBALIGN) diff --git a/tests/application_development/ram_context_for_isr/src/fake_driver.c b/tests/application_development/ram_context_for_isr/src/fake_driver.c new file mode 100644 index 000000000000..0c2ff0ce160f --- /dev/null +++ b/tests/application_development/ram_context_for_isr/src/fake_driver.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "fake_driver.h" + +#define DT_DRV_COMPAT fakedriver + +#define TEST_IRQ_NUM 27 +#define TEST_IRQ_PRIO 4 + +static void fake_driver_isr(const void *arg) +{ + const struct device *dev = (const struct device *)arg; + struct fake_driver_data *data = dev->data; + + /* Store the address of fake_driver_isr in user_data because it will be optimized by the + * compiler (even with __noinline__ attribute) + */ + data->user_data = (void *)&fake_driver_isr; + + if (data->irq_callback != NULL) { + data->irq_callback(dev, data->user_data); + } +} + +static int fake_driver_configure(const struct device *dev, int config) +{ + return 0; +} + +static int fake_driver_register_irq_callback(const struct device *dev, + fake_driver_irq_callback_t cb, void *user_data) +{ + struct fake_driver_data *data = dev->data; + + data->irq_callback = cb; + data->user_data = user_data; + + return 0; +} + +DEVICE_API(fake, fake_driver_func) = { + .configure = fake_driver_configure, + .register_irq_callback = fake_driver_register_irq_callback, +}; + +static int fake_driver_init(const struct device *dev) +{ + const struct fake_driver_config *config = dev->config; + struct fake_driver_data *data = dev->data; + + data->irq_callback = NULL; + data->user_data = NULL; + + config->irq_config_func(); + + return 0; +} + +#define FAKE_INIT(inst) \ + static struct fake_driver_data fake_driver_data_##inst; \ + static void fake_driver_irq_config_func_##inst(void) \ + { \ + IRQ_CONNECT(TEST_IRQ_NUM, TEST_IRQ_PRIO, fake_driver_isr, \ + DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(TEST_IRQ_NUM); \ + } \ + static struct fake_driver_config fake_driver_config_##inst = { \ + .irq_config_func = fake_driver_irq_config_func_##inst, \ + .irq_num = TEST_IRQ_NUM, \ + .irq_priority = TEST_IRQ_PRIO, \ + }; \ + DEVICE_DT_INST_DEFINE(inst, &fake_driver_init, NULL, &fake_driver_data_##inst, \ + &fake_driver_config_##inst, PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &fake_driver_func); + +DT_INST_FOREACH_STATUS_OKAY(FAKE_INIT) diff --git a/tests/application_development/ram_context_for_isr/src/main.c b/tests/application_development/ram_context_for_isr/src/main.c new file mode 100644 index 000000000000..a38f3a39370b --- /dev/null +++ b/tests/application_development/ram_context_for_isr/src/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 Silicon Laboratories Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Test for driver relocation to RAM for ISRs + * + * This test demonstrates how to use the fake_driver and verify + * that the callback are properly relocated to RAM. + */ + +#if !defined(CONFIG_CPU_CORTEX_M) +#error project can only run on Cortex-M for now +#endif + +#include +#include "fake_driver.h" + +static volatile bool test_flag; + +static void test_irq_callback(const struct device *dev, void *user_data) +{ + uintptr_t func_addr = (uintptr_t)test_irq_callback; + uintptr_t driver_isr_addr, arch_isr_wrapper_addr; + + test_flag = true; + + /* Retrieve the caller address (the arch specific isr wrapper since driver isr will be + * optimized by the compiler) + */ + __asm__ volatile("mov %0, lr" : "=r"(arch_isr_wrapper_addr)); + + /* retrieve the fake_driver_isr function address that was stored in user_data */ + driver_isr_addr = (uintptr_t)user_data; + + /* Check that the function and its call stack are in RAM */ + zassert_true(func_addr >= CONFIG_SRAM_BASE_ADDRESS && + func_addr <= CONFIG_SRAM_BASE_ADDRESS + CONFIG_SRAM_SIZE * 1024, + "%s is not in RAM! Address: 0x%x", __func__, func_addr); + + zassert_true(driver_isr_addr >= CONFIG_SRAM_BASE_ADDRESS && + driver_isr_addr <= CONFIG_SRAM_BASE_ADDRESS + CONFIG_SRAM_SIZE * 1024, + "fake_driver_isr is not in RAM! Address: 0x%x", driver_isr_addr); + + zassert_true(arch_isr_wrapper_addr >= CONFIG_SRAM_BASE_ADDRESS && + arch_isr_wrapper_addr <= + CONFIG_SRAM_BASE_ADDRESS + CONFIG_SRAM_SIZE * 1024, + "arch_isr_wrapper_addr is not in RAM! Address: 0x%x", arch_isr_wrapper_addr); + + TC_PRINT("Callback function address: 0x%lx\n", func_addr); + TC_PRINT("Driver ISR address: 0x%lx\n", driver_isr_addr); + TC_PRINT("Arch ISR wrapper address: 0x%lx\n", arch_isr_wrapper_addr); +} + +ZTEST(ram_context_for_isr, test_fake_driver_in_ram) +{ + const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(fakedriver)); + const struct fake_driver_api *api = DEVICE_API_GET(fake, dev); + uintptr_t dev_addr = (uintptr_t)dev; + + zassert_true(dev_addr >= CONFIG_SRAM_BASE_ADDRESS && + dev_addr <= CONFIG_SRAM_BASE_ADDRESS + CONFIG_SRAM_SIZE * 1024, + "fake driver device is not in RAM! Address: 0x%x", dev_addr); + + TC_PRINT("Fake driver device address: 0x%lx\n", dev_addr); + + zassert_not_null(api, "Failed to get fake driver API"); + + api->register_irq_callback(dev, test_irq_callback, NULL); + test_flag = false; + + NVIC_SetPendingIRQ(27); + + k_busy_wait(1000); + + zassert_true(test_flag, "ISR callback was not called"); +} + +ZTEST_SUITE(ram_context_for_isr, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/application_development/ram_context_for_isr/testcase.yaml b/tests/application_development/ram_context_for_isr/testcase.yaml new file mode 100644 index 000000000000..370ffbb1b781 --- /dev/null +++ b/tests/application_development/ram_context_for_isr/testcase.yaml @@ -0,0 +1,19 @@ +common: + tags: linker +tests: + application_development.ram_context_for_isr.arm: + arch_allow: arm + filter: CONFIG_CPU_CORTEX_M_HAS_VTOR + extra_configs: + - CONFIG_CODE_DATA_RELOCATION_SRAM=y + # In case you want to add this config, place all the possible called code in RAM + - CONFIG_TRACING_ISR=n + - CONFIG_STACK_SENTINEL=n + - CONFIG_PM=n + # Exclude mps3/corstone310 because it uses another mechanism to support relocation + # of the vector table (CONFIG_ROMSTART_RELOCATION_ROM). + platform_exclude: + - mps3/corstone310/an555 + - mps3/corstone310/fvp + - mps4/corstone315/fvp + - mps4/corstone320/fvp