cancel
Showing results for 
Search instead for 
Did you mean: 

[NUCLEO-N657X0-Q] ETH1 MAC/DMA not receiving packets & Memory Management Tool problems

damole
Associate III

Hello STM32 Community,

I am developing a system on the NUCLEO-N657X0-Q using ThreadX and NetX Duo and I am having a hard time to make the ethernet work. I want to implement a web server in my system but I cannot make the Ethernet communicate.

Problem:
I am unable to receive or transmit any Ethernet frames. The PHY link establishes successfully (from what I see on my linux terminal, autonegotiation completes, Link is UP, and the LEDs on the plug turns on normally), but pinging the Nucleo's static IP yields no response. Wireshark shows no ARP or ICMP packets originating from the Nucleo.

Trying to debug, I see that the the MAC/DMA interrupt `ETH1_IRQHandler` is never triggered by incoming network traffic. It fires exactly twice during the initial board boot sequence(where, for what I understand, some DMA errors flag are raised), but remains completely silent afterward. From the logs that I send to the serial port it appears that from the NetXDuo point of view is everything fine.

The D-cache is disabled and for now I want to keep it disabled, because activating it breaks other parts of my project (I see that in the examples the Dcache is enabled, but I guess is only matters of performance, not mandatory, right?).
Among various attempts, I was also trying to use the Memory Management Tool to move the RAM and FLASH regions in order to assign explicitly regions for the Ethernet descriptors. However, The Tool does not let me move these regions (my project is in XiP mode, if it matters).

I have then some questions about these two related issues:

A) Ethernet and NetX Duo Initialization
1. Am I missing some software initialization step, or misconfiguring the hardware in some way? I went through many examples and I do not find an answer, also given that the configuration sometimes differ among them.

B) Memory Management Tool (XiP Mode)
2. Assuming explicit descriptor placement is necessary for a fix, what is the procedure to unlock, and modify the default memory regions within the STM32CubeMX Memory Management Tool, so that I can define custom memory segments?

Any guidance on resolving the MAC silence or navigating the memory configuration tool would be highly appreciated. Thank you. I attach a .zip file with all the code and the .ioc file.

15 REPLIES 15

Hello @damole,

The reason the descriptors are in a suspended state is that they are not initialized or configured by NetX Duo in the first place. If you inspect the NetX Duo initialization function in your project, you will find that it is empty. You cannot expect the board to respond to pings or send and receive frames when ARP, ICMP, and memory allocations are not set up.

Before debugging further, I suggest examining the example I mentioned in my first reply and stepping through it to observe the required initialization tasks.

Best regards,

To improve visibility of answered topics, please click 'Accept as Solution' on the reply that resolved your issue or answered your question.
ACR
Associate II

Hello @damole ,

 

I am wondering if you get any further now, because I am facing a similar issue.

 

Best regards, 

Hello @STackPointer64,

if you are referring to the MX_NetXDuo_Init function, I am aware that is empty, i left it empty on purpose becasue i defined a custom funcion for the initialization, based on the example that you are referring to. I did this because i wanted to centralize all the initializations in the App_ThreadX_Init function. The custom function is the NetworkManager_Init that I placed in the NetworkManager.c file, which is inspired by the code that you provided. Here is a snippet of this file:

#include "tx_api.h"
#include "nx_api.h"
#include "nx_stm32_eth_driver.h"
#include <stdbool.h>
#include <stdint.h>

#include "system_config.h"
#include "sys_logger.h"
#include "system_manager.h"
#include "thread_priorities.h"
#include "network_manager.h"

#include "jsmn.h"
#include "parsing_helpers.h"
#include "assets.h"

/* Enforce compilation without FileX dependencies */
#ifndef NX_WEB_HTTP_NO_FILEX
#define NX_WEB_HTTP_NO_FILEX
#include "filex_stub.h"
#endif

#include "nx_web_http_server.h"

#define DEFAULT_MEMORY_SIZE              1024

#ifndef DEFAULT_PAYLOAD_SIZE
#define DEFAULT_PAYLOAD_SIZE            1536
#endif

#ifndef DEFAULT_ARP_CACHE_SIZE
#define DEFAULT_ARP_CACHE_SIZE          1024
#endif

#define NX_APP_DEFAULT_TIMEOUT          (10 * NX_IP_PERIODIC_RATE)
#define PORT                            80
        
#define NUM_PACKETS                     30
#define SERVER_PACKET_SIZE              (NX_WEB_HTTP_SERVER_MIN_PACKET_SIZE * 2)

#define NX_MANAGER_PACKET_MEMORY_SIZE   ((DEFAULT_PAYLOAD_SIZE + sizeof(NX_PACKET)) * NUM_PACKETS)
#define WEB_SERVER_PACKET_MEMORY_SIZE   (SERVER_PACKET_SIZE * 4)

#define WEB_SERVER_MEMORY_SIZE          (SERVER_PACKET_SIZE * 4)
#define NX_IP_INSTANCE_MEMORY_SIZE      2 * 1024

#define WEB_SERVER_STACK_SIZE           4096
#define NX_SERVER_STACK_SIZE            2* DEFAULT_MEMORY_SIZE
#define NX_BOOT_STACK_SIZE              1024
        
#ifndef NX_APP_INSTANCE_PRIORITY        
#define NX_APP_INSTANCE_PRIORITY        NX_MANAGER_PRIORITY
#endif      
        
#define NX_APP_DEFAULT_IP_ADDRESS       IP_ADDRESS(10, 10, 10, 10)
#define NX_APP_DEFAULT_NET_MASK         IP_ADDRESS(255, 255, 255, 0)

/* NetX Duo Global Variables */
NX_PACKET_POOL     nx_packet_pool;
NX_PACKET_POOL     web_server_packet_pool;
NX_IP              nx_ip_instance;
NX_WEB_HTTP_SERVER HTTP_server;

TX_THREAD nx_boot_thread;
TX_THREAD nx_srv_thread;

static NX_WEB_HTTP_SERVER_MIME_MAP app_mime_maps[] =
{
  {"css", "text/css"},
  {"svg", "image/svg+xml"},
  {"png", "image/png"},
  {"jpg", "image/jpg"}
};

/* 
 * Statically Aligned Memory Arenas 
 * Using ULONG forces strict 32-bit hardware alignment.
 * The formula (SIZE + 3) / 4 ensures safe integer ceiling division.
 */
ULONG web_server_stack[(WEB_SERVER_STACK_SIZE + 3) / 4];
ULONG nx_srv_stack[(NX_SERVER_STACK_SIZE + 3) / 4];
ULONG nx_boot_stack[(NX_BOOT_STACK_SIZE + 3) / 4];

__attribute__((section(".NetXPoolSection"), aligned(32)))
ULONG nx_packet_pool_memory[(NX_MANAGER_PACKET_MEMORY_SIZE + 3) / 4];

__attribute__((section(".NetXPoolSection"), aligned(32)))
ULONG web_server_packet_pool_memory[(WEB_SERVER_PACKET_MEMORY_SIZE + 3) / 4]; 

ULONG ip_thread_stack_memory[(NX_IP_INSTANCE_MEMORY_SIZE + 3) / 4];
ULONG arp_cache_memory[(DEFAULT_ARP_CACHE_SIZE + 3) / 4];
ULONG web_server_memory[(WEB_SERVER_MEMORY_SIZE + 3) / 4];


/* Forward declaration for the Web Server Callback */
UINT webserver_request_notify_callback(NX_WEB_HTTP_SERVER *server_ptr, UINT request_type, CHAR *resource, NX_PACKET *packet_ptr);






/* ========================================================================= */
/* HTTP RESPONSE ABSTRACTION                                                 */
/* ========================================================================= */

static UINT WebServer_SendHTTPResponse(NX_WEB_HTTP_SERVER *server_ptr, const CHAR *response_string)
{
    NX_PACKET *resp_packet;
    UINT status;
    ULONG response_len = strlen(response_string);

    /* Allocate a packet for the response */
    status = nx_packet_allocate(server_ptr->nx_web_http_server_packet_pool_ptr, &resp_packet, NX_TCP_PACKET, NX_NO_WAIT);
    if (status == NX_SUCCESS) 
    {
        /* Append the response data to the packet */
        status = nx_packet_data_append(resp_packet, (VOID *)response_string, response_len, server_ptr->nx_web_http_server_packet_pool_ptr, NX_NO_WAIT);
        if (status == NX_SUCCESS) {
            status = nx_tcp_socket_send(&(server_ptr->nx_web_http_server_current_session_ptr->nx_tcp_session_socket), resp_packet, NX_NO_WAIT);
        } else {
            nx_packet_release(resp_packet);
            System_Log("[WebServer] ERROR: NetX Packet append failed.\n\r");
        }
    }
    else
    {
         System_Log("[WebServer] ERROR: NetX Packet allocation failed (Pool empty).\n\r");
    }
    return status;
}





/* ========================================================================= */
/* NX SERVER BOOT THREAD                                                    */
/* ========================================================================= */

VOID nx_srv_boot_thread_entry(ULONG initial_input)
{
    NX_PARAMETER_NOT_USED(initial_input);

    UINT status;
    ULONG actual_status;

    System_Log("[NetworkManager] INFO: Boot thread waiting for Physical Link...\n\r");

    /* * BLOCKING WAIT: nx_ip_status_check
     * ThreadX should suspend this thread.
     * We wait for NX_IP_LINK_ENABLED. 
     * Timeout is set to 10 seconds (1000 ticks at 100Hz).
     */
    status = nx_ip_status_check(&nx_ip_instance, NX_IP_LINK_ENABLED, &actual_status, 1000);

    if (status == NX_SUCCESS)
    {
        System_Log("[NetworkManager] INFO: Link confirmed. Resuming Server Thread.\n\r");
        /* The network is physically ready, now start the web server */
        tx_thread_resume(&nx_srv_thread);
    }
    else
        System_Log("[NetworkManager] ERROR: PHY Link timeout. Check Ethernet cable.\n\r");
        /* Optional: Implement a retry logic here or signal the SystemManager */
    

    /* this thread is not needed any more, we relinquish it */
      //tx_thread_relinquish();
    System_Log("[NetworkManager] INFO: Boot sequence complete. Self-terminating.\r\n");
    tx_thread_terminate(tx_thread_identify());

}


VOID nx_srv_thread_entry(ULONG initial_input)
{
    NX_PARAMETER_NOT_USED(initial_input);
    UINT status;
    ULONG ip_address;
    ULONG network_mask;


    status = nx_web_http_server_mime_maps_additional_set(&HTTP_server,&app_mime_maps[0], 4);

    /* Start the HTTP Server */
    status = nx_web_http_server_start(&HTTP_server);
    if (status != NX_SUCCESS)
        System_Log("[NetworkManager] ERROR: Web Server Start returns with code 0x%02x.\n\r", status);
    
    else
    {
        /* Retrieve the active IP address from the NetX IP instance.*/
        status = nx_ip_address_get(&nx_ip_instance, &ip_address, &network_mask);
        
        if (status == NX_SUCCESS)
        {
            /* Extract the individual octets from the 32-bit ULONG */
            UINT octet1 = (UINT)((ip_address >> 24) & 0xFF);
            UINT octet2 = (UINT)((ip_address >> 16) & 0xFF);
            UINT octet3 = (UINT)((ip_address >> 8) & 0xFF);
            UINT octet4 = (UINT)(ip_address & 0xFF);

            System_Log("[NetworkManager] INFO: Network Interface UP. ICMP Enabled.\r\n"
           "    > Ping: %u.%u.%u.%u\r\n"
           "    > Web UI: http://%u.%u.%u.%u:%u/\r\n",
           octet1, octet2, octet3, octet4, 
           octet1, octet2, octet3, octet4, PORT);
        }
        else
        {
            System_Log("[NetworkManager] WARNING: Web Server started, but IP address retrieval failed (0x%02x).\n\r", status);
        }
    }

}


/* ========================================================================= */
/* NETWORK INITIALIZATION                                                    */
/* ========================================================================= */

UINT NetworkManager_Init(void)
{
    UINT ret = NX_SUCCESS;

    /* Initialize the NetXDuo system. */
    nx_system_initialize(); 

    /* Create the main IP packet pool */
    ret = nx_packet_pool_create(&nx_packet_pool, 
                                "nx_packet_pool", 
                                DEFAULT_PAYLOAD_SIZE, 
                                (VOID *)nx_packet_pool_memory, 
                                sizeof(nx_packet_pool_memory));    
    if (ret!= NX_SUCCESS) return ret;

    /* Create the main NX_IP instance. Since DHCP is omitted, 
       we assign the static IP and Netmask directly here. */
    ret = nx_ip_create(&nx_ip_instance, 
                       "nx_ip_instance", 
                       NX_APP_DEFAULT_IP_ADDRESS, 
                       NX_APP_DEFAULT_NET_MASK, 
                       &nx_packet_pool, 
                       nx_stm32_eth_driver,
                       (VOID *)ip_thread_stack_memory, 
                       sizeof(ip_thread_stack_memory), 
                       NX_APP_INSTANCE_PRIORITY);  
    if (ret!= NX_SUCCESS) return ret;

    /* Enable the ARP protocol */    
    ret = nx_arp_enable(&nx_ip_instance, 
                        (VOID *)arp_cache_memory, 
                        sizeof(arp_cache_memory));    
    if (ret!= NX_SUCCESS) return ret;
       
    /* Enable the ICMP (Required for Ping) */     
    ret = nx_icmp_enable(&nx_ip_instance);    
    if (ret!= NX_SUCCESS) return ret;
       
    /* Enable TCP Protocol (Required for HTTP) */   
    ret = nx_tcp_enable(&nx_ip_instance); 
    if (ret!= NX_SUCCESS) return ret;

    /* Enable UDP Protocol (Optional, but harmless to keep if needed for NTP/DNS later) */  
    ret = nx_udp_enable(&nx_ip_instance); 
    if (ret!= NX_SUCCESS) return ret;
    
    //System_Log("[NetworkManager] INFO: NetXDuo Bootstrapped. Starting web server...\n\r");

    ret =  tx_thread_create(&nx_boot_thread,
                            "nx_boot_thread",
                            nx_srv_boot_thread_entry,
                            0,
                            nx_boot_stack,
                            sizeof(nx_boot_stack),
                            NX_APP_INSTANCE_PRIORITY,
                            NX_APP_INSTANCE_PRIORITY,
                            TX_NO_TIME_SLICE,
                            TX_AUTO_START
                            );

    /* Create packet pool specifically for the web server payloads */
    ret = nx_packet_pool_create(&web_server_packet_pool, 
                                "web_server_packet_pool", 
                                SERVER_PACKET_SIZE, 
                                (VOID *)web_server_packet_pool_memory, 
                                sizeof(web_server_packet_pool_memory));    
    if (ret!= NX_SUCCESS) return ret;

    /* Create the HTTP Server */
    ret = nx_web_http_server_create(&HTTP_server,
                                    "web_http_srv",
                                    &nx_ip_instance,
                                    PORT,
                                    NX_NULL, /* No FileX media bound */
                                    (VOID *)web_server_stack,
                                    sizeof(web_server_stack),
                                    &web_server_packet_pool,
                                    NX_NULL, /* No authentication routine */
                                    webserver_request_notify_callback
                                   );
    if (ret!= NX_SUCCESS) return ret;

    /*Create the thread that launches the server. Do not start it yet*/
    ret = tx_thread_create(&nx_srv_thread,
                           "nx_srv_thread",
                           nx_srv_thread_entry,
                           0,
                           nx_srv_stack,
                           sizeof(nx_srv_stack),
                           NX_APP_INSTANCE_PRIORITY,
                           NX_APP_INSTANCE_PRIORITY,
                           TX_NO_TIME_SLICE,
                           TX_DONT_START
                        );
    return NX_SUCCESS; 
}

I took the initialization sequence from the example that you provided.
The threads initialization sequence is the following:

 

UINT App_ThreadX_Init(VOID *memory_ptr)
{
  UINT ret = TX_SUCCESS;

  /* USER CODE BEGIN App_ThreadX_MEM_POOL */

  /* USER CODE END App_ThreadX_MEM_POOL */

  /* USER CODE BEGIN App_ThreadX_Init */

  //first of all start the logger
  SysLogger_Init();
  SysLogger_Enable(); //enable logging

  DSP_Init(); //IMPORTANT: call this before imu init


  IMUManager_Init();
  SystemManager_Init();

  DSP_config_t default_dsp_config;
  memset(&default_dsp_config, 0, sizeof(DSP_config_t)); //just set it to zero for now

  IMU_config_t default_imu_config = {.imu_turn_on = true, .protocol = UART_RVC, .polling_time_ms = 0};

  SystemConfig_t default_system_config = {.dsp_config = default_dsp_config, .imu_config = default_imu_config};
  
  //send default config
  System_Send_Config(&default_system_config);

  //NetworkManager_Init();

  /* USER CODE END App_ThreadX_Init */

  return ret;
}

As you can see, i call the netwrok init sequence from the App_ThreadX_Init instead of MX_NetXDuo_Init, since they are called one after the other and I assumen that there is no difference in doing like this. 

I also tried to move the call to NetworkManager_Init to the MX_NetXDuo_Init function, but the result is the same, nothing new happen.

Best regards

damole
Associate III

Hello @ACR,

Unfortunately I am not going any further in solving this issue. I saw your post before, and also some other related to a similar problem, but i still cannot find a solution.
However, I see from your post that you can assign the ethernet descriptors region in the Memory Management Tool. For me I was not able to assign them becasue i was not able to move the ram and the flash regions (and the descriptors must be placed in the address space occupied by the ram region). Did you find a workaround for this or am I just doing something wrong (I know that i need to activate the flag in Code Generation Configuration, but I am not able to move the regions anyway)? This would be of some help for me, thanks

Best regards

ACR
Associate II

Hello @damole ,

 

By using the MMT I could generate a new linker script, with defined Section for both descriptors. 

I am not sure about what the MMT does, I mean there are several options (cacheability, shareability,...) like below: 

ACR_0-1777283647332.png

 

It seems that these have no effects, I couldn't find any changes in the generated code. There is also the MPU, where you can find this parameters. 

 

ACR_1-1777283820507.pngACR_2-1777283831667.png

But implementing this didn't resolve my problem.

 

>and the descriptors must be placed in the address space occupied by the ram region

I don't get this point, but I saw something like that in the NX TCP ECHO Client example, where the descriptors were defined in the RAM defined memory. I didn't try. 

 

Best regards,

 

 

damole
Associate III

Thanks @ACR, i will try to do so.

Regarding this:

>and the descriptors must be placed in the address space occupied by the ram region

sorry that was not clear. I mean that, at least in my project, it happens that the RAM (S) region is in an address address range that is overlapping with the range where the ethernet descriptors can be placed: if you check in the reference manual or also in the ethernet tab of CubeMX (where you can set the start addresses of the descriptors) and you open the info you will see a message saying something like "ethernet descriptors can be placed between 0xAAAAAAAA and 0xBBBBBBBB", meaning that they cannot be placed wherever you want in the memory, but they can be only in a specific range (which for me it happens to be where the RAM (S) is); I assume that this is range is the memory portion that the dedicated DMA has access to (but not sure about this).

I also saw some examples where the descriptors are in the RAM memory, and this makes me a lot of confusion, also because I am not sure that what you see in the MMT makes sense or is just random stuff if you do not activate the flags in the Code Generation Configuration, given also that there is the MPU dedicated tab that does basically the same thing.