mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'fix/emac_pll' into 'master'
docs(esp_eth): added test to verify A/MPLL configuration Closes IDF-11629 See merge request espressif/esp-idf!45978
This commit is contained in:
@@ -484,7 +484,7 @@ static esp_err_t emac_config_pll_clock(emac_esp32_t *emac)
|
||||
uint32_t expt_freq = RMII_CLK_HZ; // 50 MHz
|
||||
uint32_t real_freq = 0;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#if SOC_EMAC_REF_CLK_FROM_APLL
|
||||
// the RMII reference comes from the APLL
|
||||
periph_rtc_apll_acquire();
|
||||
emac->use_pll = true;
|
||||
@@ -493,13 +493,14 @@ static esp_err_t emac_config_pll_clock(emac_esp32_t *emac)
|
||||
if (ret == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "APLL is occupied already, it is working at %" PRIu32 " Hz", real_freq);
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#elif SOC_EMAC_REF_CLK_FROM_MPLL
|
||||
// the RMII reference comes from the MPLL
|
||||
periph_rtc_mpll_acquire();
|
||||
emac->use_pll = true;
|
||||
esp_err_t ret = periph_rtc_mpll_freq_set(expt_freq * 2, &real_freq); // cannot set 50MHz at MPLL, the nearest possible freq is 100 MHz
|
||||
if (ret == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "MPLL is occupied already, it is working at %" PRIu32 " Hz", real_freq);
|
||||
ESP_LOGW(TAG, "Trying to derive RMII clock to be %" PRIu32 " Hz...", RMII_CLK_HZ);
|
||||
}
|
||||
// Set divider of MPLL clock
|
||||
if (real_freq > RMII_CLK_HZ) {
|
||||
@@ -513,7 +514,7 @@ static esp_err_t emac_config_pll_clock(emac_esp32_t *emac)
|
||||
#endif
|
||||
// If the difference of real RMII CLK frequency is not within 50 ppm, i.e. 2500 Hz, the (A/M)PLL is unusable
|
||||
ESP_RETURN_ON_FALSE(abs((int)real_freq - (int)expt_freq) <= 2500,
|
||||
ESP_ERR_INVALID_STATE, TAG, "The (A/M)PLL is working at an unusable frequency %" PRIu32 " Hz", real_freq);
|
||||
ESP_ERR_INVALID_STATE, TAG, "EMAC RMII clock is working at an unusable frequency %" PRIu32 " Hz", real_freq);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_eth_test_utils.h"
|
||||
#include "hal/emac_hal.h" // for MAC_HAL_TDES0_* control bits
|
||||
#include "clk_ctrl_os.h"
|
||||
|
||||
#define ETHERTYPE_TX_STD 0x2222 // frame transmitted via _transmit_frame
|
||||
#define ETHERTYPE_TX_MULTI_2 0x2223 // frame transmitted via _transmit_multiple_buf_frame (2 buffers)
|
||||
@@ -591,6 +592,104 @@ TEST_CASE("internal emac ref rmii clk out", "[esp_emac_clk_out]")
|
||||
// test boards which are configured to input REF RMII CLK by default with only minor HW modification.
|
||||
}
|
||||
|
||||
#if SOC_EMAC_REF_CLK_FROM_MPLL
|
||||
// Verifies that EMAC can derive 50 MHz RMII clock when MPLL is already in use by another
|
||||
// peripheral (e.g. PSRAM). The MPLL frequency is pre-set to typical PSRAM values and EMAC
|
||||
// must find an integer divider that produces 50 MHz within the 50 ppm tolerance.
|
||||
TEST_CASE("internal emac MPLL shared with PSRAM", "[esp_emac_clk_out][skip_setup_teardown]")
|
||||
{
|
||||
uint32_t real_freq = 0;
|
||||
esp_eth_mac_t *mac;
|
||||
|
||||
// Init common MAC and PHY configs to default
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
|
||||
// Init vendor specific MAC config to default
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||
|
||||
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_OUT;
|
||||
esp32_emac_config.clock_config.rmii.clock_gpio = 23;
|
||||
|
||||
esp32_emac_config.clock_config_out_in.rmii.clock_mode = EMAC_CLK_EXT_IN;
|
||||
esp32_emac_config.clock_config_out_in.rmii.clock_gpio = 32;
|
||||
|
||||
// PSRAM default speed: MPLL at 400 MHz — EMAC divider 8 gives exactly 50 MHz
|
||||
ESP_LOGI(TAG, "Verify MPLL at 400 MHz (simulating PSRAM default)");
|
||||
TEST_ESP_OK(periph_rtc_mpll_acquire());
|
||||
TEST_ESP_OK(periph_rtc_mpll_freq_set(400 * 1000000, &real_freq));
|
||||
ESP_LOGI(TAG, "MPLL set to %" PRIu32 " Hz", real_freq);
|
||||
|
||||
mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
TEST_ASSERT_NOT_NULL(mac);
|
||||
TEST_ESP_OK(mac->del(mac));
|
||||
periph_rtc_mpll_release();
|
||||
|
||||
// PSRAM 250M speed: MPLL at 500 MHz — EMAC divider 10 gives exactly 50 MHz
|
||||
ESP_LOGI(TAG, "Verify MPLL at 500 MHz (simulating PSRAM @ 250M speed)");
|
||||
TEST_ESP_OK(periph_rtc_mpll_acquire());
|
||||
TEST_ESP_OK(periph_rtc_mpll_freq_set(500 * 1000000, &real_freq));
|
||||
ESP_LOGI(TAG, "MPLL set to %" PRIu32 " Hz", real_freq);
|
||||
|
||||
mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
TEST_ASSERT_NOT_NULL(mac);
|
||||
TEST_ESP_OK(mac->del(mac));
|
||||
periph_rtc_mpll_release();
|
||||
|
||||
// PSRAM 80M speed: MPLL at 320 MHz — no integer divider can produce 50 MHz within tolerance,
|
||||
// see AP_HEX_PSRAM_MPLL_DEFAULT_FREQ_MHZ for reference
|
||||
// Best candidate: 320/6 = 53.33 MHz (error ~3.33 MHz >> 2500 Hz tolerance)
|
||||
ESP_LOGI(TAG, "Verify MPLL at 320 MHz (simulating PSRAM @ 80M speed) — expected to fail");
|
||||
TEST_ESP_OK(periph_rtc_mpll_acquire());
|
||||
TEST_ESP_OK(periph_rtc_mpll_freq_set(320 * 1000000, &real_freq));
|
||||
ESP_LOGI(TAG, "MPLL set to %" PRIu32 " Hz", real_freq);
|
||||
|
||||
mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
TEST_ASSERT_NULL(mac);
|
||||
periph_rtc_mpll_release();
|
||||
}
|
||||
#endif // SOC_EMAC_REF_CLK_FROM_MPLL
|
||||
|
||||
#if SOC_EMAC_REF_CLK_FROM_APLL
|
||||
// Verifies that EMAC can use the APLL when it is already occupied by another peripheral
|
||||
// (e.g. I2S). Unlike MPLL, the APLL has no configurable divider — its output feeds the RMII
|
||||
// clock directly. So EMAC can only work if the APLL is already at exactly 50 MHz (+/- 50 ppm).
|
||||
TEST_CASE("internal emac APLL shared with I2S", "[esp_emac_clk_out][skip_setup_teardown]")
|
||||
{
|
||||
uint32_t real_freq = 0;
|
||||
esp_eth_mac_t *mac;
|
||||
|
||||
// Init common MAC and PHY configs to default
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
|
||||
// Init vendor specific MAC config to default
|
||||
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
|
||||
|
||||
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_OUT;
|
||||
esp32_emac_config.clock_config.rmii.clock_gpio = 0;
|
||||
|
||||
// Another peripheral has APLL at 50 MHz — exact RMII clock match, EMAC should succeed
|
||||
ESP_LOGI(TAG, "Verify APLL at 50 MHz (exact RMII clock match)");
|
||||
periph_rtc_apll_acquire();
|
||||
TEST_ESP_OK(periph_rtc_apll_freq_set(50 * 1000000, &real_freq));
|
||||
ESP_LOGI(TAG, "APLL set to %" PRIu32 " Hz", real_freq);
|
||||
|
||||
mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
TEST_ASSERT_NOT_NULL(mac);
|
||||
TEST_ESP_OK(mac->del(mac));
|
||||
periph_rtc_apll_release();
|
||||
|
||||
// I2S typical frequency: APLL at ~12.288 MHz (48 kHz * 256) — far from 50 MHz, EMAC should fail
|
||||
ESP_LOGI(TAG, "Verify APLL at ~12.288 MHz (simulating I2S @ 48 kHz) — expected to fail");
|
||||
periph_rtc_apll_acquire();
|
||||
TEST_ESP_OK(periph_rtc_apll_freq_set(12288000, &real_freq));
|
||||
ESP_LOGI(TAG, "APLL set to %" PRIu32 " Hz", real_freq);
|
||||
|
||||
mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
|
||||
TEST_ASSERT_NULL(mac);
|
||||
periph_rtc_apll_release();
|
||||
}
|
||||
#endif // SOC_EMAC_REF_CLK_FROM_APLL
|
||||
|
||||
// This test skips Ethernet initialization in the setUp and tearDown since different EMAC configurations are tested.
|
||||
// As such, allocated resources may not be freed in the tearDown when the test fails. Therefore the tets is run as
|
||||
// the last test in the suite to not affect other tests.
|
||||
|
||||
@@ -288,6 +288,19 @@ def test_esp_eth_ip101(dut: IdfDut) -> None:
|
||||
ethernet_l2_test(dut)
|
||||
|
||||
|
||||
@pytest.mark.eth_ip101
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'rmii_clko_esp32',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_esp32_emac_clko(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases(group='esp_emac_clk_out')
|
||||
|
||||
|
||||
# ----------- IP101 ESP32P4 -----------
|
||||
@pytest.mark.parametrize(
|
||||
'config, target',
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
CONFIG_IDF_TARGET="esp32"
|
||||
|
||||
CONFIG_UNITY_ENABLE_FIXTURE=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
|
||||
CONFIG_ETH_USE_ESP32_EMAC=y
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
|
||||
# interface configuration
|
||||
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
|
||||
CONFIG_ETHERNET_PHY_GENERIC=y
|
||||
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
|
||||
|
||||
CONFIG_ETHERNET_PHY_INTERFACE_RMII=y
|
||||
CONFIG_ETHERNET_RMII_CLK_OUTPUT=y
|
||||
CONFIG_ETHERNET_RMII_CLK_GPIO=0
|
||||
CONFIG_ETHERNET_RMII_CLK_EXT_LOOPBACK_EN=n
|
||||
@@ -806,3 +806,7 @@ config SOC_PHY_COMBO_MODULE
|
||||
config SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_EMAC_REF_CLK_FROM_APLL
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -400,3 +400,4 @@
|
||||
|
||||
/*--------------------------- EMAC --------------------------------*/
|
||||
#define SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK (1) /*!< REF RMII CLK output is looped back internally */
|
||||
#define SOC_EMAC_REF_CLK_FROM_APLL (1) /*!< RMII REF CLK can use APLL as internal clock source */
|
||||
|
||||
@@ -1883,6 +1883,10 @@ config SOC_EMAC_SUPPORT_SLEEP_RETENTION
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_EMAC_REF_CLK_FROM_MPLL
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_JPEG_CODEC_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -720,6 +720,7 @@
|
||||
#define SOC_EMAC_USE_MULTI_IO_MUX (1) /*!< Multiple GPIO pad options exist to connect EMAC signal via IO_MUX */
|
||||
#define SOC_EMAC_MII_USE_GPIO_MATRIX (1) /*!< EMAC MII signals are connected to GPIO pads via GPIO Matrix */
|
||||
#define SOC_EMAC_SUPPORT_SLEEP_RETENTION (1) /*!< EMAC supports register backup/restore in sleep mode */
|
||||
#define SOC_EMAC_REF_CLK_FROM_MPLL (1) /*!< RMII REF CLK can use MPLL as internal clock source */
|
||||
|
||||
/*--------------------------- JPEG --------------------------------*/
|
||||
#define SOC_JPEG_CODEC_SUPPORTED (1)
|
||||
|
||||
@@ -167,6 +167,13 @@ The Ethernet driver is composed of two parts: MAC and PHY.
|
||||
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`, {IDF_TARGET_SOC_REF_CLK_IN_GPIO} can be selected as input pin for the ``REF_CLK`` signal via IO_MUX.
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. warning::
|
||||
If the RMII clock mode is configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`, the EMAC derives the 50 MHz RMII reference clock from the MPLL via an integer divider. When PSRAM is also enabled, both peripherals share the MPLL, and PSRAM locks it to a frequency determined by its speed configuration. If PSRAM speed is configured to 80 MHz (:ref:`CONFIG_SPIRAM_SPEED`), the MPLL runs at 320 MHz, and no integer divisor of 320 MHz can produce 50 MHz within the required ±50 ppm tolerance (the closest candidate is 320 / 6 ≈ 53.33 MHz). EMAC initialization will fail in this configuration.
|
||||
|
||||
If you must use 80 MHz PSRAM speed, provide the ``REF_CLK`` from an external source (PHY or oscillator) and configure :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN` instead.
|
||||
|
||||
.. only:: not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -167,6 +167,13 @@
|
||||
|
||||
如果 RMII 时钟模式配置为 :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`,则可以通过 IO_MUX 将 {IDF_TARGET_SOC_REF_CLK_IN_GPIO} 选择为 ``REF_CLK`` 信号的输入管脚。
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. warning::
|
||||
如果 RMII 时钟模式配置为 :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`,EMAC 将通过整数分频器从 MPLL 获取 50 MHz RMII 参考时钟。当同时启用 PSRAM 时,两个外设共享 MPLL,PSRAM 会将 MPLL 锁定在由其速度配置决定的频率上。如果 PSRAM 速度配置为 80 MHz (:ref:`CONFIG_SPIRAM_SPEED`),MPLL 将运行在 320 MHz,而 320 MHz 无法通过任何整数分频得到满足 ±50 ppm 容差要求的 50 MHz(最接近的候选值为 320 / 6 ≈ 53.33 MHz)。在此配置下,EMAC 初始化将失败。
|
||||
|
||||
如果必须使用 80 MHz 的 PSRAM 速度,请通过外部时钟源(PHY 或振荡器)提供 ``REF_CLK``,并配置为 :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`。
|
||||
|
||||
.. only:: not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK
|
||||
|
||||
.. warning::
|
||||
|
||||
Reference in New Issue
Block a user