diff --git a/components/ulp/test_apps/ulp_fsm/main/test_ulp.c b/components/ulp/test_apps/ulp_fsm/main/test_ulp.c index 848c37f882..f148ed3a7c 100644 --- a/components/ulp/test_apps/ulp_fsm/main/test_ulp.c +++ b/components/ulp/test_apps/ulp_fsm/main/test_ulp.c @@ -676,3 +676,49 @@ TEST_CASE("ULP FSM can use ADC in deep sleep", "[ulp][ulp_deep_sleep_wakeup]") esp_deep_sleep_start(); UNITY_TEST_FAIL(__LINE__, "Should not get here!"); } + +static void ulp_isr(void *arg) +{ + BaseType_t yield = 0; + SemaphoreHandle_t sem = (SemaphoreHandle_t)arg; + xSemaphoreGiveFromISR(sem, &yield); + if (yield) { + portYIELD_FROM_ISR(); + } +} + +TEST_CASE("ULP FSM interrupt signal can be handled via ISRs on the main core", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + + /* Clear the RTC_SLOW_MEM region for the ULP co-processor binary to be loaded */ + hal_memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + /* ULP co-processor program to send a wakeup to the main CPU */ + const ulp_insn_t program[] = { + I_WAKE(), // send wakeup signal to main CPU + I_END(), // stop ULP timer + I_HALT() // halt + }; + + /* Create test semaphore */ + SemaphoreHandle_t ulp_isr_sem = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(ulp_isr_sem); + + /* Register ULP wakeup signal ISR */ + TEST_ASSERT_EQUAL(ESP_OK, ulp_isr_register(ulp_isr, (void *)ulp_isr_sem)); + + /* Calculate the size of the ULP co-processor binary, load it and run the ULP coprocessor */ + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ASSERT_EQUAL(ESP_OK, ulp_process_macros_and_load(0, program, &size)); + TEST_ASSERT_EQUAL(ESP_OK, ulp_run(0)); + + /* Wait from ISR to be called */ + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(ulp_isr_sem, portMAX_DELAY)); + + /* Deregister the ISR */ + TEST_ASSERT_EQUAL(ESP_OK, ulp_isr_deregister(ulp_isr, (void *)ulp_isr_sem )); + + /* Delete test semaphore */ + vSemaphoreDelete(ulp_isr_sem); +} diff --git a/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c b/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c index 8362b777a0..8336fb5d3c 100644 --- a/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c +++ b/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c @@ -20,6 +20,7 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/semphr.h" #define ULP_WAKEUP_PERIOD 1000000 // 1 second @@ -353,3 +354,83 @@ static void do_ulp_wakeup_after_short_delay_deepsleep_rtc_perip_on(void) TEST_CASE_MULTIPLE_STAGES("ULP-RISC-V is able to wakeup main CPU from deep sleep after a short delay, RTC periph powerup", "[ulp]", do_ulp_wakeup_after_short_delay_deepsleep_rtc_perip_on, check_reset_reason_ulp_wakeup); + +typedef struct { + SemaphoreHandle_t ulp_isr_sw_sem; + SemaphoreHandle_t ulp_isr_trap_sem; +} TestSemaphore_t; + +static void ulp_riscv_isr(void *arg) +{ + BaseType_t yield = 0; + TestSemaphore_t *sem = (TestSemaphore_t *)arg; + + uint32_t status = READ_PERI_REG(RTC_CNTL_INT_ST_REG); + + if (status & ULP_RISCV_SW_INT) { + xSemaphoreGiveFromISR(sem->ulp_isr_sw_sem, &yield); + } else if (status & ULP_RISCV_TRAP_INT) { + xSemaphoreGiveFromISR(sem->ulp_isr_trap_sem, &yield); + } + + if (yield) { + portYIELD_FROM_ISR(); + } +} + +TEST_CASE("ULP-RISC-V interrupt signals can be handled via ISRs on the main core", "[ulp]") +{ + /* Create test semaphores */ + TestSemaphore_t test_sem_cfg; + + test_sem_cfg.ulp_isr_sw_sem = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(test_sem_cfg.ulp_isr_sw_sem); + + test_sem_cfg.ulp_isr_trap_sem = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(test_sem_cfg.ulp_isr_trap_sem); + + /* Register ULP RISC-V signal ISR */ + TEST_ASSERT_EQUAL(ESP_OK, ulp_riscv_isr_register(ulp_riscv_isr, (void *)&test_sem_cfg, + (ULP_RISCV_SW_INT | ULP_RISCV_TRAP_INT))); + + /* Load ULP RISC-V firmware and start the ULP RISC-V Coprocessor */ + printf("Loading good ULP firmware\n"); + firmware_loaded = false; + load_and_start_ulp_firmware(ulp_main_bin_start, ulp_main_bin_length); + + /* Setup test data. We only need the ULP to send a wakeup signal to the main CPU. */ + ulp_main_cpu_command = RISCV_READ_WRITE_TEST; + + /* Wait for the ISR to be called */ + printf("Waiting for ULP wakeup signal ...\n"); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(test_sem_cfg.ulp_isr_sw_sem, portMAX_DELAY)); + printf("ULP wakeup signal interrupt triggered\n"); + + /* Reset the ULP command */ + ulp_main_cpu_command = RISCV_NO_COMMAND; + + /* Load ULP RISC-V with faulty firmware and restart the ULP RISC-V Coprocessor. + * This should cause a cocpu trap signal interrupt. + */ + printf("Loading faulty ULP firmware\n"); + firmware_loaded = false; + load_and_start_ulp_firmware(ulp_test_app3_bin_start, ulp_test_app3_bin_length); + + /* Wait for the ISR to be called */ + printf("Waiting for ULP trap signal ...\n"); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(test_sem_cfg.ulp_isr_trap_sem, portMAX_DELAY)); + printf("ULP trap signal interrupt triggered\n"); + + /* Deregister the ISR */ + TEST_ASSERT_EQUAL(ESP_OK, ulp_riscv_isr_deregister(ulp_riscv_isr, (void *)&test_sem_cfg, + (ULP_RISCV_SW_INT | ULP_RISCV_TRAP_INT))); + + /* Delete test semaphores */ + vSemaphoreDelete(test_sem_cfg.ulp_isr_sw_sem); + vSemaphoreDelete(test_sem_cfg.ulp_isr_trap_sem); + + /* Make sure ULP has a good firmware running before exiting the test */ + ulp_riscv_reset(); + firmware_loaded = false; + load_and_start_ulp_firmware(ulp_main_bin_start, ulp_main_bin_length); +} diff --git a/components/ulp/ulp_fsm/include/ulp_fsm_common.h b/components/ulp/ulp_fsm/include/ulp_fsm_common.h index e7d1013d3d..306eb59ea0 100644 --- a/components/ulp/ulp_fsm/include/ulp_fsm_common.h +++ b/components/ulp/ulp_fsm/include/ulp_fsm_common.h @@ -9,6 +9,8 @@ esp32s2/ulp.h and esp32s3/ulp.h */ +#include "esp_intr_alloc.h" + #ifdef __cplusplus extern "C" { #endif @@ -26,6 +28,32 @@ union ulp_insn; // Declared in the chip-specific ulp.h header typedef union ulp_insn ulp_insn_t; +/** + * @brief Register ULP wakeup signal ISR + * + * @note The ISR routine will only be active if the main CPU is not in deepsleep + * + * @param fn ISR callback function + * @param arg ISR callback function arguments + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if callback function is NULL + * - ESP_ERR_NO_MEM if heap memory cannot be allocated for the interrupt + */ +esp_err_t ulp_isr_register(intr_handler_t fn, void *arg); + +/** + * @brief Deregister ULP wakeup signal ISR + * + * @param fn ISR callback function + * @param arg ISR callback function arguments + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if callback function is NULL + * - ESP_ERR_INVALID_STATE if a handler matching both callback function and its arguments isn't registered + */ +esp_err_t ulp_isr_deregister(intr_handler_t fn, void *arg); + /** * @brief Resolve all macro references in a program and load it into RTC memory * @param load_addr address where the program should be loaded, expressed in 32-bit words diff --git a/components/ulp/ulp_fsm/ulp.c b/components/ulp/ulp_fsm/ulp.c index f6c21b3dd1..ebb405fd68 100644 --- a/components/ulp/ulp_fsm/ulp.c +++ b/components/ulp/ulp_fsm/ulp.c @@ -28,6 +28,9 @@ #include "ulp_common.h" #include "esp_rom_sys.h" +#include "esp_check.h" +#include "esp_private/rtc_ctrl.h" + typedef struct { uint32_t magic; uint16_t text_offset; @@ -40,6 +43,24 @@ typedef struct { static const char* TAG = "ulp"; +esp_err_t ulp_isr_register(intr_handler_t fn, void *arg) +{ + ESP_RETURN_ON_FALSE(fn, ESP_ERR_INVALID_ARG, TAG, "ULP ISR is NULL"); + REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_ULP_CP_INT_ENA_M); +#if CONFIG_IDF_TARGET_ESP32 + return rtc_isr_register(fn, arg, RTC_CNTL_SAR_INT_ST_M, 0); +#else + return rtc_isr_register(fn, arg, RTC_CNTL_ULP_CP_INT_ST_M, 0); +#endif /* CONFIG_IDF_TARGET_ESP32 */ +} + +esp_err_t ulp_isr_deregister(intr_handler_t fn, void *arg) +{ + ESP_RETURN_ON_FALSE(fn, ESP_ERR_INVALID_ARG, TAG, "ULP ISR is NULL"); + REG_CLR_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_ULP_CP_INT_ENA_M); + return rtc_isr_deregister(fn, arg); +} + esp_err_t ulp_run(uint32_t entry_point) { #if CONFIG_IDF_TARGET_ESP32 diff --git a/components/ulp/ulp_riscv/include/ulp_riscv.h b/components/ulp/ulp_riscv/include/ulp_riscv.h index bf7056f7b7..a92a27d2ff 100644 --- a/components/ulp/ulp_riscv/include/ulp_riscv.h +++ b/components/ulp/ulp_riscv/include/ulp_riscv.h @@ -10,6 +10,7 @@ #include #include "esp_err.h" #include "ulp_common.h" +#include "esp_intr_alloc.h" #ifdef __cplusplus extern "C" { @@ -33,6 +34,43 @@ typedef struct { .wakeup_source = ULP_RISCV_WAKEUP_SOURCE_TIMER, \ } +/* ULP RISC-V interrupt signals for the main CPU */ +#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) +#define ULP_RISCV_SW_INT (BIT(13)) // Corresponds to RTC_CNTL_COCPU_INT_ST_M interrupt status bit +#define ULP_RISCV_TRAP_INT (BIT(17)) // Corresponds to RTC_CNTL_COCPU_TRAP_INT_ST_M interrupt status bit +#else +#error "ULP_RISCV_SW_INT and ULP_RISCV_TRAP_INT are undefined. Please check soc/rtc_cntl_reg.h for the correct bitmap on your target SoC." +#endif /* (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) */ + +/** + * @brief Register ULP signal ISR + * + * @note The ISR routine will only be active if the main CPU is not in deepsleep + * + * @param fn ISR callback function + * @param arg ISR callback function arguments + * @param mask Bit mask to enable the required ULP RISC-V interrupt signals + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if callback function is NULL or if the interrupt bits are invalid + * - ESP_ERR_NO_MEM if heap memory cannot be allocated for the interrupt + * - other errors returned by esp_intr_alloc + */ +esp_err_t ulp_riscv_isr_register(intr_handler_t fn, void *arg, uint32_t mask); + +/** + * @brief Deregister ULP signal ISR + * + * @param fn ISR callback function + * @param arg ISR callback function arguments + * @param mask Bit mask to enable the required ULP RISC-V interrupt signals + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if callback function is NULL or if the interrupt bits are invalid + * - ESP_ERR_INVALID_STATE if a handler matching both callback function and its arguments isn't registered + */ +esp_err_t ulp_riscv_isr_deregister(intr_handler_t fn, void *arg, uint32_t mask); + /** * @brief Configure the ULP and run the program loaded into RTC memory * diff --git a/components/ulp/ulp_riscv/ulp_riscv.c b/components/ulp/ulp_riscv/ulp_riscv.c index 8c6b05ce33..f160c2e10c 100644 --- a/components/ulp/ulp_riscv/ulp_riscv.c +++ b/components/ulp/ulp_riscv/ulp_riscv.c @@ -20,9 +20,59 @@ #include "hal/misc.h" #include "ulp_common.h" #include "esp_rom_sys.h" +#include "esp_check.h" +#include "esp_private/rtc_ctrl.h" __attribute__((unused)) static const char* TAG = "ulp-riscv"; +esp_err_t ulp_riscv_isr_register(intr_handler_t fn, void *arg, uint32_t mask) +{ + /* Verify that the ISR callback is valid */ + ESP_RETURN_ON_FALSE(fn, ESP_ERR_INVALID_ARG, TAG, "ULP RISC-V ISR is NULL"); + + /* Verify that the interrupt bits are valid */ + if (!(mask & (RTC_CNTL_COCPU_INT_ST_M | RTC_CNTL_COCPU_TRAP_INT_ST_M))) { + ESP_LOGE(TAG, "Invalid bitmask for ULP RISC-V interrupts"); + return ESP_ERR_INVALID_ARG; + } + + /* Make sure we enable only the ULP interrupt bits. + * We don't want other RTC interrupts triggering this ISR. + */ + mask &= (RTC_CNTL_COCPU_INT_ST_M | RTC_CNTL_COCPU_TRAP_INT_ST_M); + + /* Register the RTC ISR */ + ESP_RETURN_ON_ERROR(rtc_isr_register(fn, arg, mask, 0), TAG, "rtc_isr_register() failed"); + + /* Enable the interrupt bits */ + SET_PERI_REG_MASK(RTC_CNTL_INT_ENA_REG, mask); + + return ESP_OK; +} + +esp_err_t ulp_riscv_isr_deregister(intr_handler_t fn, void *arg, uint32_t mask) +{ + /* Verify that the ISR callback is valid */ + ESP_RETURN_ON_FALSE(fn, ESP_ERR_INVALID_ARG, TAG, "ULP RISC-V ISR is NULL"); + + /* Verify that the interrupt bits are valid */ + if (!(mask & (RTC_CNTL_COCPU_INT_ST_M | RTC_CNTL_COCPU_TRAP_INT_ST_M))) { + ESP_LOGE(TAG, "Invalid bitmask for ULP RISC-V interrupts"); + return ESP_ERR_INVALID_ARG; + } + + /* Make sure we disable only the ULP interrupt bits */ + mask &= (RTC_CNTL_COCPU_INT_ST_M | RTC_CNTL_COCPU_TRAP_INT_ST_M); + + /* Disable the interrupt bits */ + CLEAR_PERI_REG_MASK(RTC_CNTL_INT_ENA_REG, mask); + + /* Deregister the RTC ISR */ + ESP_RETURN_ON_ERROR(rtc_isr_deregister(fn, arg), TAG, "rtc_isr_deregister() failed"); + + return ESP_OK; +} + static esp_err_t ulp_riscv_config_wakeup_source(ulp_riscv_wakeup_source_t wakeup_source) { esp_err_t ret = ESP_OK;