diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index f2a5ec89ae..4b174402cf 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -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,8 @@ 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_interrupt.c") target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles") target_link_options(${ULP_APP_NAME} PRIVATE -Wl,--gc-sections) @@ -72,7 +74,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) diff --git a/components/ulp/ld/ulp_riscv.ld b/components/ulp/ld/ulp_riscv.ld index ba58ca788f..eedc22f004 100644 --- a/components/ulp/ld/ulp_riscv.ld +++ b/components/ulp/ld/ulp_riscv.ld @@ -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 diff --git a/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_interrupt_ops.h b/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_interrupt_ops.h new file mode 100644 index 0000000000..f902507995 --- /dev/null +++ b/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_interrupt_ops.h @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Claire Xenia Wolf + * 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 diff --git a/components/ulp/ulp_riscv/ulp_core/start.S b/components/ulp/ulp_riscv/ulp_core/start.S index 02eabfe2fc..cad6db5f5a 100644 --- a/components/ulp/ulp_riscv/ulp_core/start.S +++ b/components/ulp/ulp_riscv/ulp_core/start.S @@ -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 diff --git a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_interrupt.c b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_interrupt.c new file mode 100644 index 0000000000..b3a5e052de --- /dev/null +++ b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_interrupt.c @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "ulp_riscv_register_ops.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) + +/* 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) { + // TODO + + /* Peripheral interrupts */ + + /* RTC I2C interrupt */ + + /* RTC IO interrupt */ + } +} diff --git a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_vectors.S b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_vectors.S new file mode 100644 index 0000000000..b302db429a --- /dev/null +++ b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_vectors.S @@ -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()