Hello,
I've created an application based on C2000 Ware example Webserver (lwip). In my application I turned on lwip stats and I noticed that I have tcp.proterr != 0. I investigated this and it turned out that the tcp payload which is being processed by lwip happen to change during processing. After further investigation I found that the problem is in CM driverlib function: Ethernet_removePacketsFromRxQueue. The documentation says that application driver should check OWN bit of Rx packet descritor and if this bit is set to 0 it means that the packet is owned by application and can be processed. In driverlib this codition is checked but the flag is manually set to zero before check so it can process packets that belong to DMA. I provided code fragment from driverlib function Ethernet_removePacketsFromRxQueue (lines 2890-2907):
(...)
descRead =
&channelDescPtr->descFirst[channelDescPtr->indexRead];
/* Get the status of this descriptor*/
PktFlgLen = descRead->des3;
if(0U != (descRead->des1 &
(1U<< ETHERNET_RX_NORMAL_DESC_RDES1_TSA_LBIT_POS)))
{
contextTStampAvailable = 1U;
}
/* Bit 16,17 and 18 indicate the port number(ingress)
* Passcrc bit is always set in the received packets
*Clear it before putting the
* packet in receive queue*/
PktFlgLen = PktFlgLen & 0x0BFFFFFFU; <--- OWN bit set to zero
/* Check the ownership of the packet*/
if(0x0U == (PktFlgLen & ETHERNET_DESC_OWNER)) <--- OWN bit checked
{
(...)
I also had weak performance and I foud that when Receive ISR happen EMAC often has multiple packets in Rx queue but function Ethernet_removePacketsFromRxQueue processes only one packet. I changed this function to process all packets with OWN bit set to 0. I provided my fix below:
void Ethernet_removePacketsFromRxQueue(Ethernet_DescCh *channelDescPtr,
Ethernet_CompletionMode earlyFlag)
{
Ethernet_Pkt_Desc *pktPtr = 0U;
Ethernet_Pkt_Desc *unusedPktPtr = 0U;
Ethernet_Pkt_Desc *newPktPtr = 0U;
Ethernet_HW_descriptor *descNewRxLastPtr=0U;
uint32_t PktFlgLen;
uint8_t contextTStampAvailable;
Ethernet_HW_descriptor *descRead;
uint32_t pktInd;
uint32_t des3Tmp;
contextTStampAvailable = 0U;
/*
* Pop & Free Buffers 'till the last Descriptor
* One thing we know for sure is that all the decriptors from
* the read pointer to pDescAsk are linked to each other via
* their pNext field
*/
if(earlyFlag == ETHERNET_COMPLETION_EARLY)
{
pktPtr = Ethernet_performPopOnPacketQueue(
&channelDescPtr->descQueue);
ASSERT(NULL != pktPtr);
channelDescPtr->descCount--;
pktPtr->bufferLength = channelDescPtr->chInfo->burstLength;
pktPtr->pktChannel = channelDescPtr->chInfo->chNum;
pktPtr->numPktFrags = Ethernet_getRxERICount(
Ethernet_device_struct.baseAddresses.enet_base,
channelDescPtr->chInfo->chNum);
pktPtr->flags |= ETHERNET_INTERRUPT_FLAG_RECEIVE | ETHERNET_INTERRUPT_FLAG_EARLY;
/* Pass the packet to the application*/
newPktPtr = (*Ethernet_device_struct.initConfig.pfcbRxPacket)
(channelDescPtr->devicePtr->handleApplication[0],
pktPtr);
}
else
{
descRead = &channelDescPtr->descFirst[channelDescPtr->indexRead];
PktFlgLen = descRead->des3;
for (pktInd=0;
((pktInd<channelDescPtr->descMax) && ((PktFlgLen & ETHERNET_DESC_OWNER) == 0x0U));
pktInd++)
{
/* Get the status of this descriptor*/
if(0U != (descRead->des1 &
(1U<< ETHERNET_RX_NORMAL_DESC_RDES1_TSA_LBIT_POS)))
{
contextTStampAvailable = 1U;
}
/* Recover the buffer and free it*/
pktPtr = Ethernet_performPopOnPacketQueue(
&channelDescPtr->descQueue);
if(0U != pktPtr)
{
/* Fill in the necessary packet header fields*/
pktPtr->flags = PktFlgLen & 0xFFFF8000U;
//
//Payload Length is Least 14 bits in Receive Descriptor
//Writeback
//
pktPtr->pktLength = (PktFlgLen & 0x7FFFU);
pktPtr->validLength = pktPtr->pktLength;
pktPtr->pktChannel = channelDescPtr->chInfo->chNum;
pktPtr->numPktFrags = 1U;
pktPtr->flags |= ETHERNET_INTERRUPT_FLAG_RECEIVE;
pktPtr->flags &= ~ETHERNET_INTERRUPT_FLAG_EARLY;
if(0U != contextTStampAvailable)
{
//
//Read the Next Descriptor to read the time stamp
// Move the read pointer and decrement count
//
if(channelDescPtr->indexRead ==
channelDescPtr->indexLast)
{
channelDescPtr->indexRead =
channelDescPtr->indexFirst;
}
else
{
channelDescPtr->indexRead =
channelDescPtr->indexRead + 1U;
}
channelDescPtr->descCount--;
descRead =
&channelDescPtr->descFirst[
channelDescPtr->indexRead];
if(0U != (descRead->des3 &
(1U << ETHERNET_RX_CONTEXT_DESC_RDES3_CTXT_HBIT_POS)))
{
pktPtr->timeStampLow = descRead->des0;
pktPtr->timeStampHigh = descRead->des1;
pktPtr->nextBufferDiscarded = 1;
#ifdef ETHERNET_DEBUG
Ethernet_rxContextTimeStamp++;
#endif
}
}
/* Pass the packet to the application*/
newPktPtr =
(*Ethernet_device_struct.initConfig.pfcbRxPacket)
(channelDescPtr->devicePtr->handleApplication[0],
pktPtr);
/* Move the read pointer and decrement count*/
if(channelDescPtr->indexRead ==
channelDescPtr->indexLast)
{
channelDescPtr->indexRead =
channelDescPtr->indexFirst;
}
else
{
channelDescPtr->indexRead =
channelDescPtr->indexRead + 1U;
}
channelDescPtr->descCount--;
}
/* See if we got a replacement packet*/
if(0U != newPktPtr)
{
/* We know we can immediately queue this packet*/
/* Fill in the descriptor for this buffer*/
descNewRxLastPtr =
&channelDescPtr->descFirst[channelDescPtr->indexWrite];
/* Move the write pointer and bump count*/
if( channelDescPtr->indexWrite == channelDescPtr->indexLast )
{
channelDescPtr->indexWrite = channelDescPtr->indexFirst;
}
else
{
channelDescPtr->indexWrite = channelDescPtr->indexWrite + 1U;
}
channelDescPtr->descCount++;
/* Supply buffer pointer with application supplied offset*/
descNewRxLastPtr->des0 =
(uint32_t)(&newPktPtr->dataBuffer [newPktPtr->dataOffset]);
descNewRxLastPtr->des1 = 0U;
descNewRxLastPtr->des2 = 0U;
descNewRxLastPtr->des3 = ETHERNET_DESC_OWNER
| ETHERNET_RX_DESC_IOC
| ETHERNET_RX_DESC_BUF1_VALID;
/* Push the packet buffer on the local descriptor queue */
Ethernet_performPushOnPacketQueue(&channelDescPtr->descQueue,
newPktPtr);
newPktPtr = 0U;
}
else
{
#ifdef ETHERNET_DEBUG
Ethernet_rxReplacementFailedCount++;
#endif
}
/*
* Context Descriptor results in the loss of one packet descriptor and
* it's corresponding buffer, so handle it here.
*/
if(0 != contextTStampAvailable)
{
/*
* This packet corresponding to the context descriptor is unused
* but still needs to be discarded to maintain one-to-one mapping
* between the "Packet Descriptor Queue" passed by the application
* and the "DMA Hardware Ring".
*/
unusedPktPtr = Ethernet_performPopOnPacketQueue(
&channelDescPtr->descQueue);
/*
* Get a replacement buffer from the application
*/
unusedPktPtr = (*Ethernet_device_struct.initConfig.pfcbGetPacket)();
/* Supply buffer pointer with application supplied offset */
descNewRxLastPtr =
&channelDescPtr->descFirst[channelDescPtr->indexWrite];
descNewRxLastPtr->des0 =
(uint32_t)(&unusedPktPtr->dataBuffer[unusedPktPtr->dataOffset]);
descNewRxLastPtr->des1 = 0U;
descNewRxLastPtr->des2 = 0U;
descNewRxLastPtr->des3 = ETHERNET_DESC_OWNER
| ETHERNET_RX_DESC_IOC
| ETHERNET_RX_DESC_BUF1_VALID;
/* Push the packet buffer again on
the local descriptor queue*/
Ethernet_performPushOnPacketQueue(
&channelDescPtr->descQueue,
unusedPktPtr);
/* Move the write pointer and bump count*/
if( channelDescPtr->indexWrite == channelDescPtr->indexLast )
{
channelDescPtr->indexWrite = channelDescPtr->indexFirst;
}
else
{
channelDescPtr->indexWrite = channelDescPtr->indexWrite + 1U;
}
channelDescPtr->descCount++;
}
/* Chceck next packet descriptor */
descRead = &channelDescPtr->descFirst[channelDescPtr->indexRead];
PktFlgLen = descRead->des3;
}
}
/* See if we got a replacement packet*/
if(0U != newPktPtr)
{
/* We know we can immediately queue this packet*/
/* Fill in the descriptor for this buffer*/
descNewRxLastPtr =
&channelDescPtr->descFirst[channelDescPtr->indexWrite];
/* Move the write pointer and bump count*/
if( channelDescPtr->indexWrite == channelDescPtr->indexLast )
{
channelDescPtr->indexWrite = channelDescPtr->indexFirst;
}
else
{
channelDescPtr->indexWrite = channelDescPtr->indexWrite + 1U;
}
channelDescPtr->descCount++;
/* Supply buffer pointer with application supplied offset*/
descNewRxLastPtr->des0 =
(uint32_t)(&newPktPtr->dataBuffer [newPktPtr->dataOffset]);
descNewRxLastPtr->des1 = 0U;
descNewRxLastPtr->des2 = 0U;
descNewRxLastPtr->des3 = ETHERNET_DESC_OWNER
| ETHERNET_RX_DESC_IOC
| ETHERNET_RX_DESC_BUF1_VALID;
/* Push the packet buffer on the local descriptor queue */
Ethernet_performPushOnPacketQueue(&channelDescPtr->descQueue,
newPktPtr);
}
else
{
#ifdef ETHERNET_DEBUG
Ethernet_rxReplacementFailedCount++;
#endif
}
}
Please give me feedback if this was a bug and I fixed it or I misunderstand something.
Best Regards,
JK