Page 1 of 1

Ethernet initialization error handling

Posted: Mon Jul 02, 2018 3:37 pm
by Oromis
Hi,

our ESP32-based product can connect to the internet via either WiFi or Ethernet. We recently had the problem that the LAN8720 chip broke for a customer and this prevented the ESP32 from starting up at all. We use the following code to initialize the ethernet module:

Code: Select all

    esp_err_t ret;

    eth_config_t config = DEFAULT_ETHERNET_PHY_CONFIG;
    config.phy_addr = PHY1;
    config.gpio_config = eth_gpio_config_rmii;
    config.tcpip_input = tcpip_adapter_eth_input;
    config.clock_mode = ETH_CLOCK_GPIO0_IN;
    config.phy_power_enable = phy_device_power_enable_via_gpio;

    ret = esp_eth_init(&config);
This works great when the LAN8720 is connected, but causes the ESP32 to get stuck in an infinite loop trying to reset the PHY until the task watchdog kills it and restarts the microcontroller. The output in the console:

Code: Select all

I (3385) emac: emac start !!!

I (3389) emac: emac resetting ....
I (3393) emac: emac resetting ....
I (3397) emac: emac resetting ....
I (3401) emac: emac resetting ....
I (3405) emac: emac resetting ....
I (3409) emac: emac resetting ....
I (3415) emac: emac resetting ....
I (3419) emac: emac resetting ....
[...]
I (8017) emac: emac resetting ....
I (8021) emac: emac resetting ....
I (8025) emaTask watchdog got triggered. The following tasks did not reset the watchdog in time:
 - IDLE (CPU 0)
Tasks currently running:
CPU 0: emacT
Aborting.
abort() was called at PC 0x400d1145 on core 0
So, this chunk of code within the internal "emacT" task is the culprit:

Code: Select all

void emac_reset(void)
{
    REG_SET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST);

    while (REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1) {
        //nothing to do ,if stop here,maybe emac have not clk input.
        ESP_LOGI(TAG, "emac resetting ....");
    }

    ESP_LOGI(TAG, "emac reset done");
}
This looks like a bug to me. If the PHY cannot be initialized, then some function should fail instead of bringing down the entire chip. In our case, our product would continue working (via WiFi) even if a portion of it malfunctioned.

Is there any chance that this might be fixed shortly? Is there any way to work around the issue (i.e. some way to check whether a PHY is actually connected to the ESP32 before trying to initialize it)?

Thanks a lot!

David

Re: Ethernet initialization error handling

Posted: Tue Jul 03, 2018 2:13 am
by ESP_Sprite
Suggest you post this as a bug on the ESP-IDF [url=https://github.com/espressif/esp-idf/issues]Github[/]. This is certainly something that can be amended, though.

Re: Ethernet initialization error handling

Posted: Tue Jul 03, 2018 8:43 am
by Oromis
Thanks for the response. I posted it on the Github issue tracker: https://github.com/espressif/esp-idf/issues/2141.

Do you have any idea on how to work around the issue?

Re: Ethernet initialization error handling

Posted: Wed Jul 04, 2018 6:52 am
by ESP_Sprite
You could copy the ethernet component to a components/ folder in your own project (so esp-idf prefers it above it's built-in ethernet component), and modify the emac_reset function there to have a fixed retry count... I'm not to familiar with the emac code to tell you if that would also make the rest of the code fail gracefully, though.

Re: Ethernet initialization error handling

Posted: Fri Jul 06, 2018 1:37 pm
by Oromis
Thanks for the suggestion. After a bit of trial and error I was able to modify the ethernet code to do what I want:

New implementation of emac_reset() in emac_dev.c:

Code: Select all

bool emac_reset(void)
{
    REG_SET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST);

    int retryCount = 0;
    const int maxRetries = 500 / portTICK_PERIOD_MS;    // Max 500ms timeout
    while (REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1 && ++retryCount < maxRetries) {
        // Wait for the emac to reset. If it doesn't respond, it is probably broken or not connected,
        // so we'll return an error code instead of looping indefinitely.
        vTaskDelay((TickType_t) 1);
    }

    if(REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1) {
        ESP_LOGW(TAG, "emac reset failed");
        return false;
    }

    ESP_LOGI(TAG, "emac reset done");
    return true;
}
And in emac_main.c, I added a emac_reset() call before the background task is launched. This way, esp_eth_init() will fail gracefully if the PHY is not connected at the expense of one otherwise unnecessary reset operation (which is blindingly quick if the PHY is connected normally). In function esp_eth_init_internal():

Code: Select all

    ESP_LOGI(TAG, "mac version %04xa", emac_read_mac_version());
    emac_hw_init();
    emac_macaddr_init();

    //watchdog  TODO

    // ---- New code ----
    if(!emac_reset()) {
      emac_enable_clk(false);
      return ESP_ERR_TIMEOUT;
    }
    // ---- End of new code ----

    //init task for emac
    emac_g_sem = xSemaphoreCreateBinary();
    emac_rx_xMutex = xSemaphoreCreateRecursiveMutex();
    emac_tx_xMutex = xSemaphoreCreateRecursiveMutex();
    emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t));
    xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl);
I think this is a sensible solution and could be integrated into the main ESP-IDF code with no noticeable drawbacks.

Thanks for your support!

Re: Ethernet initialization error handling

Posted: Tue Dec 25, 2018 10:02 am
by burkulesomesh43
Oromis wrote:
Fri Jul 06, 2018 1:37 pm
Thanks for the suggestion. After a bit of trial and error I was able to modify the ethernet code to do what I want:

New implementation of emac_reset() in emac_dev.c:

Code: Select all

bool emac_reset(void)
{
    REG_SET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST);

    int retryCount = 0;
    const int maxRetries = 500 / portTICK_PERIOD_MS;    // Max 500ms timeout
    while (REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1 && ++retryCount < maxRetries) {
        // Wait for the emac to reset. If it doesn't respond, it is probably broken or not connected,
        // so we'll return an error code instead of looping indefinitely.
        vTaskDelay((TickType_t) 1);
    }

    if(REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1) {
        ESP_LOGW(TAG, "emac reset failed");
        return false;
    }

    ESP_LOGI(TAG, "emac reset done");
    return true;
}
And in emac_main.c, I added a emac_reset() call before the background task is launched. This way, esp_eth_init() will fail gracefully if the PHY is not connected at the expense of one otherwise unnecessary reset operation (which is blindingly quick if the PHY is connected normally). In function esp_eth_init_internal():

Code: Select all

    ESP_LOGI(TAG, "mac version %04xa", emac_read_mac_version());
    emac_hw_init();
    emac_macaddr_init();

    //watchdog  TODO

    // ---- New code ----
    if(!emac_reset()) {
      emac_enable_clk(false);
      return ESP_ERR_TIMEOUT;
    }
    // ---- End of new code ----

    //init task for emac
    emac_g_sem = xSemaphoreCreateBinary();
    emac_rx_xMutex = xSemaphoreCreateRecursiveMutex();
    emac_tx_xMutex = xSemaphoreCreateRecursiveMutex();
    emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t));
    xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl);
I think this is a sensible solution and could be integrated into the main ESP-IDF code with no noticeable drawbacks.

Thanks for your support!
Hi,
I am dealing with same issue.
if there in no ethernet components on board it will gives emac reset error.
I can't get your solution. can you please explain briefly.

Re: Ethernet initialization error handling

Posted: Tue Dec 25, 2018 12:38 pm
by burkulesomesh43
Oromis wrote:
Fri Jul 06, 2018 1:37 pm
Thanks for the suggestion. After a bit of trial and error I was able to modify the ethernet code to do what I want:

New implementation of emac_reset() in emac_dev.c:

Code: Select all

bool emac_reset(void)
{
    REG_SET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST);

    int retryCount = 0;
    const int maxRetries = 500 / portTICK_PERIOD_MS;    // Max 500ms timeout
    while (REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1 && ++retryCount < maxRetries) {
        // Wait for the emac to reset. If it doesn't respond, it is probably broken or not connected,
        // so we'll return an error code instead of looping indefinitely.
        vTaskDelay((TickType_t) 1);
    }

    if(REG_GET_BIT(EMAC_DMABUSMODE_REG, EMAC_SW_RST) == 1) {
        ESP_LOGW(TAG, "emac reset failed");
        return false;
    }

    ESP_LOGI(TAG, "emac reset done");
    return true;
}
And in emac_main.c, I added a emac_reset() call before the background task is launched. This way, esp_eth_init() will fail gracefully if the PHY is not connected at the expense of one otherwise unnecessary reset operation (which is blindingly quick if the PHY is connected normally). In function esp_eth_init_internal():

Code: Select all

    ESP_LOGI(TAG, "mac version %04xa", emac_read_mac_version());
    emac_hw_init();
    emac_macaddr_init();

    //watchdog  TODO

    // ---- New code ----
    if(!emac_reset()) {
      emac_enable_clk(false);
      return ESP_ERR_TIMEOUT;
    }
    // ---- End of new code ----

    //init task for emac
    emac_g_sem = xSemaphoreCreateBinary();
    emac_rx_xMutex = xSemaphoreCreateRecursiveMutex();
    emac_tx_xMutex = xSemaphoreCreateRecursiveMutex();
    emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t));
    xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl);
I think this is a sensible solution and could be integrated into the main ESP-IDF code with no noticeable drawbacks.

Thanks for your support!
I am getting this error->>

Code: Select all

In function 'esp_eth_init_internal':
D:/source_codes/nibleWay/v2.0/ethernetTest/components/ethernet/emac_main.c:1099:9: error: invalid use of void expression
         if(!emac_reset()) {

Re: Ethernet initialization error handling

Posted: Fri Jan 11, 2019 9:41 am
by Oromis
True, you also need to go to the header file containing

Code: Select all

emac_reset()
(emac_dev.h) and change its return type from

Code: Select all

void emac_reset(void);
to

Code: Select all

bool emac_reset(void);
.

Re: Ethernet initialization error handling

Posted: Tue Jun 11, 2019 10:41 am
by burkulesomesh43
Oromis wrote:
Fri Jan 11, 2019 9:41 am
True, you also need to go to the header file containing

Code: Select all

emac_reset()
(emac_dev.h) and change its return type from

Code: Select all

void emac_reset(void);
to

Code: Select all

bool emac_reset(void);
.
Ok.
Thanks.