From 03f4744497e0389aa25b57c5fdcbf581276fc145 Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Tue, 29 Jul 2025 09:39:16 +0800 Subject: [PATCH 1/2] feat(riscv): add support for the DSP coprocessor --- .../FreeRTOS-Kernel/portable/riscv/port.c | 6 +- .../FreeRTOS-Kernel/portable/riscv/portasm.S | 68 +++++++++++++++++++ components/riscv/include/riscv/csr_dsp.h | 27 ++++++++ components/riscv/include/riscv/rv_utils.h | 23 ++++++- .../riscv/include/riscv/rvruntime-frames.h | 32 ++++++++- components/riscv/vectors.S | 18 ++--- .../esp32h4/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32h4/include/soc/soc_caps.h | 1 + 8 files changed, 168 insertions(+), 11 deletions(-) create mode 100644 components/riscv/include/riscv/csr_dsp.h diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c index e5dbf8238c..8e67e71af4 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c @@ -136,7 +136,11 @@ BaseType_t xPortStartScheduler(void) #if SOC_CPU_HAS_PIE /* Similarly, disable PIE */ rv_utils_disable_pie(); -#endif /* SOC_CPU_HAS_FPU */ +#endif /* SOC_CPU_HAS_PIE */ + +#if SOC_CPU_HAS_DSP + rv_utils_disable_dsp(); +#endif /* SOC_CPU_HAS_DSP */ #if SOC_CPU_HAS_HWLOOP /* Initialize the Hardware loop feature */ diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S b/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S index 1bccf990c6..667c68c9a8 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S @@ -10,6 +10,7 @@ #include "riscv/rvruntime-frames.h" #include "riscv/csr_hwlp.h" #include "riscv/csr_pie.h" +#include "riscv/csr_dsp.h" .extern pxCurrentTCBs @@ -329,6 +330,59 @@ generate_coprocessor_routine pie, PIE_COPROC_IDX, pie_enable, pie_save_regs, pie #endif /* SOC_CPU_HAS_PIE */ +#if SOC_CPU_HAS_DSP + +/** + * @brief Macros to enable and disable the DSP coprocessor on the current core + */ +.macro dsp_enable scratch_reg=a0 + li \scratch_reg, 1 + csrw CSR_DSP_STATE_REG, \scratch_reg +.endm + +/** + * @brief Disable the DSP coprocessor while returning the former status in the given register + */ +.macro dsp_disable reg + csrrw \reg, CSR_DSP_STATE_REG, zero + /* Only keep the lowest two bits, if register is 0, DSP was off */ + andi \reg, \reg, 0b11 + beqz \reg, 1f + /* It was ON, return the enable bit in \reg */ + li \reg, 1 << DSP_COPROC_IDX +1: +.endm + +/** + * @brief Macros to save and restore the DSP coprocessor registers to and from the given frame + */ +.macro dsp_save_regs frame=a0 + csrr a1, CSR_DSP_XACC_L + sw a1, RV_DSP_XACC_L(\frame) + csrr a1, CSR_DSP_XACC_H + sw a1, RV_DSP_XACC_H(\frame) + csrr a1, CSR_DSP_SAR + sw a1, RV_DSP_SAR(\frame) + csrr a1, CSR_DSP_STATUS + sw a1, RV_DSP_STATUS(\frame) +.endm + + +.macro dsp_restore_regs frame=a0 + lw a1, RV_DSP_XACC_L(\frame) + csrw CSR_DSP_XACC_L, a1 + lw a1, RV_DSP_XACC_H(\frame) + csrw CSR_DSP_XACC_H, a1 + lw a1, RV_DSP_SAR(\frame) + csrw CSR_DSP_SAR, a1 + lw a1, RV_DSP_STATUS(\frame) + csrw CSR_DSP_STATUS, a1 +.endm + +generate_coprocessor_routine dsp, DSP_COPROC_IDX, dsp_enable, dsp_save_regs, dsp_restore_regs + +#endif /* SOC_CPU_HAS_DSP */ + #if SOC_CPU_HAS_FPU /* Bit to set in mstatus to enable the FPU */ @@ -513,6 +567,12 @@ rtos_int_enter: or s2, s2, a0 #endif /* SOC_CPU_HAS_PIE */ +#if SOC_CPU_HAS_DSP + /* The current DSP coprocessor status will be returned in a0 */ + dsp_disable a0 + or s2, s2, a0 +#endif /* SOC_CPU_HAS_DSP */ + #if SOC_CPU_HAS_FPU fpu_disable a0 #endif /* SOC_CPU_HAS_FPU */ @@ -675,6 +735,14 @@ no_context_switch: pie_enable a1 1: #endif /* SOC_CPU_HAS_PIE */ + +#if SOC_CPU_HAS_DSP + /* Re-enable the DSP coprocessor if it was used */ + andi a1, s8, 1 << DSP_COPROC_IDX + beqz a1, 1f + dsp_enable a1 +1: +#endif /* SOC_CPU_HAS_DSP */ j restore_stack_pointer context_switch_requested: diff --git a/components/riscv/include/riscv/csr_dsp.h b/components/riscv/include/riscv/csr_dsp.h new file mode 100644 index 0000000000..8e17ae92f2 --- /dev/null +++ b/components/riscv/include/riscv/csr_dsp.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "soc/soc_caps.h" + +#if SOC_CPU_HAS_DSP + +#define CSR_DSP_XACC_L 0x806 +#define CSR_DSP_XACC_H 0x807 +#define CSR_DSP_SAR 0x809 +#define CSR_DSP_STATUS 0x80a + +/** + * CSR lowest 2 bits describe the following states: + * 00: Off + * 01: Initial + * 10: Clean + * 11: Dirty + */ +#define CSR_DSP_STATE_REG 0x7f3 + +#endif /* SOC_CPU_HAS_DSP */ diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index dca85fa17b..7a0ebbfcef 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -14,6 +14,7 @@ #include "riscv/csr.h" #include "riscv/interrupt.h" #include "riscv/csr_pie.h" +#include "riscv/csr_dsp.h" #include "sdkconfig.h" #if CONFIG_SECURE_ENABLE_TEE && !NON_OS_BUILD @@ -275,7 +276,27 @@ FORCE_INLINE_ATTR void rv_utils_disable_pie(void) RV_WRITE_CSR(CSR_PIE_STATE_REG, 0); } -#endif /* SOC_CPU_HAS_FPU */ +#endif /* SOC_CPU_HAS_PIE */ + + +/* ------------------------------------------------- DSP Related ---------------------------------------------------- + * + * ------------------------------------------------------------------------------------------------------------------ */ + +#if SOC_CPU_HAS_DSP + +FORCE_INLINE_ATTR void rv_utils_enable_dsp(void) +{ + RV_WRITE_CSR(CSR_DSP_STATE_REG, 1); +} + + +FORCE_INLINE_ATTR void rv_utils_disable_dsp(void) +{ + RV_WRITE_CSR(CSR_DSP_STATE_REG, 0); +} + +#endif /* SOC_CPU_HAS_DSP */ diff --git a/components/riscv/include/riscv/rvruntime-frames.h b/components/riscv/include/riscv/rvruntime-frames.h index 45f013b0e5..947a8bab63 100644 --- a/components/riscv/include/riscv/rvruntime-frames.h +++ b/components/riscv/include/riscv/rvruntime-frames.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -179,6 +179,36 @@ STRUCT_END(RvHWLPSaveArea) #endif /* SOC_CPU_HAS_HWLOOP */ +#if SOC_CPU_HAS_DSP + +/* DSP coprocessor is considered coprocessor 1, just like the HWLP, make sure both are not present on the same target */ +#define DSP_COPROC_IDX 1 + +#ifdef SOC_CPU_HAS_HWLOOP +#error "HWLP and DSP share the same coprocessor index!" +#endif + +/** + * @brief DSP save area + */ +STRUCT_BEGIN +STRUCT_FIELD (long, 4, RV_DSP_XACC_L, xacc_l) +STRUCT_FIELD (long, 4, RV_DSP_XACC_H, xacc_h) +STRUCT_FIELD (long, 4, RV_DSP_SAR, sar) +STRUCT_FIELD (long, 4, RV_DSP_STATUS, status) +STRUCT_END(RvDSPSaveArea) + +/* Redefine the coprocessor area size previously defined to 0 */ +#undef RV_COPROC1_SIZE + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) + #define RV_COPROC1_SIZE RvDSPSaveAreaSize +#else + #define RV_COPROC1_SIZE sizeof(RvDSPSaveArea) +#endif /* defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) */ + +#endif /* SOC_CPU_HAS_DSP */ + #if SOC_CPU_HAS_PIE diff --git a/components/riscv/vectors.S b/components/riscv/vectors.S index 88d67d3288..47d0b372d9 100644 --- a/components/riscv/vectors.S +++ b/components/riscv/vectors.S @@ -20,22 +20,18 @@ #if ( SOC_CPU_COPROC_NUM > 0 ) /* Targets with coprocessors present a special CSR to get Illegal Instruction exception reason */ -#ifdef __clang__ - /* Clang does not support constant declared via `equ` as operand for csrrw - * TODO: LLVM-369 - */ #define EXT_ILL_CSR 0x7F0 -#else - .equ EXT_ILL_CSR, 0x7F0 -#endif /* EXT_ILL CSR reasons are stored as follows: * - Bit 0: FPU core instruction (Load/Store instructions NOT concerned) * - Bit 1: Hardware Loop instructions - * - Bit 2: PIE core */ + * - Bit 2: PIE core + * - Bit 3: DSP core + */ .equ EXT_ILL_RSN_FPU, 1 .equ EXT_ILL_RSN_HWLP, 2 .equ EXT_ILL_RSN_PIE, 4 + .equ EXT_ILL_RSN_DSP, 8 #endif /* SOC_CPU_COPROC_NUM > 0 */ /* Macro which first allocates space on the stack to save general @@ -252,6 +248,12 @@ _panic_handler: bnez a1, rtos_save_pie_coproc #endif /* SOC_CPU_HAS_PIE */ +#if SOC_CPU_HAS_DSP + /* Check if the DSP bit is set. */ + andi a1, a0, EXT_ILL_RSN_DSP + bnez a1, rtos_save_dsp_coproc +#endif /* SOC_CPU_HAS_DSP */ + /* We cannot check the HWLP bit in a0 since a hardware bug may set this bit even though no HWLP * instruction was executed in the program at all, so check mtval (`t0`) */ #if SOC_CPU_HAS_HWLOOP diff --git a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in index dae76e5395..ab2572594a 100644 --- a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in @@ -183,6 +183,10 @@ config SOC_CPU_HAS_FPU_EXT_ILL_BUG bool default n +config SOC_CPU_HAS_DSP + bool + default y + config SOC_CPU_COPROC_NUM int default 2 diff --git a/components/soc/esp32h4/include/soc/soc_caps.h b/components/soc/esp32h4/include/soc/soc_caps.h index c8ca8b3808..fe26ac767f 100644 --- a/components/soc/esp32h4/include/soc/soc_caps.h +++ b/components/soc/esp32h4/include/soc/soc_caps.h @@ -161,6 +161,7 @@ #define SOC_BRANCH_PREDICTOR_SUPPORTED 1 #define SOC_CPU_HAS_FPU 1 #define SOC_CPU_HAS_FPU_EXT_ILL_BUG 0 // EXT_ILL CSR doesn't support FLW/FSW +#define SOC_CPU_HAS_DSP 1 #define SOC_CPU_COPROC_NUM 2 #define SOC_CPU_BREAKPOINTS_NUM 4 From 63cf7c06d339815cee3140a34a1f186d6104626c Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Tue, 29 Jul 2025 15:40:10 +0800 Subject: [PATCH 2/2] test(freertos): add a test case for the DSP coprocessor --- .../freertos/port/test_dsp_in_task.c | 101 ++++++++++++++++++ .../freertos/port/test_dsp_routines.S | 62 +++++++++++ 2 files changed, 163 insertions(+) create mode 100644 components/freertos/test_apps/freertos/port/test_dsp_in_task.c create mode 100644 components/freertos/test_apps/freertos/port/test_dsp_routines.S diff --git a/components/freertos/test_apps/freertos/port/test_dsp_in_task.c b/components/freertos/test_apps/freertos/port/test_dsp_in_task.c new file mode 100644 index 0000000000..8e3f977d8d --- /dev/null +++ b/components/freertos/test_apps/freertos/port/test_dsp_in_task.c @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" +#include +#include "soc/soc_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "unity.h" +#include "test_utils.h" + +#if SOC_CPU_HAS_DSP + +#define TEST_NUM_TASKS 4 + +typedef struct { + int32_t id; + uint32_t result; + TaskHandle_t main; + SemaphoreHandle_t sem; +} dsp_params_t; + +/** + * @brief Multiplies the given ID by a constant. + * + * @param id Value to multiply + */ +uint32_t dsp_id_mul(uint32_t id); + +/** + * @brief DSP Assembly routine need to access this constant, make it public. + * It will be used as a multiplier. + */ +const uint32_t g_dsp_constant = 100000; + +static void pinned_task(void *arg) +{ + dsp_params_t *param = (dsp_params_t*) arg; + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + param->result = dsp_id_mul(param->id); + + /* Indicate done and wait to be deleted */ + xSemaphoreGive((SemaphoreHandle_t)param->sem); + vTaskSuspend(NULL); +} + +TEST_CASE("DSP: Usage in task", "[freertos]") +{ + TaskHandle_t task_handles[CONFIG_FREERTOS_NUMBER_OF_CORES][TEST_NUM_TASKS]; + dsp_params_t params[CONFIG_FREERTOS_NUMBER_OF_CORES][TEST_NUM_TASKS]; + + SemaphoreHandle_t done_sem = xSemaphoreCreateCounting(TEST_NUM_TASKS, 0); + TEST_ASSERT_NOT_EQUAL(NULL, done_sem); + + // Create test tasks for each core + for (int i = 0; i < CONFIG_FREERTOS_NUMBER_OF_CORES; i++) { + for (int j = 0; j < TEST_NUM_TASKS; j++) { + params[i][j] = (dsp_params_t) { + .id = i * TEST_NUM_TASKS + j + 1, + .sem = done_sem, + }; + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(pinned_task, "task", 4096, (void *) ¶ms[i][j], UNITY_FREERTOS_PRIORITY + 1, &task_handles[i][j], i)); + } + } + + // Start the created tasks + for (int i = 0; i < CONFIG_FREERTOS_NUMBER_OF_CORES; i++) { + for (int j = 0; j < TEST_NUM_TASKS; j++) { + xTaskNotifyGive(task_handles[i][j]); + } + } + + // Wait for the tasks to complete + for (int i = 0; i < CONFIG_FREERTOS_NUMBER_OF_CORES * TEST_NUM_TASKS; i++) { + xSemaphoreTake(done_sem, portMAX_DELAY); + } + + // Delete the tasks + for (int i = 0; i < CONFIG_FREERTOS_NUMBER_OF_CORES; i++) { + for (int j = 0; j < TEST_NUM_TASKS; j++) { + vTaskDelete(task_handles[i][j]); + } + } + + // Check the values + for (int i = 0; i < CONFIG_FREERTOS_NUMBER_OF_CORES; i++) { + for (int j = 0; j < TEST_NUM_TASKS; j++) { + dsp_params_t* param = ¶ms[i][j]; + TEST_ASSERT_EQUAL(param->id * g_dsp_constant, param->result); + } + } + + vSemaphoreDelete(done_sem); +} + +#endif /* SOC_CPU_HAS_DSP */ diff --git a/components/freertos/test_apps/freertos/port/test_dsp_routines.S b/components/freertos/test_apps/freertos/port/test_dsp_routines.S new file mode 100644 index 0000000000..3aec8f5da2 --- /dev/null +++ b/components/freertos/test_apps/freertos/port/test_dsp_routines.S @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + #include "soc/soc_caps.h" + +#if SOC_CPU_HAS_DSP + + #include "riscv/csr_dsp.h" + + /** + * @brief Let's allow a small subset of registers to be used in the macro below + */ + .set regnum_a0, 10 + .set regnum_a1, 11 + .set regnum_a2, 12 + .set regnum_a3, 13 + .set regnum_a4, 14 + .set regnum_a5, 15 + .set regnum_a6, 16 + .set regnum_a7, 17 + + /** + * @brief The toolchain doesn't support DSP instructions yet, define it as a constant. + */ + .macro macs32 _rs1, _rs2 + .word ( ( regnum_\_rs1 << 15 ) | ( regnum_\_rs2 << 20) | 0b100<<12 | 0b1011011 ) + .endm + + .global g_dsp_constant + + /** + * @brief Multiply the given ID by the global constant defined as g_dsp_constant. + * NOTE: The goal of the function is not to be fast and efficient, on the contrary, it needs to be + * slow and long so that it will be preempted. + * + * Parameters: + * a0: 32-bit id + * + * Returns: + * a0: multiplied value + */ + .globl dsp_id_mul +dsp_id_mul: + csrw 0x809, zero // SAR = 0 + csrw 0x806, zero // XACC_L + csrw 0x807, zero // XACC_H + li a1, 1 + /* Load the constant in a2 */ + lw a2, (g_dsp_constant) +1: + // Perform ACC += a0 (=id) * a1 (=1) + macs32 a1, a0 + addi a2, a2, -1 + bnez a2, 1b + // Get the lowest bits + csrr a0, 0x806 // XACC_L + ret + + +#endif // SOC_CPU_HAS_DSP