From e35306a02b4764e59000514dbde5d3be5205fcfe Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Wed, 4 Mar 2026 15:35:26 +0800 Subject: [PATCH] test(cordic): Add test cases for cordic --- .../test_apps/.build-test-rules.yml | 10 + .../test_apps/cordic_test_apps/CMakeLists.txt | 36 + .../test_apps/cordic_test_apps/README.md | 2 + .../cordic_test_apps/main/CMakeLists.txt | 13 + .../cordic_test_apps/main/idf_component.yml | 5 + .../cordic_test_apps/main/test_app_main.c | 41 + .../cordic_test_apps/main/test_cordic_q15.c | 768 ++++++++++++++++++ .../cordic_test_apps/main/test_cordic_q31.c | 682 ++++++++++++++++ .../cordic_test_apps/sdkconfig.ci.iram_safe | 9 + .../cordic_test_apps/sdkconfig.ci.release | 7 + .../cordic_test_apps/sdkconfig.defaults | 2 + 11 files changed, 1575 insertions(+) create mode 100644 components/esp_driver_cordic/test_apps/.build-test-rules.yml create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/CMakeLists.txt create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/README.md create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/main/CMakeLists.txt create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/main/idf_component.yml create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_app_main.c create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q15.c create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q31.c create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.iram_safe create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.release create mode 100644 components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.defaults diff --git a/components/esp_driver_cordic/test_apps/.build-test-rules.yml b/components/esp_driver_cordic/test_apps/.build-test-rules.yml new file mode 100644 index 0000000000..784bd94bd8 --- /dev/null +++ b/components/esp_driver_cordic/test_apps/.build-test-rules.yml @@ -0,0 +1,10 @@ +# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps + +components/esp_driver_cordic/test_apps/cordic_test_apps: + disable: + - if: SOC_CORDIC_SUPPORTED != 1 + depends_components: + - esp_driver_cordic + - esp_hal_cordic + - esp_hw_support + - soc diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/CMakeLists.txt b/components/esp_driver_cordic/test_apps/cordic_test_apps/CMakeLists.txt new file mode 100644 index 0000000000..9455596772 --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/CMakeLists.txt @@ -0,0 +1,36 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.22) + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(cordic_test) + +idf_build_get_property(elf EXECUTABLE) +if(CONFIG_COMPILER_DUMP_RTL_FILES) + # Collect RTL directories in a variable for readability. Join them + # with commas so they are passed as a single --rtl-dirs argument to the script. + set(CORDIC_RTL_DIRS + ${CMAKE_BINARY_DIR}/esp-idf/esp_driver_cordic + ${CMAKE_BINARY_DIR}/esp-idf/hal + ${CMAKE_BINARY_DIR}/esp-idf/esp_hal_cordic + ) + string(JOIN "," CORDIC_RTL_DIRS_JOINED ${CORDIC_RTL_DIRS}) + + add_custom_target(check_test_app_sections ALL + COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py + --rtl-dirs ${CORDIC_RTL_DIRS_JOINED} + --elf-file ${CMAKE_BINARY_DIR}/cordic_test.elf + find-refs + --from-sections=.iram0.text + --to-sections=.flash.text,.flash.rodata + --exit-code + DEPENDS ${elf} + ) +endif() + +message(STATUS "Checking cordic registers are not read-write by half-word") +include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake) +check_register_rw_half_word(SOC_MODULES "cordic" + HAL_MODULES "cordic") diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/README.md b/components/esp_driver_cordic/test_apps/cordic_test_apps/README.md new file mode 100644 index 0000000000..1ffc091273 --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-S31 | +| ----------------- | --------- | \ No newline at end of file diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/main/CMakeLists.txt b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/CMakeLists.txt new file mode 100644 index 0000000000..922eb7f26e --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs "test_app_main.c" + ) + +if(CONFIG_SOC_CORDIC_SUPPORTED) + list(APPEND srcs + "test_cordic_q15.c" + "test_cordic_q31.c" +) +endif() + +idf_component_register(SRCS ${srcs} + PRIV_REQUIRES unity test_utils esp_driver_cordic ccomp_timer esp_driver_gptimer spi_flash + WHOLE_ARCHIVE) diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/main/idf_component.yml b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/idf_component.yml new file mode 100644 index 0000000000..19e4e0856d --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + ccomp_timer: "^1.0.0" + iqmath: "^1.0.0" + test_utils: + path: ${IDF_PATH}/tools/test_apps/components/test_utils diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_app_main.c b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_app_main.c new file mode 100644 index 0000000000..2fa755ae54 --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_app_main.c @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in CORDIC driver, so we reserved this threshold when checking memory leak +// A better way to check a potential memory leak is running a same case by twice, for the second time, the memory usage delta should be zero +#define LEAKS (400) + +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks_direct(LEAKS); +} + +void app_main(void) +{ + // ,-----. ,-----. ,------. ,------. ,--. ,-----. + // ' .--./' .-. '| .--. '| .-. \ | |' .--./ + // | | | | | || '--'.'| | \ :| || | + // ' '--'\' '-' '| |\ \ | '--' /| |' '--' + // `-----' `-----' `--' '--'`-------' `--' `-----' + + printf(" ,-----. ,-----. ,------. ,------. ,--. ,-----. \n"); + printf("' .--./' .-. '| .--. '| .-. \\ | |' .--./ \n"); + printf("| | | | | || '--'.'| | \\ :| || | \n"); + printf("' '--'\\' '-' '| |\\ \\ | '--' /| |' '--'\\\n"); + printf(" `-----' `-----' `--' '--'`-------' `--' `-----'\n"); + + unity_run_menu(); +} diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q15.c b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q15.c new file mode 100644 index 0000000000..85e6ea0ebd --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q15.c @@ -0,0 +1,768 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "unity.h" +#include "soc/cordic_reg.h" +#include "hal/cordic_types.h" +#include "hal/cordic_ll.h" +#include "driver/cordic.h" +#include "driver/cordic_types.h" +#include "ccomp_timer.h" +#include "esp_random.h" +#include "test_utils.h" +#include "driver/gptimer.h" +#include "esp_private/cache_utils.h" +#define GLOBAL_IQ (15) +#include "IQmathLib.h" + +#define PI 3.14159265358979323846 +static uint32_t iq_pi_x = _IQ15(PI); + +TEST_CASE("cordic install-uninstall test", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, cordic_new_engine(&engine_config, &engine)); + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test cosine and sine q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_COS, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 0, + }; + + // Test range: -π to π in Q15 format + // In Q15 format: -0x8000 = -π, 0x0000 = 0, 0x7FFF ≈ π + // Generate 32 test points uniformly distributed from -π to π + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-1.0, 1.0] + // Linear interpolation: float_value = -1.0 + (i * 2.0 / 31) + float float_value = -1.0f + (i * 2.0f / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(float_value, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Q15 format, range: -π to π) ===\n"); + printf("Input(Q15) | Angle(rad) | SW_Cosine | SW_Sine | HW_Cosine | HW_Sine | Cos_Error | Sin_Error\n"); + printf("-----------|------------|------------|------------|------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 32; i++) { + // Convert Q15 input angle to radians + // Q15 format: angle_rad = (int16_t)data_x / 32768.0 * π + // Range: -π (data_x = -0x8000) to π (data_x = 0x7FFF) + float angle_rad = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format) * M_PI; + + // Software calculation using standard math library + float sw_cosine = cosf(angle_rad); + float sw_sine = sinf(angle_rad); + // Convert hardware results to float using cordic_convert_fixed_to_float + float hw_cosine = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float hw_sine = cordic_convert_fixed_to_float(res2[i] & 0xFFFF, calc_config.iq_format); + float cos_error = fabsf(sw_cosine - hw_cosine); + float sin_error = fabsf(sw_sine - hw_sine); + + // Print comparison results + printf("0x%04lx | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + (data_x[i] & 0xFFFF), angle_rad, sw_cosine, sw_sine, hw_cosine, hw_sine, cos_error, sin_error); + + TEST_ASSERT_FLOAT_WITHIN(0.01f, sw_cosine, hw_cosine); + TEST_ASSERT_FLOAT_WITHIN(0.01f, sw_sine, hw_sine); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test phase and module q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_PHASE, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 0, + }; + + uint32_t data_x[60] = {}; + uint32_t data_y[60] = {}; + for (size_t i = 0; i < 60; i++) { + // Generate x and y values in range (-1, 1) + // Map i from [0, 59] to float range [-1.0, 1.0] + // Use different patterns for x and y to cover more combinations + float x_float = -1.0f + (i * 2.0f / 59.0f); + float y_float = -1.0f + (((i * 3) % 60) * 2.0f / 59.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + data_y[i] = cordic_convert_float_to_fixed(y_float, calc_config.iq_format); + } + uint32_t res1[60] = {}; + uint32_t res2[60] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = data_y, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 60)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Phase and Module Q15 format, range: -1 to 1) ===\n"); + printf("Input_X(Q15) | Input_Y(Q15) | SW_Phase | SW_Module | HW_Phase | HW_Module | Phase_Err | Module_Err\n"); + printf("-------------|-------------|------------|------------|------------|------------|------------|-----------\n"); + + for (size_t i = 0; i < 60; i++) { + // Convert Q15 format inputs to float + // Q15 format: float_value = (int16_t)q15_value / 32768.0f + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format); + float y_float = cordic_convert_fixed_to_float(data_y[i], calc_config.iq_format); + + // Software calculation: phase = atan2(y, x) / π, module = sqrt(x^2 + y^2) + float sw_phase = atan2f(y_float, x_float) / M_PI; + float sw_module = sqrtf(x_float * x_float + y_float * y_float); + // Convert hardware results to float using cordic_convert_fixed_to_float + uint16_t hw_module_q15 = (uint16_t)(res2[i] & 0xFFFF); + if (hw_module_q15 == 0x7FFF) { + continue; // Skip this entry, do not print + } + float hw_phase = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float hw_module = cordic_convert_fixed_to_float(res2[i] & 0xFFFF, calc_config.iq_format); + float phase_error = fabsf(sw_phase - hw_phase); + float module_error = fabsf(sw_module - hw_module); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x_float, y_float, sw_phase, sw_module, hw_phase, hw_module, phase_error, module_error); + + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_phase, hw_phase); + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_module, hw_module); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test arctan q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_ARCTAN, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 0, + }; + + // Test range: data_x in (-1, 1) in Q15 format + // In Q15 format: -0x8000 = -1.0, 0x0000 = 0.0, 0x7FFF ≈ 0.999969 + // Generate 32 test points uniformly distributed from -1 to 1 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-1.0, 1.0] (avoiding exact -1 and 1) + float x_float = -1.0f + (i * 2.0f / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Arctan Q15 format, input range: -1 to 1) ===\n"); + printf("Input(Q15) | X_Float | SW_Arctan | HW_Arctan | Error | Arctan(rad)\n"); + printf("-----------|------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format); + float sw_arctan_rad = atanf(x_float); + // Normalize to [-1, 1] range (divide by π for Q15 format output) + // Q15 arctan output is typically normalized: arctan(x) / π + float sw_arctan_normalized = sw_arctan_rad / M_PI; + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_arctan_normalized = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float arctan_error = fabsf(sw_arctan_normalized - hw_arctan_normalized); + + // Print comparison results + printf("0x%04lx | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + data_x[i], x_float, sw_arctan_normalized, hw_arctan_normalized, arctan_error, sw_arctan_rad); + + // Highlight significant errors + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_arctan_normalized, hw_arctan_normalized); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test hyperbolic cosine and sine q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_COSH, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 1, + }; + + // Test range: data_x in (-0.559, 0.559) in Q15 format + // In Q15 format: -0.559 * 32768 = -18320.512, 0.559 * 32768 = 18320.512 + // Generate 32 test points uniformly distributed from -0.559 to 0.559 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-0.559, 0.559] + float x_float = -0.559f + (i * (0.559f - (-0.559f)) / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Hyperbolic Cosine and Sine Q15 format, input range: -0.559 to 0.559) ===\n"); + printf("X_Float | SW_Cosh | SW_Sinh | HW_Cosh | HW_Sinh | Cosh_Err | Sinh_Err\n"); + printf("-------------|------------|------------|------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 32; i++) { + // Convert Q15 format input to float + // Q15 format: float_value = (int16_t)q15_value / 32768.0f + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format) * 2.0f; // multi 2 + + // Software calculation using standard math library + float sw_cosh = cosh(x_float); + float sw_sinh = sinh(x_float); + // Convert hardware results to float using cordic_convert_fixed_to_float + float hw_cosh_temp = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float hw_sinh_temp = cordic_convert_fixed_to_float(res2[i] & 0xFFFF, calc_config.iq_format); + float hw_cosh = hw_cosh_temp * 2.0f; // multi 2 + float hw_sinh = hw_sinh_temp * 2.0f; // multi 2 + + // Calculate errors + float cosh_error = fabsf(sw_cosh - hw_cosh); + float sinh_error = fabsf(sw_sinh - hw_sinh); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x_float, sw_cosh, sw_sinh, hw_cosh, hw_sinh, cosh_error, sinh_error); + + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_cosh, hw_cosh); + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_sinh, hw_sinh); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test arctanh q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_ARCHTANH, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 1, + }; + + // Test range: data_x in (-0.403, 0.403) in Q15 format + // In Q15 format: -0.403 * 32768 = -13205.504, 0.403 * 32768 = 13205.504 + // Generate 32 test points uniformly distributed from -0.403 to 0.403 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-0.403, 0.403] + float x_float = -0.403f + (i * (0.403f - (-0.403f)) / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Arctanh Q15 format, input range: -0.403 to 0.403) ===\n"); + printf("Input(Q15) | X_Float | SW_Arctanh | HW_Arctanh | Error | Arctanh(rad)\n"); + printf("-----------|------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + // Convert Q15 format input to float + // Q15 format: float_value = (int16_t)q15_value / 32768.0f + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format) * 2.0f; // multi 2 + + // Software calculation: arctanh(x) in radians + float sw_arctanh_rad = atanh(x_float); + // Normalize to [-1, 1] range (divide by π for Q15 format output) + // Q15 arctanh output is typically normalized: arctanh(x) / π + float sw_arctanh_normalized = sw_arctanh_rad; + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_arctanh_temp = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float hw_arctanh_normalized = hw_arctanh_temp * 2.0f; // multi 2 + float arctanh_error = fabsf(sw_arctanh_normalized - hw_arctanh_normalized); + + // Print comparison results + printf("0x%04lx | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + data_x[i] & 0xFFFF, x_float, sw_arctanh_normalized, hw_arctanh_normalized, arctanh_error, sw_arctanh_rad); + + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_arctanh_normalized, hw_arctanh_normalized); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test natural logarithm q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_LOGE, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 1, + }; + + // Test range: x in (0.1069, 9.3573) + // Hardware parameter: (x-1)/(x+1) * (2^-1), which should be in [-0.403, 0.403] + // Hardware output: (ln x) * (2^-2), so we need to multiply by 4 to get ln(x) + // Generate 32 test points uniformly distributed from 0.1069 to 9.3573 + float x_values[32] = {}; + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to x range [0.1069, 9.3573] + float x = 0.1069f + (i * (9.3573f - 0.1069f) / 31.0f); + x_values[i] = x; + + // Calculate hardware parameter: (x-1)/(x+1) * 0.5 + float hw_param = ((x - 1.0f) / (x + 1.0f)) * 0.5f; + + // Convert to Q15 format + data_x[i] = cordic_convert_float_to_fixed(hw_param, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Natural Logarithm Q15 format, x range: 0.1069 to 9.3573) ===\n"); + printf("X_Value | HW_Param | SW_LnX | HW_LnX | Error | LnX\n"); + printf("------------|------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + float x = x_values[i]; + + // Calculate hardware parameter: (x-1)/(x+1) * 0.5 + float hw_param = ((x - 1.0f) / (x + 1.0f)) * 0.5f; + + // Software calculation: ln(x) + float sw_lnx = logf(x); + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_lnx_scaled = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float hw_lnx = hw_lnx_scaled * 4.0f; + float lnx_error = fabsf(sw_lnx - hw_lnx); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x, hw_param, sw_lnx, hw_lnx, lnx_error, sw_lnx); + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_lnx, hw_lnx); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test square root q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_SQUARE_ROOT, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 0, + }; + + // Test range: x in (0.1069, 1) + // Hardware input: x (directly) + // Hardware output: sqrt(x) + // Generate 32 test points uniformly distributed from 0.1069 to 0.999 (avoiding exact 1) + float x_values[32] = {}; + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to x range (0.1069, 1) + float x = 0.1069f + (i * (0.999f - 0.1069f) / 31.0f); + x_values[i] = x; + + // Convert x to Q15 format + data_x[i] = cordic_convert_float_to_fixed(x, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Square Root Q15 format, x range: 0.1069 to 1) ===\n"); + printf("X_Value | SW_SqrtX | HW_SqrtX | Error\n"); + printf("------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 32; i++) { + float x = x_values[i]; + + float sw_sqrtx = sqrtf(x); + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_sqrtx = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float sqrtx_error = fabsf(sw_sqrtx - hw_sqrtx); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f", + x, sw_sqrtx, hw_sqrtx, sqrtx_error); + + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_sqrtx, hw_sqrtx); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test arctan scale 2 q15", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_ARCTAN, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 2, // Scale = 2: hardware input is (x) * 2^(-2) = x / 4 + }; + + // Test range: hardware input (x * 2^(-2)) should be in [-1, 1] + // So actual x range should be [-4, 4] in Q15 format + // Note: Q15 format range is [-1, 1], but we need to input values that represent [-4, 4] + // Hardware will scale: x_hw = x * 2^(-2) = x / 4, so x should be in [-4, 4] range + // Generate 32 test points uniformly distributed from -4 to 4 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-4, 4] + float x_float = -4.0f + (i * 8.0f / 31.0f); + // Convert to Q15 format: x_float is in [-4, 4], but Q15 range is [-1, 1] + // So we need to represent x_float / 4 in Q15 format + float x_normalized = x_float / 4.0f; // Normalize to [-1, 1] range + data_x[i] = cordic_convert_float_to_fixed(x_normalized, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Arctan Q15 format, scale=2, input range: -4 to 4) ===\n"); + printf("Input(Q15) | X_Float | SW_Arctan | HW_Arctan | Error | Arctan(rad)\n"); + printf("-----------|------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + // Convert Q15 input to float using cordic_convert_fixed_to_float + float x_normalized = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format); // This is in [-1, 1] range + // Reconstruct original x value: x_float = x_normalized * 4 + float x_float = x_normalized * 4.0f; // Original x in [-4, 4] range + // Hardware scales input: x_scaled = x_float * 2^(-2) = x_float / 4 = x_normalized + // float x_scaled = x_float / 4.0f; // This equals x_normalized + + // Software calculation: arctan(x_scaled) / π + float sw_arctan_rad = atanf(x_float); + float sw_arctan_normalized = sw_arctan_rad / M_PI; + + // Hardware output is already arctan(x * 2^(-2)) / π = arctan(x_scaled) / π + // Convert Q15 result to float using cordic_convert_fixed_to_float + // Note: res1[i] * 4 is used for scale adjustment, but we need to convert the raw value first + float hw_arctan_temp = cordic_convert_fixed_to_float(res1[i] & 0xFFFF, calc_config.iq_format); + float hw_arctan_normalized = hw_arctan_temp * 4; + + float arctan_error = fabsf(sw_arctan_normalized - hw_arctan_normalized); + + // Print comparison results + printf("0x%04lx | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + data_x[i] & 0xFFFF, x_float, sw_arctan_normalized, hw_arctan_normalized, arctan_error, sw_arctan_rad); + + // Highlight significant errors + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_arctan_normalized, hw_arctan_normalized); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test cordic performance should quicker than software iqmath", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_COS, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 0, + }; + + const int test_seed = 999; + srand(test_seed); + uint32_t iq_radians[36] = {}; + uint32_t data_x[36] = {}; + for (size_t i = 0; i < 36; i++) { + uint32_t rand_value = rand() % (0xFFFF + 1); + data_x[i] = rand_value; + iq_radians[i] = _IQmpy(rand_value, iq_pi_x); + } + + uint32_t res1[36] = {}; + uint32_t res2[36] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + ccomp_timer_start(); + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 36)); + + int64_t cordic_time = ccomp_timer_stop(); + printf("CORDIC time: %lld us\n", cordic_time); + + // Use volatile variables to prevent compiler optimization + volatile uint32_t dummy_cos = 0; + volatile uint32_t dummy_sin = 0; + + ccomp_timer_start(); + for (uint32_t i = 0; i < 36; i++) { + // Assign results to volatile variables to prevent compiler optimization + dummy_cos = _IQcos(iq_radians[i]); + dummy_sin = _IQsin(iq_radians[i]); + } + int64_t iqmath_time = ccomp_timer_stop(); + printf("IQMATH time: %lld us\n", iqmath_time); + + IDF_LOG_PERFORMANCE("CORDIC_TIME", "Cordic time sine cosine is %lld us", cordic_time); + TEST_ASSERT_LESS_THAN(iqmath_time, cordic_time); + + // Use the volatile variables to prevent dead code elimination + (void)dummy_cos; + (void)dummy_sin; + TEST_ESP_OK(cordic_delete_engine(engine)); + +} + +#if CONFIG_CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM + +/*--------------------------------------------------------------- + CORDIC work with ISR +---------------------------------------------------------------*/ +typedef struct { + TaskHandle_t task_handle; //Task handle + cordic_engine_handle_t engine_handle; // engine handle + uint32_t res1; + uint32_t res2; +} test_cordic_isr_ctx_t; + +static bool IRAM_ATTR s_alarm_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + test_cordic_isr_ctx_t *test_ctx = (test_cordic_isr_ctx_t *)user_data; + BaseType_t high_task_wakeup; + + /** + * This test would disable the cache, so must put code between cache disable and cache enable in IRAM + */ + uint32_t data_x = 0x123; + uint32_t res1 = 0; + uint32_t res2 = 0; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = &data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = &res1, + .p_data_res2 = &res2, + }; + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_COS, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 0, + }; + spi_flash_disable_interrupts_caches_and_other_cpu(); + TEST_ESP_OK(cordic_calculate_polling(test_ctx->engine_handle, &calc_config, &input_buffer, &output_buffer, 1)); + spi_flash_enable_interrupts_caches_and_other_cpu(); + + test_ctx->res1 = res1; + test_ctx->res2 = res2; + + vTaskNotifyGiveFromISR(test_ctx->task_handle, &high_task_wakeup); + return high_task_wakeup == pdTRUE; +} + +TEST_CASE("cordic can calculation in ISR", "[cordic]") +{ + static test_cordic_isr_ctx_t isr_test_ctx = {}; + isr_test_ctx.engine_handle = NULL; + isr_test_ctx.task_handle = xTaskGetCurrentTaskHandle(); + + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &isr_test_ctx.engine_handle)); + + //-------------GPTimer Init & Config---------------// + gptimer_handle_t timer = NULL; + gptimer_config_t timer_config = { + .resolution_hz = 1 * 1000 * 1000, + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &timer)); + + gptimer_event_callbacks_t cbs = { + .on_alarm = s_alarm_callback, + }; + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = 100000, // 100ms + }; + TEST_ESP_OK(gptimer_set_alarm_action(timer, &alarm_config)); + TEST_ESP_OK(gptimer_register_event_callbacks(timer, &cbs, &isr_test_ctx)); + + printf("start timer\r\n"); + TEST_ESP_OK(gptimer_enable(timer)); + TEST_ESP_OK(gptimer_start(timer)); + TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(1000))); + esp_rom_printf(DRAM_STR("CORDIC calculation done, cos is 0x%lx, sine is 0x%lx\n"), isr_test_ctx.res1, isr_test_ctx.res2); + TEST_ESP_OK(gptimer_stop(timer)); + + TEST_ESP_OK(gptimer_disable(timer)); + TEST_ESP_OK(gptimer_del_timer(timer)); + TEST_ESP_OK(cordic_delete_engine(isr_test_ctx.engine_handle)); + +} + +#endif diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q31.c b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q31.c new file mode 100644 index 0000000000..9b81167ced --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/main/test_cordic_q31.c @@ -0,0 +1,682 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "unity.h" +#include "soc/cordic_reg.h" +#include "hal/cordic_types.h" +#include "hal/cordic_ll.h" +#include "driver/cordic.h" +#include "driver/cordic_types.h" + +TEST_CASE("test cosine and sine q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_COS, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 0, + }; + + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-1.0, 1.0] + float float_value = -1.0f + (i * 2.0f / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(float_value, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Q31 format) ===\n"); + printf("Input(Q31) | SW_Cosine | SW_Sine | HW_Cosine | HW_Sine | Cos_Error | Sin_Error\n"); + printf("-------------|------------|------------|------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 32; i++) { + float angle_rad = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format) * M_PI; + float sw_cosine = cosf(angle_rad); + float sw_sine = sinf(angle_rad); + // Convert hardware results to float using cordic_convert_fixed_to_float + float hw_cosine = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float hw_sine = cordic_convert_fixed_to_float(res2[i], calc_config.iq_format); + float cos_error = fabsf(sw_cosine - hw_cosine); + float sin_error = fabsf(sw_sine - hw_sine); + + // Print comparison results + printf("0x%08lx | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + data_x[i], sw_cosine, sw_sine, hw_cosine, hw_sine, cos_error, sin_error); + + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_cosine, hw_cosine); + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_sine, hw_sine); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test phase and module q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_PHASE, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 0, + }; + + // Test range: data_x and data_y in (-1, 1) in Q31 format + // In Q31 format: -0x80000000 = -1.0, 0x00000000 = 0.0, 0x7FFFFFFF ≈ 0.999999999 + // Generate 60 test points uniformly distributed in (-1, 1) x (-1, 1) + uint32_t data_x[60] = {}; + uint32_t data_y[60] = {}; + for (size_t i = 0; i < 60; i++) { + // Generate x and y values in range (-1, 1) + // Map i from [0, 59] to float range [-1.0, 1.0] (avoiding exact -1 and 1) + // Use different patterns for x and y to cover more combinations + float x_float = -1.0f + (i * 2.0f / 59.0f); + float y_float = -1.0f + (((i * 3) % 60) * 2.0f / 59.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + data_y[i] = cordic_convert_float_to_fixed(y_float, calc_config.iq_format); + } + uint32_t res1[60] = {}; + uint32_t res2[60] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = data_y, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 60)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Phase and Module Q31 format, range: -1 to 1) ===\n"); + printf("X_Float | Y_Float | SW_Phase | SW_Module | HW_Phase | HW_Module | Phase_Err | Module_Err\n"); + printf("-------------|-------------|------------|------------|------------|------------|------------|-----------\n"); + + for (size_t i = 0; i < 60; i++) { + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format); + float y_float = cordic_convert_fixed_to_float(data_y[i], calc_config.iq_format); + float sw_phase = atan2f(y_float, x_float) / M_PI; + float sw_module = sqrtf(x_float * x_float + y_float * y_float); + // Convert hardware results to float using cordic_convert_fixed_to_float + uint32_t hw_module_q31 = res2[i]; + if (hw_module_q31 == 0x7FFFFFFF) { + continue; // Skip this entry, do not print + } + float hw_phase = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float hw_module = cordic_convert_fixed_to_float(res2[i], calc_config.iq_format); + float phase_error = fabsf(sw_phase - hw_phase); + float module_error = fabsf(sw_module - hw_module); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x_float, y_float, sw_phase, sw_module, hw_phase, hw_module, phase_error, module_error); + + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_phase, hw_phase); + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_module, hw_module); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test arctan q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_ARCTAN, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 0, + }; + + // Test range: data_x in (-1, 1) in Q31 format + // In Q31 format: -0x80000000 = -1.0, 0x00000000 = 0.0, 0x7FFFFFFF ≈ 0.999999999 + // Generate 32 test points uniformly distributed from -1 to 1 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-1.0, 1.0] (avoiding exact -1 and 1) + float x_float = -1.0f + (i * 2.0f / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Arctan Q31 format, input range: -1 to 1) ===\n"); + printf("X_Float | SW_Arctan | HW_Arctan | Error | Arctan(rad)\n"); + printf("-------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format); + float sw_arctan_rad = atanf(x_float); + // Normalize to [-1, 1] range (divide by π for Q31 format output) + // Q31 arctan output is typically normalized: arctan(x) / π + float sw_arctan_normalized = sw_arctan_rad / M_PI; + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_arctan_normalized = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float arctan_error = fabsf(sw_arctan_normalized - hw_arctan_normalized); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f", + x_float, sw_arctan_normalized, hw_arctan_normalized, arctan_error); + + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_arctan_normalized, hw_arctan_normalized); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test hyperbolic cosine and sine q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_COSH, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 1, + }; + + // Test range: data_x in (-0.559, 0.559) in Q31 format + // In Q31 format: -0.559 * 2147483648 = -1200611353.6, 0.559 * 2147483648 = 1200611353.6 + // Generate 32 test points uniformly distributed from -0.559 to 0.559 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-0.559, 0.559] + float x_float = -0.559f + (i * (0.559f - (-0.559f)) / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Hyperbolic Cosine and Sine Q31 format, input range: -0.559 to 0.559) ===\n"); + printf("X_Float | SW_Cosh | SW_Sinh | HW_Cosh | HW_Sinh | Cosh_Err | Sinh_Err\n"); + printf("-------------|------------|------------|------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 32; i++) { + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format) * 2.0f; + float sw_cosh = cosh(x_float); + float sw_sinh = sinh(x_float); + // Convert hardware results to float using cordic_convert_fixed_to_float + float hw_cosh_temp = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float hw_sinh_temp = cordic_convert_fixed_to_float(res2[i], calc_config.iq_format); + float hw_cosh = hw_cosh_temp * 2.0f; + float hw_sinh = hw_sinh_temp * 2.0f; + float cosh_error = fabsf(sw_cosh - hw_cosh); + float sinh_error = fabsf(sw_sinh - hw_sinh); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x_float, sw_cosh, sw_sinh, hw_cosh, hw_sinh, cosh_error, sinh_error); + + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_cosh, hw_cosh); + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_sinh, hw_sinh); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test arctanh q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_ARCHTANH, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 1, + }; + + // Test range: data_x in (-0.403, 0.403) in Q31 format + // In Q31 format: -0.403 * 2147483648 ≈ -865536000, 0.403 * 2147483648 ≈ 865536000 + // Generate 32 test points uniformly distributed from -0.403 to 0.403 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-0.403, 0.403] + float x_float = -0.403f + (i * (0.403f - (-0.403f)) / 31.0f); + data_x[i] = cordic_convert_float_to_fixed(x_float, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Arctanh Q31 format, input range: -0.403 to 0.403) ===\n"); + printf("X_Float | SW_Arctanh | HW_Arctanh | Error | Arctanh(rad)\n"); + printf("-------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + float x_float = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format) * 2.0f; + float sw_arctanh_rad = atanh(x_float); + // Normalize to [-1, 1] range (divide by π for Q31 format output) + // Q31 arctanh output is typically normalized: arctanh(x) / π + float sw_arctanh_normalized = sw_arctanh_rad; + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_arctanh_temp = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float hw_arctanh_normalized = hw_arctanh_temp * 2.0f; + float arctanh_error = fabsf(sw_arctanh_normalized - hw_arctanh_normalized); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x_float, sw_arctanh_normalized, hw_arctanh_normalized, arctanh_error, sw_arctanh_rad); + + TEST_ASSERT_FLOAT_WITHIN(0.0001f, sw_arctanh_normalized, hw_arctanh_normalized); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test natural logarithm q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_LOGE, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 1, + }; + + // Test range: x in (0.1069, 9.3573) + // Hardware parameter: (x-1)/(x+1) * (2^-1), which should be in [-0.403, 0.403] + // Hardware output: (ln x) * (2^-2), so we need to multiply by 4 to get ln(x) + // Generate 32 test points uniformly distributed from 0.1069 to 9.3573 + float x_values[32] = {}; + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to x range [0.1069, 9.3573] + float x = 0.1069f + (i * (9.3573f - 0.1069f) / 31.0f); + x_values[i] = x; + + // Calculate hardware parameter: (x-1)/(x+1) * 0.5 + float hw_param = ((x - 1.0f) / (x + 1.0f)) * 0.5f; + + // Convert to Q31 format + data_x[i] = cordic_convert_float_to_fixed(hw_param, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Natural Logarithm Q31 format, x range: 0.1069 to 9.3573) ===\n"); + printf("X_Value | HW_Param | SW_LnX | HW_LnX | Error | LnX\n"); + printf("------------|------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + float x = x_values[i]; + + // Calculate hardware parameter: (x-1)/(x+1) * 0.5 + float hw_param = ((x - 1.0f) / (x + 1.0f)) * 0.5f; + + // Software calculation: ln(x) + float sw_lnx = logf(x); + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_lnx_scaled = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float hw_lnx = hw_lnx_scaled * 4.0f; + float lnx_error = fabsf(sw_lnx - hw_lnx); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x, hw_param, sw_lnx, hw_lnx, lnx_error, sw_lnx); + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_lnx, hw_lnx); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test square root q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_SQUARE_ROOT, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 0, + }; + + // Test range: x in (0.1069, 1) + // Hardware input: x (directly) + // Hardware output: sqrt(x) + // Generate 32 test points uniformly distributed from 0.1069 to 0.999 (avoiding exact 1) + float x_values[32] = {}; + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to x range (0.1069, 1) + float x = 0.1069f + (i * (0.999f - 0.1069f) / 31.0f); + x_values[i] = x; + + // Convert x to Q31 format + data_x[i] = cordic_convert_float_to_fixed(x, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Square Root Q31 format, x range: 0.1069 to 1) ===\n"); + printf("X_Value | SW_SqrtX | HW_SqrtX | Error\n"); + printf("------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 32; i++) { + float x = x_values[i]; + float sw_sqrtx = sqrtf(x); + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_sqrtx = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float sqrtx_error = fabsf(sw_sqrtx - hw_sqrtx); + + // Print comparison results + printf("%10.6f | %10.6f | %10.6f | %10.6f", + x, sw_sqrtx, hw_sqrtx, sqrtx_error); + TEST_ASSERT_FLOAT_WITHIN(0.00001f, sw_sqrtx, hw_sqrtx); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test arctan scale 2 q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config = { + .function = ESP_CORDIC_FUNC_ARCTAN, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 2, // Scale = 2: hardware input is (x) * 2^(-2) = x / 4 + }; + + // Test range: hardware input (x * 2^(-2)) should be in [-1, 1] + // So actual x range should be [-4, 4] in Q31 format + // Note: Q31 format range is [-1, 1], but we need to input values that represent [-4, 4] + // Hardware will scale: x_hw = x * 2^(-2) = x / 4, so x should be in [-4, 4] range + // Generate 32 test points uniformly distributed from -4 to 4 + uint32_t data_x[32] = {}; + for (size_t i = 0; i < 32; i++) { + // Map i from [0, 31] to float range [-4, 4] + float x_float = -4.0f + (i * 8.0f / 31.0f); + // Convert to Q31 format: x_float is in [-4, 4], but Q31 range is [-1, 1] + // So we need to represent x_float / 4 in Q31 format + float x_normalized = x_float / 4.0f; // Normalize to [-1, 1] range + data_x[i] = cordic_convert_float_to_fixed(x_normalized, calc_config.iq_format); + } + uint32_t res1[32] = {}; + uint32_t res2[32] = {}; + + cordic_input_buffer_desc_t input_buffer = { + .p_data_arg1 = data_x, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer = { + .p_data_res1 = res1, + .p_data_res2 = res2, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, 32)); + + // Print header + printf("\n=== Comparison: Software vs Hardware (Arctan Q31 format, scale=2, input range: -4 to 4) ===\n"); + printf("Input(Q31) | X_Float | SW_Arctan | HW_Arctan | Error | Arctan(rad)\n"); + printf("-------------|------------|------------|------------|------------|------------\n"); + + for (size_t i = 0; i < 32; i++) { + // Convert Q31 input to float using cordic_convert_fixed_to_float + float x_normalized = cordic_convert_fixed_to_float(data_x[i], calc_config.iq_format); // This is in [-1, 1] range + // Reconstruct original x value: x_float = x_normalized * 4 + float x_float = x_normalized * 4.0f; // Original x in [-4, 4] range + // Hardware scales input: x_scaled = x_float * 2^(-2) = x_float / 4 = x_normalized + // float x_scaled = x_float / 4.0f; // This equals x_normalized + + // Software calculation: arctan(x_scaled) / π + float sw_arctan_rad = atanf(x_float); + float sw_arctan_normalized = sw_arctan_rad / M_PI; + + // Hardware output is already arctan(x * 2^(-2)) / π = arctan(x_scaled) / π + // Convert Q31 result to float using cordic_convert_fixed_to_float + float hw_arctan_temp = cordic_convert_fixed_to_float(res1[i], calc_config.iq_format); + float hw_arctan_normalized = hw_arctan_temp * 4; + + float arctan_error = fabsf(sw_arctan_normalized - hw_arctan_normalized); + + // Print comparison results + printf("0x%08lx | %10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + data_x[i], x_float, sw_arctan_normalized, hw_arctan_normalized, arctan_error, sw_arctan_rad); + + // Highlight significant errors + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_arctan_normalized, hw_arctan_normalized); + printf("\n"); + } + TEST_ESP_OK(cordic_delete_engine(engine)); +} + +TEST_CASE("test reconfig from cos q15 to log q31", "[cordic]") +{ + cordic_engine_handle_t engine = NULL; + + // Step 1: Create engine with COS function and Q15 format + cordic_engine_config_t engine_config = { + .clock_source = CORDIC_CLK_SRC_DEFAULT, + }; + TEST_ESP_OK(cordic_new_engine(&engine_config, &engine)); + + cordic_calculate_config_t calc_config_cos = { + .function = ESP_CORDIC_FUNC_COS, + .iq_format = ESP_CORDIC_FORMAT_Q15, + .iteration_count = 4, + .scale_exp = 0, + }; + + // Test COS function with Q15 format + uint32_t data_x_cos[16] = {}; + for (size_t i = 0; i < 16; i++) { + // Map i from [0, 15] to float range [-1.0, 1.0] + float float_value = -1.0f + (i * 2.0f / 15.0f); + data_x_cos[i] = cordic_convert_float_to_fixed(float_value, calc_config_cos.iq_format); + } + uint32_t res1_cos[16] = {}; + uint32_t res2_cos[16] = {}; + + cordic_input_buffer_desc_t input_buffer_cos = { + .p_data_arg1 = data_x_cos, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer_cos = { + .p_data_res1 = res1_cos, + .p_data_res2 = res2_cos, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config_cos, &input_buffer_cos, &output_buffer_cos, 16)); + + // Print header for COS test + printf("\n=== Step 1: COS function with Q15 format ===\n"); + printf("Input(Q15) | Angle(rad) | SW_Cosine | HW_Cosine | Cos_Error\n"); + printf("-----------|------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 16; i++) { + // Convert Q15 input angle to radians + float angle_rad = cordic_convert_fixed_to_float(data_x_cos[i] & 0xFFFF, calc_config_cos.iq_format) * M_PI; + float sw_cosine = cosf(angle_rad); + float hw_cosine = cordic_convert_fixed_to_float(res1_cos[i] & 0xFFFF, calc_config_cos.iq_format); + float cos_error = fabsf(sw_cosine - hw_cosine); + + printf("0x%04lx | %10.6f | %10.6f | %10.6f | %10.6f", + data_x_cos[i] & 0xFFFF, angle_rad, sw_cosine, hw_cosine, cos_error); + TEST_ASSERT_FLOAT_WITHIN(0.01f, sw_cosine, hw_cosine); + printf("\n"); + } + + // Step 2: Use NATURAL_LOG function with Q31 format (no reconfig needed, just use new calc_config) + cordic_calculate_config_t calc_config_log = { + .function = ESP_CORDIC_FUNC_LOGE, + .iq_format = ESP_CORDIC_FORMAT_Q31, + .iteration_count = 4, + .scale_exp = 1, + }; + + // Test NATURAL_LOG function with Q31 format + // Test range: x in (0.1069, 9.3573) + float x_values[16] = {}; + uint32_t data_x_log[16] = {}; + for (size_t i = 0; i < 16; i++) { + // Map i from [0, 15] to x range [0.1069, 9.3573] + float x = 0.1069f + (i * (9.3573f - 0.1069f) / 15.0f); + x_values[i] = x; + + // Calculate hardware parameter: (x-1)/(x+1) * 0.5 + float hw_param = ((x - 1.0f) / (x + 1.0f)) * 0.5f; + + // Convert to Q31 format + data_x_log[i] = cordic_convert_float_to_fixed(hw_param, calc_config_log.iq_format); + } + uint32_t res1_log[16] = {}; + uint32_t res2_log[16] = {}; + + cordic_input_buffer_desc_t input_buffer_log = { + .p_data_arg1 = data_x_log, + .p_data_arg2 = NULL, + }; + cordic_output_buffer_desc_t output_buffer_log = { + .p_data_res1 = res1_log, + .p_data_res2 = res2_log, + }; + + TEST_ESP_OK(cordic_calculate_polling(engine, &calc_config_log, &input_buffer_log, &output_buffer_log, 16)); + + // Print header for LOG test + printf("\n=== Step 2: NATURAL_LOG function with Q31 format (after reconfig) ===\n"); + printf("X_Value | HW_Param | SW_LnX | HW_LnX | Error\n"); + printf("------------|------------|------------|------------|----------\n"); + + for (size_t i = 0; i < 16; i++) { + float x = x_values[i]; + + // Calculate hardware parameter: (x-1)/(x+1) * 0.5 + float hw_param = ((x - 1.0f) / (x + 1.0f)) * 0.5f; + + // Software calculation: ln(x) + float sw_lnx = logf(x); + // Convert hardware result to float using cordic_convert_fixed_to_float + float hw_lnx_scaled = cordic_convert_fixed_to_float(res1_log[i], calc_config_log.iq_format); + float hw_lnx = hw_lnx_scaled * 4.0f; + float lnx_error = fabsf(sw_lnx - hw_lnx); + + printf("%10.6f | %10.6f | %10.6f | %10.6f | %10.6f", + x, hw_param, sw_lnx, hw_lnx, lnx_error); + TEST_ASSERT_FLOAT_WITHIN(0.001f, sw_lnx, hw_lnx); + printf("\n"); + } + + cordic_delete_engine(engine); +} diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.iram_safe b/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.iram_safe new file mode 100644 index 0000000000..5b679b5048 --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.iram_safe @@ -0,0 +1,9 @@ +CONFIG_COMPILER_DUMP_RTL_FILES=y +CONFIG_CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM=y +CONFIG_GPTIMER_ISR_CACHE_SAFE=y +CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +# silent the error check, as the error string are stored in rodata, causing RTL check failure +CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_HAL_ASSERTION_SILENT=y diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.release b/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.release new file mode 100644 index 0000000000..1a87ebbb4a --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.ci.release @@ -0,0 +1,7 @@ +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y +CONFIG_PM_DFS_INIT_AUTO=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.defaults b/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.defaults new file mode 100644 index 0000000000..fa8ac618b9 --- /dev/null +++ b/components/esp_driver_cordic/test_apps/cordic_test_apps/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_EN=n