FreeRTOS send ethernet frames

Hi,
I’m trying to forward raw ethernet frames generated by an application. I have seen several examples using lwip to handle TCP and UDP packets, however I need to work at lower level. In linux kernel is possible to work with net_device structure and related functions to receive and send raw packets. Is it possible also in FreeRTOS?

Thanks in advance

Which services from a network stack do you need at all ?
Raw ethernet frames are basically handled by your ethernet (MAC) driver.

Thanks, I just need to be able to receive ethernet frames and send them out. Actually I’m using FreeRTOS on top of the Xilinx Ultrascale. I imagine that lwip and the RTOS tcp stack is able to use such low level functionalities.

Xilinx provide a BSP that accesses the MAC hardware - can you just use that BSP without lwIP?

Yes, but I was wondering if there exist something in the middle since Xilinx BSP is quite complex and examples are not very clear.

The Xilinx support for LWIP can be found here:

This support provides the Xilinx MAC interface to LWIP so perhaps you can use it as examples for your raw interface.
If there is a middle interface it will be in LWIP and not part of FreeRTOS.

FreeRTOS does include FreeRTOS+TCP and there is a port for the Xilinx MAC here:

that code could be used as an example for creating a raw interface. Taking a simplistic look at it, there are fewer files in the +TCP interface so perhaps it will be easier to extract an example.

FreeRTOS+TCP indeed has a port for Xilinx UltraScale. I uploaded a testing makefile project for UltraScale here.

FreeRTOS+TCP lets you send raw packets by doing a system call eNetworkTxEvent.
For reception there is an application hook that you can use, see ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES .

In case you decide to use FreeRTOS+TCP, you can ask any further questions in this thread.

Thanks to everyone for the replies. All considered I’m looking to use directly FreeRTOS+TCP and I’m trying to understand the examples to make the raw interface.

I have imported FreeRTOS in my Vitis project (I started with a new HelloWorld FreeRTOS application). However, when I try to compile it I get the following error (refers to this link)

…/src/FreeRTOS-Plus-TCP/source/portable/NetworkInterface/xilinx_ultrascale/uncached_memory.c:79:26: error: ‘XPAR_PS7_DDR_0_S_AXI_HIGHADDR’ undeclared (first use in this function); did you mean ‘XPAR_PSU_DDR_0_S_AXI_HIGHADDR’?
79 | #define DDR_MEMORY_END (XPAR_PS7_DDR_0_S_AXI_HIGHADDR+1)

Anyone had this issue?

Doesn’t it say the following?

#if ( ipconfigULTRASCALE == 1 )
    /* Reserve 2 MB of memory. */
    #define uncMEMORY_SIZE        0x200000U
    #define DDR_MEMORY_END        ( XPAR_PSU_DDR_0_S_AXI_HIGHADDR )
    #define uncMEMORY_ATTRIBUTE   NORM_NONCACHE | INNER_SHAREABLE
#else
    /* Reserve 1 MB of memory. */
    #define uncMEMORY_SIZE        0x100000U
    #define DDR_MEMORY_END        ( XPAR_PS7_DDR_0_S_AXI_HIGHADDR + 1 )
    #define uncMEMORY_ATTRIBUTE   0x1C02
#endif /* ( ipconfigULTRASCALE == 1 ) */

If you #define ipconfigULTRASCALE 1 in your FreeRTOSIPConfig.h, it should compile.

Note that this module “uncached_memory.c” is shared with the Xilinx Zynq 7000 project.

Yes, the code is the one you have posted. Thanks, by setting such define I solved the issue, but now I’m getting new errors:

Looking at the code that triggers the compiler errors:

    #if EL1_NONSECURE
        XSmc_OutVar RegRead;
        RegRead = Xil_Smc( MMIO_READ_SMC_FID, ( u64 ) ( CrlApbBaseAddr ),
                           0, 0, 0, 0, 0, 0 );
        CrlApbGemCtrl = RegRead.Arg0 >> 32;
    #else
        CrlApbGemCtrl = *( volatile u32 * ) ( UINTPTR ) ( CrlApbBaseAddr );
    #endif

In my testing project, EL1_NONSECURE is defined as 0, in a file called bspconfig.h

Yes, I’ve had to change the bspconfig.h to work on top of an Hypervisor, the same change did not produced any issue when compiling without that library. With that configuration the TCP library cannot work?

I must admit that I know very little about the insides of the Xilinx BSP. So if you can not solve it, anyone else? Or post a message on the Xilinx forum?

I think I have fixed some issues regarding the use of the FreeRTOS+TCP in a Hypervisor.
Regarding the solution you proposed before:

FreeRTOS+TCP indeed has a port for Xilinx UltraScale. I uploaded a testing makefile project for UltraScale here.

FreeRTOS+TCP lets you send raw packets by doing a system call eNetworkTxEvent.
For reception there is an application hook that you can use, see ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES .

In case you decide to use FreeRTOS+TCP, you can ask any further questions in this thread.

If I understood correctly, once called the FreeRTOS_IPInit(…) I can send raw packets anytime by just using the eNetworkTxEvent() function, right? What arguments does it takes?
Regarding reception, where can I define the eApplicationProcessCustomFrameHook? And if I modify the code to call it for each packet I receive (for example by removing the first two cases of the switch, it could be a problem?

As for sending raw packets, you can take function FreeRTOS_OutputARPRequest as an example, and use the branch that calls xSendEventStructToIPTask().

The hook for unhandled frame type is relatively new, it was presented in PR #200.

It requires you to define ipconfigPROCESS_CUSTOM_ETHERNET_FRAMES as 1 in your copy of FreeRTOSIPConfig.h file.
And then you define the hook as:

eFrameProcessingResult_t eApplicationProcessCustomFrameHook( NetworkBufferDescriptor_t * const pxNetworkBuffer )
{
}

You find some documentation about the new hook here.

Thanks I have took the example suggested as starting point and I have written the following function:

void sendFrame(unsigned char *frame, size_t frame_size){
IPStackEvent_t xEventMessage;
const TickType_t xDontBlock = ( TickType_t ) 0U;
BaseType_t r;
NetworkBufferDescriptor_t * pxNetworkBuffer;

pxNetworkBuffer = pxGetNetworkBufferWithDescriptor(frame_size, xDontBlock);
memcpy(pxNetworkBuffer, frame, frame_size);

xEventMessage.eEventType = eNetworkTxEvent;
xEventMessage.pvData = pxNetworkBuffer;
xil_printf("[SEND_FRAME] ready to add packet to queue\n");
r = xSendEventStructToIPTask(&xEventMessage, xDontBlock); //Add packet to queue, xDontBlock makes it return immediately without blocking
if(r == pdTRUE){
xil_printf("[SEND_FRAME] packet added to queue\n");
}else{
xil_printf("[SEND_FRAME] unable to add packet to queue. QUEUE FULL\n");
}
}

However when I call the xSendEventStructToIPTask function, that function never returns. Debugging I found that it seems that there is an abort:

It seems that such abort is called by:

What could be the reason?

Digging in the xSendEventStructToIPTask code, I have modified my sendFrame() function this way:

void sendFrame(unsigned char *frame, size_t frame_size){
	IPStackEvent_t xEventMessage;
	const TickType_t xDontBlock = ( TickType_t ) 0U;
	BaseType_t r;
	NetworkBufferDescriptor_t * pxNetworkBuffer;

	pxNetworkBuffer = pxGetNetworkBufferWithDescriptor((size_t) frame_size, xDontBlock);
	if(pxNetworkBuffer == NULL){
		xil_printf("[sendFrame] Error: unable to allocate network buffer\n");
	}
	memcpy(pxNetworkBuffer->pucEthernetBuffer, frame, frame_size);

	xEventMessage.eEventType = eNetworkTxEvent;
	xEventMessage.pvData = pxNetworkBuffer;
	xil_printf("[SEND_FRAME] ready to add packet to queue\n");
	//r = xSendEventStructToIPTask(&xEventMessage, xDontBlock);	//Add packet to queue, xDontBlock makes it return immediately without blocking
	r = xNetworkInterfaceOutput(pxNetworkBuffer, pdTRUE );
	if(r == pdTRUE){
		xil_printf("[SEND_FRAME] packet added to queue\n");
	}else{
		xil_printf("[SEND_FRAME] unable to add packet to queue. QUEUE FULL\n");
	}
}

However, debugging I have noticed that after some time, the emacps_send_message() function does not manages to acquire TX buffers (FreeRTOS-Plus-TCP/x_emacpsif_dma.c at bfa155920ed37302ea4799728795eec2298e696c · FreeRTOS/FreeRTOS-Plus-TCP · GitHub). I suppose that the cause is that for some strange reason the messages are enqueued for transmission, but never sent and so after several messages it runs out of buffers. Anyone knows which could be the cause?

Niccolò, did you get any further with this?

if(pxNetworkBuffer == NULL){
xil_printf("[sendFrame] Error: unable to allocate network buffer\n");
}
memcpy(pxNetworkBuffer->pucEthernetBuffer, frame, frame_size);

Mind you that you may not access pxNetworkBuffer when it is NULL.

Remember that it is important to give a higher task priority to the IP-task ( see ipconfigIP_TASK_PRIORITY ).

The function xNetworkInterfaceOutput() works directly with DMA buffers, and so you may never call it from the application. Only the IP-task can use it.

it runs out of buffers. Anyone knows which could be the cause?

Does none of the packets arrive at their destination?

Can you use other things like ICMP/ping, UDP, TCP?

Hi,
unfortunately I did not managed to make it work. However I successfully managed to send ethernet frames baremetal and now I’m trying to port that code into FreeRTOS, however when moving that application code to FreeRTOS, my callback functions are no more called by when the device raises an interrupt. Do you know if there is something particular that I need to take care of when dealing with interrupts in FreeRTOS?