ulp: Added APIs to handle ULP signal ISRs for the main CPU

This commit introduces APIs to handle ULP signal ISRs when the main core
is not in sleepmode.

Closes https://github.com/espressif/esp-idf/issues/10737
This commit is contained in:
Sudeep Mohanty 2023-02-20 14:52:55 +01:00 committed by BOT
parent cf146d4b31
commit a67d15fdea
6 changed files with 264 additions and 0 deletions

View File

@ -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);
}

View File

@ -20,6 +20,7 @@
#include <sys/time.h>
#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);
}

View File

@ -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

View File

@ -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

View File

@ -10,6 +10,7 @@
#include <stdlib.h>
#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
*

View File

@ -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;