Browse Source

soc: espressif: esp32c6: Add LP Core

Add ULP Coprocessor support for ESP32C6.

Signed-off-by: Lucas Tamborrino <lucas.tamborrino@espressif.com>
pull/87487/head
Lucas Tamborrino 6 months ago committed by Benjamin Cabé
parent
commit
0b9e4e013a
  1. 3
      drivers/clock_control/clock_control_esp32.c
  2. 6
      dts/riscv/espressif/esp32c6/esp32c6_common.dtsi
  3. 63
      dts/riscv/espressif/esp32c6/esp32c6_lpcore.dtsi
  4. 12
      dts/riscv/espressif/esp32c6/esp32c6_lpcore_wroom_n4.dtsi
  5. 19
      include/zephyr/dt-bindings/clock/esp32c6_clock.h
  6. 2
      soc/espressif/Kconfig
  7. 85
      soc/espressif/Kconfig.ulp
  8. 131
      soc/espressif/esp32c6/default_lpcore.ld
  9. 47
      soc/espressif/esp32c6/hpcore_init_ulp.c
  10. 3
      soc/espressif/esp32c6/memory.h
  11. 32
      soc/espressif/esp32c6/soc_lpcore.c
  12. 25
      soc/espressif/esp32c6/start_lpcore.S
  13. 22
      soc/espressif/esp32c6/vector_table_lpcore.S
  14. 142
      soc/espressif/esp32c6/vectors_lpcore.S

3
drivers/clock_control/clock_control_esp32.c

@ -105,7 +105,8 @@ static void esp32_clock_perip_init(void)
soc_reset_reason_t rst_reason = esp_rom_get_reset_reason(0); soc_reset_reason_t rst_reason = esp_rom_get_reset_reason(0);
if ((rst_reason != RESET_REASON_CPU0_MWDT0) && (rst_reason != RESET_REASON_CPU0_MWDT1) && if ((rst_reason != RESET_REASON_CPU0_MWDT0) && (rst_reason != RESET_REASON_CPU0_MWDT1) &&
(rst_reason != RESET_REASON_CPU0_SW) && (rst_reason != RESET_REASON_CPU0_RTC_WDT)) { (rst_reason != RESET_REASON_CPU0_SW) && (rst_reason != RESET_REASON_CPU0_RTC_WDT) &&
(rst_reason != RESET_REASON_CPU0_JTAG)) {
periph_ll_disable_clk_set_rst(PERIPH_UART1_MODULE); periph_ll_disable_clk_set_rst(PERIPH_UART1_MODULE);
periph_ll_disable_clk_set_rst(PERIPH_I2C0_MODULE); periph_ll_disable_clk_set_rst(PERIPH_I2C0_MODULE);

6
dts/riscv/espressif/esp32c6/esp32c6_common.dtsi

@ -76,9 +76,15 @@
}; };
sramlp: memory@50000000 { sramlp: memory@50000000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "zephyr,memory-region", "mmio-sram"; compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x50000000 DT_SIZE_K(16)>; reg = <0x50000000 DT_SIZE_K(16)>;
zephyr,memory-region = "SRAMLP "; zephyr,memory-region = "SRAMLP ";
shmlp: memory@0 {
reg = <0x0 0x10>;
};
}; };
intc: interrupt-controller@60010000 { intc: interrupt-controller@60010000 {

63
dts/riscv/espressif/esp32c6/esp32c6_lpcore.dtsi

@ -0,0 +1,63 @@
/*
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <mem.h>
#include <freq.h>
#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/interrupt-controller/esp-esp32c6-intmux.h>
#include <zephyr/dt-bindings/clock/esp32c6_clock.h>
#include <dt-bindings/pinctrl/esp32c6-pinctrl.h>
/ {
#address-cells = <1>;
#size-cells = <1>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
device_type = "cpu";
compatible = "espressif,riscv";
riscv,isa = "rv32imac_zicsr_zifencei";
reg = <0>;
clock-source = <ESP32_RTC_FAST_CLK_SRC_XTAL_D2>;
clock-frequency = <DT_FREQ_M(20)>;
xtal-freq = <DT_FREQ_M(40)>;
};
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges;
sramlp: memory@50000000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "mmio-sram";
reg = <0x50000000 DT_SIZE_K(16)>;
shmlp: memory@0 {
reg = <0x0 0x10>;
};
};
flash: flash-controller@60002000 {
compatible = "espressif,esp32-flash-controller";
reg = <0x60002000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
flash0: flash@0 {
compatible = "soc-nv-flash";
erase-block-size = <4096>;
write-block-size = <4>;
/* Flash size is specified in SOC/SIP dtsi */
};
};
};
};

12
dts/riscv/espressif/esp32c6/esp32c6_lpcore_wroom_n4.dtsi

@ -0,0 +1,12 @@
/*
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp32c6_lpcore.dtsi"
/* 4MB flash */
&flash0 {
reg = <0x0 DT_SIZE_M(4)>;
};

19
include/zephyr/dt-bindings/clock/esp32c6_clock.h

@ -74,14 +74,19 @@
#define ESP32_SYSTIMER_MODULE 27 #define ESP32_SYSTIMER_MODULE 27
#define ESP32_SARADC_MODULE 28 #define ESP32_SARADC_MODULE 28
#define ESP32_TEMPSENSOR_MODULE 29 #define ESP32_TEMPSENSOR_MODULE 29
#define ESP32_REGDMA_MODULE 30 #define ESP32_ASSIST_DEBUG_MODULE 30
/* LP peripherals */
#define ESP32_LP_I2C0_MODULE 31 #define ESP32_LP_I2C0_MODULE 31
#define ESP32_LP_UART0_MODULE 32
/* Peripherals clock managed by the modem_clock driver must be listed last */ /* Peripherals clock managed by the modem_clock driver must be listed last */
#define ESP32_WIFI_MODULE 32 #define ESP32_WIFI_MODULE 33
#define ESP32_BT_MODULE 33 #define ESP32_BT_MODULE 34
#define ESP32_IEEE802154_MODULE 34 #define ESP32_IEEE802154_MODULE 35
#define ESP32_COEX_MODULE 35 #define ESP32_COEX_MODULE 36
#define ESP32_PHY_MODULE 36 #define ESP32_PHY_MODULE 37
#define ESP32_MODULE_MAX 37 #define ESP32_ANA_I2C_MASTER_MODULE 38
#define ESP32_MODEM_ETM_MODULE 39
#define ESP32_MODEM_ADC_COMMON_FE_MODULE 40
#define ESP32_MODULE_MAX 41
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_ESP32C6_H_ */ #endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_ESP32C6_H_ */

2
soc/espressif/Kconfig

@ -62,4 +62,6 @@ config RTC_CLK_CAL_CYCLES
If the crystal could not start, it will be switched to internal RC. If the crystal could not start, it will be switched to internal RC.
endmenu endmenu
rsource "Kconfig.ulp"
endif # SOC_FAMILY_ESPRESSIF_ESP32 endif # SOC_FAMILY_ESPRESSIF_ESP32

85
soc/espressif/Kconfig.ulp

@ -0,0 +1,85 @@
# Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
# SPDX-License-Identifier: Apache-2.0
menu "Ultra Low Power (ULP) Coprocessor"
config ULP_COPROC_ENABLED
bool "Ultra Low Power (ULP) Coprocessor"
help
Enable this feature if you plan to use the ULP Coprocessor.
Once this option is enabled, further ULP co-processor configuration will appear in the menu.
choice ULP_COPROC_TYPE
prompt "ULP Coprocessor type"
depends on ULP_COPROC_ENABLED
default ULP_COPROC_TYPE_LP_CORE if SOC_SERIES_ESP32C6
help
Choose the ULP Coprocessor type: ULP FSM (Finite State Machine) or ULP RISC-V.
config ULP_COPROC_TYPE_FSM
bool "ULP FSM (Finite State Machine)"
depends on SOC_SERIES_ESP32 || SOC_SERIES_ESP32S2 || SOC_SERIES_ESP32S3
config ULP_COPROC_TYPE_RISCV
bool "ULP RISC-V"
depends on SOC_SERIES_ESP32S2 || SOC_SERIES_ESP32S3
config ULP_COPROC_TYPE_LP_CORE
bool "LP core RISC-V"
depends on SOC_SERIES_ESP32C6
endchoice
menu "ULP RISC-V Settings"
depends on ULP_COPROC_TYPE_RISCV
config ULP_RISCV_INTERRUPT_ENABLE
bool "ULP RISC-V interrupts"
help
Turn on this setting to enabled interrupts on the ULP RISC-V core.
endmenu
menu "ULP Debugging Options"
config ULP_PANIC_OUTPUT_ENABLE
bool "Panic handler outputs to LP UART"
depends on ULP_COPROC_TYPE_LP_CORE
help
Set this option to enable panic handler functionality. If this option is
enabled then the LP Core will output a panic dump over LP UART,
similar to what the main core does. Output depends on LP UART already being
initialized and configured.
Disabling this option will reduce the LP core binary size by not
linking in panic handler functionality.
config ULP_HP_UART_CONSOLE_PRINT
bool "Route lp_core_printf to the console HP-UART"
depends on ULP_COPROC_TYPE_LP_CORE
help
Set this option to route lp_core_printf to the console HP-UART.
This allows you to easily view print outputs from the LP core, without
having to connect to the LP-UART. This option comes with the following
limitations:
1. There is no mutual exclusion between the HP-Core and the LP-Core accessing
the HP-UART, which means that if both cores are logging heavily the output
strings might get mangled together.
2. The HP-UART can only work while the HP-Core is running, which means that
if the HP-Core is in deep sleep, the LP-Core will not be able to print to the
console HP-UART.
Due to these limitations it is only recommended to use this option for easy debugging.
For more serious use-cases you should use the LP-UART.
config ULP_NORESET_UNDER_DEBUG
bool "Avoid resetting LP core when debugger is attached"
depends on ULP_COPROC_TYPE_LP_CORE
default y
help
Enable this feature to avoid resetting LP core in sleep mode when debugger is attached,
otherwise configured HW breakpoints and dcsr.ebreak* bits will be missed.
This is a workaround until it will be fixed in HW.
endmenu
endmenu # Ultra Low Power (ULP) Coprocessor

131
soc/espressif/esp32c6/default_lpcore.ld

@ -0,0 +1,131 @@
/*
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/devicetree.h>
#include <zephyr/linker/sections.h>
#include <zephyr/linker/linker-defs.h>
#include <zephyr/linker/linker-tool.h>
#include "memory.h"
#define ALIGN_DOWN(SIZE, AL) (SIZE & ~(AL - 1))
/* Ensure the end where the shared memory starts is aligned to 8 bytes
if updating this also update the same in ulp_lp_core_memory_shared.c
*/
#define ALIGNED_COPROC_MEM ALIGN_DOWN(ULP_COPROC_RESERVE_MEM, 0x8)
#define RODATA_REGION ram
#define RAMABLE_REGION ram
#define ROMABLE_REGION ram
/* User available memory segments */
_aligned_coproc_mem = ALIGNED_COPROC_MEM;
_vector_table_org = LPSRAM_IRAM_START;
_vector_table_len = 0x80;
_ram_org = _vector_table_org + _vector_table_len;
_ram_len = _aligned_coproc_mem - _vector_table_len - ULP_SHARED_MEM;
_shared_mem_org = _ram_org + _ram_len;
_shared_mem_len = ULP_SHARED_MEM;
ENTRY(reset_vector)
MEMORY
{
/*first 128byte for exception/interrupt vectors*/
vector_table(RX) : ORIGIN = _vector_table_org , LENGTH = _vector_table_len
ram(RWX) : ORIGIN = _ram_org, LENGTH = _ram_len
shared_mem_ram(RW) : ORIGIN = _shared_mem_org, LENGTH = _shared_mem_len
}
SECTIONS
{
.vector.text :
{
__mtvec_base = .;
KEEP (*(.init.vector .init.vector.*))
} > vector_table
. = ORIGIN(ram);
.text ALIGN(4):
{
*(.text.vectors) /* Default reset vector must link to offset 0x80 */
__text_region_start = ABSOLUTE(.);
*(.text)
*(.text*)
__text_region_end = ABSOLUTE(.);
} >ram
#include <zephyr/linker/rel-sections.ld>
.rodata ALIGN(4):
{
__rodata_region_start = ABSOLUTE(.);
*(.rodata .srodata)
*(.rodata* .srodata*)
__rodata_region_end = .;
} > ram
#include <zephyr/linker/common-rom/common-rom-kernel-devices.ld>
.rodata.end ALIGN(4):
{
_rodata_reserved_end = ABSOLUTE(.);
} > ram
.data ALIGN(4):
{
_image_ram_start = ABSOLUTE(.);
*(.data)
*(.data*)
*(.sdata)
*(.sdata*)
} > ram
#include <snippets-data-sections.ld>
#include <zephyr/linker/common-ram.ld>
#include <snippets-ram-sections.ld>
.data.end ALIGN(4):
{
_image_ram_end = ABSOLUTE(.);
} > ram
.data.noinit (NOLOAD):
{
. = ALIGN(4);
*(.noinit)
*(.noinit.*)
. = ALIGN(4);
} > ram
.bss ALIGN(4) :
{
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
PROVIDE(end = .);
_heap_sentry = .;
} >ram
#include <zephyr/linker/ram-end.ld>
__stack_top = ORIGIN(ram) + LENGTH(ram);
#include <zephyr/linker/debug-sections.ld>
SECTION_PROLOGUE(.riscv.attributes, 0,)
{
KEEP(*(.riscv.attributes))
KEEP(*(.gnu.attributes))
}
. = ORIGIN(shared_mem_ram);
.shared_mem (ALIGN(4)) :
{
KEEP(*(.shared_mem))
} > shared_mem_ram
}

47
soc/espressif/esp32c6/hpcore_init_ulp.c

@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include "bootloader_flash_priv.h"
#include <zephyr/storage/flash_map.h>
#include "ulp_lp_core.h"
#include "lp_core_uart.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
void IRAM_ATTR lp_core_image_init(void)
{
const uint32_t lpcore_img_off = FIXED_PARTITION_OFFSET(slot0_lpcore_partition);
const uint32_t lpcore_img_size = 0x4000;
int ret = 0;
LOG_INF("Getting LPU image at %p, size %d", (void *)lpcore_img_off, lpcore_img_size);
const uint8_t *data = (const uint8_t *)bootloader_mmap(lpcore_img_off, lpcore_img_size);
ret = ulp_lp_core_load_binary(data, lpcore_img_size);
if (ret) {
LOG_ERR("Failed to load LP core image: %d", ret);
}
LOG_INF("LP core image loaded");
/* Set LP core wakeup source as the HP CPU */
ulp_lp_core_cfg_t cfg = {
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
};
ret = ulp_lp_core_run(&cfg);
if (ret) {
LOG_ERR("Failed to start LP core: %d", ret);
}
}
void soc_late_init_hook(void)
{
lp_core_image_init();
}

3
soc/espressif/esp32c6/memory.h

@ -7,6 +7,9 @@
/* LP-SRAM (16kB) memory */ /* LP-SRAM (16kB) memory */
#define LPSRAM_IRAM_START DT_REG_ADDR(DT_NODELABEL(sramlp)) #define LPSRAM_IRAM_START DT_REG_ADDR(DT_NODELABEL(sramlp))
#define LPSRAM_SIZE DT_REG_SIZE(DT_NODELABEL(sramlp)) #define LPSRAM_SIZE DT_REG_SIZE(DT_NODELABEL(sramlp))
#define ULP_SHARED_MEM DT_REG_SIZE(DT_NODELABEL(shmlp))
#define ULP_COPROC_RESERVE_MEM (0x4000)
/* HP-SRAM (512kB) memory */ /* HP-SRAM (512kB) memory */
#define HPSRAM_START DT_REG_ADDR(DT_NODELABEL(sramhp)) #define HPSRAM_START DT_REG_ADDR(DT_NODELABEL(sramhp))
#define HPSRAM_SIZE DT_REG_SIZE(DT_NODELABEL(sramhp)) #define HPSRAM_SIZE DT_REG_SIZE(DT_NODELABEL(sramhp))

32
soc/espressif/esp32c6/soc_lpcore.c

@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc_caps.h"
#include "esp_rom_caps.h"
#include "rom/ets_sys.h"
#include "ulp_lp_core_utils.h"
#include "ulp_lp_core_lp_timer_shared.h"
#include "ulp_lp_core_memory_shared.h"
#include "ulp_lp_core_print.h"
#include <soc.h>
#include <kernel_internal.h>
extern void main(void);
/* Initialize lp core related system functions before calling user's main*/
void lp_core_startup(void)
{
#if CONFIG_ULP_HP_UART_CONSOLE_PRINT && ESP_ROM_HAS_LP_ROM
ets_install_putc1(lp_core_print_char);
#endif
ulp_lp_core_update_wakeup_cause();
/* Start Zephyr */
z_cstart();
CODE_UNREACHABLE;
}

25
soc/espressif/esp32c6/start_lpcore.S

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
.section .text.vectors
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
/* _vector_table: Only 256-byte aligned addresses are allowed */
la t0, _vector_table
csrw mtvec, t0
j __start
__start:
/* setup the stack pointer */
la sp, __stack_top
call lp_core_startup
loop:
j loop

22
soc/espressif/esp32c6/vector_table_lpcore.S

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
.section .init.vector,"ax"
.global _vector_table
.type _vector_table, @function
_vector_table:
.option push
.option norvc
.rept 30
j _panic_handler
.endr
j _interrupt_handler // All interrupts are routed to mtvec + 4*30, i.e. the 31st entry
j _panic_handler
.option pop
.size _vector_table, .-_vector_table

142
soc/espressif/esp32c6/vectors_lpcore.S

@ -0,0 +1,142 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "riscv/rvruntime-frames.h"
#include <soc/soc_caps.h>
.equ SAVE_REGS, 32
.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. */
.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 s0, RV_STK_S0(sp)
sw s1, RV_STK_S1(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 s2, RV_STK_S2(sp)
sw s3, RV_STK_S3(sp)
sw s4, RV_STK_S4(sp)
sw s5, RV_STK_S5(sp)
sw s6, RV_STK_S6(sp)
sw s7, RV_STK_S7(sp)
sw s8, RV_STK_S8(sp)
sw s9, RV_STK_S9(sp)
sw s10, RV_STK_S10(sp)
sw s11, RV_STK_S11(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
.macro save_mepc
csrr t0, mepc
sw t0, RV_STK_MEPC(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 s0, RV_STK_S0(sp)
lw s1, RV_STK_S1(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 s2, RV_STK_S2(sp)
lw s3, RV_STK_S3(sp)
lw s4, RV_STK_S4(sp)
lw s5, RV_STK_S5(sp)
lw s6, RV_STK_S6(sp)
lw s7, RV_STK_S7(sp)
lw s8, RV_STK_S8(sp)
lw s9, RV_STK_S9(sp)
lw s10, RV_STK_S10(sp)
lw s11, RV_STK_S11(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
.macro restore_mepc
lw t0, RV_STK_MEPC(sp)
csrw mepc, t0
.endm
/* _panic_handler: handle all exception */
.section .text.handlers,"ax"
.global _panic_handler
.type _panic_handler, @function
_panic_handler:
save_general_regs RV_STK_FRMSZ
save_mepc
addi t0, sp, RV_STK_FRMSZ /* Restore sp with the value when trap happened */
/* Save CSRs */
sw t0, RV_STK_SP(sp)
csrr t0, mstatus
sw t0, RV_STK_MSTATUS(sp)
csrr t0, mcause
sw t0, RV_STK_MCAUSE(sp)
csrr t0, mtvec
sw t0, RV_STK_MTVEC(sp)
csrr t0, mhartid
sw t0, RV_STK_MHARTID(sp)
csrr t0, mtval
sw t0, RV_STK_MTVAL(sp)
csrr a1, mcause /* Exception cause */
mv a0, sp /* RvExcFrame *regs */
call ulp_lp_core_panic_handler
_end:
j _end /* loop forever */
/* _interrupt_handler: handle all interrupt */
.section .text.handlers,"ax"
.global _interrupt_handler
.type _interrupt_handler, @function
_interrupt_handler:
/* Save registers & mepc to stack */
save_general_regs
save_mepc
call ulp_lp_core_intr_handler
/* Restore registers & mepc from stack */
restore_mepc
restore_general_regs
/* Exit, this will also re-enable the interrupts */
mret
Loading…
Cancel
Save