Part Number: TMS320F28388D
Other Parts Discussed in Thread: C2000WARE
Hello,
I mde an application for TMS320F28388D Connectivity Manager based on lwIP example provided in C2000 Ware 4.0.0. When I set the DEBUG symbol I encountered an error: "mem free: illegal memory: double free". The same issue is described here: https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1011110/tms320f28388d-lwip-stack-crash. After some research I think I found what is the reason for this error. In file lwip2.1.2/portsC2000/netif/f2838x I changed the function which is called in EMAC_TX0 ISR: f2838xif_process_transmit. I've changed this fragment:
(...)
do
{
pktDescPtrShadow = pktDescPtr->nextPacketDesc;
memp_free(MEMP_ETH_PKT_DESC, pPacket);
pktDescPtr = pktDescPtrShadow;
}
while(pktDescPtr != 0);
(...)
into this:
(...)
mem_free(pPacket);
(...)
It seems to me that this function should free only the pPacket (one packet fragment) because if it frees all packet fragments it violates the structure of descriptor queue defined in driverlib (ethernet.c). The loop in function Ethernet_removePacketsFromTxQueue (ethernet.c in driverlib) handles all created descriptors, by calling channeldescptr->deviceptr->initconfig.pfcbfreepacket function for each of them. When I ran lwip example again the error didn't occur.
Unfortunetaly there still was a problem. When I sent a lot of data (by rapidly refreashing the website) the program hung due to lwip heap overflow. I figured out that the heap overflows because not all of the ethernet descriptors are freed in f2838xif_process_transmit. After some research I've found the reason. The problem was in driverlib function Ethernet_removePacketsFromTxQueue (ethernet.c), line 2762:
(...)
//
//we now own the packet meaning its been transferred to the port
//
pktPtr = Ethernet_performPopOnPacketQueue(
&channelDescPtr->descQueue); <--- memory leak?
if(0U != pktPtr)
{
pktPtr->flags |= ETHERNET_INTERRUPT_FLAG_TRANSMIT;
if(ETHERNET_COMPLETION_EARLY == earlyFlag )
{
pktPtr->flags = ETHERNET_INTERRUPT_FLAG_EARLY; <--- shouldn't be "|=" instead of "="?
}
}
(...)
Firstly the flags were updated in such way that f2838xif_interrupt function (f2838xif.c) sometimes couldn't recognize packet as Tx packet. Secondly when queue is not empty, but we encounter ETHERNET_TX_DESC_LAST_DESC flag, we take from the queue descriptor which is won't be handled in f2838xif_process_transmit (because we exit do-while loop) and it causes memory leak. When I removed this error there were no memory leaks. I provided my modification of driverlib function Ethernet_removePacketsFromTxQueue (ethernet.c):
void Ethernet_removePacketsFromTxQueue
(Ethernet_DescCh *channelDescPtr,
Ethernet_CompletionMode earlyFlag)
{
Ethernet_Pkt_Desc *pktPtr = NULL;
Ethernet_HW_descriptor *descRead, *newDescRead;
//
//we now own the packet meaning its been transferred to the port
//
while(0U != Ethernet_returnTopOfPacketQueue(&channelDescPtr->descQueue))
{
newDescRead = &channelDescPtr->descFirst[channelDescPtr->indexRead];
//
// Timestamp is not written back on context descriptors, so skip
//until we have a non-context transmit descriptor
//
while((newDescRead->des3 & ETHERNET_PKT_EXTENDED_FLAG_CTXT) != 0)
{
channelDescPtr->indexRead = (channelDescPtr->indexRead + 1U) %
channelDescPtr->descMax;
newDescRead = &channelDescPtr->descFirst[channelDescPtr->indexRead];
}
do {
descRead = newDescRead;
//
//we now own the packet meaning its been transferred to the port
//
pktPtr = Ethernet_performPopOnPacketQueue(&channelDescPtr->descQueue);
if(0U != pktPtr)
{
pktPtr->flags |= ETHERNET_INTERRUPT_FLAG_TRANSMIT;
if(ETHERNET_COMPLETION_EARLY == earlyFlag )
{
pktPtr->flags |= ETHERNET_INTERRUPT_FLAG_EARLY;
}
}
//
// Capture the timestamp if "last descriptor" and "timestamp
// captured" bit is set in the descriptor.
//
if((ETHERNET_TX_DESC_LAST_DESC ==
(descRead->des3 & ETHERNET_TX_DESC_LAST_DESC)) &&
(ETHERNET_TX_DESC_TTSS ==
(descRead->des3 & ETHERNET_TX_DESC_TTSS)))
{
pktPtr->timeStampLow = descRead->des0;
pktPtr->timeStampHigh = descRead->des1;
}
//
// Save the descriptor which will be checked for condition
// "whether this is the last sengment of the packet" at the
// end of this do while loop.
//
(void)((channeldescptr->deviceptr->initconfig.pfcbfreepacket)
(channelDescPtr->devicePtr->handleApplication[0U],
pktPtr));
channelDescPtr->descCount--;
if(channelDescPtr->indexRead == channelDescPtr->indexLast)
{
channelDescPtr->indexRead = channelDescPtr->indexFirst;
}
else
{
channelDescPtr->indexRead = channelDescPtr->indexRead + 1U ;
}
//
// Fetch the nest descriptor from the descriptor array
//
newDescRead = &channelDescPtr->descFirst[channelDescPtr->indexRead];
//
// Need to clear the desc array for all the packets until
// the last packet segment is encountered, so repeat.
//
}while(ETHERNET_TX_DESC_LAST_DESC !=
(descRead->des3 & ETHERNET_TX_DESC_LAST_DESC));
}
channelDescPtr->dmaInProgress = 0U;
//
//Try to post any waiting TX packets
//
if((0U != channelDescPtr->waitQueue.count) &&
(channelDescPtr->dmaInProgress == 0U))
{
Ethernet_addPacketsIntoTxQueue(channelDescPtr);
}
}
Please give me feedback if I fixed the bug or I misunderstood something.
Thanks,
JK



(missing flag)