feat(esp_eth): Ethernet test refactor

ETH tests use setUp-tearDown to init/deinit resources
This commit is contained in:
Ondrej Kosta
2026-01-23 16:45:14 +01:00
parent f4f42e920b
commit 0fe61ada78
30 changed files with 1322 additions and 1169 deletions
@@ -1,8 +1,6 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_eth_test)
+170 -2
View File
@@ -1,9 +1,9 @@
# EMAC Test
| Supported Targets | ESP32 | ESP32-P4 |
| ----------------- | ----- | -------- |
This test app is used to test Ethernet MAC behavior with different chips.
# Ethernet Test
This test app is used to test Ethernet MAC behavior with different chips.
## Prerequisites
Install third part Python packages:
@@ -11,3 +11,171 @@ Install third part Python packages:
```bash
pip install scapy
```
## Chip-Specific Configuration
Different Ethernet chips may have deviations from the standard behavior or may not support certain features. To accommodate these chip-specific differences, the test framework provides Kconfig options that allow disabling or modifying specific tests that are incompatible with certain chips.
For example, some chips may not support 10 Mbps loopback mode, or may have known errata that require workarounds. These Kconfig options are defined in `main/Kconfig.projbuild` and can be configured through the ESP-IDF menuconfig system to adjust test behavior for the specific chip under test.
Refer to `main/Kconfig.projbuild` for the complete list of available configuration options and their descriptions.
## Test Framework Overview
The test framework provides common initialization and helper functions through `esp_eth_test_utils.h` and `esp_eth_test_utils.c`. These modules handle Ethernet initialization, event registration, and resource management automatically using Unity's `setUp()` and `tearDown()` functions.
### Unity setUp() and tearDown()
The `setUp()` function automatically performs the following initialization before each test case:
1. **Sets up event handling** (creates event group and default event loop)
2. **Initializes the Ethernet driver** by calling `esp_eth_test_eth_init()` (uses Ethernet init component by default, can be customized - see [Customizing Ethernet Initialization](#customizing-ethernet-initialization) section below)
3. **Optionally sets up esp-netif** if the `[esp-netif]` marker is used (see [Markers](#test-markers) section below)
4. **Registers event handlers**:
- `eth_test_default_event_handler()` for ETH_EVENT events (ETHERNET_EVENT_START, ETHERNET_EVENT_STOP, ETHERNET_EVENT_CONNECTED, ETHERNET_EVENT_DISCONNECTED)
- `eth_test_got_ip_event_handler()` for IP_EVENT_ETH_GOT_IP events (only when `[esp-netif]` marker is used)
The `tearDown()` function automatically performs cleanup after each test case:
1. **Frees all allocated memory** tracked by the test framework
2. **Unregisters event handlers**
3. **Stops and deinitializes the Ethernet driver** by calling `esp_eth_test_eth_deinit()` (uses Ethernet init component by default, can be customized - see [Customizing Ethernet Initialization](#customizing-ethernet-initialization) section below)
4. **Destroys the event loop and event group**
### Customizing Ethernet Initialization
By default, the test framework initializes Ethernet using the **Ethernet init component**. This provides initialization of Ethernet hardware based on Kconfig settings.
For test cases that require custom Ethernet initialization (e.g., chip not supported by Ethernet init component, specific MAC/PHY configurations, multiple Ethernet ports, or custom hardware setup), you can override the weak functions:
- **`esp_eth_test_eth_init(esp_eth_handle_t *eth_handle)`**: Override this function to provide custom Ethernet initialization. The function should initialize the Ethernet driver and return the handle via the `eth_handle` parameter.
> [!IMPORTANT]
> Initialization function must print DUT PHY identification string as it is expected by `pytest` script. The PHY identification message must be in the following format:
> ```c
> printf("DUT PHY: %s", my_phy_str);
>```
- **`esp_eth_test_eth_deinit(esp_eth_handle_t eth_handle)`**: Override this function to provide custom Ethernet deinitialization. The function should clean up the Ethernet driver resources.
### Test Markers
Test cases can use markers in their Unity test identifiers to control initialization behavior:
#### `[esp-netif]` Marker
When this marker is included in the test case identifier, `setUp()` will additionally initialize TCP/IP stack by:
- Creating an ESP-NETIF interface using `ESP_NETIF_DEFAULT_ETH()` configuration
- Attaching the Ethernet driver to the netif using `esp_eth_new_netif_glue()`
- Registering the `eth_test_got_ip_event_handler()` for IP_EVENT_ETH_GOT_IP events
**Example:**
```c
TEST_CASE("ethernet dhcp test", "[ethernet][esp-netif]")
{
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
esp_netif_t *netif = eth_test_get_netif(); // Valid when [esp-netif] is used
// Test code using TCP/IP stack...
}
```
#### `[skip_setup_teardown]` Marker
When this marker is included, `setUp()` and `tearDown()` will skip all automatic initialization and cleanup. This allows the test to perform its own custom initialization and cleanup procedures.
> [!IMPORTANT]
> When using this marker, you must manually:
> - Register event handlers using `eth_test_default_event_handler()` and/or `eth_test_got_ip_event_handler()`
> - Initialize and deinitialize all resources except for memory allocated by `eth_test_alloc()`
> [!NOTE]
> Memory allocated through `eth_test_alloc()` is automatically freed by `tearDown()` even when using this marker.
**Example:**
```c
TEST_CASE("internal emac interrupt priority", "[esp_emac][skip_setup_teardown]")
{
// Manual initialization
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ESP_OK(esp_event_loop_create_default());
// Register handlers manually if needed
// ... custom test code ...
}
```
### Helper Functions
#### Getting Initialized Handles
- **`eth_test_get_eth_handle()`**: Returns the Ethernet handle initialized by `setUp()`. Returns `NULL` if initialization failed or if `[skip_setup_teardown]` marker is used.
- **`eth_test_get_default_event_group()`**: Returns the event group handle created by `setUp()`. Use this to wait for Ethernet events using `xEventGroupWaitBits()` with the predefined bits (ETH_START_BIT, ETH_STOP_BIT, ETH_CONNECT_BIT, ETH_GOT_IP_BIT).
- **`eth_test_get_netif()`**: Returns the ESP-NETIF handle initialized by `setUp()`. **Only valid when `[esp-netif]` marker is used**, otherwise returns `NULL`.
#### PHY Register Manipulation
- **`eth_test_set_phy_reg_bits()`**: Sets specific bits in a PHY register with retry logic. Useful for configuring PHY behavior during tests.
- **`eth_test_clear_phy_reg_bits()`**: Clears specific bits in a PHY register with retry logic.
Both functions include automatic retry logic with configurable maximum attempts and delays between attempts.
#### Memory Management
- **`eth_test_alloc(size)`**: Allocates memory from the heap. The framework automatically tracks all allocations and frees them in `tearDown()`. Maximum of `MAX_HEAP_ALLOCATION_POINTERS` (20) allocations are supported.
- **`eth_test_free(ptr)`**: Frees a specific allocation. Generally not needed as `tearDown()` automatically frees all tracked allocations.
- **`eth_test_free_all()`**: Frees all tracked allocations. Called automatically by `tearDown()`.
#### Event Handlers
- **`eth_test_default_event_handler()`**: Default handler for ETH_EVENT events. Automatically registered by `setUp()` unless `[skip_setup_teardown]` is used. Sets event bits in the event group for:
- ETHERNET_EVENT_START → ETH_START_BIT
- ETHERNET_EVENT_STOP → ETH_STOP_BIT
- ETHERNET_EVENT_CONNECTED → ETH_CONNECT_BIT
- **`eth_test_got_ip_event_handler()`**: Handler for IP_EVENT_ETH_GOT_IP events. Automatically registered by `setUp()` when `[esp-netif]` marker is used. Sets ETH_GOT_IP_BIT in the event group and logs IP configuration.
### Usage Example
```c
TEST_CASE("ethernet basic test", "[ethernet][esp-netif]")
{
// Get handles initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
esp_netif_t *netif = eth_test_get_netif();
EventBits_t bits = 0;
// Start Ethernet driver
TEST_ESP_OK(esp_eth_start(eth_handle));
// Wait for Ethernet to start
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true,
pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
// Wait for link connection
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true,
pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// Wait for IP address (only when [esp-netif] is used)
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);
// Test code here...
// Stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
// Wait for stop event
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);
// tearDown() will automatically clean up everything
}
```
@@ -1,10 +1,11 @@
idf_component_register(SRCS "esp_eth_test_apps.c"
idf_component_register(SRCS "esp_eth_test_main.c"
"esp_eth_test_apps.c"
"esp_eth_test_l2.c"
"esp_eth_test_utils.c"
"esp_eth_test_esp_emac.c"
"esp_eth_test_common.c"
"esp_eth_test_main.c"
INCLUDE_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES unity test_utils esp_eth esp_netif esp_http_client esp_driver_gpio
PRIV_REQUIRES unity esp_eth esp_netif esp_http_client esp_driver_gpio
EMBED_TXTFILES dl_espressif_com_root_cert.pem
WHOLE_ARCHIVE)
@@ -1,105 +1,91 @@
menu "esp_eth TEST_APPS Configuration"
choice TARGET_ETH_CONFIG
prompt "Ethernet peripheral device"
default TARGET_USE_INTERNAL_ETHERNET
config ETH_TEST_UNITY_TEST_TASK_PRIO
int "Unity test task priority"
range 0 25
default 5
help
Select type of Ethernet interface.
Unity test task priority.
config TARGET_USE_INTERNAL_ETHERNET
depends on SOC_EMAC_SUPPORTED
select ETH_USE_ESP32_EMAC
bool "Internal EMAC"
help
Use internal Ethernet MAC controller.
config ETH_TEST_UNITY_TEST_TASK_STACK
int "Unity test task stack size"
default 4096
help
Unity test task stack size in B.
config ETH_TEST_MAC_ADDR_UI
bool "Expect Universal & Individual MAC address"
default y
help
Enable this option to expect Universal & Individual MAC address.
This should be disabled for SPI Ethernet devices.
config TARGET_USE_SPI_ETHERNET
bool "SPI Ethernet"
select ETH_USE_SPI_ETHERNET
help
Use external SPI-Ethernet module(s).
endchoice # TARGET_ETH_CONFIG
config ETH_TEST_PHY_ADDRESS_DISABLED
bool "Disable PHY address test"
default n
help
Disable PHY address testing.
This should be disabled for SPI Ethernet devices.
if TARGET_USE_INTERNAL_ETHERNET
choice TARGET_ETH_PHY_DEVICE
prompt "Ethernet PHY"
default TARGET_ETH_PHY_DEVICE_IP101
help
Select one of the devices listed here
config ETH_TEST_LOOPBACK_DISABLED
bool "Disable loopback test"
default n
help
Disable loopback testing as some chips may not support it.
config TARGET_ETH_PHY_DEVICE_IP101
bool "IP101"
config TARGET_ETH_PHY_DEVICE_LAN8720
bool "LAN8720"
config TARGET_ETH_PHY_DEVICE_KSZ8041
bool "KSZ8041"
config TARGET_ETH_PHY_DEVICE_RTL8201
bool "RTL8201"
config TARGET_ETH_PHY_DEVICE_DP83848
bool "DP83848"
endchoice # TARGET_ETH_PHY_DEVICE
config ETH_TEST_10MB_LOOPBACK_DISABLED
depends on !ETH_TEST_LOOPBACK_DISABLED
bool "Disable 10MB loopback test"
default n
help
Disable 10MB loopback testing as some chips may not support it.
config TARGET_USE_DEFAULT_EMAC_CONFIG
default y
bool "Use default EMAC config"
config ETH_TEST_10MB_LOOPBACK_IGNORE_FAILURES
depends on !ETH_TEST_LOOPBACK_DISABLED
bool "Ignore 10MB loopback test failures"
default y
help
Ignore failures in 10MB loopback tests.
if !TARGET_USE_DEFAULT_EMAC_CONFIG
config TARGET_IO_MDC
int "SMI MDC GPIO number"
default 23
config TARGET_IO_MDIO
int "SMI MDIO GPIO number"
default 18
endif
config ETH_TEST_LOOPBACK_WITH_AUTONEGOTIATION_DISABLED
depends on !ETH_TEST_LOOPBACK_DISABLED
bool "Disable loopback with autonegotiation test"
default n
help
Disable loopback testing with autonegotiation.
config TARGET_RMII_CLK_OUT
bool "REF RMII CLK output"
default n
config ETH_TEST_FILL_RX_BUFFER_ITERATIONS
int "Number of iterations for fill RX buffer test"
range 1 100
default 10
help
Number of iterations to fill RX buffer of module under test.
For internal EMAC, it should be set as ETH_DMA_RX_BUFFER_NUM.
For SPI modules, it should be set as RX_MEM_SIZE / ETH_MAX_PACKET_SIZE, where RX_MEM_SIZE is
module specific RX buffer size in bytes as defined in datasheet.
if TARGET_RMII_CLK_OUT
config TARGET_RMII_CLK_OUT_GPIO
int "REF RMII CLK Output GPIO"
default 23
config ETH_TEST_LAN8720_ERRATA_ENABLED
bool "Enable LAN8720 errata workaround"
default n
help
Enable workaround for LAN8720 errata.
Enable only when LAN8720 is under test!!
config TARGET_RMII_CLK_IN_GPIO
int "REF RMII CLK Input GPIO"
default 32
endif # TARGET_RMII_CLK_OUT
config ETH_TEST_W5500_IP6_MCAST_DEVIATION_ENABLED
bool "Enable W5500 IPv6 multicast deviation test"
default n
help
W5500 always receives IPv6 multicast packets, even if the filter is set to block multicast.
Enable only when W5500 is under test!!
endif # TARGET_USE_INTERNAL_ETHERNET
if TARGET_USE_SPI_ETHERNET
choice TARGET_ETH_SPI_DEVICE
prompt "Ethernet SPI Module"
default TARGET_ETH_PHY_DEVICE_W5500
help
Select one of the devices listed here
config TARGET_ETH_PHY_DEVICE_W5500
bool "W5500"
select ETH_SPI_ETHERNET_W5500
config TARGET_ETH_PHY_DEVICE_KSZ8851SNL
bool "KSZ8851SNL"
select ETH_SPI_ETHERNET_KSZ8851SNL
config TARGET_ETH_PHY_DEVICE_DM9051
bool "DM9051"
select ETH_SPI_ETHERNET_DM9051
endchoice # TARGET_ETH_SPI_DEVICE
config TARGET_SPI_CLOCK_MHZ
int "SPI clock speed (MHz)"
range 5 80
default 20
help
Set the clock speed (MHz) of SPI interface.
config TARGET_ETH_SPI_POLL_MS
int "SPI Ethernet Polling period in msec"
default 0
help
SPI Ethernet module polling period.
endif # TARGET_USE_SPI_ETHERNET
config ETH_TEST_STRESS_TEST_TASK_PRIO
int "Start/stop stress test task priority"
range -1 25
default -1
help
The Rx Task may occupy all the resources under heavy Rx traffic and it would not be possible
to access the Ethernet module to stop it. Therefore, the test task priority can be set higher than
the Rx task to be able to preempt the Rx task.
This option should be set for SPI Ethernet modules (>15), otherwise set -1 to use default priority.
endmenu
@@ -12,7 +12,7 @@
#include "esp_log.h"
#include "esp_http_client.h"
#include "esp_rom_md5.h"
#include "esp_eth_test_common.h"
#include "esp_eth_test_utils.h"
#include "unity.h"
#define LOOPBACK_TEST_PACKET_SIZE 256
@@ -26,35 +26,19 @@ extern const char dl_espressif_com_root_cert_pem_end[] asm("_binary_dl_espress
static md5_context_t md5_context;
static uint8_t digest[16];
static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_wait)
// Basic test to verify that the Ethernet driver can be initialized and deinitialized
TEST_CASE("ethernet init/deinit test", "[ethernet],[skip_setup_teardown]")
{
int i = 0;
ms_to_wait += 100;
for (i = 0; i < ms_to_wait / 100; i++) {
vTaskDelay(pdMS_TO_TICKS(100));
if (esp_eth_driver_uninstall(eth_hdl) == ESP_OK) {
break;
}
}
if (i < ms_to_wait / 100) {
return ESP_OK;
} else {
return ESP_FAIL;
}
esp_eth_handle_t eth_handle = NULL;
TEST_ESP_OK(esp_eth_test_eth_init(&eth_handle));
TEST_ESP_OK(esp_eth_test_eth_deinit(eth_handle));
}
TEST_CASE("ethernet io test", "[ethernet]")
{
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
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;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
extra_eth_config(eth_handle);
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
/* get default MAC address */
uint8_t mac_addr[ETH_ADDR_LEN];
@@ -65,8 +49,10 @@ TEST_CASE("ethernet io test", "[ethernet]")
TEST_ASSERT(mac_addr[0] != 0);
// *** SPI Ethernet modules deviation ***
// Rationale: The SPI Ethernet modules don't have a burned default factory MAC address hence local MAC is used
#if !CONFIG_TARGET_USE_SPI_ETHERNET
#if CONFIG_ETH_TEST_MAC_ADDR_UI
TEST_ASSERT_BITS(0b00000011, 0b00, mac_addr[0]); // Check UL&IG, should be UI
#else
TEST_ASSERT_BITS(0b00000011, 0b10, mac_addr[0]); // Check UL&IG, should be U
#endif
/* set different MAC address */
@@ -82,114 +68,23 @@ TEST_CASE("ethernet io test", "[ethernet]")
// *** SPI Ethernet modules deviation ***
// Rationale: SPI Ethernet modules PHYs and MACs are statically configured at one die, hence there is no need for PHY address
// from user's point of view
#if !CONFIG_TARGET_USE_SPI_ETHERNET
#if CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED
/* get PHY address */
int phy_addr = -1;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_PHY_ADDR, &phy_addr));
ESP_LOGI(TAG, "Ethernet PHY Address: %d", phy_addr);
TEST_ASSERT(phy_addr >= 0 && phy_addr <= 31);
#endif
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
extra_cleanup();
}
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
esp_err_t set_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts)
{
esp_eth_phy_reg_rw_data_t reg = {
.reg_addr = reg_addr,
.reg_value_p = NULL
};
uint32_t reg_value, reg_value_rb;
for (uint32_t i = 0; i < max_attempts; i++) {
reg.reg_value_p = &reg_value;
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg_value |= bitmask;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg.reg_value_p = &reg_value_rb;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
// Check if the write was successful
if ((reg_value_rb & bitmask) == bitmask) {
return ESP_OK;
}
// Add delay only if not the last attempt
if (i < max_attempts - 1) {
ESP_LOGW(TAG, "Setting PHY register %04"PRIx32" failed, retrying... (attempt %"PRIu32" of %"PRIu32")", reg_addr, i + 1, max_attempts);
vTaskDelay(pdMS_TO_TICKS(10));
}
}
return ESP_ERR_TIMEOUT;
}
esp_err_t clear_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts)
{
esp_eth_phy_reg_rw_data_t reg = {
.reg_addr = reg_addr,
.reg_value_p = NULL
};
uint32_t reg_value, reg_value_rb;
for (uint32_t i = 0; i < max_attempts; i++) {
reg.reg_value_p = &reg_value;
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg_value &= ~bitmask;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg.reg_value_p = &reg_value_rb;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
// Check if the write was successful
if ((reg_value_rb & bitmask) == 0) {
return ESP_OK;
}
// Add delay only if not the last attempt
if (i < max_attempts - 1) {
ESP_LOGW(TAG, "Clearing PHY register %04"PRIx32" failed, retrying... (attempt %"PRIu32" of %"PRIu32")", reg_addr, i + 1, max_attempts);
vTaskDelay(pdMS_TO_TICKS(10));
}
}
return ESP_ERR_TIMEOUT;
}
#endif // CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
// This test expects autonegotiation to be enabled on the other node.
TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
{
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
TEST_ESP_OK(esp_event_loop_create_default());
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
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;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
extra_eth_config(eth_handle);
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
EventBits_t bits = 0;
// this test only test layer2, so don't need to register input callback (i.e. esp_eth_update_input_path)
TEST_ESP_OK(esp_eth_start(eth_handle));
// wait for connection start
@@ -244,8 +139,8 @@ TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
// *** LAN8720 deviation ***
// Rationale: When the device is in manual 100BASE-TX or 10BASE-T modes with Auto-MDIX enabled, the PHY does not link to a
// link partner that is configured for auto-negotiation. See LAN8720 errata for more details.
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
TEST_ESP_OK(set_phy_reg_bits(eth_handle, 27, 0x8000, 3));
#if CONFIG_ETH_TEST_LAN8720_ERRATA_ENABLED
TEST_ESP_OK(eth_test_set_phy_reg_bits(eth_handle, 27, 0x8000, 3));
#endif
// start the driver and wait for connection establish
@@ -330,7 +225,7 @@ TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
// *** LAN8720 deviation ***
// Rationale: See above
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
TEST_ESP_OK(clear_phy_reg_bits(eth_handle, 27, 0x8000, 3));
TEST_ESP_OK(eth_test_clear_phy_reg_bits(eth_handle, 27, 0x8000, 3));
#endif
esp_eth_start(eth_handle);
@@ -352,16 +247,11 @@ TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
/* 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_driver_uninstall(eth_handle));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
}
// use static semaphore to avoid dynamic allocation and so need for de-allocation in case of test failure
static SemaphoreHandle_t loopback_test_case_data_received;
static StaticSemaphore_t loopback_test_case_data_received_buffer;
static esp_err_t loopback_test_case_incoming_handler(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv)
{
TEST_ASSERT(memcmp(priv, buffer, LOOPBACK_TEST_PACKET_SIZE) == 0);
@@ -372,54 +262,43 @@ static esp_err_t loopback_test_case_incoming_handler(esp_eth_handle_t eth_handle
TEST_CASE("ethernet io loopback", "[ethernet]")
{
loopback_test_case_data_received = xSemaphoreCreateBinary();
// init everything else
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
loopback_test_case_data_received = xSemaphoreCreateBinaryStatic(&loopback_test_case_data_received_buffer);
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
TEST_ESP_OK(esp_event_loop_create_default());
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
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;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
extra_eth_config(eth_handle);
// Disable autonegotiation to manually set speed and duplex mode
bool auto_nego_en = false;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
bool loopback_en = true;
// *** W5500 deviation ***
// Rationale: does not support loopback
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_W5500
// *** PHY loopback not supported deviation ***
// Rationale: Some PHYs do not support loopback at all
#if CONFIG_ETH_TEST_LOOPBACK_DISABLED
TEST_ASSERT(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en) == ESP_ERR_NOT_SUPPORTED);
goto cleanup;
return;
#else
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
#endif
eth_duplex_t duplex_modes[] = {ETH_DUPLEX_HALF, ETH_DUPLEX_FULL};
eth_speed_t speeds[] = {ETH_SPEED_100M, ETH_SPEED_10M};
emac_frame_t* test_packet = malloc(LOOPBACK_TEST_PACKET_SIZE);
emac_frame_t* test_packet = (emac_frame_t*)eth_test_alloc(LOOPBACK_TEST_PACKET_SIZE);
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, test_packet->src);
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, test_packet->dest);
for(size_t i = 0; i < LOOPBACK_TEST_PACKET_SIZE-ETH_HEADER_LEN; i++){
test_packet->data[i] = rand() & 0xff;
}
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, loopback_test_case_incoming_handler, test_packet));
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
for (int i = 0; i < sizeof(speeds) / sizeof(eth_speed_t); i++) {
eth_speed_t expected_speed = speeds[i];
for (int j = 0; j < sizeof(duplex_modes) / sizeof(eth_duplex_t); j++) {
eth_duplex_t expected_duplex = duplex_modes[j];
ESP_LOGI(TAG, "Test with %s Mbps %s duplex.", expected_speed == ETH_SPEED_10M ? "10" : "100", expected_duplex == ETH_DUPLEX_HALF ? "half" : "full");
// *** KSZ80XX, KSZ8851SNL and DM9051 deviation ***
// Rationale: do not support loopback at 10 Mbps
#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8041) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_DM9051)
// *** 10 Mbps loopback disabled deviation ***
// Rationale: Some PHYs do not support loopback at 10 Mbps
#if CONFIG_ETH_TEST_10MB_LOOPBACK_DISABLED
if ((expected_speed == ETH_SPEED_10M)) {
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &expected_speed));
continue;
@@ -465,7 +344,11 @@ TEST_CASE("ethernet io loopback", "[ethernet]")
break;
}
}
// *** 10 Mbps loopback ignore failures deviation ***
// Rationale: 10 Mbps loopback may be supported by PHY but the test is not reliable.
#if !CONFIG_ETH_TEST_10MB_LOOPBACK_IGNORE_FAILURES
TEST_ASSERT_LESS_THAN(3, i);
#endif
} else {
TEST_ASSERT(xSemaphoreTake(loopback_test_case_data_received, pdMS_TO_TICKS(1000)) == pdTRUE);
}
@@ -486,12 +369,12 @@ TEST_CASE("ethernet io loopback", "[ethernet]")
// Test with enabled autonegotiaton
ESP_LOGI(TAG, "Test with enabled autonegotiation.");
auto_nego_en = true;
// *** RTL8201, DP83848 and LAN87xx deviation ***
// Rationale: do not support autonegotiation with loopback enabled.
#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_DP83848) || \
defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720)
// *** Loopback with autonegotiation deviation ***
// Rationale: Some PHYs do not support autonegotiation with loopback enabled.
#if CONFIG_ETH_TEST_LOOPBACK_WITH_AUTONEGOTIATION_DISABLED
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
goto cleanup;
// Test passes - these devices correctly report autonegotiation is not supported with loopback
return;
#endif
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
TEST_ESP_OK(esp_eth_start(eth_handle));
@@ -500,45 +383,20 @@ TEST_CASE("ethernet io loopback", "[ethernet]")
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_packet, LOOPBACK_TEST_PACKET_SIZE));
TEST_ASSERT(xSemaphoreTake(loopback_test_case_data_received, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS)) == pdTRUE);
free(test_packet);
loopback_en = false;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
TEST_ESP_OK(esp_eth_stop(eth_handle));
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);
// *** W5500, LAN87xx, RTL8201 and DP83848 deviation ***
// Rationale: in those cases 'goto cleanup' is used to skip part of the test code. Incasing in #if block is done to prevent unused label error
#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_W5500) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720) || \
defined(CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201) || defined(CONFIG_TARGET_ETH_PHY_DEVICE_DP83848)
cleanup:
#endif
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
#ifndef CONFIG_TARGET_ETH_PHY_DEVICE_W5500
// only unregister events if the device != W5500, since w5500 doesn't support loopback and we don't register the event
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
#endif
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
}
TEST_CASE("ethernet event test", "[ethernet]")
{
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
esp_eth_mac_t *mac = mac_init(NULL, NULL);
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;
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
extra_eth_config(eth_handle);
// this test only test layer2 event, so don't need to register input callback (i.e. esp_eth_update_input_path)
TEST_ESP_OK(esp_eth_start(eth_handle));
/* wait for connection start */
@@ -552,41 +410,15 @@ TEST_CASE("ethernet event test", "[ethernet]")
/* 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);
/* driver should be uninstalled within 2 seconds */
TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
}
TEST_CASE("ethernet dhcp test", "[ethernet]")
TEST_CASE("ethernet dhcp test", "[ethernet][esp-netif]")
{
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
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());
// create TCP/IP netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
esp_eth_mac_t *mac = mac_init(NULL, NULL);
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 */
@@ -597,44 +429,15 @@ TEST_CASE("ethernet dhcp test", "[ethernet]")
/* 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(test_uninstall_driver(eth_handle, 2000));
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));
esp_netif_destroy(eth_netif);
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
}
TEST_CASE("ethernet start/stop stress test with IP stack", "[ethernet]")
TEST_CASE("ethernet start/stop stress test with IP stack", "[ethernet][esp-netif]")
{
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
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());
// create TCP/IP netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
esp_eth_mac_t *mac = mac_init(NULL, NULL);
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));
for(int j = 0; j < 2; j++) {
// run the start/stop test with disabled auto-negotiation
@@ -648,8 +451,8 @@ TEST_CASE("ethernet start/stop stress test with IP stack", "[ethernet]")
// *** LAN8720 deviation ***
// Rationale: When the device is in manual 100BASE-TX or 10BASE-T modes with Auto-MDIX enabled, the PHY does not link to a
// link partner that is configured for auto-negotiation. See LAN8720 errata for more details.
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
TEST_ESP_OK(set_phy_reg_bits(eth_handle, 27, 0x8000, 3));
#if CONFIG_ETH_TEST_LAN8720_ERRATA_ENABLED
TEST_ESP_OK(eth_test_set_phy_reg_bits(eth_handle, 27, 0x8000, 3));
#endif
}
for (int i = 0; i < 10; i++) {
@@ -665,18 +468,6 @@ TEST_CASE("ethernet start/stop stress test with IP stack", "[ethernet]")
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(test_uninstall_driver(eth_handle, 2000));
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));
esp_netif_destroy(eth_netif);
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
}
esp_err_t http_event_handle(esp_http_client_event_t *evt)
@@ -726,31 +517,13 @@ static void eth_start_download(void)
esp_rom_md5_final(digest, &md5_context);
}
TEST_CASE("ethernet download test", "[ethernet]")
TEST_CASE("ethernet download test", "[ethernet][esp-netif]")
{
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
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());
// create TCP/IP netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
esp_eth_mac_t *mac = mac_init(NULL, NULL);
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 */
@@ -773,15 +546,4 @@ TEST_CASE("ethernet download test", "[ethernet]")
/* 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(test_uninstall_driver(eth_handle, 2000));
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));
esp_netif_destroy(eth_netif);
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
}
@@ -1,206 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_event.h"
#include "esp_log.h"
#if CONFIG_TARGET_USE_SPI_ETHERNET
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_mac.h"
#endif // CONFIG_TARGET_USE_SPI_ETHERNET
#include "sdkconfig.h"
#include "esp_eth_test_common.h"
#if CONFIG_TARGET_USE_SPI_ETHERNET
#define DEFAULT_TARGET_SPI_HOST 1
#define DEFAULT_TARGET_SPI_MISO_GPIO 12
#define DEFAULT_TARGET_SPI_MOSI_GPIO 13
#define DEFAULT_TARGET_SPI_SCLK_GPIO 14
#define DEFAULT_TARGET_SPI_CS_GPIO 15
#endif // CONFIG_TARGET_USE_SPI_ETHERNET
static const char *TAG = "esp32_eth_test_common";
esp_eth_mac_t *mac_init(void *vendor_emac_config, eth_mac_config_t *mac_config)
{
esp_eth_mac_t *mac = NULL;
eth_mac_config_t mac_config_default = ETH_MAC_DEFAULT_CONFIG();
if (mac_config == NULL) {
mac_config = &mac_config_default;
}
#if CONFIG_TARGET_USE_INTERNAL_ETHERNET
eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
#if !CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG
esp32_emac_config.smi_gpio.mdc_num = CONFIG_TARGET_IO_MDC;
esp32_emac_config.smi_gpio.mdio_num = CONFIG_TARGET_IO_MDIO;
#endif // CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG
#if CONFIG_TARGET_RMII_CLK_OUT
esp32_emac_config.clock_config.rmii.clock_mode = EMAC_CLK_OUT;
esp32_emac_config.clock_config.rmii.clock_gpio = CONFIG_TARGET_RMII_CLK_OUT_GPIO;
esp32_emac_config.clock_config_out_in.rmii.clock_mode = EMAC_CLK_EXT_IN;
esp32_emac_config.clock_config_out_in.rmii.clock_gpio = CONFIG_TARGET_RMII_CLK_IN_GPIO;
#endif // CONFIG_TARGET_TEST_RMII_CLK_OUT
if (vendor_emac_config == NULL) {
vendor_emac_config = &esp32_emac_config;
}
mac = esp_eth_mac_new_esp32(vendor_emac_config, mac_config);
#elif CONFIG_TARGET_USE_SPI_ETHERNET
// Install GPIO ISR handler to be able to service SPI Eth modules interrupts
gpio_install_isr_service(0);
spi_bus_config_t buscfg = {
.miso_io_num = DEFAULT_TARGET_SPI_MISO_GPIO,
.mosi_io_num = DEFAULT_TARGET_SPI_MOSI_GPIO,
.sclk_io_num = DEFAULT_TARGET_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
TEST_ESP_OK(spi_bus_initialize(DEFAULT_TARGET_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
spi_device_interface_config_t devcfg = {
.mode = 0,
.spics_io_num = DEFAULT_TARGET_SPI_CS_GPIO,
.clock_speed_hz = CONFIG_TARGET_SPI_CLOCK_MHZ * 1000 * 1000,
.queue_size = 20
};
#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg);
#if CONFIG_TARGET_ETH_SPI_POLL_MS > 0
w5500_config.int_gpio_num = -1;
w5500_config.poll_period_ms = CONFIG_TARGET_ETH_SPI_POLL_MS;
#endif
if (vendor_emac_config == NULL) {
vendor_emac_config = &w5500_config;
}
mac = esp_eth_mac_new_w5500(vendor_emac_config, mac_config);
#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL
eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg);
#if CONFIG_TARGET_ETH_SPI_POLL_MS > 0
ksz8851snl_config.int_gpio_num = -1;
ksz8851snl_config.poll_period_ms = CONFIG_TARGET_ETH_SPI_POLL_MS;
#endif
if (vendor_emac_config == NULL) {
vendor_emac_config = &ksz8851snl_config;
}
mac = esp_eth_mac_new_ksz8851snl(vendor_emac_config, mac_config);
#elif CONFIG_TARGET_ETH_PHY_DEVICE_DM9051
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg);
#if CONFIG_TARGET_ETH_SPI_POLL_MS > 0
dm9051_config.int_gpio_num = -1;
dm9051_config.poll_period_ms = CONFIG_TARGET_ETH_SPI_POLL_MS;
#endif
if (vendor_emac_config == NULL) {
vendor_emac_config = &dm9051_config ;
}
mac = esp_eth_mac_new_dm9051(vendor_emac_config, mac_config);
#endif // CONFIG_TARGET_ETH_PHY_DEVICE_W5500
#endif // CONFIG_TARGET_USE_INTERNAL_ETHERNET
return mac;
}
esp_eth_phy_t *phy_init(eth_phy_config_t *phy_config)
{
esp_eth_phy_t *phy = NULL;
eth_phy_config_t phy_config_default = ETH_PHY_DEFAULT_CONFIG();
if (phy_config == NULL) {
phy_config = &phy_config_default;
}
#if CONFIG_TARGET_USE_INTERNAL_ETHERNET
phy_config->phy_addr = ESP_ETH_PHY_ADDR_AUTO;
#if CONFIG_TARGET_ETH_PHY_DEVICE_IP101
phy = esp_eth_phy_new_ip101(phy_config);
ESP_LOGI(TAG, "DUT PHY: IP101");
#elif CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
phy = esp_eth_phy_new_lan87xx(phy_config);
ESP_LOGI(TAG, "DUT PHY: LAN8720");
#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8041
phy = esp_eth_phy_new_ksz80xx(phy_config);
ESP_LOGI(TAG, "DUT PHY: KSZ8041");
#elif CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201
phy = esp_eth_phy_new_rtl8201(phy_config);
ESP_LOGI(TAG, "DUT PHY: RTL8201");
#elif CONFIG_TARGET_ETH_PHY_DEVICE_DP83848
phy = esp_eth_phy_new_dp83848(phy_config);
ESP_LOGI(TAG, "DUT PHY: DP83848");
#endif // CONFIG_TARGET_ETH_PHY_DEVICE_IP101
#elif CONFIG_TARGET_USE_SPI_ETHERNET
phy_config->reset_gpio_num = -1;
#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
phy = esp_eth_phy_new_w5500(phy_config);
ESP_LOGI(TAG, "DUT PHY: W5500");
#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL
phy = esp_eth_phy_new_ksz8851snl(phy_config);
ESP_LOGI(TAG, "DUT PHY: KSZ8851SNL");
#elif CONFIG_TARGET_ETH_PHY_DEVICE_DM9051
phy = esp_eth_phy_new_dm9051(phy_config);
ESP_LOGI(TAG, "DUT PHY: DM9051");
#endif // CONFIG_TARGET_ETH_PHY_DEVICE_W5500
#endif // CONFIG_TARGET_USE_INTERNAL_ETHERNET
return phy;
}
void extra_eth_config(esp_eth_handle_t eth_handle)
{
// *** SPI Ethernet modules deviation ***
// Rationale: The SPI Ethernet modules don't have a burned default factory MAC address
#if CONFIG_TARGET_USE_SPI_ETHERNET
uint8_t base_mac_addr[ETH_ADDR_LEN];
TEST_ESP_OK(esp_efuse_mac_get_default(base_mac_addr));
uint8_t local_mac[ETH_ADDR_LEN];
TEST_ESP_OK(esp_derive_local_mac(local_mac, base_mac_addr));
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, local_mac));
#endif
}
void extra_cleanup(void)
{
#if CONFIG_TARGET_USE_SPI_ETHERNET
TEST_ESP_OK(spi_bus_free(DEFAULT_TARGET_SPI_HOST));
#endif
}
/** Event handler for Ethernet events */
void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg;
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
xEventGroupSetBits(eth_event_group, ETH_START_BIT);
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
break;
default:
break;
}
}
/** Event handler for IP_EVENT_ETH_GOT_IP */
void got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
}
@@ -1,42 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include "unity.h"
#include "test_utils.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_eth.h"
#define ETH_START_BIT BIT(0)
#define ETH_STOP_BIT BIT(1)
#define ETH_CONNECT_BIT BIT(2)
#define ETH_GOT_IP_BIT BIT(3)
#define ETH_START_TIMEOUT_MS (10000)
#define ETH_CONNECT_TIMEOUT_MS (40000)
#define ETH_STOP_TIMEOUT_MS (10000)
#define ETH_GET_IP_TIMEOUT_MS (60000)
#define ETH_DOWNLOAD_END_TIMEOUT_MS (240000)
typedef struct {
uint8_t dest[ETH_ADDR_LEN];
uint8_t src[ETH_ADDR_LEN];
uint16_t proto;
uint8_t data[];
} __attribute__((__packed__)) emac_frame_t;
esp_eth_mac_t *mac_init(void *vendor_emac_config, eth_mac_config_t *mac_config);
esp_eth_phy_t *phy_init(eth_phy_config_t *phy_config);
void extra_eth_config(esp_eth_handle_t eth_handle);
void extra_cleanup(void);
/** Event handler for Ethernet events */
void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data);
/** Event handler for IP_EVENT_ETH_GOT_IP */
void got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data);
@@ -1,14 +1,18 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
// This module tests the internal ESP EMAC (Ethernet MAC) only. Tests target EMAC HAL/LL
// behavior and do not depend on a specific PHY, so they need not be run with different PHY combinations.
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_eth_test_common.h"
#include "esp_eth_test_utils.h"
#include "hal/emac_hal.h" // for MAC_HAL_TDES0_* control bits
#define ETHERTYPE_TX_STD 0x2222 // frame transmitted via _transmit_frame
@@ -31,10 +35,14 @@ typedef struct
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;
// Copy to a static buffer to avoid memory leak when TEST_ASSERT fails (`static` to avoid large stack allocation)
static uint8_t buf_copy[ETH_MAX_PACKET_SIZE];
memcpy(buf_copy, buffer, length);
free(buffer);
emac_frame_t *pkt = (emac_frame_t *)buf_copy;
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);
TEST_ASSERT_EQUAL(expected_size, length);
// frame transmitted via _transmit_frame
@@ -70,37 +78,24 @@ static esp_err_t eth_recv_esp_emac_check_cb(esp_eth_handle_t hdl, uint8_t *buffe
} else {
TEST_FAIL();
}
memset(buffer, 0, length);
free(buffer);
xSemaphoreGive(recv_info->mutex);
return ESP_OK;
}
TEST_CASE("internal emac receive/transmit", "[esp_emac]")
{
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
static StaticSemaphore_t recv_info_mutex_buffer;
recv_esp_emac_check_info_t recv_info;
recv_info.mutex = xSemaphoreCreateBinary();
recv_info.mutex = xSemaphoreCreateBinaryStatic(&recv_info_mutex_buffer);
TEST_ASSERT_NOT_NULL(recv_info.mutex);
recv_info.expected_size = 0;
recv_info.expected_size_2 = 0;
recv_info.expected_size_3 = 0;
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
esp_eth_mac_t *mac = mac_init(NULL, NULL);
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
// ---------------------------------------
// Loopback greatly simplifies the test !!
// ---------------------------------------
@@ -112,6 +107,7 @@ TEST_CASE("internal emac receive/transmit", "[esp_emac]")
// start the driver
TEST_ESP_OK(esp_eth_start(eth_handle));
// wait for connection start
EventBits_t bits = 0;
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
// wait for connection establish
@@ -119,7 +115,7 @@ TEST_CASE("internal emac receive/transmit", "[esp_emac]")
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// create test frame
emac_frame_t *test_pkt = calloc(1, ETH_MAX_PACKET_SIZE);
emac_frame_t *test_pkt = (emac_frame_t *)eth_test_alloc(ETH_MAX_PACKET_SIZE);
test_pkt->proto = ETHERTYPE_TX_STD;
memset(test_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
uint8_t local_mac_addr[ETH_ADDR_LEN] = { 0 };
@@ -194,7 +190,7 @@ TEST_CASE("internal emac receive/transmit", "[esp_emac]")
ESP_LOGI(TAG, "-- Verify transmission from multiple buffers --");
uint16_t transmit_size_2;
// allocated the second buffer
uint8_t *pkt_data_2 = malloc(ETH_MAX_PAYLOAD_LEN);
uint8_t *pkt_data_2 = (uint8_t *)eth_test_alloc(ETH_MAX_PAYLOAD_LEN);
// fill with data (reverse order to differentiate the buffers)
int j = ETH_MAX_PAYLOAD_LEN;
for (int i = 0; i < ETH_MAX_PAYLOAD_LEN; i++) {
@@ -242,7 +238,7 @@ TEST_CASE("internal emac receive/transmit", "[esp_emac]")
uint16_t transmit_size_3 = 256;
// allocated the third buffer
uint8_t *pkt_data_3 = malloc(256);
uint8_t *pkt_data_3 = (uint8_t *)eth_test_alloc(256);
// fill with data
for (int i = 0; i < 256; i++) {
pkt_data_3[i] = i & 0xFF;
@@ -279,83 +275,11 @@ TEST_CASE("internal emac receive/transmit", "[esp_emac]")
TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 3, test_pkt, transmit_size, pkt_data_2, transmit_size_2, pkt_data_3, transmit_size_3));
TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
free(test_pkt);
free(pkt_data_2);
free(pkt_data_3);
// stop Ethernet driver
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_driver_uninstall(eth_handle));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
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);
}
#define TEST_FRAMES_NUM CONFIG_ETH_DMA_RX_BUFFER_NUM
@@ -374,25 +298,14 @@ static esp_err_t eth_recv_esp_emac_err_check_cb(esp_eth_handle_t hdl, uint8_t *b
TEST_CASE("internal emac erroneous frames", "[esp_emac]")
{
SemaphoreHandle_t mutex = xSemaphoreCreateBinary();
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
static StaticSemaphore_t mutex_buffer;
SemaphoreHandle_t mutex = xSemaphoreCreateBinaryStatic(&mutex_buffer);
TEST_ASSERT_NOT_NULL(mutex);
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
TEST_ESP_OK(esp_event_loop_create_default());
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
esp_eth_mac_t *mac = mac_init(NULL, NULL);
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
// loopback greatly simplifies the test
bool loopback_en = true;
esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
@@ -402,6 +315,7 @@ TEST_CASE("internal emac erroneous frames", "[esp_emac]")
// start the driver
TEST_ESP_OK(esp_eth_start(eth_handle));
// wait for connection start
EventBits_t bits = 0;
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
// wait for connection establish
@@ -409,7 +323,7 @@ TEST_CASE("internal emac erroneous frames", "[esp_emac]")
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// create test frame
emac_frame_t *test_pkt = calloc(1, ETH_MAX_PACKET_SIZE);
emac_frame_t *test_pkt = (emac_frame_t *)eth_test_alloc(ETH_MAX_PACKET_SIZE);
test_pkt->proto = ETHERTYPE_TX_STD;
memset(test_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
uint8_t local_mac_addr[ETH_ADDR_LEN] = { 0 };
@@ -441,7 +355,7 @@ TEST_CASE("internal emac erroneous frames", "[esp_emac]")
for (i = 0; i < s_recv_frames_cnt; i++) {
emac_frame_t *recv_frame = (emac_frame_t *)s_recv_frames[i];
ESP_LOGI(TAG, "recv frame id %" PRIu8, recv_frame->data[0]);
free(recv_frame);
free(recv_frame); // free buffer allocated by emac driver
}
TEST_ASSERT_EQUAL_UINT8(TEST_FRAMES_NUM, s_recv_frames_cnt);
s_recv_frames_cnt = 0;
@@ -467,7 +381,7 @@ TEST_CASE("internal emac erroneous frames", "[esp_emac]")
for (i = 0; i < s_recv_frames_cnt; i++) {
emac_frame_t *recv_frame = (emac_frame_t *)s_recv_frames[i];
ESP_LOGI(TAG, "recv frame id %" PRIu8, recv_frame->data[0]);
free(recv_frame);
free(recv_frame); // free buffer allocated by emac driver
}
TEST_ASSERT_EQUAL_UINT8(TEST_FRAMES_NUM / 2, s_recv_frames_cnt);
s_recv_frames_cnt = 0;
@@ -495,7 +409,7 @@ TEST_CASE("internal emac erroneous frames", "[esp_emac]")
for (i = 0; i < s_recv_frames_cnt; i++) {
emac_frame_t *recv_frame = (emac_frame_t *)s_recv_frames[i];
ESP_LOGI(TAG, "recv frame id %" PRIu8, recv_frame->data[0]);
free(recv_frame);
free(recv_frame); // free buffer allocated by emac driver
}
TEST_ASSERT_EQUAL_INT(CONFIG_ETH_DMA_RX_BUFFER_NUM - 1, s_recv_frames_cnt); // one frame is missing due to "Descriptor Error"
s_recv_frames_cnt = 0;
@@ -505,33 +419,75 @@ TEST_CASE("internal emac erroneous frames", "[esp_emac]")
// 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);
free(test_pkt);
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(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_event_loop_delete_default());
extra_cleanup();
vEventGroupDelete(eth_event_group);
vSemaphoreDelete(mutex);
}
TEST_CASE("internal emac ref rmii clk out", "[esp_emac_clk_out]")
{
esp_eth_mac_t *mac = mac_init(NULL, NULL);
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
// Ethernet driver is initialized and installed in the setUp() function
// This is a simple test and it only verifies the REF RMII output CLK is configured and started, and the internal
// EMAC is clocked by it. It does not verify the whole system functionality. As such, it can be executed on the same
// test boards which are configured to input REF RMII CLK by default with only minor HW modification.
TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
extra_eth_config(eth_handle);
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
extra_cleanup();
}
// 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.
TEST_CASE("internal emac interrupt priority", "[esp_emac][skip_setup_teardown]")
{
EventBits_t bits = 0;
EventGroupHandle_t eth_event_group = xEventGroupCreate();
TEST_ASSERT(eth_event_group != NULL);
esp_netif_init();
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);
// Init common MAC and PHY configs to default
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
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 = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
if (i >= 4) {
TEST_ASSERT_NULL(mac);
} else {
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = esp_eth_phy_new_generic(&phy_config);
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));
// 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 handlers
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_test_default_event_handler, eth_event_group));
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &eth_test_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, eth_test_got_ip_event_handler));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_test_default_event_handler));
}
esp_netif_destroy(eth_netif);
}
TEST_ESP_OK(esp_event_loop_delete_default());
vEventGroupDelete(eth_event_group);
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -8,7 +8,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_eth_test_common.h"
#include "esp_eth_test_utils.h"
#include "arpa/inet.h" // for ntohs, etc.
#include "esp_log.h"
@@ -28,10 +28,6 @@
#define POKE_RESP (0xFB)
#define DUMMY_TRAFFIC (0xFF)
#define W5500_RX_MEM_SIZE (0x4000)
#define DM9051_RX_MEM_SIZE (0x4000)
#define KSZ8851SNL_RX_MEM_SIZE (0x3000)
static const char *TAG = "esp32_eth_test_l2";
typedef struct
{
@@ -110,9 +106,13 @@ void poke_and_wait(esp_eth_handle_t eth_handle, void *data, uint16_t size, uint8
uint32_t tmo;
uint32_t i;
esp_err_t tx_err = ESP_OK;
for(tmo = 0, i = 1; tmo < WAIT_AFTER_CONN_TMO_MS; tmo += WAIT_AFTER_CONN_MS, i++) {
printf("Poke attempt #%" PRIu32 "\n", i);
TEST_ESP_OK(esp_eth_transmit(eth_handle, ctrl_pkt, 60));
tx_err = esp_eth_transmit(eth_handle, ctrl_pkt, 60);
if (tx_err != ESP_OK) {
break;
}
EventBits_t bits = xEventGroupWaitBits(eth_event_group, ETH_POKE_RESP_RECV_BIT,
true, true, pdMS_TO_TICKS(WAIT_AFTER_CONN_MS));
if ((bits & ETH_POKE_RESP_RECV_BIT) == ETH_POKE_RESP_RECV_BIT) {
@@ -122,271 +122,21 @@ void poke_and_wait(esp_eth_handle_t eth_handle, void *data, uint16_t size, uint8
break;
}
}
TEST_ASSERT(tmo < WAIT_AFTER_CONN_TMO_MS);
free(ctrl_pkt);
// assert only after the allocated memory is freed
TEST_ASSERT(tmo < WAIT_AFTER_CONN_TMO_MS);
TEST_ASSERT_EQUAL_INT_MESSAGE(ESP_OK, tx_err, "esp_eth_transmit failed");
}
TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]")
{
esp_eth_mac_t *mac = mac_init(NULL, NULL);
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
// assign values to variables from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
TEST_ESP_OK(esp_event_loop_create_default());
EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
TEST_ASSERT(eth_event_state_group != NULL);
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_state_group));
EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
TEST_ASSERT(eth_event_rx_group != NULL);
s_recv_info.eth_event_group = eth_event_rx_group;
s_recv_info.check_rx_data = false;
s_recv_info.unicast_rx_cnt = 0;
s_recv_info.multicast_rx_cnt = 0;
s_recv_info.brdcast_rx_cnt = 0;
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
// test app will parse the DUT MAC from this line of log output
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
EventBits_t bits = 0;
bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
// starts switching the associated port (e.g. it runs RSTP at first)
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
emac_frame_t *pkt = malloc(1024);
pkt->proto = htons(TEST_ETH_TYPE);
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, pkt->src));
memset(pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
for (int i = 0; i < (1024 - ETH_HEADER_LEN); ++i){
pkt->data[i] = i & 0xff;
}
TEST_ESP_OK(esp_eth_transmit(eth_handle, pkt, 1024));
// give it some time to complete transmit
vTaskDelay(pdMS_TO_TICKS(500));
free(pkt);
TEST_ESP_OK(esp_eth_stop(eth_handle));
TEST_ESP_OK(esp_event_loop_delete_default());
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
phy->del(phy);
mac->del(mac);
extra_cleanup();
vEventGroupDelete(eth_event_rx_group);
vEventGroupDelete(eth_event_state_group);
}
TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
{
esp_eth_mac_t *mac = mac_init(NULL, NULL);
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
TEST_ESP_OK(esp_event_loop_create_default());
EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
TEST_ASSERT(eth_event_state_group != NULL);
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_state_group));
EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
TEST_ASSERT(eth_event_rx_group != NULL);
s_recv_info.eth_event_group = eth_event_rx_group;
s_recv_info.check_rx_data = true;
s_recv_info.unicast_rx_cnt = 0;
s_recv_info.multicast_rx_cnt = 0;
s_recv_info.brdcast_rx_cnt = 0;
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
// test app will parse the DUT MAC from this line of log output
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
EventBits_t bits = 0;
bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
// starts switching the associated port (e.g. it runs RSTP at first)
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
bits = 0;
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
true, true, pdMS_TO_TICKS(5000));
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) ==
(ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
TEST_ESP_OK(esp_eth_stop(eth_handle));
TEST_ESP_OK(esp_event_loop_delete_default());
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
phy->del(phy);
mac->del(mac);
extra_cleanup();
vEventGroupDelete(eth_event_state_group);
vEventGroupDelete(eth_event_rx_group);
}
TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]")
{
// *** SPI Ethernet modules deviation ***
// Rationale: The SPI bus is bottleneck when reading received frames from the module. The Rx Task would
// occupy all the resources under heavy Rx traffic and it would not be possible to access
// the Ethernet module to stop it. Therfore, the Rx task priority is set lower than "test" task
// to be able to be preempted.
#if CONFIG_TARGET_USE_SPI_ETHERNET
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.rx_task_prio = uxTaskPriorityGet(NULL) - 1;
esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
#else
esp_eth_mac_t *mac = mac_init(NULL, NULL);
#endif // CONFIG_TARGET_USE_SPI_ETHERNET
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
TEST_ESP_OK(esp_event_loop_create_default());
EventBits_t bits = 0;
EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
TEST_ASSERT(eth_event_state_group != NULL);
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_state_group));
EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
TEST_ASSERT(eth_event_rx_group != NULL);
s_recv_info.eth_event_group = eth_event_rx_group;
s_recv_info.check_rx_data = false;
s_recv_info.unicast_rx_cnt = 0;
s_recv_info.multicast_rx_cnt = 0;
s_recv_info.brdcast_rx_cnt = 0;
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
uint8_t dest_mac_addr[ETH_ADDR_LEN] = {};
TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
// test app will parse the DUT MAC from this line of log output
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
// create dummy data packet used for traffic generation
emac_frame_t *pkt = calloc(1, 1500);
pkt->proto = htons(TEST_ETH_TYPE);
memcpy(pkt->src, local_mac_addr, ETH_ADDR_LEN);
printf("EMAC start/stop stress test under heavy Tx traffic\n");
for (int tx_i = 0; tx_i < 10; tx_i++) {
printf("Tx Test iteration %d\n", tx_i);
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// at first, check that Tx/Rx path works as expected by poking the test script
// this also serves as main PASS/FAIL criteria
poke_and_wait(eth_handle, &tx_i, sizeof(tx_i), dest_mac_addr, eth_event_rx_group);
memcpy(pkt->dest, dest_mac_addr, ETH_ADDR_LEN);
// *** SPI Ethernet modules deviation ***
// Rationale: Transmit errors are expected only for internal EMAC since it is possible to try to queue more
// data than it is able to process at a time.
#if !CONFIG_TARGET_USE_SPI_ETHERNET
printf("Note: transmit errors are expected...\n");
#endif
// generate heavy Tx traffic
for (int j = 0; j < 150; j++) {
// return value is not checked on purpose since it is expected that it may fail time to time because
// we may try to queue more packets than hardware is able to handle
pkt->data[2] = j & 0xFF; // sequence number
esp_eth_transmit(eth_handle, pkt, 1500);
}
TEST_ESP_OK(esp_eth_stop(eth_handle));
bits = xEventGroupWaitBits(eth_event_state_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
}
printf("EMAC start/stop stress test under heavy Rx traffic\n");
for (int rx_i = 0; rx_i < 10; rx_i++) {
printf("Rx Test iteration %d\n", rx_i);
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
poke_and_wait(eth_handle, &rx_i, sizeof(rx_i), NULL, eth_event_rx_group);
// wait for dummy traffic
xEventGroupClearBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT);
s_recv_info.unicast_rx_cnt = 0;
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT, true, true, pdMS_TO_TICKS(3000));
TEST_ASSERT((bits & ETH_UNICAST_RECV_BIT) == ETH_UNICAST_RECV_BIT);
vTaskDelay(pdMS_TO_TICKS(500));
TEST_ESP_OK(esp_eth_stop(eth_handle));
bits = xEventGroupWaitBits(eth_event_state_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
printf("Recv packets: %d\n", s_recv_info.unicast_rx_cnt);
TEST_ASSERT_GREATER_THAN_INT32(0, s_recv_info.unicast_rx_cnt);
}
free(pkt);
// Add an extra delay to be sure that there is no traffic generated by the test script during the driver un-installation.
// It was observed unintended behavior of the switch used in test environment when link is set down under heavy load.
vTaskDelay(pdMS_TO_TICKS(500));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_event_loop_delete_default());
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
phy->del(phy);
mac->del(mac);
extra_cleanup();
vEventGroupDelete(eth_event_rx_group);
vEventGroupDelete(eth_event_state_group);
}
#define MAX_HEAP_ALLOCATION_POINTERS (20)
TEST_CASE("heap utilization", "[ethernet_l2]")
{
esp_eth_mac_t *mac = mac_init(NULL, NULL);
TEST_ASSERT_NOT_NULL(mac);
esp_eth_phy_t *phy = phy_init(NULL);
TEST_ASSERT_NOT_NULL(phy);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
extra_eth_config(eth_handle);
TEST_ESP_OK(esp_event_loop_create_default());
EventBits_t bits = 0;
EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
TEST_ASSERT(eth_event_state_group != NULL);
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_state_group));
EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
// use static event group to avoid dynamic memory allocation
StaticEventGroup_t eth_event_rx_group_buffer;
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(&eth_event_rx_group_buffer);
TEST_ASSERT(eth_event_rx_group != NULL);
s_recv_info.eth_event_group = eth_event_rx_group;
@@ -402,28 +152,216 @@ TEST_CASE("heap utilization", "[ethernet_l2]")
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
// *** W5500 deviation ***
// Rationale: W5500 SPI Ethernet module does not support internal loopback
#if !CONFIG_TARGET_ETH_PHY_DEVICE_W5500
EventBits_t bits = 0;
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
// starts switching the associated port (e.g. it runs RSTP at first)
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
emac_frame_t *pkt = (emac_frame_t *)eth_test_alloc(1024);
pkt->proto = htons(TEST_ETH_TYPE);
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, pkt->src));
memset(pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
for (int i = 0; i < (1024 - ETH_HEADER_LEN); ++i){
pkt->data[i] = i & 0xff;
}
TEST_ESP_OK(esp_eth_transmit(eth_handle, pkt, 1024));
// give it some time to complete transmit
vTaskDelay(pdMS_TO_TICKS(500));
TEST_ESP_OK(esp_eth_stop(eth_handle));
}
TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
{
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
// use static event group to avoid dynamic memory allocation
StaticEventGroup_t eth_event_rx_group_buffer;
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(&eth_event_rx_group_buffer);
TEST_ASSERT(eth_event_rx_group != NULL);
s_recv_info.eth_event_group = eth_event_rx_group;
s_recv_info.check_rx_data = true;
s_recv_info.unicast_rx_cnt = 0;
s_recv_info.multicast_rx_cnt = 0;
s_recv_info.brdcast_rx_cnt = 0;
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
// test app will parse the DUT MAC from this line of log output
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
EventBits_t bits = 0;
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
// starts switching the associated port (e.g. it runs RSTP at first)
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
bits = 0;
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
true, true, pdMS_TO_TICKS(5000));
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) ==
(ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
TEST_ESP_OK(esp_eth_stop(eth_handle));
}
TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]")
{
// *** SPI Ethernet modules deviation ***
// Rationale: The SPI bus is bottleneck when reading received frames from the module. The Rx Task would
// occupy all the resources under heavy Rx traffic and it would not be possible to access
// the Ethernet module to stop it. Therefore, the test task priority is set higher than the Rx task
// to be able to preempt the Rx task.
#if CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO > -1
printf("task priority: %d\n", uxTaskPriorityGet(NULL));
vTaskPrioritySet(NULL, CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO);
#endif // CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO > 0
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
// use static event group to avoid dynamic memory allocation
StaticEventGroup_t eth_event_rx_group_buffer;
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(&eth_event_rx_group_buffer);
TEST_ASSERT(eth_event_rx_group != NULL);
s_recv_info.eth_event_group = eth_event_rx_group;
s_recv_info.check_rx_data = false;
s_recv_info.unicast_rx_cnt = 0;
s_recv_info.multicast_rx_cnt = 0;
s_recv_info.brdcast_rx_cnt = 0;
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
uint8_t dest_mac_addr[ETH_ADDR_LEN] = {};
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
// test app will parse the DUT MAC from this line of log output
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
// create dummy data packet used for traffic generation
emac_frame_t *pkt = (emac_frame_t *)eth_test_alloc(1500);
pkt->proto = htons(TEST_ETH_TYPE);
memcpy(pkt->src, local_mac_addr, ETH_ADDR_LEN);
EventBits_t bits = 0;
printf("EMAC start/stop stress test under heavy Tx traffic\n");
for (int tx_i = 0; tx_i < 10; tx_i++) {
printf("Tx Test iteration %d\n", tx_i);
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// at first, check that Tx/Rx path works as expected by poking the test script
// this also serves as main PASS/FAIL criteria
poke_and_wait(eth_handle, &tx_i, sizeof(tx_i), dest_mac_addr, eth_event_rx_group);
memcpy(pkt->dest, dest_mac_addr, ETH_ADDR_LEN);
// generate heavy Tx traffic
for (int j = 0; j < 150; j++) {
// return value is not checked on purpose since it is expected that it may fail time to time because
// we may try to queue more packets than hardware is able to handle
pkt->data[2] = j & 0xFF; // sequence number
esp_eth_transmit(eth_handle, pkt, 1500);
}
TEST_ESP_OK(esp_eth_stop(eth_handle));
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
}
printf("EMAC start/stop stress test under heavy Rx traffic\n");
for (int rx_i = 0; rx_i < 10; rx_i++) {
printf("Rx Test iteration %d\n", rx_i);
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
poke_and_wait(eth_handle, &rx_i, sizeof(rx_i), NULL, eth_event_rx_group);
// wait for dummy traffic
xEventGroupClearBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT);
s_recv_info.unicast_rx_cnt = 0;
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT, true, true, pdMS_TO_TICKS(3000));
TEST_ASSERT((bits & ETH_UNICAST_RECV_BIT) == ETH_UNICAST_RECV_BIT);
vTaskDelay(pdMS_TO_TICKS(500));
TEST_ESP_OK(esp_eth_stop(eth_handle));
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
printf("Recv packets: %d\n", s_recv_info.unicast_rx_cnt);
TEST_ASSERT_GREATER_THAN_INT32(0, s_recv_info.unicast_rx_cnt);
}
// Add an extra delay to be sure that there is no traffic generated by the test script during the driver un-installation.
// It was observed unintended behavior of the switch used in test environment when link is set down under heavy load.
vTaskDelay(pdMS_TO_TICKS(500));
}
TEST_CASE("heap utilization", "[ethernet_l2]")
{
// get handles from common module initialized by setUp()
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
// use static event group to avoid dynamic memory allocation
StaticEventGroup_t eth_event_rx_group_buffer;
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(&eth_event_rx_group_buffer);
TEST_ASSERT(eth_event_rx_group != NULL);
s_recv_info.eth_event_group = eth_event_rx_group;
s_recv_info.check_rx_data = false;
s_recv_info.unicast_rx_cnt = 0;
s_recv_info.multicast_rx_cnt = 0;
s_recv_info.brdcast_rx_cnt = 0;
// *** PHY loopback not supported deviation ***
// Rationale: Some Ethernet modules do not support internal loopback
#if !CONFIG_ETH_TEST_LOOPBACK_DISABLED
// ---------------------------------------
// Loopback greatly simplifies the test !!
// ---------------------------------------
bool loopback_en = true;
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
printf("PHY loopback is enabled\n");
#else
printf("PHY loopback is disabled\n");
#endif
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
// test app will parse the DUT MAC from this line of log output
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
// start the driver
TEST_ESP_OK(esp_eth_start(eth_handle));
// wait for connection start
bits = xEventGroupWaitBits(eth_event_state_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
EventBits_t bits = 0;
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
// wait for connection establish
bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
// create test frame
emac_frame_t *test_pkt = calloc(1, ETH_MAX_PACKET_SIZE);
uint8_t test_pkt_buffer[ETH_MAX_PACKET_SIZE];
emac_frame_t *test_pkt = (emac_frame_t *)test_pkt_buffer;
test_pkt->proto = htons(TEST_ETH_TYPE);
memcpy(test_pkt->dest, local_mac_addr, ETH_ADDR_LEN); // our addr so the frame is not filtered at loopback by MAC
memcpy(test_pkt->src, local_mac_addr, ETH_ADDR_LEN);
@@ -432,9 +370,9 @@ TEST_CASE("heap utilization", "[ethernet_l2]")
test_pkt->data[i] = i & 0xFF;
}
// *** W5500 deviation ***
// Rationale: W5500 SPI Ethernet module does not support internal loopback so we need to loop frames back at test PC side
#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
// *** PHY loopback not supported deviation ***
// Rationale: Some Ethernet modules do not support internal loopback so we need to loop frames back at test PC side
#if CONFIG_ETH_TEST_LOOPBACK_DISABLED
uint8_t dest_mac_addr[ETH_ADDR_LEN] = {};
poke_and_wait(eth_handle, NULL, 0, dest_mac_addr, eth_event_rx_group);
memcpy(test_pkt->dest, dest_mac_addr, ETH_ADDR_LEN); // overwrite destination address with test PC addr
@@ -442,36 +380,20 @@ TEST_CASE("heap utilization", "[ethernet_l2]")
uint16_t transmit_size;
size_t free_heap = 0;
uint8_t *memory_p[MAX_HEAP_ALLOCATION_POINTERS] = { 0 };
int32_t mem_block;
uint8_t *p;
ESP_LOGI(TAG, "Allocate all heap");
for (mem_block = 0; mem_block < MAX_HEAP_ALLOCATION_POINTERS; mem_block++) {
do {
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
ESP_LOGD(TAG, "free heap: %i B", free_heap);
memory_p[mem_block] = malloc(free_heap);
if (free_heap < 1024) {
break;
}
}
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
p = eth_test_alloc(free_heap);
} while (p != NULL && free_heap > 1024);
ESP_LOGI(TAG, "remaining free heap: %i B", free_heap);
TEST_ASSERT_LESS_OR_EQUAL_INT(1024, free_heap);
transmit_size = ETH_MAX_PAYLOAD_LEN;
ESP_LOGI(TAG, "Verify that the driver is able to recover from `no mem` error");
// define number of iteration to fill device internal buffer (if driver's flush function didn't work as expected)
int32_t max_i = 10; // default value will be overwritten by module specific value
// *** Ethernet modules deviation ***
// Rationale: Each Ethernet module has different size of Rx buffer
#if CONFIG_TARGET_USE_INTERNAL_ETHERNET
max_i = CONFIG_ETH_DMA_RX_BUFFER_NUM + 2;
#elif CONFIG_TARGET_ETH_PHY_DEVICE_W5500
max_i = W5500_RX_MEM_SIZE / ETH_MAX_PACKET_SIZE + 2;
#elif CONFIG_TARGET_ETH_PHY_DEVICE_DM9051
max_i = DM9051_RX_MEM_SIZE / ETH_MAX_PACKET_SIZE + 2;
#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL
max_i = KSZ8851SNL_RX_MEM_SIZE / ETH_MAX_PACKET_SIZE + 2;
#endif
int32_t max_i = CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS + 2;
for (int32_t i = 0; i < max_i; i++) { // be sure to fill all the descriptors
ESP_LOGI(TAG, "transmit frame size: %" PRIu16 ", i = %" PRIi32, transmit_size, i);
@@ -483,10 +405,7 @@ TEST_CASE("heap utilization", "[ethernet_l2]")
TEST_ASSERT(bits == 0); // we don't received the frame due to "no mem"
}
ESP_LOGI(TAG, "Free previously allocated heap");
while(mem_block > 0) {
free(memory_p[mem_block]);
mem_block--;
}
eth_test_free_all();
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
ESP_LOGI(TAG, "free heap: %i B", free_heap);
for (int32_t i = 0; i < max_i; i++) {
@@ -498,16 +417,7 @@ TEST_CASE("heap utilization", "[ethernet_l2]")
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT, true, true, pdMS_TO_TICKS(200));
TEST_ASSERT((bits & ETH_UNICAST_RECV_BIT) == ETH_UNICAST_RECV_BIT); // now, we should be able to receive frames again
}
free(test_pkt);
TEST_ESP_OK(esp_eth_stop(eth_handle));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_event_loop_delete_default());
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
phy->del(phy);
mac->del(mac);
extra_cleanup();
vEventGroupDelete(eth_event_rx_group);
vEventGroupDelete(eth_event_state_group);
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
}
@@ -1,11 +1,19 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "test_utils.h"
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
void app_main(void)
void test_task(void *pvParameters)
{
unity_run_menu();
}
void app_main(void)
{
xTaskCreatePinnedToCore(test_task, "testTask", CONFIG_ETH_TEST_UNITY_TEST_TASK_STACK, NULL, CONFIG_ETH_TEST_UNITY_TEST_TASK_PRIO, NULL, tskNO_AFFINITY);
}
@@ -0,0 +1,322 @@
/*
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include <inttypes.h>
#include "esp_eth_driver.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_event.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_eth_test_utils.h"
#include "esp_check.h"
#include "ethernet_init.h"
// Local override of TEST_ASSERT and TEST_ESP_OK to fix Unity file name reporting
// when assertions are in a different file than the test. This temporarily sets Unity.
// TestFile to __FILE__ so the correct file name is reported on assertion failure.
#define ETH_TEST_ASSERT(condition) do { \
const char* _unity_test_file_save = Unity.TestFile; \
Unity.TestFile = __FILE__; \
TEST_ASSERT(condition); \
Unity.TestFile = _unity_test_file_save; \
} while(0)
#define ETH_TEST_ESP_OK(rc) do { \
const char* _unity_test_file_save = Unity.TestFile; \
Unity.TestFile = __FILE__; \
TEST_ASSERT_EQUAL_HEX32(ESP_OK, rc); \
Unity.TestFile = _unity_test_file_save; \
} while(0)
static const char *TAG = "esp32_eth_test_common";
static esp_eth_handle_t *s_eth_handles; // only because we use Ethernet Init component
static EventGroupHandle_t s_eth_event_group;
static esp_eth_handle_t s_eth_handle;
static esp_netif_t *s_eth_netif;
static esp_eth_netif_glue_handle_t s_eth_glue;
static void *s_memory_p[MAX_HEAP_ALLOCATION_POINTERS];
esp_err_t eth_test_set_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts)
{
esp_eth_phy_reg_rw_data_t reg = {
.reg_addr = reg_addr,
.reg_value_p = NULL
};
uint32_t reg_value, reg_value_rb;
for (uint32_t i = 0; i < max_attempts; i++) {
reg.reg_value_p = &reg_value;
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg_value |= bitmask;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg.reg_value_p = &reg_value_rb;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
// Check if the write was successful
if ((reg_value_rb & bitmask) == bitmask) {
return ESP_OK;
}
// Add delay only if not the last attempt
if (i < max_attempts - 1) {
ESP_LOGW(TAG, "Setting PHY register %04"PRIx32" failed, retrying... (attempt %"PRIu32" of %"PRIu32")", reg_addr, i + 1, max_attempts);
vTaskDelay(pdMS_TO_TICKS(10));
}
}
return ESP_ERR_TIMEOUT;
}
esp_err_t eth_test_clear_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts)
{
esp_eth_phy_reg_rw_data_t reg = {
.reg_addr = reg_addr,
.reg_value_p = NULL
};
uint32_t reg_value, reg_value_rb;
for (uint32_t i = 0; i < max_attempts; i++) {
reg.reg_value_p = &reg_value;
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg_value &= ~bitmask;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
reg.reg_value_p = &reg_value_rb;
ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (ret != ESP_OK) {
return ret;
}
// Check if the write was successful
if ((reg_value_rb & bitmask) == 0) {
return ESP_OK;
}
// Add delay only if not the last attempt
if (i < max_attempts - 1) {
ESP_LOGW(TAG, "Clearing PHY register %04"PRIx32" failed, retrying... (attempt %"PRIu32" of %"PRIu32")", reg_addr, i + 1, max_attempts);
vTaskDelay(pdMS_TO_TICKS(10));
}
}
return ESP_ERR_TIMEOUT;
}
__attribute__((weak)) esp_err_t esp_eth_test_eth_init(esp_eth_handle_t *eth_handle)
{
esp_err_t ret = ESP_OK;
uint8_t eth_port_cnt = 0;
ESP_LOGI(TAG, "Default Ethernet Initialization...");
ESP_GOTO_ON_ERROR(ethernet_init_all(&s_eth_handles, &eth_port_cnt), err, TAG, "Failed to initialize Ethernet");
ESP_GOTO_ON_FALSE(eth_port_cnt == 1, ESP_FAIL, err, TAG, "Multiple Ethernet devices detected, the test cannot continue...");
*eth_handle = s_eth_handles[0];
err:
return ret;
}
__attribute__((weak)) esp_err_t esp_eth_test_eth_deinit(esp_eth_handle_t eth_handle)
{
esp_err_t ret = ESP_OK;
ESP_LOGI(TAG, "Default Ethernet Deinitialization...");
ESP_GOTO_ON_FALSE(s_eth_handles[0] == eth_handle, ESP_FAIL, err, TAG, "Ethernet handle does not match");
ESP_GOTO_ON_ERROR(ethernet_deinit_all(s_eth_handles), err, TAG, "Failed to deinitialize Ethernet");
err:
return ret;
}
void setUp(void)
{
if (strstr(Unity.CurrentDetail1, "[skip_setup_teardown]") != NULL) {
return;
}
// Use Unity to check if the initialization is successful, it's intended to fail the test if initialization fails
s_eth_event_group = xEventGroupCreate();
ETH_TEST_ASSERT(s_eth_event_group != NULL);
ETH_TEST_ESP_OK(esp_event_loop_create_default());
ETH_TEST_ESP_OK(esp_eth_test_eth_init(&s_eth_handle));
eth_dev_info_t phy_info = ethernet_init_get_dev_info(s_eth_handle);
ESP_LOGI(TAG, "DUT PHY: %s", phy_info.name);
if (strstr(Unity.CurrentDetail1, "[esp-netif]") != NULL) {
esp_netif_init();
// create TCP/IP netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
s_eth_netif = esp_netif_new(&netif_cfg);
// combine driver with netif
s_eth_glue = esp_eth_new_netif_glue(s_eth_handle);
ETH_TEST_ESP_OK(esp_netif_attach(s_eth_netif, s_eth_glue));
// register user defined event handlers
ETH_TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &eth_test_got_ip_event_handler, s_eth_event_group));
}
ETH_TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_test_default_event_handler, s_eth_event_group));
}
void tearDown(void)
{
eth_test_free_all();
if (strstr(Unity.CurrentDetail1, "[skip_setup_teardown]") != NULL) {
return;
}
// Use regular error checking instead of ETH_TEST_ESP_OK to ensure cleanup always completes
// even if errors occur (e.g., if test failed and resources are in invalid state)
// This is critical because TEST_ASSERT failures use longjmp which can skip cleanup
esp_err_t ret;
if (s_eth_glue != NULL) {
ret = esp_eth_del_netif_glue(s_eth_glue);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "esp_eth_del_netif_glue failed: %s", esp_err_to_name(ret));
}
s_eth_glue = NULL;
}
if (s_eth_netif != NULL) {
esp_netif_destroy(s_eth_netif);
s_eth_netif = NULL;
ret = esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_test_got_ip_event_handler);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "esp_event_handler_unregister IP event failed: %s", esp_err_to_name(ret));
}
}
ret = esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_test_default_event_handler);
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
// ESP_ERR_INVALID_STATE is OK - handler might already be unregistered
ESP_LOGW(TAG, "esp_event_handler_unregister failed: %s", esp_err_to_name(ret));
}
if (s_eth_handle != NULL) {
ret = esp_eth_test_eth_deinit(s_eth_handle);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "esp_eth_test_eth_deinit failed: %s", esp_err_to_name(ret));
ESP_LOGI(TAG, "Trying to stop Ethernet driver and deinitialize it again...");
ret = esp_eth_stop(s_eth_handle);
vTaskDelay(pdMS_TO_TICKS(500));
if (ret != ESP_OK) {
ESP_LOGW(TAG, "esp_eth_stop failed: %s", esp_err_to_name(ret));
}
// try to deinitialize Ethernet driver again
ret = esp_eth_test_eth_deinit(s_eth_handle);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "esp_eth_test_eth_deinit failed: %s", esp_err_to_name(ret));
}
}
s_eth_handle = NULL;
}
ret = esp_event_loop_delete_default();
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
// ESP_ERR_INVALID_STATE is OK - loop might already be deleted
ESP_LOGW(TAG, "esp_event_loop_delete_default failed: %s", esp_err_to_name(ret));
}
if (s_eth_event_group != NULL) {
vEventGroupDelete(s_eth_event_group);
s_eth_event_group = NULL;
}
}
esp_eth_handle_t eth_test_get_eth_handle(void)
{
return s_eth_handle;
}
EventGroupHandle_t eth_test_get_default_event_group(void)
{
return s_eth_event_group;
}
esp_netif_t *eth_test_get_netif(void)
{
return s_eth_netif;
}
void* eth_test_alloc(size_t size)
{
for (int i = 0; i < MAX_HEAP_ALLOCATION_POINTERS; i++) {
if (s_memory_p[i] == NULL) {
s_memory_p[i] = malloc(size);
return s_memory_p[i];
}
}
return NULL;
}
void eth_test_free(void *ptr)
{
for (int i = 0; i < MAX_HEAP_ALLOCATION_POINTERS; i++) {
if (s_memory_p[i] == ptr) {
free(s_memory_p[i]);
s_memory_p[i] = NULL;
return;
}
}
return;
}
void eth_test_free_all(void)
{
for (int i = 0; i < MAX_HEAP_ALLOCATION_POINTERS; i++) {
if (s_memory_p[i] != NULL) {
free(s_memory_p[i]);
s_memory_p[i] = NULL;
}
}
}
/** Event handler for Ethernet events */
void eth_test_default_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg;
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Link Up");
xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
xEventGroupSetBits(eth_event_group, ETH_START_BIT);
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
break;
default:
break;
}
}
/** Event handler for IP_EVENT_ETH_GOT_IP */
void eth_test_got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
}
@@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include "esp_err.h"
#include "unity.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_eth.h"
#define ETH_START_BIT BIT(0)
#define ETH_STOP_BIT BIT(1)
#define ETH_CONNECT_BIT BIT(2)
#define ETH_GOT_IP_BIT BIT(3)
#define ETH_START_TIMEOUT_MS (10000)
#define ETH_CONNECT_TIMEOUT_MS (40000)
#define ETH_STOP_TIMEOUT_MS (10000)
#define ETH_GET_IP_TIMEOUT_MS (60000)
#define ETH_DOWNLOAD_END_TIMEOUT_MS (240000)
#define MAX_HEAP_ALLOCATION_POINTERS 20
typedef struct {
uint8_t dest[ETH_ADDR_LEN];
uint8_t src[ETH_ADDR_LEN];
uint16_t proto;
uint8_t data[];
} __attribute__((__packed__)) emac_frame_t;
/** @brief Get the Ethernet handle initialized by setUp()
*
* @return esp_eth_handle_t The Ethernet handle, if initialized successfully, otherwise NULL
*/
esp_eth_handle_t eth_test_get_eth_handle(void);
/** @brief Get the Event Group handle initialized by setUp()
*
* @return EventGroupHandle_t The Event Group handle, if initialized successfully, otherwise NULL
*/
EventGroupHandle_t eth_test_get_default_event_group(void);
/** @brief Get the ESP-NETIF handle initialized by setUp()
*
* @note Valid output only available if the test case is using [esp-netif] identifier
*
* @return esp_netif_t The Network Interface handle if initialized successfully, otherwise NULL
*/
esp_netif_t *eth_test_get_netif(void);
/** @brief Set the PHY register bits
*
* @param eth_handle The Ethernet handle
* @param reg_addr The PHY register address
* @param bitmask The bits to set
* @param max_attempts The maximum number of set attempts
* @return esp_err_t The error code
*/
esp_err_t eth_test_set_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts);
/** @brief Clear the PHY register bits
*
* @param eth_handle The Ethernet handle
* @param reg_addr The PHY register address
* @param bitmask The bits to clear
* @param max_attempts The maximum number of clear attempts
* @return esp_err_t The error code
*/
esp_err_t eth_test_clear_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts);
/** @brief Initialize Ethernet driver (default implementation uses ethernet_init component).
*
* @param[out] eth_handle Initialized Ethernet driver handle
* @return esp_err_t
*/
esp_err_t esp_eth_test_eth_init(esp_eth_handle_t *eth_handle);
/** @brief Deinitialize Ethernet driver (default implementation uses ethernet_init component).
*
* @param eth_handle Ethernet driver handle to deinitialize
* @return esp_err_t
*/
esp_err_t esp_eth_test_eth_deinit(esp_eth_handle_t eth_handle);
/** @brief Default event handler function header for Ethernet events to be registered by `esp_event_handler_register()` in the test
* with event base `ETH_EVENT` and event ID `ESP_EVENT_ANY_ID`
*
* @note This function is intended to be registered as a callback function only when [skip_setup_teardown] identifier is used. Otherwise,
* it's registered automatically by the Common test app during setUp() phase.
*/
void eth_test_default_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data);
/** @brief Default event handler function header for IP_EVENT_ETH_GOT_IP events to be registered by `esp_event_handler_register()` in the test
* with event base `IP_EVENT` and event ID `IP_EVENT_ETH_GOT_IP`
*
* @note This function is intended to be registered as a callback function only when [skip_setup_teardown] identifier is used. Otherwise,
* it's registered automatically by the Common test app during setUp() phase.
*/
void eth_test_got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data);
/** @brief Allocate memory from the heap
*
* @note The the Common test app keeps track of all allocated memory and frees it automatically during tearDown() phase.
* The maximum number of allocations is limited to `MAX_HEAP_ALLOCATION_POINTERS`.
*
* @param size The size of the memory to allocate
* @return void* The pointer to the allocated memory, NULL if allocation failed
*/
void* eth_test_alloc(size_t size);
/** @brief Free memory from the heap
*
* @note All allocated memory is freed automatically during tearDown() phase. Use this function only when required by the test
* procedure.
*
* @param ptr The pointer to the memory to free
* @return void
*/
void eth_test_free(void *ptr);
/** @brief Free all memory from the heap
*
* @note All allocated memory is freed automatically during tearDown() phase. Use this function only when required by the test
* procedure.
*
* @return void
*/
void eth_test_free_all(void);
@@ -0,0 +1,4 @@
## IDF Component Manager Manifest File
dependencies:
espressif/ethernet_init:
version: "^1.3.0"
+11 -10
View File
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import contextlib
import logging
@@ -195,8 +195,10 @@ def ethernet_l2_test(dut: IdfDut, test_if: str = '') -> None:
pipe_rcv, pipe_send = Pipe(False)
tx_proc = Process(target=target_if.traffic_gen, args=(dut_mac, pipe_rcv, ))
tx_proc.start()
dut.expect_exact('Ethernet Stopped')
pipe_send.send(0) # just send some dummy data
try:
dut.expect_exact('Ethernet Stopped')
finally:
pipe_send.send(0) # just send some dummy data to stop traffic generation
tx_proc.join(5)
if tx_proc.exitcode is None:
tx_proc.terminate()
@@ -204,17 +206,16 @@ def ethernet_l2_test(dut: IdfDut, test_if: str = '') -> None:
dut.expect_unity_test_output(extra_before=res.group(1))
def ethernet_heap_alloc_test(dut: IdfDut) -> None:
target_if = EthTestIntf(ETH_TYPE)
def ethernet_heap_alloc_test(dut: IdfDut, test_if: str = '') -> None:
target_if = EthTestIntf(ETH_TYPE, test_if)
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('\n')
dut.expect_exact('Enter test for running.')
dut.write('"heap utilization"')
res = dut.expect(r'DUT PHY: (\w+)')
dut_phy = res.group(1).decode('utf-8')
# W5500 does not support internal loopback, we need to loopback for it
if 'W5500' in dut_phy:
res = dut.expect(r'PHY loopback is (enabled|disabled)')
phy_loopback = res.group(1).decode('utf-8')
# Some chips do not support internal loopback, we need to loopback at test PC side
if phy_loopback == 'disabled':
logging.info('Starting loopback server...')
res = dut.expect(
r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})'
@@ -2,7 +2,23 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_DM9051=y
# Config Ethernet Init
CONFIG_ETHERNET_SPI_SUPPORT=y
CONFIG_ETHERNET_INTERNAL_SUPPORT=n
CONFIG_ETHERNET_SPI_DEV0_DM9051=y
CONFIG_ETHERNET_SPI_DEV1_NONE=y
CONFIG_ETHERNET_SPI_CLOCK_MHZ=20
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# SPI specific test configuration
CONFIG_ETH_TEST_MAC_ADDR_UI=n
CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED=y
CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO=20
# W5500 specific test configuration
# FILL_RX_BUFFER_ITERATIONS = 16384 / 1522 = 10.8
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=11
CONFIG_ETH_TEST_10MB_LOOPBACK_DISABLED=y
@@ -3,9 +3,17 @@ 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=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_DP83848=y
CONFIG_ETH_RMII_CLK_OUTPUT=y
CONFIG_ETH_RMII_CLK_OUT_GPIO=17
# Config Ethernet Init
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_DP83848=y
CONFIG_ETHERNET_PHY_INTERFACE_RMII=y
CONFIG_ETHERNET_RMII_CLK_OUTPUT=y
CONFIG_ETHERNET_RMII_CLK_EXT_LOOPBACK_EN=n
CONFIG_ETHERNET_RMII_CLK_GPIO=17
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# DP83848 specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
CONFIG_ETH_TEST_LOOPBACK_WITH_AUTONEGOTIATION_DISABLED=y
@@ -5,5 +5,10 @@ CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y
# interface configuration
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_GENERIC=y
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
@@ -5,7 +5,11 @@ CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y
# interface configuration
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_GENERIC=y
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG=y
# specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
CONFIG_ETH_TEST_10MB_LOOPBACK_IGNORE_FAILURES=y
@@ -3,7 +3,13 @@ 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=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8041=y
# Config Ethernet Init
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_KSZ80XX=y
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# KSZ8041 specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
CONFIG_ETH_TEST_10MB_LOOPBACK_DISABLED=y
@@ -2,7 +2,22 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL=y
# Config Ethernet Init
CONFIG_ETHERNET_SPI_SUPPORT=y
CONFIG_ETHERNET_INTERNAL_SUPPORT=n
CONFIG_ETHERNET_SPI_DEV0_KSZ8851SNL=y
CONFIG_ETHERNET_SPI_DEV1_NONE=y
CONFIG_ETHERNET_SPI_CLOCK_MHZ=20
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# SPI specific test configuration
CONFIG_ETH_TEST_MAC_ADDR_UI=n
CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED=y
CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO=20
# KSZ8851SNL specific test configuration
# FILL_RX_BUFFER_ITERATIONS = 12288 / 1522 = 8.1
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=9
@@ -5,7 +5,17 @@ CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720=y
CONFIG_ETH_RMII_CLK_OUTPUT=y
CONFIG_ETH_RMII_CLK_OUT_GPIO=17
# interface configuration
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_LAN87XX=y
CONFIG_ETHERNET_PHY_INTERFACE_RMII=y
CONFIG_ETHERNET_RMII_CLK_OUTPUT=y
CONFIG_ETHERNET_RMII_CLK_EXT_LOOPBACK_EN=n
CONFIG_ETHERNET_RMII_CLK_GPIO=17
CONFIG_ETHERNET_PHY_ADDR=0
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# LAN8720 specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
CONFIG_ETH_TEST_LOOPBACK_WITH_AUTONEGOTIATION_DISABLED=y
CONFIG_ETH_TEST_LAN8720_ERRATA_ENABLED=y
@@ -3,11 +3,18 @@ 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=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201=y
# Config Ethernet Init
CONFIG_ETHERNET_PHY_RTL8201=y
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_RMII_CLK_INPUT=y
CONFIG_ETHERNET_MDC_GPIO=16
CONFIG_ETHERNET_MDIO_GPIO=17
CONFIG_ETHERNET_PHY_RST_GPIO=-1
CONFIG_ETHERNET_PHY_ADDR=0
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG=n
CONFIG_TARGET_IO_MDC=16
CONFIG_TARGET_IO_MDIO=17
# RTL8201 specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
CONFIG_ETH_TEST_LOOPBACK_WITH_AUTONEGOTIATION_DISABLED=y
@@ -2,7 +2,24 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_W5500=y
# Config Ethernet Init
CONFIG_ETHERNET_SPI_SUPPORT=y
CONFIG_ETHERNET_INTERNAL_SUPPORT=n
CONFIG_ETHERNET_SPI_DEV0_W5500=y
CONFIG_ETHERNET_SPI_DEV1_NONE=y
CONFIG_ETHERNET_SPI_CLOCK_MHZ=20
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# SPI specific test configuration
CONFIG_ETH_TEST_MAC_ADDR_UI=n
CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED=y
CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO=20
# W5500 specific test configuration
# FILL_RX_BUFFER_ITERATIONS = 16384 / 1522 = 10.8
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=11
CONFIG_ETH_TEST_LOOPBACK_DISABLED=y
CONFIG_ETH_TEST_W5500_IP6_MCAST_DEVIATION_ENABLED=y
@@ -2,9 +2,26 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_DM9051=y
# Config Ethernet Init
CONFIG_ETHERNET_SPI_SUPPORT=y
CONFIG_ETHERNET_INTERNAL_SUPPORT=n
CONFIG_ETHERNET_SPI_DEV0_DM9051=y
CONFIG_ETHERNET_SPI_DEV1_NONE=y
CONFIG_ETHERNET_SPI_CLOCK_MHZ=20
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
CONFIG_TARGET_ETH_SPI_POLL_MS=10
# Polling mode (interrupt GPIO -1 = polling, polling period 10 ms)
CONFIG_ETHERNET_SPI_INT0_GPIO=-1
CONFIG_ETHERNET_SPI_POLLING0_MS_VAL=10
# SPI specific test configuration
CONFIG_ETH_TEST_MAC_ADDR_UI=n
CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED=y
CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO=20
# W5500 specific test configuration
# FILL_RX_BUFFER_ITERATIONS = 16384 / 1522 = 10.8
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=11
CONFIG_ETH_TEST_10MB_LOOPBACK_DISABLED=y
@@ -2,9 +2,25 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL=y
# Config Ethernet Init
CONFIG_ETHERNET_SPI_SUPPORT=y
CONFIG_ETHERNET_INTERNAL_SUPPORT=n
CONFIG_ETHERNET_SPI_DEV0_KSZ8851SNL=y
CONFIG_ETHERNET_SPI_DEV1_NONE=y
CONFIG_ETHERNET_SPI_CLOCK_MHZ=20
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
CONFIG_TARGET_ETH_SPI_POLL_MS=10
# Polling mode (interrupt GPIO -1 = polling, polling period 10 ms)
CONFIG_ETHERNET_SPI_INT0_GPIO=-1
CONFIG_ETHERNET_SPI_POLLING0_MS_VAL=10
# SPI specific test configuration
CONFIG_ETH_TEST_MAC_ADDR_UI=n
CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED=y
CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO=20
# KSZ8851SNL specific test configuration
# FILL_RX_BUFFER_ITERATIONS = 12288 / 1522 = 8.1
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=9
@@ -2,9 +2,27 @@ CONFIG_IDF_TARGET="esp32"
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_SPI_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_W5500=y
# Config Ethernet Init
CONFIG_ETHERNET_SPI_SUPPORT=y
CONFIG_ETHERNET_INTERNAL_SUPPORT=n
CONFIG_ETHERNET_SPI_DEV0_W5500=y
CONFIG_ETHERNET_SPI_DEV1_NONE=y
CONFIG_ETHERNET_SPI_CLOCK_MHZ=20
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
CONFIG_TARGET_ETH_SPI_POLL_MS=10
# Polling mode (interrupt GPIO -1 = polling, polling period 10 ms)
CONFIG_ETHERNET_SPI_INT0_GPIO=-1
CONFIG_ETHERNET_SPI_POLLING0_MS_VAL=10
# SPI specific test configuration
CONFIG_ETH_TEST_MAC_ADDR_UI=n
CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED=y
CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO=20
# W5500 specific test configuration
# FILL_RX_BUFFER_ITERATIONS = 16384 / 1522 = 10.8
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=11
CONFIG_ETH_TEST_LOOPBACK_DISABLED=y
CONFIG_ETH_TEST_W5500_IP6_MCAST_DEVIATION_ENABLED=y
@@ -6,7 +6,12 @@ CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y
# interface configuration
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_GENERIC=y
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
@@ -5,13 +5,15 @@ CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y
# interface configuration
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_GENERIC=y
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
CONFIG_TARGET_USE_DEFAULT_EMAC_CONFIG=y
CONFIG_TARGET_RMII_CLK_OUT=y
CONFIG_ETHERNET_PHY_INTERFACE_RMII=y
CONFIG_ETHERNET_RMII_CLK_OUTPUT=y
CONFIG_ETHERNET_RMII_CLK_EXT_LOOPBACK_EN=y
# Test board needs to be modified!
# Connect GPIO23 to GPIO32 via wire.
CONFIG_TARGET_RMII_CLK_OUT_GPIO=23
CONFIG_TARGET_RMII_CLK_IN_GPIO=32
CONFIG_ETHERNET_RMII_CLK_GPIO=23
CONFIG_ETHERNET_RMII_CLK_EXT_LOOPBACK_IN_GPIO=32
@@ -6,7 +6,12 @@ CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
CONFIG_ETH_USE_ESP32_EMAC=y
CONFIG_ESP_TASK_WDT=n
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y
# interface configuration
CONFIG_ETHERNET_INTERNAL_SUPPORT=y
CONFIG_ETHERNET_PHY_GENERIC=y
CONFIG_ETHERNET_DEFAULT_EVENT_HANDLER=n
# specific test configuration
CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS=10
+1
View File
@@ -41,3 +41,4 @@ warning: unknown kconfig symbol 'UNITY_FREERTOS_STACK_SIZE' assigned to '12288'
warning: unknown kconfig symbol 'WPA3_SAE' assigned to 'y' in .*/components/wpa_supplicant/test_apps/sdkconfig.defaults
ld: warning: ignoring duplicate libraries
archive library: .+ the table of contents is empty
WARNING: The following Kconfig variables were used in "if" clauses