pxDuplicateNetworkBufferWithDescriptor sometimes asserts

I’m using BufferAllocation_1.c in an LPC1788 project. After some random time, I get an assert error in pxDuplicateNetworkBufferWithDescriptor(). It looks like sometimes pxGetNetworkBufferWithDescriptor() will return a valid buffer but the pointer inside that points to pucEthernetBuffer is NULL. This case is not checked, and so the memcpy asserts a hard fault. I’ve turned on some debugging which shows the gets and puts, and it can be seen that the pucEthernetBuffer is NULL on multiple occasions, but it’s only the last one where the code assersted. ~~~ BUFGET[15]: 000000001000ac68 (0000000020004c58) BUFGET[16]: 000000001000ac94 (0000000020005258) BUFGET[17]: 000000001000acc0 (0000000020004058) BUFPUT[14]: 000000001000ac3c (0000000020004058) (now 28) BUFPUT[15]: 000000001000ac68 (0000000020004658) (now 29) BUFPUT[16]: 000000001000ac94 (0000000020004c58) (now 30) BUFPUT[17]: 000000001000acc0 (0000000020005258) (now 31) BUFGET[18]: 000000001000acec (0000000020004658) BUFPUT[18]: 000000001000acec (0000000000000000) (now 31) BUFGET[21]: 000000001000ad70 (0000000020004c58) BUFGET[23]: 000000001000adc8 (0000000020005258) BUFPUT[21]: 000000001000ad70 (0000000020004658) (now 30) BUFPUT[23]: 000000001000adc8 (0000000020004c58) (now 31) BUFGET[19]: 000000001000ad18 (0000000020004058) BUFPUT[19]: 000000001000ad18 (0000000020005258) (now 31) BUFGET[20]: 000000001000ad44 (0000000020004658) BUFPUT[20]: 000000001000ad44 (0000000020004058) (now 31) BUFGET[27]: 000000001000ae78 (0000000020004c58) TCP: No active socket on port 443 (a030052ip:52509) BUFGET[22]: 000000001000ad9c (0000000020004058) BUFPUT[25]: 000000001000ae20 (0000000000000000) (now 29) BUFPUT[27]: 000000001000ae78 (0000000020004658) (now 30) TCP: No active socket on port 443 (a030052ip:52510) BUFGET[24]: 000000001000adf4 (0000000000000000) ~~~ In case it is related, here is my xNetworkInterfaceOutput() function ~~~ BaseTypet xNetworkInterfaceOutput( NetworkBufferDescriptort * const pxNetworkBuffer , BaseTypet xReleaseAfterSend ) { BaseTypet xReturn = pdFAIL; int32_t x;
/* Attempt to obtain access to a Tx buffer. */
for( x = 0; x < niMAX_TX_ATTEMPTS; x++ )
{
    if( EMAC_CheckTransmitIndex() == TRUE )
    {
        /* Will the data fit in the Tx buffer? */
        if( pxNetworkBuffer->xDataLength < EMAC_ETH_MAX_FLEN ) /*_RB_ The size needs to come from FreeRTOSIPConfig.h. */
        {
            /* copy the buffer to the Tx DMA descriptor. */
            EMAC_PACKETBUF_Type TXBuffer;
            TXBuffer.ulDataLen = pxNetworkBuffer->xDataLength;
            TXBuffer.pbDataBuf = (uint32_t *)pxNetworkBuffer->pucEthernetBuffer;
            EMAC_WritePacketBuffer( &TXBuffer );
            EMAC_UpdateTxProduceIndex();

            /* The EMAC now owns the buffer. */
            pxNetworkBuffer->pucEthernetBuffer = NULL;

            iptraceNETWORK_INTERFACE_TRANSMIT();

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

        break;
    }
    else
    {
        vTaskDelay( niTX_BUFFER_FREE_WAIT );
    }
}

/* Finished with the network buffer. */
if( xReleaseAfterSend != pdFALSE )
    vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );

return xReturn;
} ~~~

pxDuplicateNetworkBufferWithDescriptor sometimes asserts

Hi, The problem is this line: ~~~ /* The EMAC now owns the buffer. */ pxNetworkBuffer->pucEthernetBuffer = NULL; ~~~ There is a small difference between BufferAllocation 1 and 2: When using BufferAllocation_1.c, the pointer pucEthernetBuffer will never get set again, so that is why it becomes and remains NULL. BufferAllocation_2.c is different: pucEthernetBuffer will get set when calling pxGetNetworkBufferWithDescriptor(). Still you can use BufferAllocation_1 in combination with zero-copy transmissions as here above: ~~~ /* Will the data fit in the Tx buffer? / if( pxNetworkBuffer->xDataLength < EMAC_ETH_MAX_FLEN ) { / copy the buffer to the Tx DMA descriptor. */ EMAC_PACKETBUF_Type TXBuffer; TXBuffer.ulDataLen = pxNetworkBuffer->xDataLength; TXBuffer.pbDataBuf = (uint32_t *)pxNetworkBuffer->pucEthernetBuffer; EMAC_WritePacketBuffer( &TXBuffer ); EMAC_UpdateTxProduceIndex();
     /* The EMAC now owns the buffer. */
– pxNetworkBuffer->pucEthernetBuffer = NULL; + /* Do not release it until it has been sent. */ + xReleaseAfterSend = pdFALSE;
     iptraceNETWORK_INTERFACE_TRANSMIT();

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

 break;
~~~ In the above code, the pxNetworkBuffer will not be released. It has to be released later: as soon as the DMA reports that the packet has been sent. At that moment you need to know the address of the buffer, and call the following function to get the original NetworkBufferDescriptor_t: ~~~ NetworkBufferDescriptort *pxBuffer = pxPacketBufferto_NetworkBuffer( pvDMABuffer ); configASSERT( pxBuffer != NULL ); vReleaseNetworkBufferAndDescriptor( pxBuffer ); ~~~ Also, make sure that this is defined in your FreeRTOSIPConfig.h : ~~~

define ipconfigZEROCOPYTX_DRIVER 1

~~~ In this STM32Fx driver you see an example of this technique.