Merge branch 'feature/dsp_coprocessor_support' into 'master'

feat(riscv): add support for the DSP coprocessor

Closes IDF-13087 and LLVM-369

See merge request espressif/esp-idf!40866
This commit is contained in:
Omar Chebib
2025-08-08 11:06:22 +08:00
10 changed files with 331 additions and 11 deletions

View File

@@ -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 */

View File

@@ -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:

View File

@@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <math.h>
#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 *) &params[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 = &params[i][j];
TEST_ASSERT_EQUAL(param->id * g_dsp_constant, param->result);
}
}
vSemaphoreDelete(done_sem);
}
#endif /* SOC_CPU_HAS_DSP */

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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