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