FreeRTOS+TCP GetUDPPayloadBuffer ISR

Hello,

I have an application using +TCP. I’m using BufferAllocation_1.

Is FreeRTOS_GetUDPPayloadBuffer() safe to call from an ISR using this buffer allocation scheme. Looking at the source, it would seem that BufferAllocation_1 does have pxNetworkBufferGetFromISR, but I don’t see where that’s ever being called.

Thank You!

FreeRTOS_GetUPDPayloadBuffer() calls pxGetNetworkBufferWithDescriptor(), which in turn calls xSemaphoreTake() - so FreeRTOS_GetUPDPayloadBuffer() is not safe to call from an interrupt.

You could add your own buffer allocation scheme (BufferAllocation_3?) that is safe to call from an interrupt. Alternatively, the more common method is for the interrupt unblock a task that has a priority above any application task so the interrupt service routine returns directly to the unblocked task - then perform the buffer allocation from that task.

All agreed with Richard’s remarks.

It sounds like you have an ISR, and when it is triggered you want to send a UDP packet? Or something else?

Yes true, BufferAllocation_1.c still has a function pxNetworkBufferGetFromISR(), but we discourage using it, and look for an alternative.

What @rtel mentions is also called:

/*
 * A deferred interrupt handler task that processes
 */
static void prvEMACHandlerTask( void * pvParameters );

This task is started from NetworkInterface.c ( e.g. here ), and normally the priority is set by a macro niEMAC_HANDLER_TASK_PRIORITY ( may be called differently in other interfaces ).

When using FreeRTOS+TCP, it is recommended to use the following scheme of priorities:

  • higher: the network interface: prvEMACHandlerTask()
  • normal: the IP-task: prvIPTask() ( macro ipconfigIP_TASK_PRIORITY )
  • lower: all tasks that make use of the +TCP library.

Tasks that do not use the +TCP library can get any priority they like.

Richard and Hein,

Appreciate the responses. Richard’s answer is pretty much what I expected. I was rather sure GetUDPPayloadBuffer wasn’t going to be ISR Safe.

That being said, It would be great to have more information about Richard’s Deferred Interrupt suggestion. It will probably work perfectly, but what priority would be suggested? It needs to be tended to rather quickly (at least in reference to the tick timer), since I have no way of knowing when another transaction may be demanded by interrupt. Ideally, I’d like the unblocked interrupt handler to be of the absolute highest priority - guaranteeing that it will be the task activated upon exiting the ISR, but doing do seems to break the recommendation that prvEMACHandlerTask and prvIPTask be of higher priority than any task that use the TCP Libraries.

Is a (relatively) short Deferred Interrupt handler task that makes use of +TCP going to hurt anything if it has a higher priority than those tasks?

Thanks very much!

Before replying in detail, can you tell a little more about the application, without the implementation details?
I mean: what peripheral is causing an ISR?
And then you want to send zero-copy UDP packet? Is there also returning network traffic?
What platform are you using? How fast is the LAN?

Hein,

Sorry - Should have provided that info with the last response.

The platform is PIC32MZ-EF. Network speed is 100Mbps.

The PIC32 has one SPI port configured as a slave, and a secondary processor is the master. The secondary can send data to the PIC32 SPI Port at any time and that data needs to be sent via UDP. I’m using a hardware interrupt not because of the amount of data (it’s small), but because it can occasionally happen in very rapid succession (PIC32 ticks are 1mS, and SPI Data may arrive in bursts on roughly that same time scale).

The basic idea is SPI-RX Interrupt, GetUDPPayloadBuffer, Load buffer from received SPI data, Send via UDP-ZeroCopy.

There is no returning network traffic caused by the send.

Thanks again!

Thank you, that gives a clear picture.

You would be able to create a function GetUDPPayloadBuffer() with the FromISR suffix, but as you might guess, it would be “mission impossible” to create a sendto() function that runs from an ISR.

How many bytes do you receive in an SPI message? Varying from… to …?

Are you using BufferAllocation_1 or 2?

What about the following: before starting you preallocate e.g. 4 UDP payload buffers. You put their addresses in a queue called “empty_buffers”.
The SPI interrupt will pass the address of a free buffer to SPI/DMA.
Also, when a buffer is full, the buffer will be passed to a queue called “full_buffers”.

A high-priority task udp_send() is blocking on the queue “full_buffers”. As soon as it gets a packet, it will send it to the LAN. At the same time, it will call GetUDPPayloadBuffer() to create a new buffer and pass it to “empty_buffers”.
( The buffer that was passed to sendto() will be released by the Network interface. )

Of course you can put the UDP buffers into an envelope, a struct that gives some more information like: number of bytes read, time-stamp or whatever debug information:

#define MAX_UDP_BUFFERS    4
struct xEnvelope
{
    uint8_t *pucUDPBuffer;
    size_t uxMaxLength;
    size_t uxActualLength;
    uint32_t ulTimeStamp;
    enum eBufferStatus eStatus;
} xEnvelopes[ MAX_UDP_BUFFERS ];

In this solution you will use queues between a single sender and a single receiver. That means that you can also use a stream buffer in stead.

About the priorities:

higher: the network interface: prvEMACHandlerTask()
normal: the IP-task: prvIPTask() ( macro ipconfigIP_TASK_PRIORITY )
below-normal: the new udp_send() task
lower: all other tasks that make use of the +TCP library.

1 Like

Hein,

Thank you very much for the suggestion. I implemented something similar to what you mention - putting some pre allocated UDPBuffers into a queue and retrieving them within the ISR. This actually worked out really well, because I then pass the same buffer pointers to yet another queue where a “Data Processing” task picks them up and does some work on them before transmitting them.

Excellent suggestion.

Thank You.