feat(cordic): Add cordic oneshot driver for esp32s31

This commit is contained in:
C.S.M
2026-03-04 15:36:23 +08:00
parent e35306a02b
commit 0a096a46ad
7 changed files with 424 additions and 0 deletions
@@ -0,0 +1,24 @@
idf_build_get_property(target IDF_TARGET)
set(srcs)
set(public_include "include")
set(requires esp_hal_cordic)
if(${target} STREQUAL "linux")
set(priv_requires "")
else()
set(priv_requires esp_pm)
endif()
if(CONFIG_SOC_CORDIC_SUPPORTED)
list(APPEND srcs
"cordic.c"
)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${public_include}
PRIV_REQUIRES "${priv_requires}"
REQUIRES "${requires}"
LDFRAGMENTS linker.lf
)
+10
View File
@@ -0,0 +1,10 @@
menu "ESP-Driver:Cordic Configurations"
depends on SOC_CORDIC_SUPPORTED
config CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM
bool "Place cordic oneshot calculation function into IRAM"
default n
help
Place cordic oneshot function into IRAM so that cordic oneshot function
can be safely used in ISR even when cache is disabled.
endmenu
+185
View File
@@ -0,0 +1,185 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_err.h"
#include "cordic_private.h"
#include "driver/cordic_types.h"
#include "driver/cordic.h"
#include "hal/cordic_hal.h"
#include "hal/cordic_ll.h"
#include "soc/cordic_reg.h"
#include "soc/soc.h"
#include "hal/cordic_periph.h"
ESP_LOG_ATTR_TAG(TAG, "cordic");
typedef struct cordic_platform_t {
_lock_t mutex; // platform level mutex lock.
cordic_engine_handle_t engine_handle; // array of cordic instances.
} cordic_platform_t;
static cordic_platform_t s_cordic_platform = {}; // singleton platform
static bool cordic_engine_occupied(void)
{
return s_cordic_platform.engine_handle != NULL;
}
esp_err_t cordic_new_engine(const cordic_engine_config_t *cordic_cfg, cordic_engine_handle_t *ret_engine)
{
esp_err_t ret = ESP_OK;
cordic_engine_handle_t engine = NULL;
ESP_RETURN_ON_FALSE(cordic_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(ret_engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
engine = (cordic_engine_handle_t)heap_caps_calloc(1, sizeof(cordic_engine_t), CORDIC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(engine, ESP_ERR_NO_MEM, TAG, "no memory for cordic engine");
bool engine_found = false;
_lock_acquire(&s_cordic_platform.mutex);
if (cordic_engine_occupied() == false) {
s_cordic_platform.engine_handle = engine;
engine_found = true;
}
_lock_release(&s_cordic_platform.mutex);
ESP_GOTO_ON_FALSE(engine_found, ESP_ERR_NOT_FOUND, err, TAG, "no free controller");
cordic_hal_init(&engine->hal);
cordic_ll_enable_bus_clock(true);
cordic_ll_reset_module_register();
cordic_ll_enable_clock(true);
cordic_clock_source_t clk_src = cordic_cfg->clock_source ? cordic_cfg->clock_source : CORDIC_CLK_SRC_DEFAULT;
cordic_ll_set_clock_source(clk_src);
*ret_engine = engine;
return ESP_OK;
err:
cordic_delete_engine(engine);
return ret;
}
esp_err_t cordic_calculate_polling(cordic_engine_handle_t engine, const cordic_calculate_config_t *calc_cfg, cordic_input_buffer_desc_t *input_buffer_desc, cordic_output_buffer_desc_t *output_buffer_desc, size_t buffer_depth)
{
ESP_RETURN_ON_FALSE_ISR(engine && calc_cfg && input_buffer_desc && input_buffer_desc->p_data_arg1 && output_buffer_desc && output_buffer_desc->p_data_res1 && buffer_depth > 0, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(calc_cfg->function < ESP_CORDIC_FUNC_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid function");
ESP_RETURN_ON_FALSE_ISR(calc_cfg->iq_format < ESP_CORDIC_IQ_SIZE_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid iq_format");
ESP_RETURN_ON_FALSE_ISR(calc_cfg->scale_exp >= cordic_hal_algorithm_allowable_scale[calc_cfg->function][0] && calc_cfg->scale_exp <= cordic_hal_algorithm_allowable_scale[calc_cfg->function][1], ESP_ERR_INVALID_ARG, TAG, "invalid scale_exp");
ESP_RETURN_ON_FALSE_ISR(calc_cfg->iteration_count <= CORDIC_LL_PRECISION_MAX && calc_cfg->iteration_count > 0, ESP_ERR_INVALID_ARG, TAG, "invalid iteration_count");
// Determine if function requires two arguments
bool is_two_args = (calc_cfg->function == ESP_CORDIC_FUNC_PHASE || calc_cfg->function == ESP_CORDIC_FUNC_MODULUS);
ESP_RETURN_ON_FALSE_ISR(is_two_args == (input_buffer_desc->p_data_arg2 != NULL), ESP_ERR_INVALID_ARG, TAG, "invalid argument");
BaseType_t in_isr = xPortInIsrContext();
// Convert timeout from milliseconds to ticks
uint32_t timeout_ms = (buffer_depth / 100 + 1) * CORDIC_ONESHOT_CALCULATE_TIMEOUT_MS;
TickType_t timeout_ticks = pdMS_TO_TICKS(timeout_ms);
TickType_t start_tick = 0;
if (in_isr) {
start_tick = xTaskGetTickCountFromISR();
} else {
start_tick = xTaskGetTickCount();
}
// Configure hardware registers based on calculation config
cordic_ll_set_calculate_function(engine->hal.dev, calc_cfg->function);
cordic_ll_set_calculate_mode(engine->hal.dev, CORDIC_LL_MODE_REG);
cordic_ll_set_calculate_precision(engine->hal.dev, calc_cfg->iteration_count - 1);
cordic_ll_set_calculate_scale(engine->hal.dev, calc_cfg->scale_exp);
cordic_ll_set_calculate_result_number(engine->hal.dev, 2);
if (is_two_args) {
cordic_ll_set_calculate_argument_number(engine->hal.dev, 2);
} else {
cordic_ll_set_calculate_argument_number(engine->hal.dev, 1);
}
cordic_ll_set_calculate_result_format(engine->hal.dev, calc_cfg->iq_format);
cordic_ll_set_calculate_argument_format(engine->hal.dev, calc_cfg->iq_format);
// Get function pointers from HAL layer arrays using format and argument count as indices
size_t arg_num = is_two_args ? 1 : 0;
cordic_set_argument_func_t set_argument_func = cordic_hal_set_argument_funcs[calc_cfg->iq_format][arg_num];
cordic_get_result_func_t get_result_func = cordic_hal_get_result_funcs[calc_cfg->iq_format];
for (size_t i = 0; i < buffer_depth; i++) {
// Use function pointer to set arguments
set_argument_func(&engine->hal, input_buffer_desc, i);
cordic_ll_start_calculate(engine->hal.dev);
// Wait for result ready with timeout
while (cordic_ll_is_calculate_result_ready(engine->hal.dev) == 0) {
TickType_t current_tick = 0;
if (in_isr) {
current_tick = xTaskGetTickCountFromISR();
} else {
current_tick = xTaskGetTickCount();
}
// TickType_t is unsigned, so subtraction handles overflow correctly
TickType_t elapsed_ticks = current_tick - start_tick;
if (elapsed_ticks >= timeout_ticks) {
ESP_DRAM_LOGE(TAG, "CORDIC calculation timeout after %d ms", timeout_ms);
cordic_ll_reset_module_register();
return ESP_ERR_TIMEOUT;
}
}
// Use function pointer to get results
get_result_func(&engine->hal, output_buffer_desc, i);
}
return ESP_OK;
}
esp_err_t cordic_delete_engine(cordic_engine_handle_t engine)
{
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(engine, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
cordic_ll_enable_bus_clock(false);
cordic_ll_enable_clock(false);
cordic_hal_deinit(&engine->hal);
_lock_acquire(&s_cordic_platform.mutex);
s_cordic_platform.engine_handle = NULL;
_lock_release(&s_cordic_platform.mutex);
heap_caps_free(engine);
return ret;
}
float cordic_convert_fixed_to_float(uint32_t fixed_value, cordic_iq_format_t iq_format)
{
if (iq_format == ESP_CORDIC_FORMAT_Q15) {
return (float)(int16_t)(fixed_value & 0xFFFF) / CORDIC_Q15_SCALE_FACTOR;
} else if (iq_format == ESP_CORDIC_FORMAT_Q31) {
return (float)(int32_t)fixed_value / CORDIC_Q31_SCALE_FACTOR;
} else {
return 0.0f;
}
}
uint32_t cordic_convert_float_to_fixed(float float_value, cordic_iq_format_t iq_format)
{
if (iq_format == ESP_CORDIC_FORMAT_Q15) {
return (uint32_t)(int16_t)(float_value * CORDIC_Q15_SCALE_FACTOR) & 0xFFFF;
} else if (iq_format == ESP_CORDIC_FORMAT_Q31) {
return (uint32_t)(int32_t)(float_value * CORDIC_Q31_SCALE_FACTOR);
} else {
return 0;
}
}
@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdatomic.h>
#include "esp_attr.h"
#include "esp_heap_caps.h"
#include "hal/cordic_hal.h"
#include "esp_intr_types.h"
#include "sdkconfig.h"
#include "driver/cordic.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM
#define CORDIC_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define CORDIC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#define CORDIC_ONESHOT_CALCULATE_TIMEOUT_MS (2) // 2ms is enough for cordic one shot calculation api
#define CORDIC_Q15_SCALE_FACTOR (32768.0f) // 2^15, scale factor for Q15 format conversion
#define CORDIC_Q31_SCALE_FACTOR (2147483648.0f) // 2^31, scale factor for Q31 format conversion
typedef struct cordic_engine_t cordic_engine_t;
// Function pointer type for setting CORDIC arguments
typedef void (*cordic_set_argument_func_t)(cordic_hal_context_t *hal, const cordic_input_buffer_desc_t *input_buffer, size_t index);
// Function pointer type for getting CORDIC results
typedef void (*cordic_get_result_func_t)(cordic_hal_context_t *hal, cordic_output_buffer_desc_t *output_buffer, size_t index);
struct cordic_engine_t {
cordic_hal_context_t hal; // HAL context
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,115 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/cordic_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief CORDIC engine configuration structure
*/
typedef struct {
cordic_clock_source_t clock_source; /*!< CORDIC clock source */
} cordic_engine_config_t;
/**
* @brief CORDIC calculation configuration structure
*/
typedef struct {
cordic_func_t function; /*!< CORDIC calculation function type (e.g., cosine, sine, arctan) */
cordic_iq_format_t iq_format; /*!< IQ data format */
uint16_t iteration_count; /*!< Internal CORDIC iteration count */
uint16_t scale_exp; /*!< Input scaling exponent n, effective scaling is 2^n (range depends on function type) */
} cordic_calculate_config_t;
/**
* @brief Create a new CORDIC engine instance
*
* This function creates and initializes a CORDIC engine with the specified configuration.
* The engine handle returned can be used for subsequent CORDIC calculations.
*
* @param[in] cordic_cfg Pointer to CORDIC engine configuration structure. Must not be NULL.
* @param[out] ret_engine Pointer to store the created CORDIC engine handle. Must not be NULL.
*
* @return
* - ESP_OK: CORDIC engine created successfully.
* - ESP_ERR_INVALID_ARG: Invalid argument (NULL pointer or invalid scale exponent).
* - ESP_ERR_NO_MEM: Failed to allocate memory for the engine.
*/
esp_err_t cordic_new_engine(const cordic_engine_config_t *cordic_cfg, cordic_engine_handle_t *ret_engine);
/**
* @brief Start one-shot CORDIC calculation
*
* This function performs CORDIC calculations on a batch of data points. It processes each data point
* sequentially: sets arguments, starts calculation, waits for completion, and retrieves results.
*
* @note The function can be safely used in ISR, but for the ISR run in cache-safe, please
* enable ``CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM`` to place the control functions into IRAM.
* @note This function is not guaranteed to be thread-safe.
*
* @param[in] engine CORDIC engine handle created by cordic_new_engine(). Must not be NULL.
* @param[in] calc_cfg Pointer to CORDIC calculation configuration structure. Must not be NULL.
* @param[in] input_buffer_desc Pointer to input buffer descriptor containing input data. Must not be NULL.
* @param[out] output_buffer_desc Pointer to output buffer descriptor for storing results. Must not be NULL.
* @param[in] buffer_depth Number of data points to process.
*
* @return
* - ESP_OK: All calculations completed successfully.
* - ESP_ERR_INVALID_ARG: Invalid argument (NULL pointer, zero buffer_depth, or invalid timeout).
* - ESP_ERR_TIMEOUT: Calculation timeout.
*/
esp_err_t cordic_calculate_polling(cordic_engine_handle_t engine, const cordic_calculate_config_t *calc_cfg, cordic_input_buffer_desc_t *input_buffer_desc, cordic_output_buffer_desc_t *output_buffer_desc, size_t buffer_depth);
/**
* @brief Delete a CORDIC engine instance
*
* This function deletes a CORDIC engine and frees all associated resources including the mutex.
* After calling this function, the engine handle becomes invalid and should not be used.
*
* @param[in] engine CORDIC engine handle to delete. Must not be NULL.
*
* @return
* - ESP_OK: Engine deleted successfully.
* - ESP_ERR_INVALID_ARG: Invalid argument (NULL pointer).
*/
esp_err_t cordic_delete_engine(cordic_engine_handle_t engine);
/**
* @brief Convert CORDIC hex value to float
*
* This function converts a hexadecimal value from CORDIC hardware output to a floating-point number.
* The conversion depends on the IQ format specified by the iq_format parameter.
*
* @param[in] fixed_value Hexadecimal value from CORDIC hardware output.
* @param[in] iq_format IQ data format.
*
* @return Converted floating-point value. Returns 0.0f if iq_format is invalid.
*/
float cordic_convert_fixed_to_float(uint32_t fixed_value, cordic_iq_format_t iq_format);
/**
* @brief Convert float to CORDIC hex value
*
* This function converts a floating-point number to a hexadecimal value suitable for CORDIC hardware input.
* The conversion depends on the IQ format specified by the iq_format parameter.
*
* @param[in] float_value Floating-point value to convert. Should be in the range [-1.0, 1.0].
* @param[in] iq_format IQ data format.
*
* @return Converted hexadecimal value. Returns 0 if iq_format is invalid.
*/
uint32_t cordic_convert_float_to_fixed(float float_value, cordic_iq_format_t iq_format);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "soc/soc_caps.h"
#include "hal/cordic_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief CORDIC engine handle type
*/
typedef struct cordic_engine_t *cordic_engine_handle_t;
#ifdef __cplusplus
}
#endif
+19
View File
@@ -0,0 +1,19 @@
[mapping:esp_driver_cordic]
archive: libesp_driver_cordic.a
entries:
if CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM = y:
cordic: cordic_calculate_polling (noflash)
[mapping:esp_hal_cordic]
archive: libesp_hal_cordic.a
entries:
if CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM = y:
cordic_periph: cordic_hal_algorithm_allowable_scale (noflash)
cordic_hal: cordic_hal_set_argument_funcs (noflash)
cordic_hal: cordic_hal_get_result_funcs (noflash)
cordic_hal: cordic_hal_set_argument_q15_two_args (noflash)
cordic_hal: cordic_hal_set_argument_q15_one_arg (noflash)
cordic_hal: cordic_hal_set_argument_q31_two_args (noflash)
cordic_hal: cordic_hal_set_argument_q31_one_arg (noflash)
cordic_hal: cordic_hal_get_result_q15 (noflash)
cordic_hal: cordic_hal_get_result_q31 (noflash)