Merge branch 'feature/emac_start_stop_test' into 'master'

test_emac: extended Ethernet start/stop stress test

Closes IDF-4540

See merge request espressif/esp-idf!16755
This commit is contained in:
Ondrej Kosta 2022-02-15 13:23:01 +00:00
commit 7512bdecfe
9 changed files with 282 additions and 41 deletions

View File

@ -169,6 +169,7 @@ before_script:
- retry_failed git clone --depth 1 --branch $PYTEST_EMBEDDED_TAG https://gitlab-ci-token:${BOT_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/idf/pytest-embedded.git - retry_failed git clone --depth 1 --branch $PYTEST_EMBEDDED_TAG https://gitlab-ci-token:${BOT_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/idf/pytest-embedded.git
- cd pytest-embedded && bash foreach.sh install - cd pytest-embedded && bash foreach.sh install
- pip install pytest-rerunfailures - pip install pytest-rerunfailures
- pip install scapy
- cd $IDF_PATH - cd $IDF_PATH
default: default:

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -265,7 +265,6 @@ esp_err_t esp_eth_start(esp_eth_handle_t hdl)
esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl; esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl;
ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null"); ESP_GOTO_ON_FALSE(eth_driver, ESP_ERR_INVALID_ARG, err, TAG, "ethernet driver handle can't be null");
esp_eth_phy_t *phy = eth_driver->phy; esp_eth_phy_t *phy = eth_driver->phy;
esp_eth_mac_t *mac = eth_driver->mac;
// check if driver has stopped // check if driver has stopped
esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP; esp_eth_fsm_t expected_fsm = ESP_ETH_FSM_STOP;
ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_START), ESP_GOTO_ON_FALSE(atomic_compare_exchange_strong(&eth_driver->fsm, &expected_fsm, ESP_ETH_FSM_START),
@ -274,7 +273,6 @@ esp_err_t esp_eth_start(esp_eth_handle_t hdl)
if (eth_driver->auto_nego_en == true) { if (eth_driver->auto_nego_en == true) {
ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_RESTART, &eth_driver->auto_nego_en), err, TAG, "phy negotiation failed"); ESP_GOTO_ON_ERROR(phy->autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_RESTART, &eth_driver->auto_nego_en), err, TAG, "phy negotiation failed");
} }
ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "start mac failed");
ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, &eth_driver, sizeof(esp_eth_driver_t *), 0), ESP_GOTO_ON_ERROR(esp_event_post(ETH_EVENT, ETHERNET_EVENT_START, &eth_driver, sizeof(esp_eth_driver_t *), 0),
err, TAG, "send ETHERNET_EVENT_START event failed"); err, TAG, "send ETHERNET_EVENT_START event failed");
ESP_GOTO_ON_ERROR(phy->get_link(phy), err, TAG, "phy get link status failed"); ESP_GOTO_ON_ERROR(phy->get_link(phy), err, TAG, "phy get link status failed");

View File

@ -33,7 +33,7 @@
static const char *TAG = "esp.emac"; static const char *TAG = "esp.emac";
#define PHY_OPERATION_TIMEOUT_US (1000) #define PHY_OPERATION_TIMEOUT_US (1000)
#define MAC_STOP_TIMEOUT_MS (100) #define MAC_STOP_TIMEOUT_US (250)
#define FLOW_CONTROL_LOW_WATER_MARK (CONFIG_ETH_DMA_RX_BUFFER_NUM / 3) #define FLOW_CONTROL_LOW_WATER_MARK (CONFIG_ETH_DMA_RX_BUFFER_NUM / 3)
#define FLOW_CONTROL_HIGH_WATER_MARK (FLOW_CONTROL_LOW_WATER_MARK * 2) #define FLOW_CONTROL_HIGH_WATER_MARK (FLOW_CONTROL_LOW_WATER_MARK * 2)
@ -66,6 +66,8 @@ typedef struct {
static esp_err_t esp_emac_alloc_driver_obj(const eth_mac_config_t *config, emac_esp32_t **emac_out_hdl, void **out_descriptors); static esp_err_t esp_emac_alloc_driver_obj(const eth_mac_config_t *config, emac_esp32_t **emac_out_hdl, void **out_descriptors);
static void esp_emac_free_driver_obj(emac_esp32_t *emac, void *descriptors); static void esp_emac_free_driver_obj(emac_esp32_t *emac, void *descriptors);
static esp_err_t emac_esp32_start(esp_eth_mac_t *mac);
static esp_err_t emac_esp32_stop(esp_eth_mac_t *mac);
static esp_err_t emac_esp32_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth) static esp_err_t emac_esp32_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth)
{ {
@ -152,11 +154,11 @@ static esp_err_t emac_esp32_set_link(esp_eth_mac_t *mac, eth_link_t link)
switch (link) { switch (link) {
case ETH_LINK_UP: case ETH_LINK_UP:
ESP_GOTO_ON_ERROR(esp_intr_enable(emac->intr_hdl), err, TAG, "enable interrupt failed"); ESP_GOTO_ON_ERROR(esp_intr_enable(emac->intr_hdl), err, TAG, "enable interrupt failed");
emac_hal_start(&emac->hal); emac_esp32_start(mac);
break; break;
case ETH_LINK_DOWN: case ETH_LINK_DOWN:
ESP_GOTO_ON_ERROR(esp_intr_disable(emac->intr_hdl), err, TAG, "disable interrupt failed"); ESP_GOTO_ON_ERROR(esp_intr_disable(emac->intr_hdl), err, TAG, "disable interrupt failed");
emac_hal_stop(&emac->hal); emac_esp32_stop(mac);
break; break;
default: default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown link status"); ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown link status");
@ -389,9 +391,9 @@ static esp_err_t emac_esp32_stop(esp_eth_mac_t *mac)
if ((ret = emac_hal_stop(&emac->hal)) == ESP_OK) { if ((ret = emac_hal_stop(&emac->hal)) == ESP_OK) {
break; break;
} }
to += 20; to += 25;
vTaskDelay(pdMS_TO_TICKS(20)); esp_rom_delay_us(25);
} while (to < MAC_STOP_TIMEOUT_MS); } while (to < MAC_STOP_TIMEOUT_US);
return ret; return ret;
} }

View File

@ -1,7 +1,15 @@
# EMAC Test
| Supported Targets | ESP32 | | Supported Targets | ESP32 |
| ----------------- | ----- | | ----------------- | ----- |
This test app is used to test MAC layer behavior with different PHY chips: This test app is used to test MAC layer behavior with different PHY chips:
- ip101 - IP101
- lan8720 - LAN8720
## Prerequisites
Install third part Python packages:
```bash
pip install scapy
```

View File

@ -17,6 +17,10 @@
#define ETH_MULTICAST_RECV_BIT BIT(1) #define ETH_MULTICAST_RECV_BIT BIT(1)
#define ETH_UNICAST_RECV_BIT BIT(2) #define ETH_UNICAST_RECV_BIT BIT(2)
#define POKE_REQ 0xFA
#define POKE_RESP 0xFB
#define DUMMY_TRAFFIC 0xFF
typedef struct { typedef struct {
uint8_t dest[6]; uint8_t dest[6];
uint8_t src[6]; uint8_t src[6];
@ -173,12 +177,14 @@ TEST_CASE("ethernet_broadcast_transmit", "[esp_eth]")
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_start(eth_handle)); // start Ethernet driver state machine TEST_ASSERT_EQUAL(ESP_OK, esp_eth_start(eth_handle)); // start Ethernet driver state machine
TEST_ASSERT(xSemaphoreTake(mutex, pdMS_TO_TICKS(3000))); TEST_ASSERT(xSemaphoreTake(mutex, pdMS_TO_TICKS(3000)));
// even if PHY (IP101) indicates autonegotiation done and link up, it sometimes may miss few packets after atonego reset, hence wait a bit
vTaskDelay(pdMS_TO_TICKS(100));
emac_frame_t *pkt = malloc(1024); emac_frame_t *pkt = malloc(1024);
pkt->proto = 0x2222; pkt->proto = 0x2222;
memset(pkt->dest, 0xff, 6); // broadcast addr memset(pkt->dest, 0xff, 6); // broadcast addr
for (int i = 128; i < 1024; ++i){ for (int i = 0; i < (1024 - ETH_HEADER_LEN); ++i){
((uint8_t*)pkt)[i] = i & 0xff; pkt->data[i] = i & 0xff;
} }
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_transmit(eth_handle, pkt, 1024)); TEST_ASSERT_EQUAL(ESP_OK, esp_eth_transmit(eth_handle, pkt, 1024));
@ -201,8 +207,8 @@ esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t
// check header // check header
if (pkt->proto == 0x2222 && length == 1024) { if (pkt->proto == 0x2222 && length == 1024) {
// check content // check content
for (int i = 128; i < 1024; ++i) { for (int i = 0; i < (length - ETH_HEADER_LEN); ++i) {
if (buffer[i] != (i & 0xff)) { if (pkt->data[i] != (i & 0xff)) {
return ESP_OK; return ESP_OK;
} }
} }
@ -263,6 +269,171 @@ TEST_CASE("recv_pkt", "[esp_eth]")
vEventGroupDelete(eth_event_group); vEventGroupDelete(eth_event_group);
} }
typedef struct
{
SemaphoreHandle_t mutex;
int rx_pkt_cnt;
} recv_info_t;
TEST_CASE("start_stop_stress_test", "[esp_eth]")
{
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:
xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
break;
case ETHERNET_EVENT_DISCONNECTED:
break;
case ETHERNET_EVENT_START:
xEventGroupSetBits(eth_event_group, ETH_START_BIT);
break;
case ETHERNET_EVENT_STOP:
xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
break;
default:
break;
}
}
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;
}
recv_info_t recv_info;
recv_info.mutex = xSemaphoreCreateBinary();
TEST_ASSERT_NOT_NULL(recv_info.mutex);
recv_info.rx_pkt_cnt = 0;
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); // apply default MAC configuration
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); // create MAC instance
TEST_ASSERT_NOT_NULL(mac);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_IP101)
esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // create PHY instance
#elif defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX)
esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
#endif
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_ASSERT_EQUAL(ESP_OK, esp_eth_driver_install(&config, &eth_handle)); // install driver
TEST_ASSERT_NOT_NULL(eth_handle);
TEST_ASSERT_EQUAL(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, &eth_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, 6); // broadcast addr
memcpy(ctrl_pkt->src, local_mac_addr, 6);
// create dummy data packet used for traffic generation
emac_frame_t *pkt = calloc(1, 1500);
pkt->proto = 0x2222;
// 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, 6);
memcpy(pkt->src, local_mac_addr, 6);
printf("EMAC start/stop stress test under heavy Tx traffic\n");
for (int tx_i = 0; tx_i < 10; tx_i++) {
TEST_ASSERT_EQUAL(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);
// even if PHY (IP101) indicates autonegotiation done and link up, it sometimes may miss few packets after atonego reset, hence wait a bit
vTaskDelay(pdMS_TO_TICKS(100));
// 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_ASSERT_EQUAL(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);
// 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;
esp_eth_transmit(eth_handle, pkt, 1500);
}
TEST_ASSERT_EQUAL(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("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_ASSERT_EQUAL(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);
// even if PHY (IP101) indicates autonegotiation done and link up, it sometimes may miss few packets after atonego reset, hence wait a bit
vTaskDelay(pdMS_TO_TICKS(100));
ctrl_pkt->data[0] = POKE_REQ;
ctrl_pkt->data[1] = rx_i;
TEST_ASSERT_EQUAL(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);
vTaskDelay(pdMS_TO_TICKS(500));
TEST_ASSERT_EQUAL(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", recv_info.rx_pkt_cnt);
TEST_ASSERT_GREATER_THAN_INT32(0, recv_info.rx_pkt_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));
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_delete_default());
TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_uninstall(eth_handle));
phy->del(phy);
mac->del(mac);
vEventGroupDelete(eth_event_group);
vSemaphoreDelete(recv_info.mutex);
}
void app_main(void) void app_main(void)
{ {
unity_run_menu(); unity_run_menu();

View File

@ -5,10 +5,13 @@ import contextlib
import logging import logging
import os import os
import socket import socket
from collections.abc import Callable
from threading import Thread
from typing import Iterator from typing import Iterator
import pytest import pytest
from pytest_embedded import Dut from pytest_embedded import Dut
from scapy.all import Ether, raw
@contextlib.contextmanager @contextlib.contextmanager
@ -35,18 +38,44 @@ def configure_eth_if() -> Iterator[socket.socket]:
so.close() so.close()
def send_eth_packet(mac: bytes) -> None: def send_eth_packet(mac: str) -> None:
with configure_eth_if() as so: with configure_eth_if() as so:
so.settimeout(10) so.settimeout(10)
pkt = bytearray() payload = bytearray(1010)
pkt += mac # dest for i, _ in enumerate(payload):
pkt += so.getsockname()[4] # src payload[i] = i & 0xff
pkt += bytes.fromhex('2222') # proto eth_frame = Ether(dst=mac, src=so.getsockname()[4], type=0x2222) / raw(payload)
pkt += bytes(1010) # padding to 1024
for i in range(128, 1024):
pkt[i] = i & 0xff
try: try:
so.send(pkt) so.send(raw(eth_frame))
except Exception as e:
raise e
def recv_resp_poke(i: int) -> None:
with configure_eth_if() as so:
so.settimeout(10)
try:
eth_frame = Ether(so.recv(60))
if eth_frame.type == 0x2222 and eth_frame.load[0] == 0xfa:
if eth_frame.load[1] != i:
raise Exception('Missed Poke Packet')
eth_frame.dst = eth_frame.src
eth_frame.src = so.getsockname()[4]
eth_frame.load = bytes.fromhex('fb') # POKE_RESP code
so.send(raw(eth_frame))
except Exception as e:
raise e
def traffic_gen(mac: str, enabled: Callable) -> None:
with configure_eth_if() as so:
payload = bytes.fromhex('ff') # DUMMY_TRAFFIC code
payload += bytes(1485)
eth_frame = Ether(dst=mac, src=so.getsockname()[4], type=0x2222) / raw(payload)
try:
while enabled() == 1:
so.send(raw(eth_frame))
except Exception as e: except Exception as e:
raise e raise e
@ -67,9 +96,9 @@ def actual_test(dut: Dut) -> None:
with configure_eth_if() as so: with configure_eth_if() as so:
so.settimeout(30) so.settimeout(30)
dut.write('"ethernet_broadcast_transmit"') dut.write('"ethernet_broadcast_transmit"')
pkt = so.recv(1024) eth_frame = Ether(so.recv(1024))
for i in range(128, 1024): for i in range(0, 1010):
if pkt[i] != i & 0xff: if eth_frame.load[i] != i & 0xff:
raise Exception('Packet content mismatch') raise Exception('Packet content mismatch')
dut.expect_unity_test_output() dut.expect_unity_test_output()
@ -79,11 +108,33 @@ def actual_test(dut: Dut) -> None:
r'([\s\S]*)' 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})' 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})'
) )
send_eth_packet(bytes.fromhex('ffffffffffff')) # broadcast frame # pylint: disable=no-value-for-parameter send_eth_packet('ff:ff:ff:ff:ff:ff') # broadcast frame
send_eth_packet(bytes.fromhex('010000000000')) # multicast frame # pylint: disable=no-value-for-parameter send_eth_packet('01:00:00:00:00:00') # multicast frame
send_eth_packet(bytes.fromhex(res.group(2).decode('utf-8').replace(':', ''))) # unicast fram # pylint: disable=no-value-for-parameter, line-too-long # noqa send_eth_packet(res.group(2)) # unicast frame
dut.expect_unity_test_output(extra_before=res.group(1)) dut.expect_unity_test_output(extra_before=res.group(1))
dut.expect_exact("Enter next test, or 'enter' to see menu")
dut.write('"start_stop_stress_test"')
res = dut.expect(
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})'
)
# Start/stop under heavy Tx traffic
for tx_i in range(10):
recv_resp_poke(tx_i)
# Start/stop under heavy Rx traffic
traffic_en = 1
thread = Thread(target=traffic_gen, args=(res.group(2), lambda:traffic_en, ))
thread.start()
try:
for rx_i in range(10):
recv_resp_poke(rx_i)
finally:
traffic_en = 0
thread.join()
dut.expect_unity_test_output()
@pytest.mark.esp32 @pytest.mark.esp32
@pytest.mark.ip101 @pytest.mark.ip101

View File

@ -336,17 +336,16 @@ void emac_hal_start(emac_hal_context_t *hal)
/* Flush Transmit FIFO */ /* Flush Transmit FIFO */
emac_ll_flush_trans_fifo_enable(hal->dma_regs, true); emac_ll_flush_trans_fifo_enable(hal->dma_regs, true);
/* Flush Receive FIFO */
emac_ll_flush_recv_frame_enable(hal->dma_regs, true); /* Start DMA transmission */
emac_ll_start_stop_dma_transmit(hal->dma_regs, true);
/* Start DMA reception */
emac_ll_start_stop_dma_receive(hal->dma_regs, true);
/* Enable transmit state machine of the MAC for transmission on the MII */ /* Enable transmit state machine of the MAC for transmission on the MII */
emac_ll_transmit_enable(hal->mac_regs, true); emac_ll_transmit_enable(hal->mac_regs, true);
/* Enable receive state machine of the MAC for reception from the MII */ /* Enable receive state machine of the MAC for reception from the MII */
emac_ll_receive_enable(hal->mac_regs, true); emac_ll_receive_enable(hal->mac_regs, true);
/* Start DMA transmission */
emac_ll_start_stop_dma_transmit(hal->dma_regs, true);
/* Start DMA reception */
emac_ll_start_stop_dma_receive(hal->dma_regs, true);
/* Clear all pending interrupts */ /* Clear all pending interrupts */
emac_ll_clear_all_pending_intr(hal->dma_regs); emac_ll_clear_all_pending_intr(hal->dma_regs);
@ -356,18 +355,24 @@ esp_err_t emac_hal_stop(emac_hal_context_t *hal)
{ {
/* Stop DMA transmission */ /* Stop DMA transmission */
emac_ll_start_stop_dma_transmit(hal->dma_regs, false); emac_ll_start_stop_dma_transmit(hal->dma_regs, false);
/* Stop DMA reception */
emac_ll_start_stop_dma_receive(hal->dma_regs, false);
if (emac_ll_transmit_frame_ctrl_status(hal->mac_regs) != 0x0) { if (emac_ll_transmit_frame_ctrl_status(hal->mac_regs) != 0x0) {
/* Previous transmit in progress */ /* Previous transmit in progress */
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
} }
/* Disable receive state machine of the MAC for reception from the MII */
emac_ll_transmit_enable(hal->mac_regs, false);
/* Disable transmit state machine of the MAC for transmission on the MII */ /* Disable transmit state machine of the MAC for transmission on the MII */
emac_ll_receive_enable(hal->mac_regs, false); emac_ll_receive_enable(hal->mac_regs, false);
/* Disable receive state machine of the MAC for reception from the MII */
emac_ll_transmit_enable(hal->mac_regs, false);
if (emac_ll_receive_read_ctrl_state(hal->mac_regs) != 0x0) {
/* Previous receive copy in progress */
return ESP_ERR_INVALID_STATE;
}
/* Stop DMA reception */
emac_ll_start_stop_dma_receive(hal->dma_regs, false);
/* Disable Ethernet MAC and DMA Interrupt */ /* Disable Ethernet MAC and DMA Interrupt */
emac_ll_disable_all_intr(hal->dma_regs); emac_ll_disable_all_intr(hal->dma_regs);

View File

@ -342,6 +342,11 @@ static inline uint32_t emac_ll_transmit_frame_ctrl_status(emac_mac_dev_t *mac_re
return mac_regs->emacdebug.mactfcs; return mac_regs->emacdebug.mactfcs;
} }
static inline uint32_t emac_ll_receive_read_ctrl_state(emac_mac_dev_t *mac_regs)
{
return mac_regs->emacdebug.mtlrfrcs;
}
/* emacmiidata */ /* emacmiidata */
static inline void emac_ll_set_phy_data(emac_mac_dev_t *mac_regs, uint32_t data) static inline void emac_ll_set_phy_data(emac_mac_dev_t *mac_regs, uint32_t data)
{ {

View File

@ -232,7 +232,7 @@ void emac_hal_start(emac_hal_context_t *hal);
* @param hal EMAC HAL context infostructure * @param hal EMAC HAL context infostructure
* @return * @return
* - ESP_OK: succeed * - ESP_OK: succeed
* - ESP_ERR_INVALID_STATE: previous frame transmission is not completed. When this error occurs, * - ESP_ERR_INVALID_STATE: previous frame transmission/reception is not completed. When this error occurs,
* wait and reapeat the EMAC stop again. * wait and reapeat the EMAC stop again.
*/ */
esp_err_t emac_hal_stop(emac_hal_context_t *hal); esp_err_t emac_hal_stop(emac_hal_context_t *hal);