From 6a09d980898e810b61dc60f81f9147956b4c2bc9 Mon Sep 17 00:00:00 2001 From: Ondrej Kosta Date: Mon, 26 Feb 2024 17:13:01 +0100 Subject: [PATCH] feat(esp_eth): added option to configure interrupt priority --- components/esp_eth/include/esp_eth_mac.h | 3 + components/esp_eth/src/esp_eth_mac_esp.c | 22 +++-- .../esp_eth/test_apps/main/CMakeLists.txt | 2 +- ...eth_test_hal.c => esp_eth_test_esp_emac.c} | 80 ++++++++++++++++--- .../esp_eth/test_apps/pytest_esp_eth.py | 8 +- .../test_app_vfs_l2tap/main/test_vfs_l2tap.c | 2 - docs/en/api-reference/network/esp_eth.rst | 20 +++-- 7 files changed, 109 insertions(+), 28 deletions(-) rename components/esp_eth/test_apps/main/{esp_eth_test_hal.c => esp_eth_test_esp_emac.c} (82%) diff --git a/components/esp_eth/include/esp_eth_mac.h b/components/esp_eth/include/esp_eth_mac.h index d10029f22e..35f88e8857 100644 --- a/components/esp_eth/include/esp_eth_mac.h +++ b/components/esp_eth/include/esp_eth_mac.h @@ -467,6 +467,7 @@ typedef struct { eth_data_interface_t interface; /*!< EMAC Data interface to PHY (MII/RMII) */ eth_mac_clock_config_t clock_config; /*!< EMAC Interface clock configuration */ eth_mac_dma_burst_len_t dma_burst_len; /*!< EMAC DMA burst length for both Tx and Rx */ + int intr_priority; /*!< EMAC interrupt priority, if set to 0 or a negative value, the driver will try to allocate an interrupt with a default priority */ #if SOC_EMAC_USE_IO_MUX eth_mac_dataif_gpio_config_t emac_dataif_gpio; /*!< EMAC MII/RMII data plane GPIO configuration */ #endif // SOC_EMAC_USE_IO_MUX @@ -494,6 +495,7 @@ typedef struct { } \ }, \ .dma_burst_len = ETH_DMA_BURST_LEN_32, \ + .intr_priority = 0, \ } #elif CONFIG_IDF_TARGET_ESP32P4 #define ETH_ESP32_EMAC_DEFAULT_CONFIG() \ @@ -518,6 +520,7 @@ typedef struct { } \ }, \ .dma_burst_len = ETH_DMA_BURST_LEN_32, \ + .intr_priority = 0, \ .emac_dataif_gpio = \ { \ .rmii = \ diff --git a/components/esp_eth/src/esp_eth_mac_esp.c b/components/esp_eth/src/esp_eth_mac_esp.c index 82efca1835..6ad4157e86 100644 --- a/components/esp_eth/src/esp_eth_mac_esp.c +++ b/components/esp_eth/src/esp_eth_mac_esp.c @@ -41,6 +41,8 @@ static const char *TAG = "esp.emac"; #define FLOW_CONTROL_LOW_WATER_MARK (CONFIG_ETH_DMA_RX_BUFFER_NUM / 3) #define FLOW_CONTROL_HIGH_WATER_MARK (FLOW_CONTROL_LOW_WATER_MARK * 2) +#define EMAC_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED + #define RMII_CLK_HZ (50000000) #define RMII_10M_SPEED_RX_TX_CLK_DIV (19) #define RMII_100M_SPEED_RX_TX_CLK_DIV (1) @@ -664,6 +666,11 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config esp_eth_mac_t *ret = NULL; emac_esp32_t *emac = NULL; ESP_RETURN_ON_FALSE(config, NULL, TAG, "can't set mac config to null"); + if (esp32_config->intr_priority > 0) { + ESP_RETURN_ON_FALSE(1 << (esp32_config->intr_priority) & EMAC_ALLOW_INTR_PRIORITY_MASK, NULL, + TAG, "invalid interrupt priority: %d", esp32_config->intr_priority); + } + ret_code = esp_emac_alloc_driver_obj(config, &emac); ESP_RETURN_ON_FALSE(ret_code == ESP_OK, NULL, TAG, "alloc driver object failed"); @@ -675,14 +682,19 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_esp32_emac_config_t *esp32_config } /* initialize hal layer driver */ emac_hal_init(&emac->hal); + /* alloc interrupt */ - if (config->flags & ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE) { - ret_code = esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, ESP_INTR_FLAG_IRAM, - emac_isr_default_handler, &emac->hal, &(emac->intr_hdl)); + int isr_flags = 0; + if (esp32_config->intr_priority > 0) { + isr_flags |= 1 << (esp32_config->intr_priority); } else { - ret_code = esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, - emac_isr_default_handler, &emac->hal, &(emac->intr_hdl)); + isr_flags |= ESP_INTR_FLAG_LOWMED; } + if (config->flags & ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE) { + isr_flags |= ESP_INTR_FLAG_IRAM; + } + ret_code = esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, isr_flags, + emac_isr_default_handler, &emac->hal, &(emac->intr_hdl)); ESP_GOTO_ON_FALSE(ret_code == ESP_OK, NULL, err, TAG, "alloc emac interrupt failed"); #ifdef SOC_EMAC_USE_IO_MUX diff --git a/components/esp_eth/test_apps/main/CMakeLists.txt b/components/esp_eth/test_apps/main/CMakeLists.txt index 95df0f1a17..a1c785ae45 100644 --- a/components/esp_eth/test_apps/main/CMakeLists.txt +++ b/components/esp_eth/test_apps/main/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register(SRCS "esp_eth_test_apps.c" "esp_eth_test_l2.c" - "esp_eth_test_hal.c" + "esp_eth_test_esp_emac.c" "esp_eth_test_common.c" "esp_eth_test_main.c" INCLUDE_DIRS "." diff --git a/components/esp_eth/test_apps/main/esp_eth_test_hal.c b/components/esp_eth/test_apps/main/esp_eth_test_esp_emac.c similarity index 82% rename from components/esp_eth/test_apps/main/esp_eth_test_hal.c rename to components/esp_eth/test_apps/main/esp_eth_test_esp_emac.c index fd10a8c756..43cfa34897 100644 --- a/components/esp_eth/test_apps/main/esp_eth_test_hal.c +++ b/components/esp_eth/test_apps/main/esp_eth_test_esp_emac.c @@ -18,7 +18,7 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) -static const char *TAG = "esp32_eth_test_hal"; +static const char *TAG = "eth_test_esp_emac"; typedef struct { @@ -26,12 +26,12 @@ typedef struct uint16_t expected_size; uint16_t expected_size_2; uint16_t expected_size_3; -} recv_hal_check_info_t; +} recv_esp_emac_check_info_t; -static esp_err_t eth_recv_hal_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) +static esp_err_t eth_recv_esp_emac_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) { emac_frame_t *pkt = (emac_frame_t *)buffer; - recv_hal_check_info_t *recv_info = (recv_hal_check_info_t *)priv; + recv_esp_emac_check_info_t *recv_info = (recv_esp_emac_check_info_t *)priv; uint16_t expected_size = recv_info->expected_size + recv_info->expected_size_2 + recv_info->expected_size_3; ESP_LOGI(TAG, "recv frame size: %" PRIu16, expected_size); @@ -75,9 +75,9 @@ static esp_err_t eth_recv_hal_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, ui return ESP_OK; } -TEST_CASE("hal receive/transmit", "[emac_hal]") +TEST_CASE("internal emac receive/transmit", "[esp_emac]") { - recv_hal_check_info_t recv_info; + recv_esp_emac_check_info_t recv_info; recv_info.mutex = xSemaphoreCreateBinary(); TEST_ASSERT_NOT_NULL(recv_info.mutex); recv_info.expected_size = 0; @@ -106,7 +106,7 @@ TEST_CASE("hal receive/transmit", "[emac_hal]") bool loopback_en = true; esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en); - TEST_ESP_OK(esp_eth_update_input_path(eth_handle, eth_recv_hal_check_cb, &recv_info)); + TEST_ESP_OK(esp_eth_update_input_path(eth_handle, eth_recv_esp_emac_check_cb, &recv_info)); // start the driver TEST_ESP_OK(esp_eth_start(eth_handle)); @@ -297,11 +297,71 @@ TEST_CASE("hal receive/transmit", "[emac_hal]") vSemaphoreDelete(recv_info.mutex); } +TEST_CASE("internal emac interrupt priority", "[esp_emac]") +{ + EventBits_t bits = 0; + EventGroupHandle_t eth_event_group = xEventGroupCreate(); + TEST_ASSERT(eth_event_group != NULL); + test_case_uses_tcpip(); + TEST_ESP_OK(esp_event_loop_create_default()); + for (int i = -1; i <= 4; i++) { + // create TCP/IP netif + esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *eth_netif = esp_netif_new(&netif_cfg); + eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG(); + esp32_emac_config.intr_priority = i; + ESP_LOGI(TAG, "set interrupt priority %i: ", i); + esp_eth_mac_t *mac = mac_init(&esp32_emac_config, NULL); + if (i >= 4) { + TEST_ASSERT_NULL(mac); + } + else { + TEST_ASSERT_NOT_NULL(mac); + esp_eth_phy_t *phy = phy_init(NULL); + TEST_ASSERT_NOT_NULL(phy); + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + esp_eth_handle_t eth_handle = NULL; + // install Ethernet driver + TEST_ESP_OK(esp_eth_driver_install(ð_config, ð_handle)); + extra_eth_config(eth_handle); + // combine driver with netif + esp_eth_netif_glue_handle_t glue = esp_eth_new_netif_glue(eth_handle); + TEST_ESP_OK(esp_netif_attach(eth_netif, glue)); + // register user defined event handers + TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group)); + TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group)); + + // start Ethernet driver + TEST_ESP_OK(esp_eth_start(eth_handle)); + /* wait for IP lease */ + bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS)); + TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT); + // stop Ethernet driveresp_event_handler_unregister + TEST_ESP_OK(esp_eth_stop(eth_handle)); + /* wait for connection stop */ + bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS)); + TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT); + + TEST_ESP_OK(esp_eth_del_netif_glue(glue)); + /* driver should be uninstalled within 2 seconds */ + TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle)); + TEST_ESP_OK(phy->del(phy)); + TEST_ESP_OK(mac->del(mac)); + TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler)); + TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)); + extra_cleanup(); + } + esp_netif_destroy(eth_netif); + } + TEST_ESP_OK(esp_event_loop_delete_default()); + vEventGroupDelete(eth_event_group); +} + #if CONFIG_IDF_TARGET_ESP32P4 // IDF-8993 #include "hal/emac_hal.h" #include "hal/emac_ll.h" #include "soc/emac_mac_struct.h" -static esp_err_t eth_recv_err_hal_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) +static esp_err_t eth_recv_err_esp_emac_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) { SemaphoreHandle_t mutex = (SemaphoreHandle_t)priv; free(buffer); @@ -309,7 +369,7 @@ static esp_err_t eth_recv_err_hal_check_cb(esp_eth_handle_t hdl, uint8_t *buffer return ESP_OK; } -TEST_CASE("hal erroneous frames", "[emac_hal]") +TEST_CASE("internal emac erroneous frames", "[esp_emac]") { SemaphoreHandle_t mutex = xSemaphoreCreateBinary(); TEST_ASSERT_NOT_NULL(mutex); @@ -334,7 +394,7 @@ TEST_CASE("hal erroneous frames", "[emac_hal]") bool loopback_en = true; esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en); - TEST_ESP_OK(esp_eth_update_input_path(eth_handle, eth_recv_err_hal_check_cb, mutex)); + TEST_ESP_OK(esp_eth_update_input_path(eth_handle, eth_recv_err_esp_emac_check_cb, mutex)); // start the driver TEST_ESP_OK(esp_eth_start(eth_handle)); diff --git a/components/esp_eth/test_apps/pytest_esp_eth.py b/components/esp_eth/test_apps/pytest_esp_eth.py index ace87f45e4..3e954ed2c7 100644 --- a/components/esp_eth/test_apps/pytest_esp_eth.py +++ b/components/esp_eth/test_apps/pytest_esp_eth.py @@ -127,8 +127,8 @@ def ethernet_test(dut: IdfDut) -> None: dut.run_all_single_board_cases(group='ethernet', timeout=980) -def ethernet_int_emac_hal_test(dut: IdfDut) -> None: - dut.run_all_single_board_cases(group='emac_hal') +def ethernet_int_emac_test(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='esp_emac', timeout=120) def ethernet_l2_test(dut: IdfDut) -> None: @@ -250,8 +250,8 @@ def test_esp_ethernet(dut: IdfDut) -> None: @pytest.mark.parametrize('config', [ 'default_ip101', ], indirect=True) -def test_esp_emac_hal(dut: IdfDut) -> None: - ethernet_int_emac_hal_test(dut) +def test_esp_emac(dut: IdfDut) -> None: + ethernet_int_emac_test(dut) dut.serial.hard_reset() ethernet_heap_alloc_test(dut) diff --git a/components/esp_netif/test_apps/test_app_vfs_l2tap/main/test_vfs_l2tap.c b/components/esp_netif/test_apps/test_app_vfs_l2tap/main/test_vfs_l2tap.c index 31386083dd..074112bbe8 100644 --- a/components/esp_netif/test_apps/test_app_vfs_l2tap/main/test_vfs_l2tap.c +++ b/components/esp_netif/test_apps/test_app_vfs_l2tap/main/test_vfs_l2tap.c @@ -196,8 +196,6 @@ static void ethernet_deinit(test_vfs_eth_network_t *network_hndls) { TEST_ESP_OK(esp_eth_stop(network_hndls->eth_handle)); TEST_ESP_OK(esp_eth_del_netif_glue(network_hndls->glue)); - /* driver should be uninstalled within 2 seconds */ - //TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000)); esp_eth_driver_uninstall(network_hndls->eth_handle); TEST_ESP_OK(network_hndls->phy->del(network_hndls->phy)); TEST_ESP_OK(network_hndls->mac->del(network_hndls->mac)); diff --git a/docs/en/api-reference/network/esp_eth.rst b/docs/en/api-reference/network/esp_eth.rst index 93d7fd42d1..62d5db23c9 100644 --- a/docs/en/api-reference/network/esp_eth.rst +++ b/docs/en/api-reference/network/esp_eth.rst @@ -226,7 +226,7 @@ The Ethernet driver is composed of two parts: MAC and PHY. You need to set up the necessary parameters for MAC and PHY respectively based on your Ethernet board design, and then combine the two together to complete the driver installation. -Configuration for MAC is described in :cpp:class:`eth_mac_config_t`, including: +Basic common configuration for MAC layer is described in :cpp:class:`eth_mac_config_t`, including: .. list:: @@ -236,15 +236,23 @@ Configuration for MAC is described in :cpp:class:`eth_mac_config_t`, including: * :cpp:member:`eth_mac_config_t::flags`: specifying extra features that the MAC driver should have, it could be useful in some special situations. The value of this field can be OR'd with macros prefixed with ``ETH_MAC_FLAG_``. For example, if the MAC driver should work when the cache is disabled, then you should configure this field with :c:macro:`ETH_MAC_FLAG_WORK_WITH_CACHE_DISABLE`. - :SOC_EMAC_SUPPORTED: * :cpp:member:`eth_esp32_emac_config_t::smi_mdc_gpio_num` and :cpp:member:`eth_esp32_emac_config_t::smi_mdio_gpio_num`: the GPIO number used to connect the SMI signals. +.. only:: SOC_EMAC_SUPPORTED - :SOC_EMAC_SUPPORTED: * :cpp:member:`eth_esp32_emac_config_t::interface`: configuration of MAC Data interface to PHY (MII/RMII). + Specific configuration for **internal MAC module** is described in :cpp:class:`eth_esp32_emac_config_t`, including: - :SOC_EMAC_SUPPORTED: * :cpp:member:`eth_esp32_emac_config_t::clock_config`: configuration of EMAC Interface clock (``REF_CLK`` mode and GPIO number in case of RMII). + .. list:: - :SOC_EMAC_USE_IO_MUX: * :cpp:member:`eth_esp32_emac_config_t::emac_dataif_gpio`: configuration of EMAC MII/RMII data plane GPIO numbers. + * :cpp:member:`eth_esp32_emac_config_t::smi_mdc_gpio_num` and :cpp:member:`eth_esp32_emac_config_t::smi_mdio_gpio_num`: the GPIO number used to connect the SMI signals. - :not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK: * :cpp:member:`eth_esp32_emac_config_t::clock_config_out_in`: configuration of EMAC input interface clock when ``REF_CLK`` signal is generated internally and is looped back to the EMAC externally. The mode must be always configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`. This option is valid only when configuration of :cpp:member:`eth_esp32_emac_config_t::clock_config` is set to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`. + * :cpp:member:`eth_esp32_emac_config_t::interface`: configuration of MAC Data interface to PHY (MII/RMII). + + * :cpp:member:`eth_esp32_emac_config_t::clock_config`: configuration of EMAC Interface clock (``REF_CLK`` mode and GPIO number in case of RMII). + + * :cpp:member:`eth_esp32_emac_config_t::intr_priority`: sets the priority of the MAC interrupt. If it is set to ``0`` or a negative value, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority. Note that *Low* and *Medium* interrupt priorities (1 to 3) can be set since these can be handled in C. + + :SOC_EMAC_USE_IO_MUX: * :cpp:member:`eth_esp32_emac_config_t::emac_dataif_gpio`: configuration of EMAC MII/RMII data plane GPIO numbers. + + :not SOC_EMAC_RMII_CLK_OUT_INTERNAL_LOOPBACK: * :cpp:member:`eth_esp32_emac_config_t::clock_config_out_in`: configuration of EMAC input interface clock when ``REF_CLK`` signal is generated internally and is looped back to the EMAC externally. The mode must be always configured to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_EXT_IN`. This option is valid only when configuration of :cpp:member:`eth_esp32_emac_config_t::clock_config` is set to :cpp:enumerator:`emac_rmii_clock_mode_t::EMAC_CLK_OUT`. Configuration for PHY is described in :cpp:class:`eth_phy_config_t`, including: