Browse Source
Add ULP Coprocessor support for ESP32C6. Signed-off-by: Lucas Tamborrino <lucas.tamborrino@espressif.com>pull/87487/head
14 changed files with 642 additions and 66 deletions
@ -0,0 +1,63 @@
@@ -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 */ |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@
@@ -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)>; |
||||
}; |
@ -0,0 +1,85 @@
@@ -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 |
@ -0,0 +1,131 @@
@@ -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 |
||||
} |
@ -0,0 +1,47 @@
@@ -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(); |
||||
} |
@ -0,0 +1,32 @@
@@ -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; |
||||
} |
@ -0,0 +1,25 @@
@@ -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 |
@ -0,0 +1,22 @@
@@ -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 |
@ -0,0 +1,142 @@
@@ -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…
Reference in new issue