Merge branch 'feature/ulp_riscv_interrupt_handling' into 'master'

feat(ulp-riscv): Added interrupt handling for ULP RISC-V

Closes IDFGH-9866 and IDF-1713

See merge request espressif/esp-idf!27802
pull/13090/head
Sudeep Mohanty 2024-01-19 15:44:27 +08:00
commit 82f2294bcb
21 zmienionych plików z 854 dodań i 26 usunięć

Wyświetl plik

@ -57,6 +57,7 @@ set(bypassWarning "${IDF_TARGET}")
if(ULP_COCPU_IS_RISCV)
#risc-v ulp uses extra files for building:
list(APPEND ULP_S_SOURCES
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_vectors.S"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/start.S"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_adc.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_lock.c"
@ -64,7 +65,9 @@ if(ULP_COCPU_IS_RISCV)
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_print.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c")
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_gpio.c"
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_interrupt.c")
target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles")
target_link_options(${ULP_APP_NAME} PRIVATE -Wl,--gc-sections)
@ -72,7 +75,8 @@ if(ULP_COCPU_IS_RISCV)
target_sources(${ULP_APP_NAME} PRIVATE ${ULP_S_SOURCES})
#Makes the csr utillies for riscv visible:
target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/include"
"${IDF_PATH}/components/ulp/ulp_riscv/shared/include")
"${IDF_PATH}/components/ulp/ulp_riscv/shared/include"
"${IDF_PATH}/components/riscv/include")
target_link_options(${ULP_APP_NAME} PRIVATE SHELL:-T ${IDF_PATH}/components/ulp/ld/${IDF_TARGET}.peripherals.ld)
target_link_options(${ULP_APP_NAME} PRIVATE "-Wl,--no-warn-rwx-segments")
target_compile_definitions(${ULP_APP_NAME} PRIVATE IS_ULP_COCPU)

Wyświetl plik

@ -17,7 +17,7 @@ SECTIONS
. = ORIGIN(ram);
.text :
{
*start.S.obj(.text.vectors) /* Default reset vector must link to offset 0x0 */
*ulp_riscv_vectors.S.obj(.text.vectors) /* Default reset vector must link to offset 0x0 */
*(.text)
*(.text*)
} >ram

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,6 +13,7 @@ extern "C" {
#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "ulp_riscv_register_ops.h"
#include "ulp_riscv_interrupt.h"
typedef enum {
GPIO_NUM_0 = 0, /*!< GPIO0, input and output */
@ -35,10 +36,21 @@ typedef enum {
GPIO_NUM_17 = 17, /*!< GPIO17, input and output */
GPIO_NUM_18 = 18, /*!< GPIO18, input and output */
GPIO_NUM_19 = 19, /*!< GPIO19, input and output */
GPIO_NUM_20 = 20,
GPIO_NUM_20 = 20, /*!< GPIO20, input and output */
GPIO_NUM_21 = 21, /*!< GPIO21, input and output */
GPIO_NUM_MAX,
} gpio_num_t;
typedef enum {
ULP_RISCV_GPIO_INTR_DISABLE = 0, /*!< Disable RTC GPIO interrupt */
ULP_RISCV_GPIO_INTR_POSEDGE = 1, /*!< RTC GPIO interrupt type : rising edge */
ULP_RISCV_GPIO_INTR_NEGEDGE = 2, /*!< RTC GPIO interrupt type : falling edge */
ULP_RISCV_GPIO_INTR_ANYEDGE = 3, /*!< RTC GPIO interrupt type : both rising and falling edge */
ULP_RISCV_GPIO_INTR_LOW_LEVEL = 4, /*!< RTC GPIO interrupt type : input low level trigger */
ULP_RISCV_GPIO_INTR_HIGH_LEVEL = 5, /*!< RTC GPIO interrupt type : input high level trigger */
ULP_RISCV_GPIO_INTR_MAX
} ulp_riscv_gpio_int_type_t;
typedef enum {
RTCIO_MODE_OUTPUT = 0,
RTCIO_MODE_OUTPUT_OD = 1,
@ -119,6 +131,27 @@ static inline void ulp_riscv_gpio_pulldown_disable(gpio_num_t gpio_num)
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD0_REG + gpio_num*4, RTC_IO_TOUCH_PAD0_RDE);
}
/**
* @brief Set RTC IO interrupt type and handler
*
* @param gpio_num GPIO number
* @param intr_type Interrupt type (See rtc_io_types.h)
* @param handler Interrupt handler
* @param arg Interrupt handler argument
*
* @return ESP_OK on success
*/
esp_err_t ulp_riscv_gpio_isr_register(gpio_num_t gpio_num, ulp_riscv_gpio_int_type_t intr_type, intr_handler_t handler, void *arg);
/**
* @brief Remove RTC IO interrupt handler
*
* @param gpio_num GPIO number
*
* @return ESP_OK on success
*/
esp_err_t ulp_riscv_gpio_isr_deregister(gpio_num_t gpio_num);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,67 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "riscv/interrupt.h"
#ifdef __cplusplus
extern "C"
{
#endif
/* ULP RISC-V Interrupt sources */
typedef enum {
ULP_RISCV_SW_INTR_SOURCE = 0, /**< Interrupt triggered by SW */
ULP_RISCV_RTCIO0_INTR_SOURCE, /**< Interrupt triggered by RTCIO 0 */
ULP_RISCV_RTCIO1_INTR_SOURCE, /**< Interrupt triggered by RTCIO 1 */
ULP_RISCV_RTCIO2_INTR_SOURCE, /**< Interrupt triggered by RTCIO 2 */
ULP_RISCV_RTCIO3_INTR_SOURCE, /**< Interrupt triggered by RTCIO 3 */
ULP_RISCV_RTCIO4_INTR_SOURCE, /**< Interrupt triggered by RTCIO 4 */
ULP_RISCV_RTCIO5_INTR_SOURCE, /**< Interrupt triggered by RTCIO 5 */
ULP_RISCV_RTCIO6_INTR_SOURCE, /**< Interrupt triggered by RTCIO 6 */
ULP_RISCV_RTCIO7_INTR_SOURCE, /**< Interrupt triggered by RTCIO 7 */
ULP_RISCV_RTCIO8_INTR_SOURCE, /**< Interrupt triggered by RTCIO 8 */
ULP_RISCV_RTCIO9_INTR_SOURCE, /**< Interrupt triggered by RTCIO 9 */
ULP_RISCV_RTCIO10_INTR_SOURCE, /**< Interrupt triggered by RTCIO 10 */
ULP_RISCV_RTCIO11_INTR_SOURCE, /**< Interrupt triggered by RTCIO 11 */
ULP_RISCV_RTCIO12_INTR_SOURCE, /**< Interrupt triggered by RTCIO 12 */
ULP_RISCV_RTCIO13_INTR_SOURCE, /**< Interrupt triggered by RTCIO 13 */
ULP_RISCV_RTCIO14_INTR_SOURCE, /**< Interrupt triggered by RTCIO 14 */
ULP_RISCV_RTCIO15_INTR_SOURCE, /**< Interrupt triggered by RTCIO 15 */
ULP_RISCV_RTCIO16_INTR_SOURCE, /**< Interrupt triggered by RTCIO 16 */
ULP_RISCV_RTCIO17_INTR_SOURCE, /**< Interrupt triggered by RTCIO 17 */
ULP_RISCV_RTCIO18_INTR_SOURCE, /**< Interrupt triggered by RTCIO 18 */
ULP_RISCV_RTCIO19_INTR_SOURCE, /**< Interrupt triggered by RTCIO 19 */
ULP_RISCV_RTCIO20_INTR_SOURCE, /**< Interrupt triggered by RTCIO 20 */
ULP_RISCV_RTCIO21_INTR_SOURCE, /**< Interrupt triggered by RTCIO 21 */
ULP_RISCV_MAX_INTR_SOURCE, /**< Total number of ULP RISC-V interrupt sources */
} ulp_riscv_interrupt_source_t;
/**
* @brief Allocate interrupt handler for a ULP RISC-V interrupt source
*
* @param source ULP RISC-V interrupt source
* @param handler Interrupt handler
* @param arg Interrupt handler argument
*
* @return esp_err_t ESP_OK when successful
*/
esp_err_t ulp_riscv_intr_alloc(ulp_riscv_interrupt_source_t source, intr_handler_t handler, void *arg);
/**
* @brief Free ULP RISC-V interrupt handler
*
* @param source ULP RISC-V interrupt source
*
* @return esp_err_t ESP_OK when successful
*/
esp_err_t ulp_riscv_intr_free(ulp_riscv_interrupt_source_t source);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,98 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Claire Xenia Wolf <claire@yosyshq.com>
* SPDX-FileContributor: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* This header file defines custom instructions for interrupt handling on the
* ULP RISC-V. The architecture of the processor and therefore, the interrupt
* handling is based on the PicoRV32 CPU. Details about the operations are
* available at https://github.com/YosysHQ/picorv32#custom-instructions-for-irq-handling
*/
/* Define encoding for all general purpose RISC-V registers */
#define regnum_zero 0
#define regnum_ra 1
#define regnum_sp 2
#define regnum_gp 3
#define regnum_tp 4
#define regnum_t0 5
#define regnum_t1 6
#define regnum_t2 7
#define regnum_s0 8
#define regnum_s1 9
#define regnum_a0 10
#define regnum_a1 11
#define regnum_a2 12
#define regnum_a3 13
#define regnum_a4 14
#define regnum_a5 15
#define regnum_a6 16
#define regnum_a7 17
#define regnum_s2 18
#define regnum_s3 19
#define regnum_s4 20
#define regnum_s5 21
#define regnum_s6 22
#define regnum_s7 23
#define regnum_s8 24
#define regnum_s9 25
#define regnum_s10 26
#define regnum_s11 27
#define regnum_t3 28
#define regnum_t4 29
#define regnum_t5 30
#define regnum_t6 31
/* Define encoding for special interrupt handling registers, viz., q0, q1, q2 and q3 */
#define regnum_q0 0
#define regnum_q1 1
#define regnum_q2 2
#define regnum_q3 3
/* All custom interrupt handling instructions follow the standard R-type instruction format from RISC-V ISA
* with the same opcode of custom0 (0001011).
*/
#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \
.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0))
/**
* Instruction: getq rd, qs
* Description: This instruction copies the value of Qx into a general purpose register rd
*/
#define getq_insn(_rd, _qs) \
r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011)
/**
* Instruction: setq qd, rs
* Description: This instruction copies the value of general purpose register rs to Qx
*/
#define setq_insn(_qd, _rs) \
r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011)
/**
* Instruction: retirq
* Description: This instruction copies the value of Q0 to CPU PC, and renables interrupts
*/
#define retirq_insn() \
r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011)
/**
* Instruction: maskirq rd, rs
* Description: This instruction copies the value of the register IRQ Mask to the register rd, and copies the value
* of register rs to to IRQ mask.
*/
#define maskirq_insn(_rd, _rs) \
r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011)
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -13,6 +13,7 @@ extern "C" {
#include "sdkconfig.h"
#include <stdint.h>
#include "ulp_riscv_register_ops.h"
#include "ulp_riscv_interrupt.h"
/**
* @brief Wakeup main CPU from sleep or deep sleep.
@ -112,6 +113,26 @@ void static inline ulp_riscv_delay_cycles(uint32_t cycles)
*/
void ulp_riscv_gpio_wakeup_clear(void);
/**
* @brief Enable ULP RISC-V SW Interrupt
*
* @param handler Interrupt handler
* @param arg Interrupt handler argument
*/
void ulp_riscv_enable_sw_intr(intr_handler_t handler, void *arg);
/**
* @brief Disable ULP RISC-V SW Interrupt
*/
void ulp_riscv_disable_sw_intr(void);
/**
* @brief Trigger ULP RISC-V SW Interrupt
*
* @note The SW interrupt will only trigger if it has been enabled previously using ulp_riscv_enable_sw_intr().
*/
void ulp_riscv_trigger_sw_intr(void);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -1,28 +1,26 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
.section .text.vectors
.global irq_vector
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
#include "ulp_riscv_interrupt_ops.h"
/* Interrupt handler */
.balign 16
irq_vector:
ret
.section .text
.section .text
.global __start
.type __start, %function
__start:
/* setup the stack pointer */
la sp, __stack_top
call ulp_riscv_rescue_from_monitor
call main
call ulp_riscv_halt
/* setup the stack pointer */
la sp, __stack_top
/* Enable interrupts globally */
maskirq_insn(zero, zero)
/* Start ULP user code */
call ulp_riscv_rescue_from_monitor
call main
call ulp_riscv_halt
loop:
j loop
j loop
.size __start, .-__start

Wyświetl plik

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ulp_riscv_gpio.h"
#include "include/ulp_riscv_gpio.h"
esp_err_t ulp_riscv_gpio_isr_register(gpio_num_t gpio_num, ulp_riscv_gpio_int_type_t intr_type, intr_handler_t handler, void *arg)
{
if (gpio_num < 0 || gpio_num >= GPIO_NUM_MAX) {
return ESP_ERR_INVALID_ARG;
}
if (intr_type < 0 || intr_type >= ULP_RISCV_GPIO_INTR_MAX) {
return ESP_ERR_INVALID_ARG;
}
if (!handler) {
return ESP_ERR_INVALID_ARG;
}
/* Set the interrupt type */
REG_SET_FIELD(RTC_GPIO_PIN0_REG + 4*gpio_num, RTC_GPIO_PIN0_INT_TYPE, intr_type);
/* Set the interrupt handler */
return ulp_riscv_intr_alloc(ULP_RISCV_RTCIO0_INTR_SOURCE + gpio_num, handler, arg);
}
esp_err_t ulp_riscv_gpio_isr_deregister(gpio_num_t gpio_num)
{
return ulp_riscv_intr_free(ULP_RISCV_RTCIO0_INTR_SOURCE + gpio_num);
}

Wyświetl plik

@ -0,0 +1,132 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "include/ulp_riscv_interrupt.h"
#include "ulp_riscv_register_ops.h"
#include "ulp_riscv_interrupt.h"
#include "ulp_riscv_gpio.h"
#include "soc/sens_reg.h"
#define ULP_RISCV_TIMER_INT (1 << 0U) /* Internal Timer Interrupt */
#define ULP_RISCV_EBREAK_ECALL_ILLEGAL_INSN_INT (1 << 1U) /* EBREAK, ECALL or Illegal instruction */
#define ULP_RISCV_BUS_ERROR_INT (1 << 2U) /* Bus Error (Unaligned Memory Access) */
#define ULP_RISCV_PERIPHERAL_INTERRUPT (1 << 31U) /* RTC Peripheral Interrupt */
#define ULP_RISCV_INTERNAL_INTERRUPT (ULP_RISCV_TIMER_INT | ULP_RISCV_EBREAK_ECALL_ILLEGAL_INSN_INT | ULP_RISCV_BUS_ERROR_INT)
/* Interrupt handler structure */
typedef struct {
intr_handler_t handler;
void *arg;
} ulp_riscv_intr_handler_t;
/* Statically store all interrupt handlers */
static ulp_riscv_intr_handler_t s_intr_handlers[ULP_RISCV_MAX_INTR_SOURCE];
esp_err_t ulp_riscv_intr_alloc(ulp_riscv_interrupt_source_t source, intr_handler_t handler, void *arg)
{
/* Check the validity of the interrupt source */
if (source < 0 || source >= ULP_RISCV_MAX_INTR_SOURCE || handler == NULL) {
return ESP_ERR_INVALID_ARG;
}
/* Register interrupt handler */
if (s_intr_handlers[source].handler == NULL) {
s_intr_handlers[source].handler = handler;
s_intr_handlers[source].arg = arg;
} else {
/* Error: The interrupt handler for this interrupt source has already been allocated */
return ESP_ERR_NOT_FOUND;
}
return ESP_OK;
}
esp_err_t ulp_riscv_intr_free(ulp_riscv_interrupt_source_t source)
{
/* Check the validity of the interrupt source */
if (source < 0 || source >= ULP_RISCV_MAX_INTR_SOURCE) {
return ESP_ERR_INVALID_ARG;
}
/* De-register interrupt handler */
if (s_intr_handlers[source].handler != NULL) {
s_intr_handlers[source].handler = NULL;
s_intr_handlers[source].arg = NULL;
}
return ESP_OK;
}
/* This function -
* - Checks RTC peripheral interrupt status bit
* - Calls interrupt handler if it is registered
* - Clears interrupt bit
*/
static inline void ulp_riscv_handle_rtc_periph_intr(uint32_t status)
{
/* SW interrupt */
if (status & SENS_COCPU_SW_INT_ST) {
const ulp_riscv_intr_handler_t* entry = &s_intr_handlers[ULP_RISCV_SW_INTR_SOURCE];
if (entry->handler) {
entry->handler(entry->arg);
}
SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_SW_INT_CLR);
}
}
/* This function -
* - Checks if one or more RTC IO interrupt status bits are set
* - Calls the interrupt handler for the RTC IO if it is registered
* - Clears all interrupt bits
*/
static inline void ulp_riscv_handle_rtc_io_intr(uint32_t status)
{
uint32_t handler_idx = 0;
for (int i = 0; i < GPIO_NUM_MAX; i++) {
if (status & (1U << i)) {
handler_idx = ULP_RISCV_RTCIO0_INTR_SOURCE + i;
ulp_riscv_intr_handler_t* entry = &s_intr_handlers[handler_idx];
if (entry->handler) {
entry->handler(entry->arg);
}
}
}
REG_SET_FIELD(RTC_GPIO_STATUS_W1TC_REG, RTC_GPIO_STATUS_INT_W1TC, status);
}
/* This is the global interrupt handler for ULP RISC-V.
* It is called from ulp_riscv_vectors.S
*/
void __attribute__((weak)) _ulp_riscv_interrupt_handler(uint32_t q1)
{
/* Call respective interrupt handlers based on the interrupt status in q1 */
/* Internal Interrupts */
if (q1 & ULP_RISCV_INTERNAL_INTERRUPT) {
// TODO
}
/* External/Peripheral interrupts */
if (q1 & ULP_RISCV_PERIPHERAL_INTERRUPT) {
/* RTC Peripheral interrupts */
uint32_t cocpu_int_st = READ_PERI_REG(SENS_SAR_COCPU_INT_ST_REG);
if (cocpu_int_st) {
ulp_riscv_handle_rtc_periph_intr(cocpu_int_st);
}
/* RTC IO interrupts */
uint32_t rtcio_int_st = REG_GET_FIELD(RTC_GPIO_STATUS_REG, RTC_GPIO_STATUS_INT);
if (rtcio_int_st) {
ulp_riscv_handle_rtc_io_intr(rtcio_int_st);
}
/* TODO: RTC I2C interrupt */
}
}

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -9,6 +9,7 @@
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"
void ulp_riscv_rescue_from_monitor(void)
{
@ -46,3 +47,28 @@ void ulp_riscv_gpio_wakeup_clear(void)
{
SET_PERI_REG_MASK(RTC_CNTL_ULP_CP_TIMER_REG, RTC_CNTL_ULP_CP_GPIO_WAKEUP_CLR);
}
void ulp_riscv_enable_sw_intr(intr_handler_t handler, void *arg)
{
/* Enable ULP RISC-V SW interrupt */
SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_ENA_REG, SENS_COCPU_SW_INT_ENA);
/* Register interrupt handler */
if (handler) {
ulp_riscv_intr_alloc(ULP_RISCV_SW_INTR_SOURCE, handler, arg);
}
}
void ulp_riscv_disable_sw_intr(void)
{
/* Disable ULP RISC-V SW interrupt */
CLEAR_PERI_REG_MASK(SENS_SAR_COCPU_INT_ENA_REG, SENS_COCPU_SW_INT_ENA);
/* De-register interrupt handler */
ulp_riscv_intr_free(ULP_RISCV_SW_INTR_SOURCE);
}
void ulp_riscv_trigger_sw_intr(void)
{
SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SW_INT_TRIGGER);
}

Wyświetl plik

@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ulp_riscv_interrupt_ops.h"
#include "riscv/rvruntime-frames.h"
.equ SAVE_REGS, 17
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
/* Macro which first allocates space on the stack to save general
* purpose registers, and then save them. GP register is excluded.
* The default size allocated on the stack is CONTEXT_SIZE, but it
* can be overridden.
*
* Note: We don't save the callee-saved s0-s11 registers to save space
*/
.macro save_general_regs cxt_size=CONTEXT_SIZE
addi sp, sp, -\cxt_size
sw ra, RV_STK_RA(sp)
sw tp, RV_STK_TP(sp)
sw t0, RV_STK_T0(sp)
sw t1, RV_STK_T1(sp)
sw t2, RV_STK_T2(sp)
sw a0, RV_STK_A0(sp)
sw a1, RV_STK_A1(sp)
sw a2, RV_STK_A2(sp)
sw a3, RV_STK_A3(sp)
sw a4, RV_STK_A4(sp)
sw a5, RV_STK_A5(sp)
sw a6, RV_STK_A6(sp)
sw a7, RV_STK_A7(sp)
sw t3, RV_STK_T3(sp)
sw t4, RV_STK_T4(sp)
sw t5, RV_STK_T5(sp)
sw t6, RV_STK_T6(sp)
.endm
/* Restore the general purpose registers (excluding gp) from the context on
* the stack. The context is then deallocated. The default size is CONTEXT_SIZE
* but it can be overridden. */
.macro restore_general_regs cxt_size=CONTEXT_SIZE
lw ra, RV_STK_RA(sp)
lw tp, RV_STK_TP(sp)
lw t0, RV_STK_T0(sp)
lw t1, RV_STK_T1(sp)
lw t2, RV_STK_T2(sp)
lw a0, RV_STK_A0(sp)
lw a1, RV_STK_A1(sp)
lw a2, RV_STK_A2(sp)
lw a3, RV_STK_A3(sp)
lw a4, RV_STK_A4(sp)
lw a5, RV_STK_A5(sp)
lw a6, RV_STK_A6(sp)
lw a7, RV_STK_A7(sp)
lw t3, RV_STK_T3(sp)
lw t4, RV_STK_T4(sp)
lw t5, RV_STK_T5(sp)
lw t6, RV_STK_T6(sp)
addi sp,sp, \cxt_size
.endm
.section .text.vectors
.global irq_vector
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
/* Interrupt handler */
.balign 0x10
irq_vector:
/* Save the general gurpose register context before handling the interrupt */
save_general_regs
/* Fetch the interrupt status from the custom q1 register into a0 */
getq_insn(a0, q1)
/* Call the global C interrupt handler. The interrupt status is passed as the argument in a0.
* We do not re-enable interrupts before calling the C handler as ULP RISC-V does not
* support nested interrupts.
*/
jal _ulp_riscv_interrupt_handler
/* Restore the register context after returning from the C interrupt handler */
restore_general_regs
/* Exit interrupt handler by executing the custom retirq instruction which will retore pc and re-enable interrupts */
retirq_insn()

Wyświetl plik

@ -191,6 +191,37 @@ In case your RTC I2C based ULP RISC-V program is not working as expected, the fo
* Other methods of debugging problems would be to ensure that the RTC I2C controller is operational **only** on the main CPU **without** any ULP RISC-V code interfering and **without** any sleep mode being activated. This is the basic configuration under which the RTC I2C peripheral must work. This way you can rule out any potential issues due to the ULP or sleep modes.
ULP RISC-V Interrupt Handling
------------------------------
The ULP RISC-V core supports interrupt handling from certain internal and external events. By design, the ULP RISC-V core can handle interrupts from the following sources:
.. list-table:: ULP RISC-V interrupt sources
:widths: 10 5 5
:header-rows: 1
* - Interrupt Source
- Type
- IRQ
* - Internal Timer Interrupt
- Internal
- 0
* - EBREAK or ECALL or Illegal Instruction
- Internal
- 1
* - Unalligned Memory Access
- Internal
- 2
* - RTC Peripheral Sources
- External
- 31
Interrupt handling is enabled via special 32-bit registers q0-q3 and custom R-type instructions. For more information, see *{IDF_TARGET_NAME} Technical Reference Manual* > *ULP Coprocessor* > *ULP-RISC-V* > *ULP-RISC-V Interrupts* [`PDF <{IDF_TARGET_TRM_EN_URL}>`__].
All interrupts are enabled globally during start up. When an interrupt occurs, the processor jumps to the IRQ vector. The IRQ vector performs the task of saving the register context and then calling the global interrupt dispatcher. The ULP RISC-V driver implements a *weak* interrupt dispatcher :cpp:func:`_ulp_riscv_interrupt_handler` which serves as the central point for handling all interrupts. This global dispatcher calls respective interrupt handlers which have been allocated via the :cpp:func:`ulp_riscv_intr_alloc`.
Interrupt handling on the ULP RISC-V is not full featured yet. At present, interrupt handling for internal interrupt source is not supported. Support is provided for 2 RTC peripheral sources, viz., software triggered interrupt and RTC IO triggered interrupts. ULP RISC-V does not support nested interrupts. If users need custom interrupt handling then they may override the default global interrupt dispatcher by defining their own :cpp:func:`_ulp_riscv_interrupt_handler`.
Debugging Your ULP RISC-V Program
----------------------------------
@ -211,6 +242,7 @@ Application Examples
* ULP RISC-V Coprocessor uses bit-banged UART driver to print: :example:`system/ulp/ulp_riscv/uart_print`.
* ULP RISC-V Coprocessor reads external temperature sensor while main CPU is in deep sleep: :example:`system/ulp/ulp_riscv/ds18b20_onewire`.
* ULP RISC-V Coprocessor reads external I2C temperature and humidity sensor (BMP180) while the main CPU is in Deep-sleep and wakes up the main CPU once a threshold is met: :example:`system/ulp/ulp_riscv/i2c`.
* ULP RISC-V Coprocessor handles software interrupt and RTC IO interrupt: :example:`system/ulp/ulp_riscv/interrupts`.
API Reference
-------------

Wyświetl plik

@ -360,6 +360,12 @@ examples/system/ulp/ulp_riscv/i2c:
depends_components:
- ulp
examples/system/ulp/ulp_riscv/interrupts:
enable:
- if: SOC_RISCV_COPROC_SUPPORTED == 1
depends_components:
- ulp
examples/system/ulp/ulp_riscv/touch:
enable:
- if: SOC_RISCV_COPROC_SUPPORTED == 1

Wyświetl plik

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ulp_riscv_interrupts)

Wyświetl plik

@ -0,0 +1,71 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |
# ULP-RISC-V Interrupt Handling:
This example demonstrates how the ULP-RISC-V coprocessor can register and handle interrupts. Currently this example supports handling of -
- Software triggered interrupts
- RTC IO triggered interrupts
The example keeps a count of the software interrupts triggered on the ULP RISC-V core and wakes up the main processor from deep sleep after a certain threshold.
Additionaly, it wakes up the main processor from deep sleep when a button is pressed and the GPIO interrupt is triggered.
## How to use example
### Hardware Required
The example can be run on any ESP32-S2 or ESP32-S3 based development board connected to a computer with a single USB cable for flashing and monitoring. The external interface should have 3.3V outputs. You may use e.g. 3.3V compatible USB-to-Serial dongle.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
By default, this example uses GPIO#0 which is connected to the "Boot" button on esp32-s2/s3 development boards. To trigger the GPIO interrupt on the ULP RISC-V core, press the Boot button.
## Example Output
Output from main CPU:
```
Not a ULP RISC-V wakeup, initializing it!
Entering in deep sleep
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
pro cpu reset by JTAG
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x12b4
load:0x403c9700,len:0x4
load:0x403c9704,len:0xaf4
load:0x403cc700,len:0x2bcc
entry 0x403c9898
W (91) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
ULP RISC-V woke up the main CPU!
ULP RISC-V SW Interrupt triggered 5 times.
Entering in deep sleep
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT)
pro cpu reset by JTAG
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x12b4
load:0x403c9700,len:0x4
load:0x403c9704,len:0xaf4
load:0x403cc700,len:0x2bcc
entry 0x403c9898
W (91) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
ULP RISC-V woke up the main CPU!
ULP RISC-V GPIO Interrupt triggered.
Entering in deep sleep
```

Wyświetl plik

@ -0,0 +1,27 @@
# Set usual component variables
set(COMPONENT_SRCS "ulp_riscv_example_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
set(COMPONENT_REQUIRES ulp)
register_component()
#
# ULP support additions to component CMakeLists.txt.
#
# 1. The ULP app name must be unique (if multiple components use ULP).
set(ulp_app_name ulp_${COMPONENT_NAME})
#
# 2. Specify all C and Assembly source files.
# Files should be placed into a separate directory (in this case, ulp/),
# which should not be added to COMPONENT_SRCS.
set(ulp_riscv_sources "ulp/main.c")
#
# 3. List all the component source files which include automatically
# generated ULP export file, ${ulp_app_name}.h:
set(ulp_exp_dep_srcs "ulp_riscv_example_main.c")
#
# 4. Call function to build ULP binary and embed in project using the argument
# values above.
ulp_embed_binary(${ulp_app_name} "${ulp_riscv_sources}" "${ulp_exp_dep_srcs}")

Wyświetl plik

@ -0,0 +1,10 @@
menu "ULP RISC-V Interrupt Example Configuration"
config EXAMPLE_GPIO_INT
int "GPIO interrupt pin"
range 0 21
default 0
help
GPIO number to trigger an interrupt on the ULP RISC-V core.
endmenu

Wyświetl plik

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ULP RISC-V interrupts example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
This code runs on ULP RISC-V coprocessor
*/
#include "ulp_riscv_utils.h"
#include "ulp_riscv_gpio.h"
#define WAKE_PIN CONFIG_EXAMPLE_GPIO_INT
int sw_int_cnt = 0;
int wake_by_sw = 0;
int wake_by_gpio = 0;
/* SW Interrupt Handler */
void sw_int_handler(void *arg)
{
sw_int_cnt++;
}
/* GPIO Interrupt Handler */
void gpio_int_handler(void *arg)
{
wake_by_gpio = 1;
ulp_riscv_wakeup_main_processor();
}
int main(void) {
/* Register SW interrupt handler */
ulp_riscv_enable_sw_intr(sw_int_handler, NULL);
/* Configure GPIO in input mode for interrupt */
ulp_riscv_gpio_init(WAKE_PIN);
ulp_riscv_gpio_input_enable(WAKE_PIN);
/* Register GPIO interrupt handler */
ulp_riscv_gpio_isr_register(WAKE_PIN, ULP_RISCV_GPIO_INTR_POSEDGE, gpio_int_handler, NULL);
while (1) {
/* Trigger SW interrupt */
ulp_riscv_trigger_sw_intr();
if ((sw_int_cnt % 5 == 0) && !wake_by_gpio) {
wake_by_sw = 1;
ulp_riscv_wakeup_main_processor();
}
ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS);
}
return 0;
}

Wyświetl plik

@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ULP RISC-V interrupts example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "esp_sleep.h"
#include "ulp_riscv.h"
#include "ulp_main.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
static void init_ulp_program(void);
void app_main(void)
{
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
/* not a wakeup from ULP, load the firmware */
if (cause != ESP_SLEEP_WAKEUP_ULP) {
printf("Not a ULP RISC-V wakeup, initializing it! \n");
init_ulp_program();
}
/* ULP RISC-V handled an interrupt on GPIO#0 */
if (cause == ESP_SLEEP_WAKEUP_ULP) {
printf("ULP RISC-V woke up the main CPU! \n");
if (ulp_wake_by_sw) {
printf("ULP RISC-V SW Interrupt triggered %lu times.\r\n", ulp_sw_int_cnt);
ulp_wake_by_sw = 0;
} else if (ulp_wake_by_gpio) {
printf("ULP RISC-V GPIO Interrupt triggered.\r\n");
ulp_wake_by_gpio = 0;
}
}
/* Go back to sleep, only the ULP RISC-V will run */
printf("Entering in deep sleep\n\n");
/* Small delay to ensure the messages are printed */
vTaskDelay(100);
ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup());
esp_deep_sleep_start();
}
static void init_ulp_program(void)
{
esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
ESP_ERROR_CHECK(err);
/* The first argument is the period index, which is not used by the ULP-RISC-V timer
* The second argument is the period in microseconds, which gives a wakeup time period of: 20ms
*/
ulp_set_wakeup_period(0, 20000);
/* Start the program */
err = ulp_riscv_run();
ESP_ERROR_CHECK(err);
}

Wyświetl plik

@ -0,0 +1,30 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import time
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
def test_ulp_riscv_interrupts(dut: Dut) -> None:
dut.expect_exact('Not a ULP RISC-V wakeup, initializing it!')
dut.expect_exact('Entering in deep sleep')
# Give the chip time to enter deepsleep and trigger a software interrupt
time.sleep(5)
# Verify if SW interrupt got triggered
dut.expect_exact('ULP RISC-V SW Interrupt triggered 5 times.')
# Set GPIO#0 using DTR
dut.serial.proc.setDTR(True)
time.sleep(1)
dut.serial.proc.setDTR(False)
# Verify if GPIO interrupt got triggered
dut.expect_exact('ULP RISC-V GPIO Interrupt triggered.', timeout=5)

Wyświetl plik

@ -0,0 +1,9 @@
# Enable ULP
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_RISCV=y
CONFIG_ULP_COPROC_RESERVE_MEM=4096
# Set log level to Warning to produce clean output
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_BOOTLOADER_LOG_LEVEL=2
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
CONFIG_LOG_DEFAULT_LEVEL=2