heinbali01 wrote on Friday, November 25, 2016:
Michael,
Your code is releasing a Network Buffer just after sending it to the IP-task. That will most probably cause the hard-fault.
stack goes to hard-fault (according to debug the kast point is prvTaskExitError )
hard-fault’s are difficult to solve.
It sounds strange to me that a hard-fault has something to do with prvTaskExitError()
, or even that mentioned function is being called.
I suppose you have read the documentation about how to debug hard-faults?
Sometimes I use a volatile counter for debugging: increasing its value at certain points in the code that are suspect. When the hard-fault comes, at least I see what was the last instruction/function called.
I am using BufferAllocation_2.c scheme with heap4.c memory management
The is a perfect choice.
… with 26k of RAM for heap
Your ipconfigNETWORK_MTU
is 1024, that is good. Or even 640 bytes. That will allow to use a longer sliding TCP window. See documentation about FREERTOS_SO_WIN_PROPERTIES
on the website.
Try tuning the value of ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS
, the maximum number of buffers. The function uxGetMinimumFreeNetworkBuffers()
will show the lowest number of available buffers ever.
In your main.c
I read:
/* Wait until the network is up before creating the servers. The notification is given from the network event hook. */
ulTaskNotifyTake(pdTRUE, xInitialBlockTime);
Let it block for ever (using portMAX_DELAY
) and let it unblock immediately when the IP-stack is up and running:
void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent )
{
static BaseType_t xTasksAlreadyCreated = pdFALSE;
/* If the network has just come up...*/
if( eNetworkEvent == eNetworkUp )
{
/* Wake-up the web server task. */
xTaskNotifyGive( xWebServerTaskHandle );
}
About task priorities:
- prvEMACTask ( Higher )
- IP-task
- Tasks that use the IP-stack such as
prvWebServerTask
( Lower )
Other schemes may be used but we normally prefer the above order.
for(;;)
{
/* Wait for the EMAC interrupt to indicate that another packet has been
* received. The while() loop is only needed if INCLUDE_vTaskSuspend is
* set to 0 in FreeRTOSConfig.h. */
/* HT: It would be a bit cheaper to use xTaskNotifyGive() and ulTaskNotifyTake() for this purpose. */
/* HT: No need to test for the return value when using 'portMAX_DELAY' :-) */
while(xSemaphoreTake(xEthernetMACRxEventSemaphore, portMAX_DELAY) == pdFALSE);
//printf("Semaphore captured ... \n\r");
/* At least one packet has been received. */
while(CheckIsDataAvailable())
{
//printf("After index check ... \n\r");
// Obtain the length, minus the CRC. The CRC is four bytes but the length is already minus 1.
dataLength = (size_t) CheckAvailableDataSize();//GetReceivedDataSize() - (cRCLength - 1);
if(dataLength > 0)
{
networkBuffer = pxGetNetworkBufferWithDescriptor(0, (TickType_t )0);
/* HT: Remember that pxGetNetworkBufferWithDescriptor() may fail and return NULL. */
/* HT: In that case the RX message must still be popped to free the DMA slot. */
networkBuffer->xDataLength = dataLength;
rxBuffer._buffer = networkBuffer->pucEthernetBuffer;
rxBuffer._bufferCapacity = networkBuffer->xDataLength;
Read(&rxBuffer);
rxEvent.pvData = (void *) networkBuffer;
// printf("Received data: ");
// printStringHexSymbols(networkBuffer->pucEthernetBuffer, dataLength, -1);
// Data was received and stored. Send a message to the IP task to let it know.
/* HT: You really don't want to wait in case the queue is full? Not even a few ms. ? */
if(xSendEventStructToIPTask(&rxEvent, (TickType_t)0) == pdFAIL)
{
/* HT: Yes the function vReleaseNetworkBufferAndDescriptor() must be called.
Message can not be delivered to the IP-task. */
//vReleaseNetworkBufferAndDescriptor(networkBuffer);
iptraceETHERNET_RX_EVENT_LOST();
}
else
{
/* HT: This would be a iptraceNETWORK_INTERFACE_RECEIVE trace point. */
iptraceETHERNET_RX_EVENT_LOST();
}
/* HT: you're freeing a packet that you've just sent to the IP-task. */
vReleaseNetworkBufferAndDescriptor(networkBuffer);
iptraceNETWORK_INTERFACE_RECEIVE();
}
And some comments about the interrupt-handler:
void EthernetIrqHandler()
{
uint32_t interruptCause;
/* HT: You need a variable to remember if a task switch is required. */
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
while((interruptCause = LPC_EMAC->IntStatus) != 0)
{
//printf("Interrupt raised ... \n\r");
/* Clear the interrupt. */
LPC_EMAC->IntClear = interruptCause;
/* Clear fatal error conditions. NOTE: The driver does not clear all
* errors, only those actually experienced. For future reference, range
* errors are not actually errors so can be ignored. */
if((interruptCause & INT_TX_UNDERRUN) != 0)
LPC_EMAC->Command |= CR_TX_RES;
/* Unblock the deferred interrupt handler task if the event was an Rx. */
if((interruptCause & INT_RX_DONE) != 0)
- xSemaphoreGiveFromISR(xEthernetMACRxEventSemaphore, NULL);
+ xSemaphoreGiveFromISR(xEthernetMACRxEventSemaphore, &xHigherPriorityTaskWoken);
}
/* ulInterruptCause is used for convenience here. A context switch is
* wanted, but coding portEND_SWITCHING_ISR( 1 ) would likely result in a
* compiler warning. */
/* HT: the parameter of portEND_SWITCHING_ISR() must be non-zero if a task-switch is required.
You don't know if a switch is required, this depends on the above call xSemaphoreGiveFromISR() */
- portEND_SWITCHING_ISR(interruptCause);
/* HT: use the new name and the correct variable. */
+ portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
The task prvEMACTask()
is also sometimes called a “deferred interrupt handler”. The interrupt takes place, it registers the interrupt cause and it defers the work to EthernetIrqHandler()
.
I think that it also must pass TX-done and TX-error events, because you are lending out the Network Buffer to the DMA:
BaseType_t xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer, BaseType_t xReleaseAfterSend)
{
BaseType_t result = pdFAIL;
struct EthernetBuffer txBuffer;
/* Attempt to obtain access to a Tx buffer. */
for(uint32_t x = 0; x < MAX_TX_ATTEMPTS; x++)
{
if( CheckTransmissionAvailable() == 1) //todo add check!
{
//todo: umv: packetization
if( pxNetworkBuffer->xDataLength < ETH_MAX_FLEN )
{
txBuffer._buffer = pxNetworkBuffer->pucEthernetBuffer;
txBuffer._bufferCapacity = pxNetworkBuffer->xDataLength;
txBuffer._storedBytes = pxNetworkBuffer->xDataLength;
/* HT: Write() a non-blocking function that only passes the buffer to DMA. */
Write(&txBuffer);
iptraceNETWORK_INTERFACE_TRANSMIT();
result = pdPASS;
break;
}
}
/* HT: In production version, you could block here on a TX-done event to make it faster. */
else vTaskDelay(TX_CHECK_BUFFER_TIME);
}
//vReleaseNetworkBufferAndDescriptor(pxNetworkBuffer);
return pdPASS;
}
In the above function, if xReleaseAfterSend
is true, you get the ownership of the Network Buffer, and the driver is obliged to release it.
You have chosen for :
#define ipconfigZERO_COPY_TX_DRIVER ( 1 )
and therefore xReleaseAfterSend
will always be true.
But remember that vReleaseNetworkBufferAndDescriptor()
must be called once the packet has been sent, also in case sending failed.
Regards.