DHCP non-aligned buffer causes assert in FreeRTOS_ReleaseUDPPayloadBuffer()

alager12345 wrote on Thursday, March 07, 2019:

I fired up DHCP and found that when the offer comes in to prvProcessDHCPReplies(),
down at the bottom it calls FreeRTOS_ReleaseUDPPayloadBuffer()
which in turn calls pxUDPPayloadBuffer_to_NetworkBuffer(). In this function it checks to see if the pointer to the buffer is aligned. This check is failing because the pucBuffer is 0x2000524e.
I’m using BufferAllocation_1.c and I added __attribute__ ((aligned (8))); to both the ucBuffers and in NetworkInterface.c and to xNetworkBuffers in BufferAllocation_1.c, but to no avail.

When I look at the contents of ram where pucBuffer is pointing, it looks correct for the DHCP packet just received, so I don’t think it’s memory corruption.
I’m running on the LPC1788 and have 16.7k ram free at compile time.
I don’t have these issues with the ARP module or with ping replies, those have been working great.

What could be causing the buffers to be missaligned?

Thanks for any ideas!

heinbali01 wrote on Friday, March 08, 2019:

If the alignment test fails, and you are using BufferAllocation_1.c, I would suggest to recheck your implementation of vNetworkInterfaceAllocateRAMToBuffers().

You could try to remove the alignment test, but I’m afraid that the next instruction will crash.
And if your CPU/compiler allows for 32-bits access on a 16-bit aligned location, that would be less efficient. Network Buffers are accessed with 32-bit instructions wherever possible.

Here is an implementation ( from the Xilinx driver ), that is know to work well:

void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
#define niBUFFER_1_PACKET_SIZE		1536

static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) );
uint8_t *ucRAMBuffer = ucNetworkPackets;
uint32_t ul;

	for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
		pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
		*( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
		ucRAMBuffer += niBUFFER_1_PACKET_SIZE;

It uses a 32-byte alignment, which has to do with the caching system.

Note that ipBUFFER_PADDING has a value of 10.

All EMACs that I’ve seen until now, know about the 2-byte offset: the first byte after the 12-byte Ethernet Header is 32-bits aligned.
Some EMACs work with a pointer to the Ethernet Header, others want a pointer to 2 bytes before that header, and treat those 2 bytes as dummy.

Things would have been easier if the Ethernet Header contained 16 bytes, in stead of 14.

alager12345 wrote on Friday, March 08, 2019:

Hmmm, something isn’t right. I replaced my vNetworkInterfaceAllocateRAMToBuffers() with the one you just posted, with the minor change of #define niBUFFER_1_PACKET_SIZE 1068 due to limited ram. This value still fits the (buff-28)/8=integer rule.
But when I run this, it still asserts due to the alignment. The pucBuffer is 0x2000404e
How can this be, if the attribute is for 32bit alignment?

More information on this. This is the second run through the routine. The first time through pucBuffer is 0x10006100, so the test passes. The second time through is when pucBuffer is 0x2000404e.
So the code that is picking the second buffer isn’t picking on the correct alignment maybe?

alager12345 wrote on Friday, March 08, 2019:

Clue number two on those addresses. The first one 0x10006100 ( which works) is in bss while the second one 0x2000404e (which fails) is located in the peripheral ram which is access by the ethernet module directly during RX.

alager12345 wrote on Friday, March 08, 2019:

Looking at this more, is this a fundamental error in how the buffers are used?
FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload ); contains a pointer to a UDP Payload. On this hardware that is in the peripheral ram 0x2000404e. One can not simply subtract 10 from that address to get to the descriptor address back in 0x1000nnnn!

alager12345 wrote on Saturday, March 09, 2019:

Well I think I finally have this sorted! The previous post is what sorted it for me. On this platform, and the way FreeRTOS uses the buffers, I don’t see how a complete zero-copy driver can work. I need one copy from the peripheral ram into the static buffers. This, I beleive, applies equally to BufferAllocation_2.c on this platform.

For anyone else trodding down this path, attached is the NetworkInterface.c that is working with the LPC1788.

The main points of interest are below:
In prvEMACHandlerTask()

if( xDataLength > 0U )
	// Obtain a network buffer to pass this data into the
	// stack.  No storage allocation is required as the network buffer is already allocated.
	pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( 0, ( TickType_t ) 0 );

	if( pxNetworkBuffer != NULL )
		/* begin custom read code */
		// get a temporary structure for reading from the MAC
        EMAC_PACKETBUF_Type pRXBuff;

		// it's buffer pointer goes to the same buffer we just got from above
		pRXBuff.pbDataBuf = (uint32_t *)pxNetworkBuffer->pucEthernetBuffer;

		// tell it how much data it's going to store
		pRXBuff.ulDataLen = xDataLength;
        pxNetworkBuffer->xDataLength = xDataLength;

		// **	copy the data from the peripheral buffer to our static buffer
		//		almost a zero copy driver...
        EMAC_ReadPacketBuffer( &pRXBuff );

		/* end custom read code */

		xRxEvent.pvData = ( void * ) pxNetworkBuffer;

		/* Data was received and stored.  Send a message to the IP task to let it know. */
		if( xSendEventStructToIPTask( &xRxEvent, ( TickType_t ) 0 ) == pdFAIL )
			vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );




// check for TX buffers to release
		if( txFreeCount )
			while( txFreeCount )

I then took the vClearTXBuffer() out of the ISR so it can clear the buffer AND the descriptor:

// release the buffer after the DMA transmitted it.
static void vClearTXBuffer( void )
	NetworkBufferDescriptor_t *pxBuffer = trackNetworkBuffers[ trackTail++ ];
	if( trackTail >= ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS )
		trackTail = 0;

	if( xTCPWindowLoggingLevel > 2 )
		FreeRTOS_debug_printf( ( "Clearing buff %#x\n", pxBuffer ) );

    configASSERT( pxBuffer != NULL );
	vReleaseNetworkBufferAndDescriptor( pxBuffer );

The TX_DONE IRQ handler was simplified

if( ( ulInterruptCause & EMAC_INT_TX_DONE ) != 0UL )
	// keep track of how many buffers have been sent

And finally

/* Will the data fit in the Tx buffer? */
if( pxNetworkBuffer->xDataLength < EMAC_ETH_MAX_FLEN ) /*_RB_ The size needs to come from FreeRTOSIPConfig.h. */
	/* format the data for the Tx DMA. */
	TXBuffer.ulDataLen = pxNetworkBuffer->xDataLength;
	TXBuffer.pbDataBuf = (uint32_t *)pxNetworkBuffer->pucEthernetBuffer;

     /* track the DMA buffer pointer for release later (in the ISR) */
	trackNetworkBuffers[ trackHead++ ] = pxNetworkBuffer;
	if( trackHead >= ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS )
		trackHead = 0;

	if( xTCPWindowLoggingLevel > 2 )
		FreeRTOS_debug_printf( ( "Sending buff %#x\n", pxNetworkBuffer ) );

	/* copy the buffer to the Tx DMA descriptor. */
	EMAC_WritePacketBuffer( &TXBuffer );

     /* Do not release it until it has been sent. */
	xReleaseAfterSend = pdFALSE;


	/* The Tx has been initiated. */
	xReturn = pdPASS;

Maybe this can get rolled into the supplied NetworkInterface.c file for LPC17xx? It is very incomplete.