Ethernet initialization error handling

Oromis
Posts: 21
Joined: Mon Sep 25, 2017 1:44 pm

Ethernet initialization error handling

Postby Oromis » Mon Jul 02, 2018 3:37 pm

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

ESP_Sprite
Posts: 9766
Joined: Thu Nov 26, 2015 4:08 am

Re: Ethernet initialization error handling

Postby ESP_Sprite » Tue Jul 03, 2018 2:13 am

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.

Oromis
Posts: 21
Joined: Mon Sep 25, 2017 1:44 pm

Re: Ethernet initialization error handling

Postby Oromis » Tue Jul 03, 2018 8:43 am

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?

ESP_Sprite
Posts: 9766
Joined: Thu Nov 26, 2015 4:08 am

Re: Ethernet initialization error handling

Postby ESP_Sprite » Wed Jul 04, 2018 6:52 am

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.

Oromis
Posts: 21
Joined: Mon Sep 25, 2017 1:44 pm

Re: Ethernet initialization error handling

Postby Oromis » 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!

burkulesomesh43
Posts: 132
Joined: Tue Aug 14, 2018 6:21 am
Location: India

Re: Ethernet initialization error handling

Postby burkulesomesh43 » Tue Dec 25, 2018 10:02 am

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.
--
Somesh Burkule

burkulesomesh43
Posts: 132
Joined: Tue Aug 14, 2018 6:21 am
Location: India

Re: Ethernet initialization error handling

Postby burkulesomesh43 » Tue Dec 25, 2018 12:38 pm

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()) {
--
Somesh Burkule

Oromis
Posts: 21
Joined: Mon Sep 25, 2017 1:44 pm

Re: Ethernet initialization error handling

Postby Oromis » 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);
.

burkulesomesh43
Posts: 132
Joined: Tue Aug 14, 2018 6:21 am
Location: India

Re: Ethernet initialization error handling

Postby burkulesomesh43 » Tue Jun 11, 2019 10:41 am

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.
--
Somesh Burkule

Who is online

Users browsing this forum: KMTsvetanov, snutw_ and 123 guests