Skip to main content
Associate
June 10, 2026
Question

NetX Duo Ethernet Initialization Timing Issue

  • June 10, 2026
  • 5 replies
  • 81 views


In many NetX Duo Ethernet sample projects, HAL_ETH_Start_IT() is not called depending on
the timing of the PHY link‑up. When this happens, Ethernet communication never starts.

The behavior appears to be related to how App_Link_Thread_Entry() handles link status:

- nx_ip_interface_status_check() is used to check the NX_IP_LINK_ENABLED status.
- nx_ip_driver_direct_command() issues NX_LINK_ENABLE or NX_LINK_DISABLE when the PHY link state changes.

Normal behavior

- If the PHY is link-up at startup, HAL_ETH_Start_IT() is called during initialization.
- If the PHY is link-down at startup, App_Link_Thread_Entry() later detects the link-up,
  issues NX_LINK_ENABLE, and HAL_ETH_Start_IT() is called correctly.

Problem case

If the PHY is link-down at startup but becomes link-up before App_Link_Thread_Entry() starts running:

- The thread does not issue NX_LINK_ENABLE or NX_LINK_DISABLE.
- HAL_ETH_Start_IT() is not called.
- Ethernet communication cannot be established.

In PoE environments, the power‑on to link‑up time is generally consistent,
but it depends on the specific equipment used, such as the PoE injector.
With certain devices, this issue occurs frequently.
Although reconnecting the Ethernet cable restores communication, this is not feasible in PoE setups.

Is there a recommended approach to ensure that HAL_ETH_Start_IT() is always called regardless of the PHY link‑up timing?
 

 
 

 

 

5 replies

Pavel A.
June 10, 2026

Hi,

This does not look reasonable. If the link is down at startup but becomes up later, the App_Link task will detect it and call HAL_ETH_Start_IT. Debug to find why the App_Link_Thread_Entry won’t run.

 

KikuyaAuthor
Associate
June 11, 2026

The thread is running correctly. The issue is that the link-state transition is missed.

Here is the relevant part of App_Link_Thread_Entry():


static VOID App_Link_Thread_Entry(ULONG thread_input)
{
ULONG actual_status;
UINT linkdown = 0, status;

while (1)
{
/* Send request to check if the Ethernet cable is connected. */
status = nx_ip_interface_status_check(&NetXDuoEthIpInstance, 0,
NX_IP_LINK_ENABLED,
&actual_status, 10);

if (status == NX_SUCCESS)
{
if (linkdown == 1)
{
linkdown = 0;

/* The network cable is connected. */
printf("The network cable is connected.\n");

/* Send request to enable PHY Link. */
nx_ip_driver_direct_command(&NetXDuoEthIpInstance,
NX_LINK_ENABLE,
&actual_status);

/* Remaining DHCP-related code omitted */
}
}
else
{
if (linkdown == 0)
{
linkdown = 1;

/* The network cable is not connected. */
printf("The network cable is not connected.\n");

nx_ip_driver_direct_command(&NetXDuoEthIpInstance,
NX_LINK_DISABLE,
&actual_status);
}
}

tx_thread_sleep(NX_APP_CABLE_CONNECTION_CHECK_PERIOD);
}
}


App_Link_Thread_Entry() issues NX_LINK_ENABLE only when it detects a transition from link-down to link-up.
The previous link state is tracked by linkdown, which is initialized to 0.
Because of this initialization, if the PHY is already link-up when the thread starts,
the first status check returns NX_SUCCESS while linkdown is still 0.
The condition if (linkdown == 1) is therefore false, so NX_LINK_ENABLE is never issued.
As a result, HAL_ETH_Start_IT() is never called and the Ethernet interface remains disabled.
The thread is running, but in this scenario it never observes a link-down to link-up transition, so NX_LINK_ENABLE is not issued.

To recover, I currently have to unplug the Ethernet cable once to force a link-down event.
After reconnecting it, the thread detects the link-down to link-up transition and correctly issues NX_LINK_ENABLE.

Therefore, the thread is running, but the initial link-up condition is missed because no link-down to link-up transition is observed.

 

KikuyaAuthor
Associate
June 11, 2026

While investigating this further, I noticed a difference in the implementation of _nx_driver_enable()
between X-CUBE-AZRTOS-F7 (1.1.0) and X-CUBE-AZRTOS-H7 (3.5.0), which are the latest available versions.

_nx_driver_enable() is called when NX_LINK_ENABLE is issued, and it is responsible for starting
the Ethernet interface by calling HAL_ETH_Start_IT().

In the F7 package, _nx_driver_enable() reads the PHY link state only once:


PHYLinkState = nx_eth_phy_get_link_state();

In contrast, the H7 package waits for the PHY to become link-up before proceeding:


tickstart = HAL_GetTick();

do
{
PHYLinkState = nx_eth_phy_get_link_state();

} while ((PHYLinkState <= ETH_PHY_STATUS_LINK_DOWN) && ((HAL_GetTick() - tickstart) < PHY_LINK_TIMEOUT));

PHY_LINK_TIMEOUT is defined in nx_stm32_eth_driver.h as:


#ifndef PHY_LINK_TIMEOUT
#define PHY_LINK_TIMEOUT (5000U)
#endif

Because of this, the H7 implementation appears to be more tolerant of delayed PHY link-up events 
and may be less susceptible to the timing issue described above.

This issue has been observed on actual STM32F767 hardware in a PoE environment.
Would it make sense to apply the H7 implementation to the F7 driver as well?

One question I have is whether blocking in this loop for a prolonged period could have any undesirable side effects,
since _nx_driver_enable() can be called from App_Link_Thread_Entry().
 

Pavel A.
June 11, 2026

In line 5 linkdown is a local variable initialized to 0, for some mysterious reason. Of course, in line 16 if (linkdown == 1) is false.

Please fix this logic and it will properly detect the link state.

 

 

KikuyaAuthor
Associate
June 12, 2026

You are correct that initializing linkdown to 0 is the root cause of the issue.
I do not know why the sample code was implemented this way either.

For now, I modified App_Link_Thread_Entry() to check the PHY link state using NX_IP_LINK_ENABLED when the thread starts and initialize linkdown accordingly.
If the PHY is already link-up, linkdown is set to 0. In that case, NX_LINK_ENABLE is issued immediately. Even if NX_LINK_ENABLE has already been issued, it appears that NX_ALREADY_ENABLED is returned, so this does not cause any problems.

I also decided not to port the H7 _nx_driver_enable() implementation to the F7 driver.

After checking multiple NetX Duo Ethernet sample projects, I found that all of them use the same App_Link_Thread_Entry() implementation with linkdown initialized to 0.
It would be helpful if ST could correct this in the sample projects.

For reference, I have never observed this issue with FreeRTOS + LwIP under the same conditions.