This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MCU-PLUS-SDK-AM243X: The App_shutdownNetworkStack function is not functioning properly.

Part Number: MCU-PLUS-SDK-AM243X
Other Parts Discussed in Thread: SYSCONFIG, AM2431

Tool/software: AM243X-EVM

Hello,

I am currently testing the dual-MAC functionality of icssg using the AM243x EVM.

Both Ethernet ports are in use, and the system is based on FreeRTOS.

In the simplest example, icssg tcpserver, the App_shutdownNetworkStack() function within the app_main.c file exists inside the appMain function.

However, it is located below an infinite loop and therefore never actually executes.

For the feature I'm developing, there are cases where I need to bring down the lwip stack.

I modified the code to exit the while(1) loop under specific conditions and then execute App_shutdownNetworkStack().

static void App_shutdownNetworkStack()
{
    for (uint32_t netifIdx = 0U; netifIdx < ENET_SYSCFG_NETIF_COUNT; netifIdx++)
    {
        LwipifEnetApp_netifClose(hlwipIfApp, NETIF_INST_ID0 + netifIdx);
    }
    return;
}

However, this function has the following issues:

1. The LwipifEnetApp_netifClose function called within this function invokes netif_remove.

The netif_remove function checks LWIP's core lock.

Therefore, calling this function directly causes the MCU to enter an infinite loop due to an assert.

So, I modified this function as follows.

static void App_shutdownNetworkStack()
{
    sys_lock_tcpip_core();  // Add for netif_remove
    
    for (uint32_t netifIdx = 0U; netifIdx < ENET_SYSCFG_NETIF_COUNT; netifIdx++)
    {
        LwipifEnetApp_netifClose(hlwipIfApp, NETIF_INST_ID0 + netifIdx);
    }
    
    sys_unlock_tcpip_core();    // Add for netif_remove
    return;
}

2. After calling the modified function, the first netif out of two appears to be removed normally.

However, when removing the second netif, it enters an infinite loop due to an assert.

The reason the second netif_remove enters an infinite loop can be found in the Lwip2Enet_close function within the lwip2enet.c file of the mcu+sdk.

The first part of this function is as follows.

void Lwip2Enet_close(Lwip2Enet_Handle hLwip2Enet, struct netif *netif)
{
    Lwip2Enet_assert(NULL != hLwip2Enet);

    /* Stop and delete the tick timer */
    ClockP_stop(&hLwip2Enet->pacingClkObj);
    ClockP_destruct(&hLwip2Enet->pacingClkObj);

    Lwip2Enet_netif_t* pInterface = Lwip2Enet_getInterfaceObj(hLwip2Enet, netif);
    
    .....
}

This function stops and releases the Clock of the pacingClkObj associated with the hLwip2Enet handle.

The problem is that the hLwip2Enet handle is not allocated per netif. This handle appears to exist as a single instance within LWIP.

** The Lwip2Enet_allocateObj function does not allocate an additional handle if hLwip2Enet is already allocated.

Therefore, the second `netif_remove` is called while the `pacingClkObj` of `hLwip2Enet` has already been removed during the first `netif_remove`.

At this point, since pacingClkObj is no longer valid, an assert causes an infinite loop.

Please fix this part.

Also, if there is a workaround that can be implemented before this is reflected in the next SDK release, please let me know.

Best Regards,

Jinwon Jang

  • Hi Jinwon Jang,

    This is a known issue that we identified within the feature set of LwIP interface. We are working on it to get it fixed. A partial implementation is currently available which you can test in your setup to deal with this. I am attaching a patch file to implement the same.

    Please note that there are few issues still persistent in the patch related to handling the flags properly. This is only a reference implementation, and is not a production-ready code which can be integrated into the final product.

    Please let us know if you face any issues verifying this implementation.

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/908/0001_2D00_all_2D00_socs_2D00_LwIP_2D00_Enet_2D00_Fix_2D00_LwIP_2D00_close_2D00_sequence.patch

    Regards,
    Teja.

  • Hi Teja,

    Thank you for your prompt reply.

    I'm writing because I encountered an issue while applying the patch file you attached.

    In appmain.c, there's a call to the killFlag function.

    However, the killFlag function cannot be found, causing a compilation error.

    Can you explain what this function does?

    Best regards,
    Jinwon Jang

  • Hi Jinwon Jang,

    Please let me check this on my end and send you the updated patch. Since the following 2 days are holidays in India, please expect a response on or before Monday.

    Thanks for your patience,

    Regards,
    Teja.

  • Hi Teja,

    I still haven't received the updated patch you said you would send last time.

    Please let me know when I can expect it.

    Thank you for your assistance.

    Best Regards,

    Jinwon Jang

  • Hi Jinwon Jang,

    Firstly, apologies for the delay. I am occupied in another internal commitment. 

    Coming to the Killflag function, it sets the shutdown function for the poll function. You can enable it by adding the following piece of code in $(mcu_plus_sdk)/source/networking/enet/core/sysconfig/networking/.meta/enet_cpsw/templates/enet_lwipif.c.xdt, right before LwipifEnetApp_destructPollTask function.

    void KillFlag (LwipifEnetApp_Handle handle)
    {
        LwipifEnetApp_Object* pObj = (LwipifEnetApp_Object*) handle;
        pObj->task.pollTask.shutDownFlag = true;
    }

    This should solve the compilation issue and enable the functionality. Please let me know if there are any further queries.

    Regards,
    Teja.

  • Hi Teja,

    Thank you for your response.

    Even after applying the patch you provided, the result remains the same.
    When removing the second netif, the issue still occurs at the same point where problems were previously encountered.

    To summarize the cause of the problem once more:

    1. The App_shutdownNetworkStack() function calls the LwipifEnetApp_netifClose() function for each netif opened via the for loop, attempting to close them all.
    2. The LwipifEnetApp_netifClose() function calls the netif_remove() function.
    3. The netif_remove() function removes the netif and finally calls the function registered in the remove_callback (LWIPIF_LWIP_stop() function).
    4. The LWIPIF_LWIP_stop() function calls the Lwip2Enet_close() function.
    5. The Lwip2Enet_close() function stops and destructs hLwip2Enet->pacingClkObj (Timer).
    6. hLwip2Enet is not allocated individually for each netif. Even if two netifs are added, only one hLwip2Enet (Handler) is allocated. (See the Lwip2Enet_allocateObj() function)
    7. Consequently, when the Lwip2Enet_close() function is called to close the second netif, it attempts to stop hLwip2Enet->pacingClkObj.
    8. However, hLwip2Enet->pacingClkObj was already removed when the first netif was removed, causing an assert to occur.

    While using only one netif should be fine, this sequence clearly has issues when using two netifs.

    Please review and fix this part.

    Thank you.

    Best Regards,
    Jinwon Jang

  • Hi Teja,

    While debugging Lwip2Enet_Close() function, I discovered a critical error in the code.
    This error stems from LWIPIF_LWIP_stop() function passing an incorrect handle to Lwip2Enet_Close() function.

    static void LWIPIF_LWIP_stop(struct netif *netif)
    {
        Lwip2Enet_Handle hLwip2Enet;
    
        /* Get the pointer to the private data */
        hLwip2Enet = (Lwip2Enet_Handle)netif->state;
    
        /* Call low-level close function */
        Lwip2Enet_close(hLwip2Enet, netif);
    
        /* Enet controller has been stopped. */
    }
    

    In this code, netif->state represents a Lwip2Enet_netif_t *.
    However, this variable is cast to Lwip2Enet_Handle.


    If netif is the first interface, this section functions without issue.
    However, if netif is the second interface, hLwip2Enet is mapped to an incorrect address.

    I attempted to fix this section, but in lwip2enet.c, the only way is to directly assign the address of gLwip2EnetObj as global variable.
    However, I do not consider this a good approach.


    Since the LWIPIF_LWIP_stop() function is the callback function for netif_remove(), it is reasonable that it should only handle the close operation for the interface.

    Therefore, I think it would be more consistent for Lwip2Enet_Close() to take a Lwip2Enet_netif_t * as its argument, rather than a Lwip2Enet_Handle, and it would be better to handle the close of Lwip2Enet_Handle in a separate function.

    Please review this part.

    Thank you.

    Best Regars,

    Jinwon Jang

  • Hi Jinwon Jang,

    In this code, netif->state represents a Lwip2Enet_netif_t *.
    However, this variable is cast to Lwip2Enet_Handle.

    The first member in the Lwip2Enet_Handle, which is a pointer for a library object Lwip2Enet_Obj, is Lwip2Enet_netif_t. So, we can access both of them with the same pointer. Irrespective of how many netifs are used, there will be only one hLwip2Enet, which was earlier being closed during the close sequence of first netif.

    I have tested the patch again internally for single netif usecases. I will forward the request to our development team to extend this implementation for dual netif usecase. I will update the details on the ETA in 2 days time. 

    Regards,
    Teja.

  • Hi Teja,

    Thank you for reply quickly.

    As shown in the Lwip2Enet_open() function code below, when opening two netifs, each netif->state points to a different Lwip2Enet_netif_t variable.

    Lwip2Enet_Handle Lwip2Enet_open(Enet_Type enetType, uint32_t instId, struct netif *netif, uint32_t netifIdx)
    {
        uint32_t txChIdList[LWIPIF_MAX_TX_CHANNELS_PER_PHERIPHERAL];
        uint32_t rxChIdList[LWIPIF_MAX_RX_CHANNELS_PER_PHERIPHERAL];
        EnetApp_GetMacAddrOutArgs          macAddr;
        EnetApp_HandleInfo                 handleInfo;
        Lwip2Enet_Handle    hLwip2Enet = Lwip2Enet_allocateObj();
        Lwip2Enet_netif_t*  pInterface = &(hLwip2Enet->interfaceInfo[hLwip2Enet->numOpenedNetifs++]);
        gInterface = pInterface;
        uint32_t txChIdCount = 0, rxChIdCount = 0;
    
        /* .... */
        
    
        /* Updating the netif params */
        Lwip2Enet_assert(netif->hwaddr_len == ENET_MAC_ADDR_LEN);
        netif->state = (void *)pInterface;
        
        if (hLwip2Enet->isInitDone == false)
        {
            /* Enable polling mode only when interrupt mode is disabled */
            if((pInterface->hRx[0]->disableEvent) && (pInterface->hTx[0]->disableEvent)){
                
                hLwip2Enet->isInitDone = true;
                Lwip2Enet_createTimer(hLwip2Enet);
                ClockP_start(&hLwip2Enet->pacingClkObj);
                /* assert if clk period is not valid  */
                Lwip2Enet_assert(0U != hLwip2Enet->appInfo.timerPeriodUs);
            }
        }
        
        return hLwip2Enet;
    }
    
    

    It is actually mapped as follows.

    • The first netif->state points to hLwip2Enet->interfaceInfo[0].
    • The second netif->state points to hLwip2Enet->interfaceInfo[1].

    Therefore, the first netif->state matches the address of hLwip2Enet, as you mentioned, but the second netif->state does not match the address of hLwip2Enet.

    So, when removing the second netif, an incorrect hLwip2Enet address is passed to the Lwip2Enet_close() function.


    Best Regards,

    Jinwon Jang

  • Hi Jinwon Jang,

    I have understood the issue here. To handle this better, we need to make some fundamental changes to the sequence. The development team intimated that the close sequence fix would not be available atleast till Q1 of 2026. I will plan to take this activity. Since I am already in some commitments and the team is away for next week due to holidays in India, I would need about 3 weeks to finish this implementation. 

    Please let me know if you are ok with the timelines. Thank you for your patience.

    Regards,
    Teja.

  • Hi, Teja,

    I agree with your opinion that fundamental changes are necessary.

    I'm currently working on some fixes to the SDK, but since I'm still in the process of understanding this part of the code,
    I expect your solution will be much more logically clear.

    Thank you.

    Best Regards,

    Jinwon Jang

  • Hi Jinwon Jang,

    The requirement is being tracked internally, and this wouldn't be available as part of the standard SDK atleast until end of Q1 2026. Please let us know if this timeline works for you. If not, we have to check for the possible next steps.

    Thanks and regards,
    Teja.

  • Hi Teja,

    I understand your circumstances.

    However, due to our development schedule, significant delays have already occurred, and Q2 next year is too late.

    Our product validation testing is scheduled for Q1 next year, so implementation should be completed by the end of January.

    Please consider feasible solutions to meet our schedule, despite the challenges.

    Trank you.

    Best regards,

    Jinwon Jang

  • Hi Jinwon Jang,

    Please let me check with our team, and get back with feasibility analysis. We will check if it would be possible to take it up in the time line that you need, i.e, implementation done by end of January.

    Please expect a response by EoD tomorrow.

    Thanks and regards,
    Teja.

  • Hi Jinwong Jang,

    Can you please provide some more details on the project that you need this for, and why you need this functionality for the application? This gives us more context, and take these into consideration in our planning. I still cannot confirm that this feature will be available by January. I am in discussions with the internal team regarding the same.

    Thanks and regards,
    Teja.

  • Hi Teja,

    I apologize in advance that I cannot provide detailed explanations about our project's contents due to regulations.

    To explain roughly, the following features are required:

    • Designed using the AM2431, an R5 single-core processor.
      • There are about a dozen models using this platform.
    • Some models use two 1Gb Fiber Ethernet ports for redundancy.
      • The two Ethernet ports connect to separate PHY ICs using the dual-MAC functionality of the AM2431's icssg0.
    •  A switch between the bootloader and firmware is required for firmware management, similar to DOS.
      • When the firmware exits, it must hold the last values of existing outputs (including analog and digital).
      • Because of the last-value hold feature for outputs, the firmware must switch to the bootloader without resetting the MCU.
      • Due to this feature, models using Ethernet require LWIP to exit gracefully for re-entry into the firmware.

    The model we are developing is a change to a new platform due to processor obsolete issues in the legacy model.

    Therefore, the user experience must be as same as possible for cross-use between the legacy model and the new model.

    The most important aspects of the SDK's LWIP are the following two points:

    • Stability of communication using the two Ethernet ports
    • Stability of LWIP's initialization and deinitialization operations

    We request your review of these points.

    Thank you.

    Best Regards,

    Jinwon Jang

  • Hi Jinwong Jang,

    Please don't get me wrong, but can you also please give some insights on the end equipment, and the scale of the project? This helps us in understanding the volumes, and planning accordingly. 

    Thanks and regards,
    Teja.