Merge branch 'fix/i2s_tdm_5slot_clock_test' into 'master'

fix(hal): prevent overflow in fractional clock division

See merge request espressif/esp-idf!51357
This commit is contained in:
Chen Chen
2026-08-03 15:14:41 +08:00
2 changed files with 15 additions and 6 deletions
+6 -5
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -16,7 +16,7 @@
#endif #endif
__attribute__((always_inline)) __attribute__((always_inline))
static inline uint32_t _sub_abs(uint32_t a, uint32_t b) static inline uint64_t _sub_abs(uint64_t a, uint64_t b)
{ {
return a > b ? a - b : b - a; return a > b ? a - b : b - a;
} }
@@ -78,9 +78,10 @@ uint32_t hal_utils_calc_clk_div_frac_accurate(const hal_utils_clk_info_t *clk_in
// Carry bit if the decimal is greater than 1.0 - 1.0 / ((max_fract - 1) * 2) // Carry bit if the decimal is greater than 1.0 - 1.0 / ((max_fract - 1) * 2)
if (freq_error < clk_info->exp_freq_hz - clk_info->exp_freq_hz / (clk_info->max_fract - 1) * 2) { if (freq_error < clk_info->exp_freq_hz - clk_info->exp_freq_hz / (clk_info->max_fract - 1) * 2) {
// Search the closest fraction, time complexity O(n) // Search the closest fraction, time complexity O(n)
for (uint32_t sub = 0, a = 2, b = 0, min = UINT32_MAX; min && a < clk_info->max_fract; a++) { uint64_t min = UINT64_MAX;
b = (a * freq_error + clk_info->exp_freq_hz / 2) / clk_info->exp_freq_hz; for (uint32_t a = 2, b = 0; min && a < clk_info->max_fract; a++) {
sub = _sub_abs(clk_info->exp_freq_hz * b, freq_error * a); b = ((uint64_t)a * freq_error + clk_info->exp_freq_hz / 2) / clk_info->exp_freq_hz;
uint64_t sub = _sub_abs((uint64_t)clk_info->exp_freq_hz * b, (uint64_t)freq_error * a);
if (sub < min) { if (sub < min) {
div_denom = a; div_denom = a;
div_numer = b; div_numer = b;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -80,6 +80,14 @@ TEST_CASE("test_fractal_division", "[clk_div]")
TEST_ASSERT_EQUAL_UINT32(9, clk_div.integer); TEST_ASSERT_EQUAL_UINT32(9, clk_div.integer);
TEST_ASSERT_UINT32_WITHIN(clk_info.exp_freq_hz * 0.0001, clk_info.exp_freq_hz, real_freq); TEST_ASSERT_UINT32_WITHIN(clk_info.exp_freq_hz * 0.0001, clk_info.exp_freq_hz, real_freq);
// Fractional division whose intermediate products exceed UINT32_MAX
clk_info.exp_freq_hz = 42 * 1000 * 1000 + 336 * 1000;
real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div);
TEST_ASSERT_EQUAL_UINT32(3, clk_div.integer);
TEST_ASSERT_EQUAL_UINT32(286, clk_div.numerator);
TEST_ASSERT_EQUAL_UINT32(367, clk_div.denominator);
TEST_ASSERT_EQUAL_UINT32(42335977, real_freq);
// Fractal division with no error // Fractal division with no error
clk_info.exp_freq_hz = 50 * 1000 * 1000; clk_info.exp_freq_hz = 50 * 1000 * 1000;
real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div); real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);