heinbali01 wrote on Sunday, April 23, 2017:
Hi Sasha,
I’m writing a from-scratch STM32F7 zero-copy Ethernet driver for FreeRTOS+UDP 9.0.0.
Congratulations !
Do you really mean +UDP, or +TCP?
FreeRTOS+TCP started in the /Labs section. It is the successor of +UDP. It is more complete, and more often used. If you want to use it UDP-only, that is still possible:
#define ipconfigUSE_TCP 0
Moving from +UDP to +TCP shouldn’t be hard. The principles are all the same.
The drivers for both releases are 99% the same. FreeRTOS+TCP has an extra parameter when calling xNetworkInterfaceOutput()
:
BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend );
When bReleaseAfterSend
is true, the driver ( NetworkInterface
) is responsible for releasing pxDescriptor
. When you have zero-copy transmissions:
#define ipconfigZERO_COPY_TX_DRIVER 1
… then bReleaseAfterSend
will always be true, so it is safe to pass the memory to a DMA descriptor. Once the transfer is ready, the original Network Buffer must be released.
I like to work with a counting semaphore that keeps track of the number of available DMA descriptors for transmission (TX
).
In the current STM32F4 driver, it looks like this:
/* As soon as the EMAC has sent a frame, the original Network Buffer must be released. */
ucPayLoad = ( uint8_t * )DMATxDescToClear->Buffer1Addr;
/* Look-up the Network buffer that owns a certain payload. */
pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad );
/* Release the Network buffer. */
vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ;
/* Mark that it has been released already, just to be sure. */
DMATxDescToClear->Buffer1Addr = ( uint32_t )0u;
/* Tell the counting semaphore that one more TX descriptor is available. */
xSemaphoreGive( xTXDescriptorSemaphore );
for ICMP-over-IPv4 packets, the checksum field in the ICMP packet must always be 0x0000 in both modes
I also encountered this ‘feature’ on STM32F4’s EMAC :
if( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP )
{
pxPacket->xICMPPacket.xICMPHeader.usChecksum = ( uint16_t )0u;
}
Just to summarise, for IPv4, the following check-sums exist:
- IP-header has a checksum over a so-called pseudo header (16-bits)
- Protocol-checksum (16-bits)
- A 4-byte Frame Check Sequence (FCS) that we do not get to see in the library.
Checksums 1) and 2) can either be set and checked by the EMAC, or by the library.
The last checksum can only be added by the EMAC.
Now when a packet has 100 bytes, we send 100 bytes to the EMAC, and the EMAC will secretly add 4-byte frame-check.
But there is a minimum frame length of 60 + 4 bytes. That is needed for a proper collision detection on the LAN.
In FreeRTOS+TCP, you can define the following:
#define ipconfigETHERNET_MINIMUM_PACKET_BYTES ( 60 )
ARP and ICMP messages can be as short as 42 bytes. The 18 extra bytes will be set to zero. So 60 bytes will be sent to the EMAC.
But it doesn’t appear like FreeRTOS+UDP provides four unused bytes at the end of an Ethernet buffer.
True because the EMAC adds 4 bytes on its own.
In order to get the checksum to be automatically inserted,
I must tell the DMA peripheral that the frame is four bytes
bigger than what FreeRTOS+UDP suggests
That is new for me, I haven’t encountered that yet. But I haven’t used an STM32F7 either.
Have I missed something about the buffer allocation size that makes this OK?
FreeRTOS+TCP will guarantee that all network buffers have a minimum size of ipconfigETHERNET_MINIMUM_PACKET_BYTES
. That is particularly important when using BufferAllocation_2.c
, which allocates just enough bytes for pucEthernetBuffer
.
/* Adding pad bytes. */
#if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES )
{
if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES )
{
BaseType_t xIndex;
for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength;
xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES;
xIndex++ )
{
/* FreeRTOS+TCP guarantees that pucEthernetBuffer is long enough. */
pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0u;
}
pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES;
}
}
#endif
Or is there a way to specify end-of-buffer padding somewhere? I’ve noticed that this page is missing some details.
An EMAC will understand the trailing zero’s at the end. They will be transmitted, followed by an FCS.
Here you find the latest NetworkInterface.c for the STM32F4.
This and other drivers will (hopefully) soon appear in the official FreeRTOS+TCP release. Regards.