diff --git a/components/esp_eth/test_apps/main/esp_eth_test_l2.c b/components/esp_eth/test_apps/main/esp_eth_test_l2.c index c094c12e78..1c3fd76492 100644 --- a/components/esp_eth/test_apps/main/esp_eth_test_l2.c +++ b/components/esp_eth/test_apps/main/esp_eth_test_l2.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -9,20 +9,112 @@ #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_eth_test_common.h" +#include "arpa/inet.h" // for ntohs, etc. +#include "esp_log.h" + +#define TEST_ETH_TYPE 0x2222 +#define TEST_CTRL_ETH_TYPE (TEST_ETH_TYPE + 1) + +#define WAIT_AFTER_CONN_MS 2500 +#define WAIT_AFTER_CONN_TMO_MS 20000 #define ETH_BROADCAST_RECV_BIT BIT(0) #define ETH_MULTICAST_RECV_BIT BIT(1) #define ETH_UNICAST_RECV_BIT BIT(2) +#define ETH_POKE_RESP_RECV_BIT BIT(3) #define POKE_REQ 0xFA #define POKE_RESP 0xFB #define DUMMY_TRAFFIC 0xFF +typedef struct +{ + EventGroupHandle_t eth_event_group; + int unicast_rx_cnt; + int multicast_rx_cnt; + int brdcast_rx_cnt; + + bool check_rx_data; +} recv_info_t; + +esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) { + recv_info_t *recv_info = (recv_info_t*)priv; + EventGroupHandle_t eth_event_group = recv_info->eth_event_group; + emac_frame_t *pkt = (emac_frame_t *)buffer; + // check header + if (pkt->proto == TEST_ETH_TYPE) { // data packet + uint8_t local_mac_addr[ETH_ADDR_LEN]; + esp_eth_ioctl(hdl, ETH_CMD_G_MAC_ADDR, local_mac_addr); + // check data content + if (recv_info->check_rx_data) { + if (length == 1024) { + for (int i = 0; i < (length - ETH_HEADER_LEN); ++i) { + if (pkt->data[i] != (i & 0xff)) { + printf("payload mismatch\n"); + free(buffer); + return ESP_OK; + } + } + } + } + + if (memcmp(pkt->dest, "\xff\xff\xff\xff\xff\xff", ETH_ADDR_LEN) == 0) { + recv_info->brdcast_rx_cnt++; + xEventGroupSetBits(eth_event_group, ETH_BROADCAST_RECV_BIT); + } else if (pkt->dest[0] & 0x1) { + recv_info->multicast_rx_cnt++; + xEventGroupSetBits(eth_event_group, ETH_MULTICAST_RECV_BIT); + } else if (memcmp(pkt->dest, local_mac_addr, ETH_ADDR_LEN) == 0) { + recv_info->unicast_rx_cnt++; + xEventGroupSetBits(eth_event_group, ETH_UNICAST_RECV_BIT); + } + } else if (ntohs(pkt->proto) == TEST_CTRL_ETH_TYPE) { // control packet + if (pkt->data[0] == POKE_RESP) { + printf("Poke response received\n"); + xEventGroupSetBits(eth_event_group, ETH_POKE_RESP_RECV_BIT); + } + } + free(buffer); + return ESP_OK; +} + +/** + * @brief The function sends a "POKE" request message over the Ethernet and waits until the test script sends a reply. + * Multiple "POKE" attempts are issued when timeout for the reply expires. + * This function is used to drive the test flow and to ensure that data path between the test points + * has been established. I.e. if DUT is connected in network with a switch, even if link is indicated up, + * it may take some time the switch starts forwarding the associated port (e.g. it runs RSTP at first). + */ +void poke_and_wait(esp_eth_handle_t eth_handle, void *data, uint16_t size, EventGroupHandle_t eth_event_group) +{ + // create a control frame to control test flow between the UT and the Python test script + emac_frame_t *ctrl_pkt = calloc(1, 60); + ctrl_pkt->proto = htons(TEST_CTRL_ETH_TYPE); + memset(ctrl_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, ctrl_pkt->src); + + ctrl_pkt->data[0] = POKE_REQ; + if (data != NULL && size > 0) { + memcpy(&ctrl_pkt->data[1], data, size); + } + + uint32_t tmo; + uint32_t i; + 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)); + 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) { + break; + } + } + TEST_ASSERT(tmo < WAIT_AFTER_CONN_TMO_MS); + free(ctrl_pkt); +} + TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]") { - EventGroupHandle_t eth_event_group = xEventGroupCreate(); - TEST_ASSERT(eth_event_group != NULL); - esp_eth_mac_t *mac = mac_init(NULL, NULL); TEST_ASSERT_NOT_NULL(mac); esp_eth_phy_t *phy = phy_init(NULL); @@ -32,19 +124,33 @@ TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]") TEST_ESP_OK(esp_eth_driver_install(&config, ð_handle)); // install driver TEST_ASSERT_NOT_NULL(eth_handle); extra_eth_config(eth_handle); + TEST_ESP_OK(esp_event_loop_create_default()); - TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group)); + 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, ð_event_handler, eth_event_state_group)); + EventGroupHandle_t eth_event_rx_group = xEventGroupCreate(); + TEST_ASSERT(eth_event_rx_group != NULL); + recv_info_t recv_info = { + .eth_event_group = eth_event_rx_group, + .check_rx_data = false, + .unicast_rx_cnt = 0, + .multicast_rx_cnt = 0, + .brdcast_rx_cnt = 0 + }; + + TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &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(3000)); + bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(3000)); 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) - vTaskDelay(pdMS_TO_TICKS(1000)); + poke_and_wait(eth_handle, NULL, 0, eth_event_rx_group); emac_frame_t *pkt = malloc(1024); - pkt->proto = 0x2222; + pkt->proto = 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){ @@ -61,44 +167,12 @@ TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]") phy->del(phy); mac->del(mac); extra_cleanup(); - vEventGroupDelete(eth_event_group); + vEventGroupDelete(eth_event_rx_group); + vEventGroupDelete(eth_event_state_group); } -static uint8_t local_mac_addr[ETH_ADDR_LEN] = {}; - -esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) { - EventGroupHandle_t eth_event_group = (EventGroupHandle_t)priv; - emac_frame_t *pkt = (emac_frame_t *) buffer; - // check header - if (pkt->proto == 0x2222 && length == 1024) { - // check content - for (int i = 0; i < (length - ETH_HEADER_LEN); ++i) { - if (pkt->data[i] != (i & 0xff)) { - printf("payload mismatch\n"); - return ESP_OK; - } - } - if (memcmp(pkt->dest, "\xff\xff\xff\xff\xff\xff", ETH_ADDR_LEN) == 0) { - printf("broadcast received...\n"); - xEventGroupSetBits(eth_event_group, ETH_BROADCAST_RECV_BIT); - } else if (pkt->dest[0] & 0x1) { - printf("multicast received...\n"); - xEventGroupSetBits(eth_event_group, ETH_MULTICAST_RECV_BIT); - } else if (memcmp(pkt->dest, local_mac_addr, ETH_ADDR_LEN) == 0) { - printf("unicast received...\n"); - xEventGroupSetBits(eth_event_group, ETH_UNICAST_RECV_BIT); - } - } else { - printf("unexpected frame (protocol: 0x%x, length: %" PRIu32 ")\n", pkt->proto, length); - } - return ESP_OK; -}; - TEST_CASE("ethernet recv_pkt", "[ethernet_l2]") { - EventGroupHandle_t eth_event_group = xEventGroupCreate(); - TEST_ASSERT(eth_event_group != NULL); - esp_eth_mac_t *mac = mac_init(NULL, NULL); TEST_ASSERT_NOT_NULL(mac); esp_eth_phy_t *phy = phy_init(NULL); @@ -108,19 +182,41 @@ TEST_CASE("ethernet recv_pkt", "[ethernet_l2]") TEST_ESP_OK(esp_eth_driver_install(&config, ð_handle)); // install driver TEST_ASSERT_NOT_NULL(eth_handle); extra_eth_config(eth_handle); - TEST_ESP_OK(esp_event_loop_create_default()); - TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine + 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, ð_event_handler, eth_event_state_group)); + EventGroupHandle_t eth_event_rx_group = xEventGroupCreate(); + TEST_ASSERT(eth_event_rx_group != NULL); + recv_info_t recv_info = { + .eth_event_group = eth_event_rx_group, + .check_rx_data = true, + .unicast_rx_cnt = 0, + .multicast_rx_cnt = 0, + .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, eth_event_group)); + TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &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_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT, + bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(3000)); + 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, 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)); @@ -130,45 +226,13 @@ TEST_CASE("ethernet recv_pkt", "[ethernet_l2]") phy->del(phy); mac->del(mac); extra_cleanup(); - vEventGroupDelete(eth_event_group); + vEventGroupDelete(eth_event_state_group); + vEventGroupDelete(eth_event_rx_group); } -typedef struct -{ - SemaphoreHandle_t mutex; - int rx_pkt_cnt; -} recv_info_t; - -static esp_err_t eth_recv_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) -{ - emac_frame_t *pkt = (emac_frame_t *)buffer; - recv_info_t *recv_info = (recv_info_t *)priv; - - if (pkt->proto == 0x2222) { - switch (pkt->data[0]) - { - case POKE_RESP: - xSemaphoreGive(recv_info->mutex); - break; - - case DUMMY_TRAFFIC: - (recv_info->rx_pkt_cnt)++; - break; - default: - break; - } - } - free(buffer); - return ESP_OK; -} TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]") { - recv_info_t recv_info; - recv_info.mutex = xSemaphoreCreateBinary(); - TEST_ASSERT_NOT_NULL(recv_info.mutex); - recv_info.rx_pkt_cnt = 0; - esp_eth_mac_t *mac = mac_init(NULL, NULL); TEST_ASSERT_NOT_NULL(mac); esp_eth_phy_t *phy = phy_init(NULL); @@ -179,28 +243,32 @@ TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]" 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, ð_event_handler, eth_event_state_group)); + EventGroupHandle_t eth_event_rx_group = xEventGroupCreate(); + TEST_ASSERT(eth_event_rx_group != NULL); + recv_info_t recv_info = { + .eth_event_group = eth_event_rx_group, + .check_rx_data = false, + .unicast_rx_cnt = 0, + .multicast_rx_cnt = 0, + .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, eth_recv_cb, &recv_info)); - - 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, ð_event_handler, eth_event_group)); - - // create a control frame to control test flow between the UT and the Python test script - emac_frame_t *ctrl_pkt = calloc(1, 60); - ctrl_pkt->proto = 0x2222; - memset(ctrl_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr - memcpy(ctrl_pkt->src, local_mac_addr, ETH_ADDR_LEN); + TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &recv_info)); // create dummy data packet used for traffic generation emac_frame_t *pkt = calloc(1, 1500); - pkt->proto = 0x2222; + pkt->proto = TEST_ETH_TYPE; // we don't care about dest MAC address much, however it is better to not be broadcast or multifcast to not flood // other network nodes memset(pkt->dest, 0xBA, ETH_ADDR_LEN); @@ -208,62 +276,51 @@ TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]" 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(3000)); + bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(3000)); 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) - vTaskDelay(pdMS_TO_TICKS(1000)); - // at first, check that Tx/Rx path works as expected by poking the test script // this also serves as main PASS/FAIL criteria - ctrl_pkt->data[0] = POKE_REQ; - ctrl_pkt->data[1] = tx_i; - TEST_ESP_OK(esp_eth_transmit(eth_handle, ctrl_pkt, 60)); - TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(3000))); - printf("Tx Test iteration %d\n", tx_i); + poke_and_wait(eth_handle, &tx_i, sizeof(tx_i), eth_event_rx_group); // generate heavy Tx traffic printf("Note: transmit errors are expected...\n"); 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[0] = j & 0xFF; // sequence number + 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)); + 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("Ethernet stopped\n"); } printf("EMAC start/stop stress test under heavy Rx traffic\n"); for (int rx_i = 0; rx_i < 10; rx_i++) { - recv_info.rx_pkt_cnt = 0; - 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(3000)); - 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) - vTaskDelay(pdMS_TO_TICKS(1000)); - - ctrl_pkt->data[0] = POKE_REQ; - ctrl_pkt->data[1] = rx_i; - TEST_ESP_OK(esp_eth_transmit(eth_handle, ctrl_pkt, 60)); - TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(3000))); 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(3000)); + TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT); + poke_and_wait(eth_handle, &rx_i, sizeof(rx_i), eth_event_rx_group); + + // wait for dummy traffic + 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)); + 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", recv_info.rx_pkt_cnt); - TEST_ASSERT_GREATER_THAN_INT32(0, recv_info.rx_pkt_cnt); + printf("Recv packets: %d\n", recv_info.unicast_rx_cnt); + TEST_ASSERT_GREATER_THAN_INT32(0, recv_info.unicast_rx_cnt); printf("Ethernet stopped\n"); } - free(ctrl_pkt); free(pkt); TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)); @@ -272,6 +329,6 @@ TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]" phy->del(phy); mac->del(mac); extra_cleanup(); - vEventGroupDelete(eth_event_group); - vSemaphoreDelete(recv_info.mutex); + vEventGroupDelete(eth_event_rx_group); + vEventGroupDelete(eth_event_state_group); } diff --git a/components/esp_eth/test_apps/pytest_esp_eth.py b/components/esp_eth/test_apps/pytest_esp_eth.py index da28328c81..70d99db93d 100644 --- a/components/esp_eth/test_apps/pytest_esp_eth.py +++ b/components/esp_eth/test_apps/pytest_esp_eth.py @@ -1,11 +1,10 @@ -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 import contextlib import logging import os import socket -import time from multiprocessing import Pipe, Process, connection from typing import Iterator @@ -42,8 +41,10 @@ class EthTestIntf(object): logging.info('Use %s for testing', self.target_if) @contextlib.contextmanager - def configure_eth_if(self) -> Iterator[socket.socket]: - so = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(self.eth_type)) + def configure_eth_if(self, eth_type:int=0) -> Iterator[socket.socket]: + if eth_type == 0: + eth_type = self.eth_type + so = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(eth_type)) so.bind((self.target_if, 0)) try: yield so @@ -62,15 +63,16 @@ class EthTestIntf(object): except Exception as e: raise e - def recv_resp_poke(self, i: int) -> None: - with self.configure_eth_if() as so: - so.settimeout(10) + def recv_resp_poke(self, i:int=0) -> None: + eth_type_ctrl = self.eth_type + 1 + with self.configure_eth_if(eth_type_ctrl) as so: + so.settimeout(30) try: eth_frame = Ether(so.recv(60)) - - if eth_frame.type == self.eth_type and eth_frame.load[0] == 0xfa: + if eth_frame.load[0] == 0xfa: if eth_frame.load[1] != i: raise Exception('Missed Poke Packet') + logging.info('Poke Packet received...') eth_frame.dst = eth_frame.src eth_frame.src = so.getsockname()[4] eth_frame.load = bytes.fromhex('fb') # POKE_RESP code @@ -118,6 +120,11 @@ def ethernet_l2_test(dut: Dut) -> None: with target_if.configure_eth_if() as so: so.settimeout(30) dut.write('"ethernet broadcast transmit"') + + # wait for POKE msg to be sure the switch already started forwarding the port's traffic + # (there might be slight delay due to the RSTP execution) + target_if.recv_resp_poke() + eth_frame = Ether(so.recv(1024)) for i in range(0, 1010): if eth_frame.load[i] != i & 0xff: @@ -130,7 +137,9 @@ def ethernet_l2_test(dut: Dut) -> None: r'([\s\S]*)' 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})' ) - time.sleep(1) + # wait for POKE msg to be sure the switch already started forwarding the port's traffic + # (there might be slight delay due to the RSTP execution) + target_if.recv_resp_poke() target_if.send_eth_packet('ff:ff:ff:ff:ff:ff') # broadcast frame target_if.send_eth_packet('01:00:00:00:00:00') # multicast frame target_if.send_eth_packet(res.group(2)) # unicast frame @@ -145,19 +154,20 @@ def ethernet_l2_test(dut: Dut) -> None: # Start/stop under heavy Tx traffic for tx_i in range(10): target_if.recv_resp_poke(tx_i) + dut.expect_exact('Ethernet stopped') - # Start/stop under heavy Rx traffic - pipe_rcv, pipe_send = Pipe(False) - tx_proc = Process(target=target_if.traffic_gen, args=(res.group(2), pipe_rcv, )) - tx_proc.start() - try: - for rx_i in range(10): - target_if.recv_resp_poke(rx_i) - finally: + for rx_i in range(10): + target_if.recv_resp_poke(rx_i) + # Start/stop under heavy Rx traffic + pipe_rcv, pipe_send = Pipe(False) + tx_proc = Process(target=target_if.traffic_gen, args=(res.group(2), pipe_rcv, )) + tx_proc.start() + dut.expect_exact('Ethernet stopped') pipe_send.send(0) # just send some dummy data tx_proc.join(5) if tx_proc.exitcode is None: tx_proc.terminate() + dut.expect_unity_test_output(extra_before=res.group(1))