Hi All,
I’m looking for some pointers on debugging a FreeRTOS+TCP issue on an STM32H7.
Using FreeRTOS: 11.2.0
With FreeRTOS+TCP: 4.3.3
The unit receives requests and sends responses via UDP.
On the whole it performs really well however the stack seems to occasionally lockup, requiring a reboot to recover.
I’ve managed to reliably reproduce this recently having added file offloads via UDP. Responding to a UDP request with typically 8MB worth of UDP packets, sent back to back. Using the zero-copy mechanism.
During a transfer, pinging the unit appears to be enough to trigger the lockup. Without pinging it, multiple (>50) back-to-back 200MB transfers completed without issue. While pinging it, the first transfer doesn’t typically complete before it becomes unresponsive.
Checked a few instances from the host side, while anecdotal it seems the crash occurs if a UDP packet from the host arrives immediately before the ping request. i.e the application code is somewhere between picking up the request and sending its response.
Highlighted row being a request from the desktop to the MCU. Packets before are the end of the last data block. Blue packets after the ping are retries due to the lack of response from the MCU.
Using the included ethernet driver, with the following CMake snippet:
# Add FreeRTOS-Plus-TCP
set(FREERTOS_PLUS_TCP_BUFFER_ALLOCATION "1" CACHE STRING "" FORCE)
set(FREERTOS_PLUS_TCP_NETWORK_IF "STM32" CACHE STRING "" FORCE)
set(FREERTOS_PLUS_TCP_STM32_IF_DRIVER "H7" CACHE STRING "" FORCE) # We're going to use the HAL driver
add_subdirectory(lib/FreeRTOS-Plus-TCP)
target_compile_definitions(freertos_plus_tcp_network_if PRIVATE
STM32H7
)
target_compile_options(freertos_plus_tcp PRIVATE
# Prevent memcpy and memset being replaced by the compiler which can lead to unaigned access issues
-fno-builtin-memcpy -fno-builtin-memset
)
target_link_libraries(freertos_plus_tcp_network_if PRIVATE
stm32cubemx
)
Using the following FreeRTOSIPConfig.h contents:
#ifndef FREERTOS_IP_CONFIG_H
#define FREERTOS_IP_CONFIG_H
/* Set to 1 to print out debug messages. If ipconfigHAS_DEBUG_PRINTF is set to
* 1 then FreeRTOS_debug_printf should be defined to the function used to print
* out the debugging messages. */
#define ipconfigHAS_DEBUG_PRINTF (ipconfigENABLE)
extern void IP_SUPPORT_DebugPrintf( const char *pcFormatString, ... );
#define FreeRTOS_debug_printf( X ) IP_SUPPORT_DebugPrintf X
/* Set to 1 to print out non debugging messages, for example the output of the
* FreeRTOS_netstat() command, and ping replies. If ipconfigHAS_PRINTF is set to 1
* then FreeRTOS_printf should be set to the function used to print out the
* messages. */
#define ipconfigHAS_PRINTF (ipconfigENABLE)
extern void IP_SUPPORT_Printf( const char *pcFormatString, ... );
#define FreeRTOS_printf( X ) IP_SUPPORT_Printf X
/* Define the byte order of the target MCU (the MCU FreeRTOS+TCP is executing
* on). Valid options are pdFREERTOS_BIG_ENDIAN and pdFREERTOS_LITTLE_ENDIAN. */
#define ipconfigBYTE_ORDER (pdFREERTOS_LITTLE_ENDIAN)
/* STM32 hardware can perform checksum offloading */
#define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM (ipconfigENABLE)
#define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM (ipconfigENABLE)
#if 0 /* Hardware filtering is supported but doesn't appear to work correctly with the STM32H7. */
/* STM32 hardware supports packet filtering */
#define ipconfigETHERNET_DRIVER_FILTERS_PACKETS (ipconfigENABLE)
#endif
/* STM32 driver when using IPv4 and IPv6 has an unused variable, making the define below a nop */
#define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES (ipconfigDISABLE)
/* Supress warnings related to the settings above */
#define ipconfigPORT_SUPPRESS_WARNING (ipconfigENABLE)
/* STM32 hardware supports zero-copy */
#define ipconfigZERO_COPY_TX_DRIVER (ipconfigENABLE)
#define ipconfigZERO_COPY_RX_DRIVER (ipconfigENABLE)
/* Support linking of RX messages */
#define ipconfigUSE_LINKED_RX_MESSAGES (ipconfigENABLE)
/* Define task priority and stack size */
#define ipconfigIP_TASK_PRIORITY (configMAX_PRIORITIES - 2U)
#define ipconfigIP_TASK_STACK_SIZE_WORDS (configMINIMAL_STACK_SIZE * 5U)
/** Support for network down event */
#define ipconfigSUPPORT_NETWORK_DOWN_EVENT (ipconfigENABLE)
/* Call network event hook when a network event occurs. */
//#define ipconfigUSE_NETWORK_EVENT_HOOK (ipconfigENABLE)
/* Support static IP addressing only */
#define ipconfigUSE_DHCP (ipconfigDISABLE)
/* Include support for FreeRTOS_inet_addr as well as FreeRTOS_indet_addr_quick() */
#define ipconfigINCLUDE_FULL_INET_ADDR (ipconfigENABLE)
/* ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS defines the total number of network buffer that
* are available to the IP stack. The total number of network buffers is limited
* to ensure the total amount of RAM that can be consumed by the IP stack is capped
* to a pre-determinable value. */
#define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS (64)
/* Do not use TCP use windowing mechanism, as we're on a local network we expect good reliability */
#define ipconfigUSE_TCP_WIN (ipconfigDISABLE)
/* Disable DNS support we'll be dealing with IPs only */
#define ipconfigUSE_DNS (ipconfigDISABLE)
/* If ipconfigSUPPORT_SELECT_FUNCTION is set to 1 then the FreeRTOS_select()
* (and associated) API function is available. */
#define ipconfigSUPPORT_SELECT_FUNCTION (ipconfigENABLE)
/* Each TCP socket has a circular buffers for Rx and Tx, which have a fixed
* maximum size.*/
#define ipconfigTCP_RX_BUFFER_LENGTH (1 * 1024)
#define ipconfigTCP_TX_BUFFER_LENGTH (1 * 1024)
/* Include support for TCP keep-alive messages. */
#define ipconfigTCP_KEEP_ALIVE (ipconfigENABLE)
#define ipconfigTCP_KEEP_ALIVE_INTERVAL (20) /* in seconds */
/* Enable socket callbacks */
#define ipconfigUSE_CALLBACKS (ipconfigENABLE)
/* Monitor transmission, reception and buffer allocation failures */
#include "diag.h"
#define iptraceNETWORK_INTERFACE_RECEIVE() DIAG_IncEthernetRx()
#define iptraceNETWORK_INTERFACE_TRANSMIT() DIAG_IncEthernetTx()
#define iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER() DIAG_IncEthernetBufferAllocFailed()
/* How often to check link status */
#define ipconfigPHY_LS_HIGH_CHECK_TIME_MS (2000U)
/* Allow extended waits for UDP buffers */
#define ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS (pdMS_TO_TICKS(2000))
#endif
Application wise, UDP packets are being sent with the following sequence in a tight loop.
// Receive packet, to obtain source address
// Process request
/* Allocate buffer for outgoing data */
uint8_t *pabyDataOut = (uint8_t*)FreeRTOS_GetUDPPayloadBuffer_Multi(OUTPUT_BUFFER_LEN, OUTPUT_BUFFER_ALLOC_TIMEOUT, ipTYPE_IPv4);
// Encode response
/* Send UDP response */
if (0U == FreeRTOS_sendto(xSocket,
pabyDataOut, stOutputStream.bytes_written,
FREERTOS_ZERO_COPY,
pstSourceAddr, uiSourceAddrLength)) {
/* Send failed, we still own buffer, release it */
FreeRTOS_ReleaseUDPPayloadBuffer(pabyDataOut);
pabyDataOut = NULL;
}
For receiving the socket is opened, a notification callback is registered and socket is placed in non-blocking mode before being bound to its port:
/* Register callback */
F_TCP_UDP_Handler_t stHandlers = {
.pxOnUDPReceive = HandleUDPPacketReceivedNotification,
};
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_UDP_RECV_HANDLER, (void*)&stHandlers, sizeof(stHandlers));
/* Set to non-blocking mode */
BaseType_t xTimeout = 0;
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_RCVTIMEO, (void*)&xTimeout, sizeof(xTimeout));
The function HandleUDPPacketReceivedNotification simply sends a direct to task notification to the task handing network communication amongst other background activities.
Any debugging pointers would be much appreciated.
Thanks,
Phil

