mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
feat(cordic): Add cordic oneshot driver for esp32s31
This commit is contained in:
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user