Merge branch 'feature/emac_intr_prio' into 'master'

feat(esp_eth): added option to configure interrupt priority

Closes IDF-7969

See merge request espressif/esp-idf!29300
This commit is contained in:
Ondrej Kosta 2024-03-19 21:09:30 +08:00
commit 9d0b8d62b0
7 changed files with 109 additions and 28 deletions

View File

@ -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 = \

View File

@ -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

View File

@ -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 "."

View File

@ -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(&eth_config, &eth_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, &eth_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));

View File

@ -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)

View File

@ -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));

View File

@ -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: