espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.71k stars 7.3k forks source link

[ESP32S3][ULP-RISC-V] How can I handle the IRQs inside the ULP-RISC-V without waking up the main processor? (IDFGH-9866) #11188

Closed andylinpersonal closed 9 months ago

andylinpersonal commented 1 year ago

Answers checklist.

General issue report

How can I handle the IRQs inside the ULP-RISC-V without waking up the main processor?

Environment

Description

Hello, according to the ESP32-S3 TRM 2.6.3 ULP­-RISC­-V Interrupts, the ULP-RISC-V should be capable of implementing user-defined ISRs and handling IRQs on their own.

But here we need some more clarification and documentation related to interrupt handling due to its non-standard nature. (Not conforming to RISC-V privileged ISA)

  1. Where should we put the ISRs inside the ULP-RISC-V’s memory space?

  2. Structure of reset vector and interrupt vector table. Information from the current start.S is insufficient.

  3. More detailed description and examples of how the internal/external IRQs should be handled, and the usage of mentioned custom instructions.

    1. Interrupt status register for distinguishing between different IRQ.
    2. When the ULP-RISC-V's ISRs would be triggered.
    3. Calling convention and stack handling in the ISRs. Seems like only changed from mret to retirq.
    4. Does the interrupt controller support nested interrupt?
  4. Document implemented CSRs. Tested CSRs: cycle[h].

  5. (Suggestion) Add the custom instructions (section 2.6.3.3) into riscv32-esp-elf.

The following code is my preliminary testing:

Testing Code

Modified ${IDF_PATH}/components/ulp/cmake/CMakeLists.txt

From c492b909bcd080182c7c759bc5756cbc47e710e7 Mon Sep 17 00:00:00 2001
From: Andy Lin <andylinpersonal@gmail.com>
Date: Thu, 13 Apr 2023 00:40:27 +0800
Subject: [PATCH] Remove the default start file from the ULP-RISC-V

---
 components/ulp/cmake/CMakeLists.txt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt
index 5a945563cf..e72b4bdb28 100644
--- a/components/ulp/cmake/CMakeLists.txt
+++ b/components/ulp/cmake/CMakeLists.txt
@@ -57,7 +57,6 @@ 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/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"
         "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c"
--
2.25.1

./CMakeLists.txt

# The following five 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(isr_esp32s3)

./main/CMakeLists.txt

# Set usual component variables

idf_component_register(SRCS "ulp_isr_loader.c"
                    INCLUDE_DIRS .
                    REQUIRES soc nvs_flash ulp driver console)

set(ulp_app_name ulp_isr_ulp)
set(ulp_riscv_sources "ulp/main.c" "ulp/start.S")
set(ulp_exp_dep_srcs "ulp_isr_loader.c")
ulp_embed_binary(${ulp_app_name} "${ulp_riscv_sources}" "${ulp_exp_dep_srcs}")

./main/Kconfig

menu "ESP32S3 ULP-RISCV ISR UART TX Configuration"

    orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"

    config ULP_ISR_UART_TXD
        int "UART TXD pin number"
        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
        default 4
        help
            GPIO number for ULP UART TX pin. See UART documentation for more information
            about available pin numbers for UART. Should be a pin accessible from RTC IO Mux.

endmenu

./main/ulp/start.S

/*
 * SPDX-FileCopyrightText: 2022 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

/* Interrupt handler */
.balign 16
irq_vector:
    // IRQ=0
     j IRQ0_Handler // This handler is called on
    // Can I register more than one entry in the irq_vector?

    .section .text
    .global Default_Handler
    .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
loop:
    j loop
    .size __start, .-__start

.weak Default_Error_Handler
.type Default_Error_Handler, %function
Default_Error_Handler:
Infinite_Loop:
    j Infinite_Loop
    .size   Default_Error_Handler, .-Default_Error_Handler

.weak Default_Handler
.type Default_Handler, %function
Default_Handler:
    ret
    .size   Default_Handler, .-Default_Handler

.weak IRQ0_Handler
    .set IRQ0_Handler, Default_Handler
.weak IRQ1_Handler
    .set IRQ1_Handler, Default_Error_Handler
.weak IRQ2_Handler
    .set IRQ2_Handler, Default_Error_Handler
.weak IRQ31_Handler
    .set IRQ31_Handler, Default_Handler

./main/ulp/main.c


/*
 * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */
/* ULP-RISC-V 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.h"
#include "ulp_riscv_print.h"
#include "ulp_riscv_utils.h"

#include "sdkconfig.h"
#include "ulp_riscv_uart_ulp_core.h"

#include <stdbool.h>
#include <stdint.h>

#include <soc/rtc_cntl_reg.h>
#include <soc/rtc_io_reg.h>
#include <soc/sens_reg.h>

static const char *TAG = "(ulp_isr@ULP) ";
static const char *ISR_TAG = "(ulp_isr@ULP ISR) ";

static ulp_riscv_uart_t s_print_uart;
uint32_t loop_counter;

/**
 * @note See ESP32-S3 TRM ch2.6.3.3 fig.2­-28 getq instruction
 * Testing result: working
 **/

inline static uint32_t getq0() {
    register uint32_t result asm("a0");
    /* getq a0, q0 */
    asm volatile inline(".byte 0x0b, 0x05, 0x00, 0x00\n\t" : "=r"(result));
    return result;
}

inline static uint32_t getq1() {
    register uint32_t result asm("a0");
    /* getq a0, q1 */
    asm volatile inline(".byte 0x0b, 0x85, 0x00, 0x00" : "=r"(result));
    return result;
}

inline static uint32_t getq2() {
    register uint32_t result asm("a0");
    /* getq a0, q2 */
    asm volatile inline(".byte 0x0b, 0x05, 0x01, 0x00" : "=r"(result));
    return result;
}

inline static uint32_t getq3() {
    register uint32_t result asm("a0");
    /* getq a0, q3 */
    asm volatile inline(".byte 0x0b, 0x85, 0x01, 0x00" : "=r"(result));
    return result;
}

/**
 * @note See ESP32-S3 TRM ch2.6.3.3 fig.2­-28 setq instruction
 * Testing result: working
 **/

inline static void setq0(uint32_t val) {
    register uint32_t input asm("a0") = val;
    /* setq q0, a0 */
    asm volatile inline(".byte 0x0b, 0x00, 0x05, 0x02" : : "r"(input));
}

inline static void setq1(uint32_t val) {
    register uint32_t input asm("a0") = val;
    /* setq q1, a0 */
    asm volatile inline(".byte 0x8b, 0x00, 0x05, 0x02" : : "r"(input));
}

inline static void setq2(uint32_t val) {
    register uint32_t input asm("a0") = val;
    /* setq q2, a0 */
    asm volatile inline(".byte 0x0b, 0x01, 0x05, 0x02" : : "r"(input));
}

inline static void setq3(uint32_t val) {
    register uint32_t input asm("a0") = val;
    /* setq q3, a0 */
    asm volatile inline(".byte 0x8b, 0x01, 0x05, 0x02" : : "r"(input));
}

/** @note See ESP32-S3 TRM ch2.6.3.3 fig.2­-29 retirq instruction */

/* retirq */
#define retirq()                                                                                                       \
    do {                                                                                                               \
        asm volatile inline(".byte 0x0b, 0x00, 0x00, 0x04");                                                           \
    } while (0)

/**
 * @note See ESP32-S3 TRM ch2.6.3.3 fig.2­-30 maskirq instruction
 * @note Broken? Not update into Q1 at all.
 */
inline static uint32_t maskirq(uint32_t newmask) {
    register uint32_t irq_mask asm("t0") = newmask;
    register uint32_t result asm("a0");
    /* maskirq a0, t1 */
    asm volatile inline(".byte 0x0b, 0x85, 0x02, 0x06" : "=r"(result) : "r"(irq_mask));
    return result;
}

#define __disable_irq() maskirq(0UL)
#define __enable_irq(old) maskirq(old)

/**
 * @note Read 64-bit cycle counter from the CSRs cycle[h]
 */
inline static uint64_t read_cycle() {
    union {
        struct {
            uint32_t rdcyclel;
            uint32_t rdcycleh;
        } csrs;
        uint64_t cycle;
    } rdcycle;
    asm volatile inline("rdcycleh %0\n\t"
                        "rdcycle %1\n\t"
                        : "=r"(rdcycle.csrs.rdcycleh), "=r"(rdcycle.csrs.rdcyclel));
    return rdcycle.cycle;
}

void ulp_riscv_print_hex_ext(uint32_t h, uint32_t bytes) {
    int x;
    int c;
    char buf[2] = {0, 0};

    h <<= (32 - 8 * (bytes));

    // Does not print '0x', only the digits (8 digits to print)
    for (x = 0; x < bytes * 2; x++) {
        c = (h >> 28) & 0xf; // extract the leftmost byte
        if (c < 10) {
            buf[0] = '0' + c;
        } else {
            buf[0] = 'A' + c - 10;
        }
        ulp_riscv_print_str(buf);
        h <<= 4; // move the 2nd leftmost byte to the left, to be extracted next
    }
}

void ulp_riscv_print_bin(int val, uint32_t bits, bool space) {
    val <<= (32 - bits);
    while (bits--) {
        ulp_riscv_print_str(val & 0x80000000 ? "1" : "0");
        val <<= 1;
        if (bits % 8 == 0 && space)
            ulp_riscv_print_str(" ");
    }
}

void dump_intr_qregs(const char *tag) {
#define DUMP_QREG(i)                                                                                                   \
    {                                                                                                                  \
        uint32_t reg = getq##i();                                                                                      \
        ulp_riscv_print_str("\tQ" #i ": ");                                                                            \
        ulp_riscv_print_bin(reg, 32, true);                                                                            \
        ulp_riscv_print_str(" ");                                                                                      \
        ulp_riscv_print_hex(reg);                                                                                      \
        ulp_riscv_print_str("\r\n");                                                                                   \
    }
    uint32_t reg;
    ulp_riscv_print_str(tag);
    ulp_riscv_print_str("Interrupt registers:\r\n");

    DUMP_QREG(0);
    DUMP_QREG(1);
    DUMP_QREG(2);
    DUMP_QREG(3);

#undef DUMP_QREG
}

int main(void) {
    // TX pin for the bit-bang UART.
    ulp_riscv_uart_cfg_t cfg = {
        .tx_pin = CONFIG_ULP_ISR_UART_TXD,
    };

    ulp_riscv_uart_init(&s_print_uart, &cfg);
    ulp_riscv_print_install((putc_fn_t)ulp_riscv_uart_putc, &s_print_uart);

    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("At function: ");
    ulp_riscv_print_str(__func__);
    ulp_riscv_print_str("@");
    ulp_riscv_print_hex((uint32_t)&main);
    ulp_riscv_print_str("\r\n");

    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("SENS_SAR_COCPU_INT_ENA_REG: ");
    ulp_riscv_print_bin(READ_PERI_REG(SENS_SAR_COCPU_INT_ENA_REG), 32, true);
    ulp_riscv_print_str("\r\n");

    /**
     * @note Where can I enable the peripheral interrupts inside the ULP-RISC-V.
     */
    SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_ENA_REG, SENS_COCPU_SARADC1_INT_ENA | SENS_COCPU_TSENS_INT_ENA |
                                                      SENS_COCPU_START_INT_ENA | SENS_COCPU_SW_INT_ENA);

    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("After Configure SENS_SAR_COCPU_INT_ENA_REG: ");
    ulp_riscv_print_bin(READ_PERI_REG(SENS_SAR_COCPU_INT_ENA_REG), 32, true);
    ulp_riscv_print_str("\r\n");

    loop_counter = 0;

    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("Before disable irq\r\n");
    dump_intr_qregs(TAG);
    /**
     * @note Crashed and triggered IRQ0 here.
     */
    uint32_t old_mask = __disable_irq();
    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("Backuped irq mask: ");
    ulp_riscv_print_hex(old_mask);
    ulp_riscv_print_str("\r\n");

    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("After disable irq\r\n");
    dump_intr_qregs(TAG);

    /**
     * @note Not working. Q1 is not updated at all.
     */
    __enable_irq(0x80000007);

    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("After re-enable irq\r\n");
    dump_intr_qregs(TAG);

    /**
     * @note Q1 gets updated.
     */
    setq1(0x80000007);
    ulp_riscv_print_str(TAG);
    ulp_riscv_print_str("After enable irq 0,1,2,31\r\n");
    dump_intr_qregs(TAG);

    setq2(loop_counter);
    setq3(loop_counter);

    while (1) {
        uint64_t cycle = read_cycle();
        loop_counter = getq3();
        ulp_riscv_print_str(TAG);
        ulp_riscv_print_hex(loop_counter);
        ulp_riscv_print_str(" @ cycle ");
        ulp_riscv_print_hex((uint32_t)(cycle >> 32));
        ulp_riscv_print_str("_");
        ulp_riscv_print_hex((uint32_t)cycle);
        ulp_riscv_print_str("\r\n");
        dump_intr_qregs(TAG);
        setq3(loop_counter + 1);

        if (loop_counter == 1) {
            ulp_riscv_print_str(TAG);
            ulp_riscv_print_str("Before triggering SW_INT.\r\n\tSENS_SAR_COCPU_INT_ST_REG:\r\n\t");
            ulp_riscv_print_bin(READ_PERI_REG(SENS_SAR_COCPU_INT_ST_REG), 32, true);
            ulp_riscv_print_str("\r\n");
            // trigger SW_INT,
            SET_PERI_REG_MASK(RTC_CNTL_COCPU_CTRL_REG, RTC_CNTL_COCPU_SW_INT_TRIGGER);
            ulp_riscv_print_str(TAG);
            ulp_riscv_print_str("After triggered SW_INT.\r\n\tSENS_SAR_COCPU_INT_ST_REG:\r\n\t");
            ulp_riscv_print_bin(READ_PERI_REG(SENS_SAR_COCPU_INT_ST_REG), 32, true);
            ulp_riscv_print_str("\r\n");
        }

        if (loop_counter == 2) {
            asm volatile inline(".byte 0x00, 0x13, 0x0, 0x0, 0x0");
            const uint8_t testbuf[5] = {0x0, 0x13, 0x0, 0x0, 0x0};
            asm volatile inline(".byte 0x00\n\t"
                                "c.nop");
            ((void (*)())((const char *)&testbuf + 1))();
        }

        if (loop_counter == 3) {
            // Can I handle EBREAK / ECALL in the ULP-RISC-V's ISR without waking up the main processor?
            asm volatile inline("ebreak");
            asm volatile inline("ecall");
        }

        // trap
        if (loop_counter == 4) {
            ((void (*)(void))0xDEADBEEF)();
        }

        ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS);
    }
}

bool do_handle(uint32_t irq) {
    ulp_riscv_print_str(ISR_TAG);
    ulp_riscv_print_str("IRQ");
    ulp_riscv_print_hex(irq);
    ulp_riscv_print_str(" Handler Triggered\r\n");

    /**
     * @note 2.6.3.4 RTC Peripheral Interrupts
     * @brief Handle peripheral interrupts listed on the table 2-11.
     */
    uint32_t cocpu_int_st = READ_PERI_REG(SENS_SAR_COCPU_INT_ST_REG);

    if (cocpu_int_st & SENS_COCPU_TOUCH_DONE_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_TOUCH_DONE_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_TOUCH_INACTIVE_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_TOUCH_INACTIVE_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_TOUCH_ACTIVE_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_TOUCH_ACTIVE_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_SARADC1_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_SARADC1_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_SARADC2_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_SARADC2_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_TSENS_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_TSENS_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_START_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_START_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_SW_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_SW_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_SWD_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_SWD_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_TOUCH_APPROACH_LOOP_DONE_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_TOUCH_APPROACH_LOOP_DONE_INT_CLR);
    }
    if (cocpu_int_st & SENS_COCPU_TOUCH_SCAN_DONE_INT_ST) {
        SET_PERI_REG_MASK(SENS_SAR_COCPU_INT_CLR_REG, SENS_COCPU_TOUCH_SCAN_DONE_INT_CLR);
    }

    dump_intr_qregs(ISR_TAG);
    if (cocpu_int_st) {
        ulp_riscv_print_str(ISR_TAG);
        ulp_riscv_print_str("Peripheral interrupt handled\r\n\tSENS_SAR_COCPU_INT_ST_REG:\r\n\t");
        ulp_riscv_print_bin(cocpu_int_st, 32, true);
        ulp_riscv_print_str("\r\n");
        goto handled;
    }

    /**
     * @note TODO: Handle interrupt from RTC_IO
     */

    /**
     * @note TODO: Handle interrupt from RTC I2C
     */

handled:
    return true;
fatal:
    return false;
}

#define ISR_PROLOGUE()                                                                                                 \
    asm volatile inline("c.addi16sp sp, -0x40\n\t"                                                                     \
                        "c.swsp ra, 0x3c(sp)\n\t"                                                                      \
                        "c.swsp t0, 0x38(sp)\n\t"                                                                      \
                        "c.swsp t1, 0x34(sp)\n\t"                                                                      \
                        "c.swsp t2, 0x30(sp)\n\t"                                                                      \
                        "c.swsp a0, 0x2c(sp)\n\t"                                                                      \
                        "c.swsp a1, 0x28(sp)\n\t"                                                                      \
                        "c.swsp a2, 0x24(sp)\n\t"                                                                      \
                        "c.swsp a3, 0x20(sp)\n\t"                                                                      \
                        "c.swsp a4, 0x1c(sp)\n\t"                                                                      \
                        "c.swsp a5, 0x18(sp)\n\t"                                                                      \
                        "c.swsp a6, 0x14(sp)\n\t"                                                                      \
                        "c.swsp a7, 0x10(sp)\n\t"                                                                      \
                        "c.swsp t3,  0xc(sp)\n\t"                                                                      \
                        "c.swsp t4,  0x8(sp)\n\t"                                                                      \
                        "c.swsp t5,  0x4(sp)\n\t"                                                                      \
                        "c.swsp t6,  0x0(sp)\n\t")

#define ISR_EPILOGUE()                                                                                                 \
    do {                                                                                                               \
        asm volatile inline("c.lwsp ra, 0x3c(sp)\n\t"                                                                  \
                            "c.lwsp t0, 0x38(sp)\n\t"                                                                  \
                            "c.lwsp t1, 0x34(sp)\n\t"                                                                  \
                            "c.lwsp t2, 0x30(sp)\n\t"                                                                  \
                            "c.lwsp a0, 0x2c(sp)\n\t"                                                                  \
                            "c.lwsp a1, 0x28(sp)\n\t"                                                                  \
                            "c.lwsp a2, 0x24(sp)\n\t"                                                                  \
                            "c.lwsp a3, 0x20(sp)\n\t"                                                                  \
                            "c.lwsp a4, 0x1c(sp)\n\t"                                                                  \
                            "c.lwsp a5, 0x18(sp)\n\t"                                                                  \
                            "c.lwsp a6, 0x14(sp)\n\t"                                                                  \
                            "c.lwsp a7, 0x10(sp)\n\t"                                                                  \
                            "c.lwsp t3,  0xc(sp)\n\t"                                                                  \
                            "c.lwsp t4,  0x8(sp)\n\t"                                                                  \
                            "c.lwsp t5,  0x4(sp)\n\t"                                                                  \
                            "c.lwsp t6,  0x0(sp)\n\t"                                                                  \
                            "c.addi16sp sp, 0x40\n\t");                                                                \
        retirq(); /* custom implemented mret in the ULP-RISC-V */                                                      \
    } while (0);

#define _ISR_BODY(irqno)                                                                                               \
    do {                                                                                                               \
        ISR_PROLOGUE();                                                                                                \
        if (!do_handle((irqno))) {                                                                                     \
            while (1)                                                                                                  \
                ;                                                                                                      \
        }                                                                                                              \
        ISR_EPILOGUE();                                                                                                \
    } while (0)

#define ISR(irqno)                                                                                                     \
    __attribute__((used)) __attribute__((naked)) __attribute__((noreturn)) void IRQ##irqno##_Handler() {               \
        _ISR_BODY(irqno);                                                                                              \
    }

ISR(0)
ISR(1)
ISR(2)
ISR(31)

__attribute__((used)) __attribute__((noreturn)) __attribute__((naked)) void Default_Error_Handler() {
    _ISR_BODY(0xFFFFFFFF);
}

./main/ulp_isr_loader.c


#include "esp_log.h"
#include "esp_sleep.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ulp_isr_ulp.h"
#include "ulp_riscv.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>

#include <soc/rtc_cntl_reg.h>
#include <soc/rtc_io_reg.h>
#include <soc/sens_reg.h>

static const char *TAG = "ulp_isr";

extern const uint8_t ulp_isr_ulp_bin_start[] asm("_binary_ulp_isr_ulp_bin_start");
extern const uint8_t ulp_isr_ulp_bin_end[] asm("_binary_ulp_isr_ulp_bin_end");

int setup_ulp_isr() {
    esp_err_t err = ulp_riscv_load_binary(ulp_isr_ulp_bin_start, (ulp_isr_ulp_bin_end - ulp_isr_ulp_bin_start));
    ESP_ERROR_CHECK(err);

    ESP_LOGI(TAG, "ULP program loaded at %08X:%08X", (unsigned)ulp_isr_ulp_bin_start, (unsigned)ulp_isr_ulp_bin_end);

    /* 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, 50000);

    /* Start the program */
    err = ulp_riscv_run();
    ESP_ERROR_CHECK(err);
    return 0;
}

void app_main() {
    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    ESP_LOGI(TAG, "wakeup: %i", cause);
    setup_ulp_isr();

    /* Go back to sleep, only the ULP Risc-V will run */
    ESP_LOGI(TAG, "Entering in deep sleep");

    /* Small delay to ensure the messages are printed */
    vTaskDelay(100);

    /* RTC peripheral power domain needs to be kept on to detect
       the GPIO state change */
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);

    // ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());
    esp_deep_sleep_start();
}

Logging Messages

Following are log from the each channels

From the ULP-RISC-V

(ulp_isr@ULP) At function: main@00000174
(ulp_isr@ULP) SENS_SAR_COCPU_INT_ENA_REG: 00000000 00000000 00000000 00000000
(ulp_isr@ULP) After Configure SENS_SAR_COCPU_INT_ENA_REG: 00000000 00000000 00000000 11101000
(ulp_isr@ULP) Before disable irq
(ulp_isr@ULP) Interrupt registers:
        Q0: 01011111 10011010 00010101 11111000  5f9a15f8
        Q1: 00011110 11111011 01100000 10100001  1efb60a1
        Q2: 00100001 10110101 01110101 11101000  21b575e8
        Q3: 01110011 11010101 11000101 01101010  73d5c56a
(ulp_isr@ULP ISR) IRQ00000000 Handler Triggered
(ulp_isr@ULP ISR) Interrupt registers:
        Q0: 00000000 00000000 00000010 01000000  00000240
        Q1: 10000000 00000000 00000000 00000000  80000000
        Q2: 00100001 10110101 01110101 11101000  21b575e8
        Q3: 01110011 11010101 11000101 01101010  73d5c56a
(ulp_isr@ULP ISR) Peripheral interrupt handled
        SENS_SAR_COCPU_INT_ST_REG:
        00000000 00000000 00000000 01000000
(ulp_isr@ULP) Backuped irq mask: ffffffff
(ulp_isr@ULP) After disable irq
(ulp_isr@ULP) Interrupt registers:
        Q0: 00000000 00000000 00000010 01000000  00000240
        Q1: 10000000 00000000 00000000 00000000  80000000
        Q2: 00100001 10110101 01110101 11101000  21b575e8
        Q3: 01110011 11010101 11000101 01101010  73d5c56a
(ulp_isr@ULP) After re-enable irq
(ulp_isr@ULP) Interrupt registers:
        Q0: 00000000 00000000 00000010 01000000  00000240
        Q1: 10000000 00000000 00000000 00000000  80000000
        Q2: 00100001 10110101 01110101 11101000  21b575e8
        Q3: 01110011 11010101 11000101 01101010  73d5c56a
(ulp_isr@ULP) After enable irq 0,1,2,31
(ulp_isr@ULP) Interrupt registers:
        Q0: 00000000 00000000 00000010 01000000  00000240
        Q1: 10000000 00000000 00000000 00000111  80000007
        Q2: 00100001 10110101 01110101 11101000  21b575e8
        Q3: 01110011 11010101 11000101 01101010  73d5c56a
(ulp_isr@ULP) 00000000 @ cycle 00000000_01fe21c8
(ulp_isr@ULP) Interrupt registers:
        Q0: 00000000 00000000 00000010 01000000  00000240
        Q1: 10000000 00000000 00000000 00000111  80000007
        Q2: 00000000 00000000 00000000 00000000  00000000
        Q3: 00000000 00000000 00000000 00000000  00000000
(ulp_isr@ULP) 00000001 @ cycle 00000000_035ccd0f
(ulp_isr@ULP) Interrupt registers:
        Q0: 00000000 00000000 00000010 01000000  00000240
        Q1: 10000000 00000000 00000000 00000111  80000007
        Q2: 00000000 00000000 00000000 00000000  00000000
        Q3: 00000000 00000000 00000000 00000001  00000001
(ulp_isr@ULP) Before triggering SW_INT.
        SENS_SAR_COCPU_INT_ST_REG:
        00000000 00000000 00000000 00000000
(ulp_isr@ULP) After triggered SW_INT.
        SENS_SAR_COCPU_INT_ST_REG:
        00000000 00000000 00000000 10000000
(ulp_isr@ULP) 00000002 @ cycle 00000000_04f8dc45
(ulp_isr@ULP) Interrupt registers:
        Q0: 00000000 00000000 00000010 01000000  00000240
        Q1: 10000000 00000000 00000000 00000111  80000007
        Q2: 00000000 00000000 00000000 00000000  00000000
        Q3: 00000000 00000000 00000000 00000010  00000002

From the main processor

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWERON),boot:0x3b (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
Octal Flash Mode Enabled
For OPI Flash, Use Default Flash Boot Mode
mode:SLOW_RD, clock div:1
load:0x3fce3818,len:0x16f4
load:0x403c9700,len:0x4
load:0x403c9704,len:0xc00
load:0x403cc700,len:0x2eb0
SHA-256 comparison failed:
Calculated: 719c48f35a75e6514d589525226f5d4a6f0138c26a8d7088c97518733c7840ef
Expected: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Attempting to boot anyway...
entry 0x403c9908
I (52) boot: ESP-IDF v5.1-dev-4558-gc492b909bc 2nd stage bootloader
I (52) boot: compile time Apr 13 2023 02:08:14
I (52) boot: Multicore bootloader
I (57) boot: chip revision: v0.1
I (60) boot.esp32s3: Boot SPI Speed : 80MHz
I (65) boot.esp32s3: SPI Mode       : SLOW READ
I (70) boot.esp32s3: SPI Flash Size : 2MB
I (75) boot: Enabling RNG early entropy source...
I (81) boot: Partition Table:
I (84) boot: ## Label            Usage          Type ST Offset   Length
I (91) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (99) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (106) boot:  2 factory          factory app      00 00 00010000 00100000
I (114) boot: End of partition table
I (118) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=0a0d8h ( 41176) map
I (133) esp_image: segment 1: paddr=0001a100 vaddr=3fc94200 size=03758h ( 14168) load
I (137) esp_image: segment 2: paddr=0001d860 vaddr=40374000 size=027b8h ( 10168) load
I (145) esp_image: segment 3: paddr=00020020 vaddr=42000020 size=180e8h ( 98536) map
I (168) esp_image: segment 4: paddr=00038110 vaddr=403767b8 size=0d9c4h ( 55748) load
I (178) esp_image: segment 5: paddr=00045adc vaddr=600fe000 size=00070h (   112) load
I (185) boot: Loaded app from partition at offset 0x10000
I (185) boot: Disabling RNG early entropy source...
I (198) cpu_start: Multicore app
I (199) cpu_start: Pro cpu up.
I (199) cpu_start: Starting app cpu, entry point is 0x40375600
I (0) cpu_start: App cpu up.
I (216) cpu_start: Pro cpu start user code
I (216) cpu_start: cpu freq: 240000000 Hz
I (216) cpu_start: Application information:
I (219) cpu_start: Project name:     isr_esp32s3
I (224) cpu_start: App version:      1
I (229) cpu_start: Compile time:     Apr 13 2023 02:08:09
I (235) cpu_start: ELF file SHA256:  d36bb4fd4c58db97...
I (241) cpu_start: ESP-IDF:          v5.1-dev-4558-gc492b909bc
I (247) cpu_start: Min chip rev:     v0.1
I (252) cpu_start: Max chip rev:     v0.99
I (257) cpu_start: Chip rev:         v0.1
I (262) heap_init: Initializing. RAM available for dynamic allocation:
I (269) heap_init: At 3FC98A08 len 000375F8 (221 KiB): DRAM
I (275) heap_init: At 3FCD8000 len 00011710 (69 KiB): DRAM
I (281) heap_init: At 3FCE9710 len 00005724 (21 KiB): STACK/DRAM
I (288) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (294) heap_init: At 600FE070 len 00001F90 (7 KiB): RTCRAM
I (301) spi_flash: detected chip: mxic (opi)
I (305) spi_flash: flash io: opi_dtr
W (310) spi_flash: Detected size(32768k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (323) sleep: Configure to isolate all GPIO pins in sleep state
I (330) sleep: Enable automatic switching of GPIO sleep configuration
I (337) esp_apptrace: Initialized TRAX on CPU0
I (342) app_start: Starting scheduler on CPU0
I (136) esp_apptrace: Initialized TRAX on CPU1
I (357) app_start: Starting scheduler on CPU1
I (347) main_task: Started on CPU0
I (367) main_task: Calling app_main()
I (367) ulp_isr: wakeup: 0
I (367) ulp_isr: ULP program loaded at 3C02600A:3C02696F
I (377) ulp_isr: Entering in deep sleep
sudeep-mohanty commented 1 year ago

Hi @andylinpersonal Thanks for sharing the extensive investigation on this. We'll take a look and get back to you.

5ami commented 1 year ago

Hi @andylinpersonal, Thank you for post. I am also Interested in this, and noticed lack of documentation in The TRM as The interrupt controller in ULP-RISC-V is implemented using a customized instruction set instead of the standard RISC-V Privileged ISA specification.

andylinpersonal commented 1 year ago

Hi @sudeep-mohanty,

Is there any update about how to bring up ULP-RISC-V's own interrupt controller correctly?

If I want to monitor the RTC GPIO pins in the ULP-RISC-V without the intervention of the main CPUs, can I use the interrupt mechanism partially mentioned in the TRM? Or I can only poll the pins manually?

Best regards, thanks

sudeep-mohanty commented 1 year ago

Hi @andylinpersonal, Apologies for the delayed response. We are yet to work on the ULP RISC-V interrupt controller owing to some other engagements but we do plan to work on it asap. Apologies again for the delay.

Regarding your question about monitoring GPIO pins - At the moment, to monitor the GPIO level, you will have to poll the state. However, if your use case is to wakeup the ULP core with a GPIO trigger (as opposed to periodic wakeup using ULP timer) then we do have an example demonstrating this.

andylinpersonal commented 9 months ago

a6461eab775d84e85962d14eaa11f8dfea944f9d 70241d13a29f4530d39383cc412f170e079b5f6d b9ecc1e57ab5b2ad1bdaacdb0c600f05b694853f 94e2516f6cf5ab73f6b0efa20720957c3e2e1974 82f2294bcbb2b67b8a2a79d8ab2af222e9cd0eff

Basic interrupt handling and RTC GPIO interrupt have been merged recently so closing now.