Browse Source

Merge branch 'bugfix/apll_coeff_calculate_v4.4' into 'release/v4.4'

i2s: impove the clock division calculation (v4.4)

See merge request espressif/esp-idf!16783
Michael (XIAO Xufeng) 4 years ago
parent
commit
f4c97455c4

+ 64 - 129
components/driver/i2s.c

@@ -29,8 +29,7 @@
 #include "esp_private/gdma.h"
 #endif
 
-#include "soc/rtc.h"
-
+#include "soc/clk_ctrl_os.h"
 #include "esp_intr_alloc.h"
 #include "esp_err.h"
 #include "esp_check.h"
@@ -888,123 +887,54 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num)
                    I2S clock operation
   -------------------------------------------------------------*/
 #if SOC_I2S_SUPPORTS_APLL
-/**
- * @brief Get APLL frequency
- */
-static float i2s_apll_get_freq(int bits_per_sample, int sdm0, int sdm1, int sdm2, int odir)
+static esp_err_t i2s_set_apll_freq(uint32_t expt_freq, uint32_t *real_freq)
 {
-    int f_xtal = (int)rtc_clk_xtal_freq_get() * 1000000;
-
-#if CONFIG_IDF_TARGET_ESP32
-    /* ESP32 rev0 silicon issue for APLL range/accuracy, please see ESP32 ECO document for more information on this */
-    if (esp_efuse_get_chip_ver() == 0) {
-        sdm0 = 0;
-        sdm1 = 0;
-    }
-#endif
-    float fout = f_xtal * (sdm2 + sdm1 / 256.0f + sdm0 / 65536.0f + 4);
-    if (fout < SOC_I2S_APLL_MIN_FREQ || fout > SOC_I2S_APLL_MAX_FREQ) {
-        return SOC_I2S_APLL_MAX_FREQ;
-    }
-    float fpll = fout / (2 * (odir + 2)); //== fi2s (N=1, b=0, a=1)
-    return fpll / 2;
-}
+    uint32_t rtc_xtal_freq = (uint32_t)rtc_clk_xtal_freq_get();
+    ESP_RETURN_ON_FALSE(rtc_xtal_freq, ESP_ERR_INVALID_STATE, TAG, "Get xtal clock frequency failed, it has not been set yet");
+
+    /* Reference formula: apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) / ((o_div + 2) * 2)
+     *                                ----------------------------------------------   -----------------
+     *                                     350 MHz <= Numerator <= 500 MHz                Denominator
+     */
+    int o_div = 0; // range: 0~31
+    int sdm0 = 0;  // range: 0~255
+    int sdm1 = 0;  // range: 0~255
+    int sdm2 = 0;  // range: 0~63
+    /* Firstly try to satisfy the condition that the operation frequency of numerator should be greater than 350 MHz,
+     * i.e. xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) >= 350 MHz, '+1' in the following code is to get the ceil value.
+     * With this condition, as we know the 'o_div' can't be greater than 31, then we can calculate the APLL minimum support frequency is
+     * 350 MHz / ((31 + 2) * 2) = 5303031 Hz (for ceil) */
+    o_div = (int)(SOC_APLL_MULTIPLIER_OUT_MIN_HZ / (float)(expt_freq * 2) + 1) - 2;
+    ESP_RETURN_ON_FALSE(o_div < 32, ESP_ERR_INVALID_ARG, TAG, "Expected frequency is too small");
+    if (o_div < 0) {
+        /* Try to satisfy the condition that the operation frequency of numerator should be smaller than 500 MHz,
+         * i.e. xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) <= 500 MHz, we need to get the floor value in the following code.
+         * With this condition, as we know the 'o_div' can't be smaller than 0, then we can calculate the APLL maximum support frequency is
+         * 500 MHz / ((0 + 2) * 2) = 125000000 Hz */
+        o_div = (int)(SOC_APLL_MULTIPLIER_OUT_MAX_HZ / (float)(expt_freq * 2)) - 2;
+        ESP_RETURN_ON_FALSE(o_div >= 0, ESP_ERR_INVALID_ARG, TAG, "Expected frequency is too small");
+    }
+    // sdm2 = (int)(((o_div + 2) * 2) * apll_freq / xtal_freq) - 4
+    sdm2 = (int)(((o_div + 2) * 2 * expt_freq) / (rtc_xtal_freq * 1000000)) - 4;
+    // numrator = (((o_div + 2) * 2) * apll_freq / xtal_freq) - 4 - sdm2
+    float numrator = (((o_div + 2) * 2 * expt_freq) / ((float)rtc_xtal_freq * 1000000)) - 4 - sdm2;
+    // If numrator is bigger than 255/256 + 255/65536 + (1/65536)/2 = 1 - (1 / 65536)/2, carry bit to sdm2
+    if (numrator > 1.0 - (1.0 / 65536.0) / 2.0) {
+        sdm2++;
+    }
+    // If numrator is smaller than (1/65536)/2, keep sdm0 = sdm1 = 0, otherwise calculate sdm0 and sdm1
+    else if (numrator > (1.0 / 65536.0) / 2.0) {
+        // Get the closest sdm1
+        sdm1 = (int)(numrator * 65536.0 + 0.5) / 256;
+        // Get the closest sdm0
+        sdm0 = (int)(numrator * 65536.0 + 0.5) % 256;
+    }
+    rtc_clk_apll_enable(true, sdm0, sdm1, sdm2, o_div);
+    *real_freq = (uint32_t)(rtc_xtal_freq * 1000000 * (4 + sdm2 + (float)sdm1/256.0 + (float)sdm0/65536.0) / (((float)o_div + 2) * 2));
 
-/**
- * @brief     APLL calculate function, was described by following:
- *            APLL Output frequency is given by the formula:
- *
- *            apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2)
- *            apll_freq = fout / ((o_div + 2) * 2)
- *
- *            The dividend in this expression should be in the range of 240 - 600 MHz.
- *            In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0.
- *            * sdm0  frequency adjustment parameter, 0..255
- *            * sdm1  frequency adjustment parameter, 0..255
- *            * sdm2  frequency adjustment parameter, 0..63
- *            * o_div  frequency divider, 0..31
- *
- *            The most accurate way to find the sdm0..2 and odir parameters is to loop through them all,
- *            then apply the above formula, finding the closest frequency to the desired one.
- *            But 256*256*64*32 = 134,217,728 loops are too slow with ESP32
- *            1. We will choose the parameters with the highest level of change,
- *               With 350MHz<fout<500MHz, we limit the sdm2 from 4 to 9,
- *               Take average frequency close to the desired frequency, and select sdm2
- *            2. Next, we look for sequences of less influential and more detailed parameters,
- *               also by taking the average of the largest and smallest frequencies closer to the desired frequency.
- *            3. And finally, loop through all the most detailed of the parameters, finding the best desired frequency
- *
- * @param[in]       rate             The I2S Frequency (MCLK)
- * @param[in]       bits_per_sample  The bits per sample
- * @param[out]      sdm0             The sdm 0
- * @param[out]      sdm1             The sdm 1
- * @param[out]      sdm2             The sdm 2
- * @param[out]      odir             The odir
- */
-static void i2s_apll_calculate_fi2s(int rate, int bits_per_sample, int *sdm0, int *sdm1, int *sdm2, int *odir)
-{
-    int _odir, _sdm0, _sdm1, _sdm2;
-    float avg;
-    float min_rate, max_rate, min_diff;
-
-    *sdm0 = 0;
-    *sdm1 = 0;
-    *sdm2 = 0;
-    *odir = 0;
-    min_diff = SOC_I2S_APLL_MAX_FREQ;
-
-    for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
-        max_rate = i2s_apll_get_freq(bits_per_sample, 255, 255, _sdm2, 0);
-        min_rate = i2s_apll_get_freq(bits_per_sample, 0, 0, _sdm2, 31);
-        avg = (max_rate + min_rate) / 2;
-        if (abs(avg - rate) < min_diff) {
-            min_diff = abs(avg - rate);
-            *sdm2 = _sdm2;
-        }
-    }
-    min_diff = SOC_I2S_APLL_MAX_FREQ;
-    for (_odir = 0; _odir < 32; _odir ++) {
-        max_rate = i2s_apll_get_freq(bits_per_sample, 255, 255, *sdm2, _odir);
-        min_rate = i2s_apll_get_freq(bits_per_sample, 0, 0, *sdm2, _odir);
-        avg = (max_rate + min_rate) / 2;
-        if (abs(avg - rate) < min_diff) {
-            min_diff = abs(avg - rate);
-            *odir = _odir;
-        }
-    }
-    min_diff = SOC_I2S_APLL_MAX_FREQ;
-    for (_sdm2 = 4; _sdm2 < 9; _sdm2 ++) {
-        max_rate = i2s_apll_get_freq(bits_per_sample, 255, 255, _sdm2, *odir);
-        min_rate = i2s_apll_get_freq(bits_per_sample, 0, 0, _sdm2, *odir);
-        avg = (max_rate + min_rate) / 2;
-        if (abs(avg - rate) < min_diff) {
-            min_diff = abs(avg - rate);
-            *sdm2 = _sdm2;
-        }
-    }
-
-    min_diff = SOC_I2S_APLL_MAX_FREQ;
-    for (_sdm1 = 0; _sdm1 < 256; _sdm1 ++) {
-        max_rate = i2s_apll_get_freq(bits_per_sample, 255, _sdm1, *sdm2, *odir);
-        min_rate = i2s_apll_get_freq(bits_per_sample, 0, _sdm1, *sdm2, *odir);
-        avg = (max_rate + min_rate) / 2;
-        if (abs(avg - rate) < min_diff) {
-            min_diff = abs(avg - rate);
-            *sdm1 = _sdm1;
-        }
-    }
-
-    min_diff = SOC_I2S_APLL_MAX_FREQ;
-    for (_sdm0 = 0; _sdm0 < 256; _sdm0 ++) {
-        avg = i2s_apll_get_freq(bits_per_sample, _sdm0, *sdm1, *sdm2, *odir);
-        if (abs(avg - rate) < min_diff) {
-            min_diff = abs(avg - rate);
-            *sdm0 = _sdm0;
-        }
-    }
+    return ESP_OK;
 }
 #endif
-
 /**
  * @brief   Config I2S source clock and get its frequency
  *
@@ -1020,21 +950,26 @@ static uint32_t i2s_config_source_clock(i2s_port_t i2s_num, bool use_apll, uint3
 {
 #if SOC_I2S_SUPPORTS_APLL
     if (use_apll) {
-        int sdm0 = 0;
-        int sdm1 = 0;
-        int sdm2 = 0;
-        int odir = 0;
-        if ((mclk / p_i2s[i2s_num]->hal_cfg.chan_bits / p_i2s[i2s_num]->hal_cfg.total_chan) < SOC_I2S_APLL_MIN_RATE) {
-            ESP_LOGE(TAG, "mclk is too small");
+        /* Calculate the expected APLL  */
+        int div = (int)((SOC_APLL_MIN_HZ / mclk) + 1);
+        /* apll_freq = mclk * div
+         * when div = 1, hardware will still divide 2
+         * when div = 0, the final mclk will be unpredictable
+         * So the div here should be at least 2 */
+        div = div < 2 ? 2 : div;
+        uint32_t expt_freq = mclk * div;
+        /* Set APLL coefficients to the given frequency */
+        uint32_t real_freq = 0;
+        esp_err_t ret = i2s_set_apll_freq(expt_freq, &real_freq);
+        if (ret != ESP_OK) {
+            ESP_LOGE(TAG, "set APLL coefficients failed");
             return 0;
         }
-        i2s_apll_calculate_fi2s(mclk, p_i2s[i2s_num]->hal_cfg.sample_bits, &sdm0, &sdm1, &sdm2, &odir);
-        ESP_LOGI(TAG, "APLL Enabled, coefficient: sdm0=%d, sdm1=%d, sdm2=%d, odir=%d", sdm0, sdm1, sdm2, odir);
-        rtc_clk_apll_enable(true, sdm0, sdm1, sdm2, odir);
+        ESP_LOGI(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
         /* Set I2S_APLL as I2S module clock source */
         i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_APLL);
         /* In APLL mode, there is no sclk but only mclk, so return 0 here to indicate APLL mode */
-        return 0;
+        return real_freq;
     }
     /* Set I2S_D2CLK (160M) as default I2S module clock source */
     i2s_hal_set_clock_src(&(p_i2s[i2s_num]->hal), I2S_CLK_D2CLK);
@@ -1079,7 +1014,7 @@ static esp_err_t i2s_calculate_adc_dac_clock(int i2s_num, i2s_hal_clock_cfg_t *c
     clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk;
 
     /* Check if the configuration is correct */
-    ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
+    ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
 
     return ESP_OK;
 }
@@ -1117,7 +1052,7 @@ static esp_err_t i2s_calculate_pdm_tx_clock(int i2s_num, i2s_hal_clock_cfg_t *cl
     clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk;
 
     /* Check if the configuration is correct */
-    ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
+    ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
 
     return ESP_OK;
 }
@@ -1155,7 +1090,7 @@ static esp_err_t i2s_calculate_pdm_rx_clock(int i2s_num, i2s_hal_clock_cfg_t *cl
     clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk;
 
     /* Check if the configuration is correct */
-    ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
+    ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
 
     return ESP_OK;
 }
@@ -1211,7 +1146,7 @@ static esp_err_t i2s_calculate_common_clock(int i2s_num, i2s_hal_clock_cfg_t *cl
     clk_cfg->mclk_div = clk_cfg->sclk / clk_cfg->mclk;
 
     /* Check if the configuration is correct */
-    ESP_RETURN_ON_FALSE(!clk_cfg->sclk || clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
+    ESP_RETURN_ON_FALSE(clk_cfg->mclk <= clk_cfg->sclk, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
 
     return ESP_OK;
 }

+ 23 - 18
components/hal/i2s_hal.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -26,27 +26,32 @@ static void i2s_hal_mclk_div_decimal_cal(i2s_hal_clock_cfg_t *clk_cfg, i2s_ll_mc
     cal->mclk_div = clk_cfg->mclk_div;
     cal->a = 1;
     cal->b = 0;
-    /* If sclk = 0 means APLL clock applied, mclk_div should set to 1 */
-    if (!clk_cfg->sclk) {
-        cal->mclk_div = 1;
+
+    uint32_t freq_diff = abs(clk_cfg->sclk - clk_cfg->mclk * cal->mclk_div);
+    if (!freq_diff) {
+        return;
+    }
+    float decimal = freq_diff / (float)clk_cfg->mclk;
+    // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / 126.0
+    if (decimal > 125.0 / 126.0) {
+        cal->mclk_div++;
         return;
     }
-    uint32_t freq_diff = clk_cfg->sclk - clk_cfg->mclk * cal->mclk_div;
     uint32_t min = ~0;
     for (int a = 2; a <= I2S_LL_MCLK_DIVIDER_MAX; a++) {
-        for (int b = 1; b < a; b++) {
-            ma = freq_diff * a;
-            mb = clk_cfg->mclk * b;
-            if (ma == mb) {
-                cal->a = a;
-                cal->b = b;
-                return;
-            }
-            if (abs((mb - ma)) < min) {
-                cal->a = a;
-                cal->b = b;
-                min = abs(mb - ma);
-            }
+        // Calculate the closest 'b' in this loop, no need to loop 'b' to seek the closest value
+        int b = (int)(a * (freq_diff / (double)clk_cfg->mclk) + 0.5);
+        ma = freq_diff * a;
+        mb = clk_cfg->mclk * b;
+        if (ma == mb) {
+            cal->a = a;
+            cal->b = b;
+            return;
+        }
+        if (abs((mb - ma)) < min) {
+            cal->a = a;
+            cal->b = b;
+            min = abs(mb - ma);
         }
     }
 }

+ 5 - 13
components/hal/include/hal/i2s_types.h

@@ -1,16 +1,8 @@
-// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+/*
+ * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 
 #pragma once
 

+ 11 - 6
components/soc/esp32/include/soc/soc_caps.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -140,18 +140,23 @@
 
 #define SOC_I2C_SUPPORT_APB     (1)
 
+/*-------------------------- APLL CAPS ----------------------------------------*/
+#define SOC_CLK_APLL_SUPPORTED       (1)
+// apll_multiplier_out = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)
+#define SOC_APLL_MULTIPLIER_OUT_MIN_HZ (350000000) // 350 MHz
+#define SOC_APLL_MULTIPLIER_OUT_MAX_HZ (500000000) // 500 MHz
+#define SOC_APLL_MIN_HZ    (5303031)   // 5.303031 MHz
+#define SOC_APLL_MAX_HZ    (125000000) // 125MHz
+
 /*-------------------------- I2S CAPS ----------------------------------------*/
 // ESP32 have 2 I2S
-#define SOC_I2S_NUM                 (2)
+#define SOC_I2S_NUM                 (2U)
+#define SOC_I2S_SUPPORTS_APLL       (1) // ESP32 support APLL
 #define SOC_I2S_SUPPORTS_PDM_TX     (1)
 #define SOC_I2S_SUPPORTS_PDM_RX     (1)
 #define SOC_I2S_SUPPORTS_ADC        (1) // ESP32 support ADC and DAC
 #define SOC_I2S_SUPPORTS_DAC        (1)
 
-#define SOC_I2S_SUPPORTS_APLL       (1)// ESP32 support APLL
-#define SOC_I2S_APLL_MIN_FREQ       (250000000)
-#define SOC_I2S_APLL_MAX_FREQ       (500000000)
-#define SOC_I2S_APLL_MIN_RATE       (10675) //in Hz, I2S Clock rate limited by hardware
 #define SOC_I2S_TRANS_SIZE_ALIGN_WORD (1) // I2S DMA transfer size must be aligned to word
 #define SOC_I2S_LCD_I80_VARIANT       (1) // I2S has a special LCD mode that can generate Intel 8080 TX timing
 

+ 9 - 4
components/soc/esp32s2/include/soc/soc_caps.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -135,14 +135,19 @@
 #define SOC_I2C_SUPPORT_REF_TICK   (1)
 #define SOC_I2C_SUPPORT_APB        (1)
 
+/*-------------------------- APLL CAPS ----------------------------------------*/
+#define SOC_CLK_APLL_SUPPORTED       (1)
+// apll_multiplier_out = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)
+#define SOC_APLL_MULTIPLIER_OUT_MIN_HZ (350000000) // 350 MHz
+#define SOC_APLL_MULTIPLIER_OUT_MAX_HZ (500000000) // 500 MHz
+#define SOC_APLL_MIN_HZ    (5303031)   // 5.303031 MHz
+#define SOC_APLL_MAX_HZ    (125000000) // 125MHz
+
 /*-------------------------- I2S CAPS ----------------------------------------*/
 // ESP32-S2 have 1 I2S
 #define SOC_I2S_NUM                (1)
 #define SOC_I2S_SUPPORTS_APLL      (1)// ESP32-S2 support APLL
 #define SOC_I2S_SUPPORTS_DMA_EQUAL (1)
-#define SOC_I2S_APLL_MIN_FREQ      (250000000)
-#define SOC_I2S_APLL_MAX_FREQ      (500000000)
-#define SOC_I2S_APLL_MIN_RATE      (10675) //in Hz, I2S Clock rate limited by hardware
 #define SOC_I2S_LCD_I80_VARIANT    (1)
 
 /*-------------------------- LCD CAPS ----------------------------------------*/

+ 0 - 1
tools/ci/check_copyright_ignore.txt

@@ -1141,7 +1141,6 @@ components/hal/include/hal/esp_flash_err.h
 components/hal/include/hal/gpio_hal.h
 components/hal/include/hal/i2c_hal.h
 components/hal/include/hal/i2c_types.h
-components/hal/include/hal/i2s_types.h
 components/hal/include/hal/interrupt_controller_hal.h
 components/hal/include/hal/interrupt_controller_types.h
 components/hal/include/hal/ledc_hal.h