I contacted the customer support center, and they asked me to post it here.
Earlier this year, there was a UDP transmission problem in TivaWare for C Series Software (using version 2.1.4.178),
but it seems to have not been fixed in version 2.2.0.295 either.
This symptom is caused by fragmentation of data that exceeds the MTU size.
Specifically, a CRC error occurs on the data receiving side, and the data appears to be partially corrupted.
My guess is that the problem is with the function below.
(path: third_party\lwip-1.4.1\ports\tiva-tm4c129\netif\tiva-tm4c129.c)static err_t tivaif_transmit(struct netif *psNetif, struct pbuf *p) { tStellarisIF *pIF; tDescriptor *pDesc; struct pbuf *pBuf; uint32_t ui32NumChained, ui32NumDescs; bool bFirst; SYS_ARCH_DECL_PROTECT(lev); LWIP_DEBUGF(NETIF_DEBUG, ("tivaif_transmit 0x%08x, len %d\n", p, p->tot_len)); /** * This entire function must run within a "critical section" to preserve * the integrity of the transmit pbuf queue. */ SYS_ARCH_PROTECT(lev); /* Update our transmit attempt counter. */ DRIVER_STATS_INC(TXCount); /** * Increase the reference count on the packet provided so that we can * hold on to it until we are finished transmitting its content. */ pbuf_ref(p); /** * Determine whether all buffers passed are within SRAM and, if not, copy * the pbuf into SRAM-resident buffers so that the Ethernet DMA can access * the data. */ p = tivaif_check_pbuf(p); /* Make sure we still have a valid buffer (it may have been copied) */ if(!p) { LINK_STATS_INC(link.memerr); SYS_ARCH_UNPROTECT(lev); return(ERR_MEM); } /* Get our state data from the netif structure we were passed. */ pIF = (tStellarisIF *)psNetif->state; /* Make sure that the transmit descriptors are not all in use */ pDesc = &(pIF->pTxDescList->pDescriptors[pIF->pTxDescList->ui32Write]); if(pDesc->pBuf) { /** * The current write descriptor has a pbuf attached to it so this * implies that the ring is full. Reject this transmit request with a * memory error since we can't satisfy it just now. */ pbuf_free(p); LINK_STATS_INC(link.memerr); DRIVER_STATS_INC(TXNoDescCount); SYS_ARCH_UNPROTECT(lev); return (ERR_MEM); } /* How many pbufs are in the chain passed? */ ui32NumChained = (uint32_t)pbuf_clen(p); /* How many free transmit descriptors do we have? */ ui32NumDescs = (pIF->pTxDescList->ui32Read > pIF->pTxDescList->ui32Write) ? (pIF->pTxDescList->ui32Read - pIF->pTxDescList->ui32Write) : ((NUM_TX_DESCRIPTORS - pIF->pTxDescList->ui32Write) + pIF->pTxDescList->ui32Read); /* Do we have enough free descriptors to send the whole packet? */ if(ui32NumDescs < ui32NumChained) { /* No - we can't transmit this whole packet so return an error. */ pbuf_free(p); LINK_STATS_INC(link.memerr); DRIVER_STATS_INC(TXNoDescCount); SYS_ARCH_UNPROTECT(lev); return (ERR_MEM); } /* Tag the first descriptor as the start of the packet. */ bFirst = true; pDesc->Desc.ui32CtrlStatus = DES0_TX_CTRL_FIRST_SEG; /* Here, we know we can send the packet so write it to the descriptors */ pBuf = p; while(ui32NumChained) { /* Get a pointer to the descriptor we will write next. */ pDesc = &(pIF->pTxDescList->pDescriptors[pIF->pTxDescList->ui32Write]); /* Fill in the buffer pointer and length */ pDesc->Desc.ui32Count = (uint32_t)pBuf->len; pDesc->Desc.pvBuffer1 = pBuf->payload; /* Tag the first descriptor as the start of the packet. */ if(bFirst) { bFirst = false; pDesc->Desc.ui32CtrlStatus = DES0_TX_CTRL_FIRST_SEG; } else { pDesc->Desc.ui32CtrlStatus = 0; } pDesc->Desc.ui32CtrlStatus |= (DES0_TX_CTRL_IP_ALL_CKHSUMS | DES0_TX_CTRL_CHAINED); /* Decrement our descriptor counter, move on to the next buffer in the * pbuf chain. */ ui32NumChained--; pBuf = pBuf->next; /* Update the descriptor list write index. */ pIF->pTxDescList->ui32Write++; if(pIF->pTxDescList->ui32Write == NUM_TX_DESCRIPTORS) { pIF->pTxDescList->ui32Write = 0; } /* If this is the last descriptor, mark it as the end of the packet. */ if(!ui32NumChained) { pDesc->Desc.ui32CtrlStatus |= (DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_INTERRUPT); /* Tag the descriptor with the original pbuf pointer. */ pDesc->pBuf = p; } else { /* Set the lsb of the pbuf pointer. We use this as a signal that * we should not free the pbuf when we are walking the descriptor * list while processing the transmit interrupt. We only free the * pbuf when processing the last descriptor used to transmit its * chain. */ pDesc->pBuf = (struct pbuf *)((uint32_t)p + 1); } DRIVER_STATS_INC(TXBufQueuedCount); /* Hand the descriptor over to the hardware. */ pDesc->Desc.ui32CtrlStatus |= DES0_TX_CTRL_OWN; } /* Tell the transmitter to start (in case it had stopped). */ EMACTxDMAPollDemand(EMAC0_BASE); /* Update lwIP statistics */ LINK_STATS_INC(link.xmit); SYS_ARCH_UNPROTECT(lev); return(ERR_OK); }
We modified the function as follows and confirmed the normal operation.
The corrected line is 103 and 110.static err_t if_transmit(struct netif *psNetif, struct pbuf *p) { tStellarisIF *pIF; tDescriptor *pDesc; struct pbuf *pBuf; uint32_t ui32NumChained, ui32NumDescs; bool bFirst; SYS_ARCH_DECL_PROTECT(lev); LWIP_DEBUGF(NETIF_DEBUG, ("tivaif_transmit 0x%08x, len %d\n", p, p->tot_len)); /** * This entire function must run within a "critical section" to preserve * the integrity of the transmit pbuf queue. */ SYS_ARCH_PROTECT(lev); /* Update our transmit attempt counter. */ DRIVER_STATS_INC(TXCount); /** * Increase the reference count on the packet provided so that we can * hold on to it until we are finished transmitting its content. */ pbuf_ref(p); /** * Determine whether all buffers passed are within SRAM and, if not, copy * the pbuf into SRAM-resident buffers so that the Ethernet DMA can access * the data. */ p = tivaif_check_pbuf(p); /* Make sure we still have a valid buffer (it may have been copied) */ if(!p) { LINK_STATS_INC(link.memerr); SYS_ARCH_UNPROTECT(lev); return(ERR_MEM); } /* Get our state data from the netif structure we were passed. */ pIF = (tStellarisIF *)psNetif->state; /* Make sure that the transmit descriptors are not all in use */ pDesc = &(pIF->pTxDescList->pDescriptors[pIF->pTxDescList->ui32Write]); if(pDesc->pBuf) { /** * The current write descriptor has a pbuf attached to it so this * implies that the ring is full. Reject this transmit request with a * memory error since we can't satisfy it just now. */ pbuf_free(p); LINK_STATS_INC(link.memerr); DRIVER_STATS_INC(TXNoDescCount); SYS_ARCH_UNPROTECT(lev); return (ERR_MEM); } /* How many pbufs are in the chain passed? */ ui32NumChained = (uint32_t)pbuf_clen(p); /* How many free transmit descriptors do we have? */ ui32NumDescs = (pIF->pTxDescList->ui32Read > pIF->pTxDescList->ui32Write) ? (pIF->pTxDescList->ui32Read - pIF->pTxDescList->ui32Write) : ((NUM_TX_DESCRIPTORS - pIF->pTxDescList->ui32Write) + pIF->pTxDescList->ui32Read); /* Do we have enough free descriptors to send the whole packet? */ if(ui32NumDescs < ui32NumChained) { /* No - we can't transmit this whole packet so return an error. */ pbuf_free(p); LINK_STATS_INC(link.memerr); DRIVER_STATS_INC(TXNoDescCount); SYS_ARCH_UNPROTECT(lev); return (ERR_MEM); } /* Tag the first descriptor as the start of the packet. */ bFirst = true; pDesc->Desc.ui32CtrlStatus = DES0_TX_CTRL_FIRST_SEG; /* Here, we know we can send the packet so write it to the descriptors */ pBuf = p; while(ui32NumChained) { /* Get a pointer to the descriptor we will write next. */ pDesc = &(pIF->pTxDescList->pDescriptors[pIF->pTxDescList->ui32Write]); /* Fill in the buffer pointer and length */ pDesc->Desc.ui32Count = (uint32_t)pBuf->len; pDesc->Desc.pvBuffer1 = pBuf->payload; /* Tag the first descriptor as the start of the packet. */ if(bFirst) { bFirst = false; pDesc->Desc.ui32CtrlStatus = (DES0_TX_CTRL_FIRST_SEG | DES0_TX_CTRL_IP_ALL_CKHSUMS); } else { pDesc->Desc.ui32CtrlStatus = 0; } pDesc->Desc.ui32CtrlStatus |= (//DES0_TX_CTRL_IP_ALL_CKHSUMS | DES0_TX_CTRL_CHAINED); /* Decrement our descriptor counter, move on to the next buffer in the * pbuf chain. */ ui32NumChained--; pBuf = pBuf->next; /* Update the descriptor list write index. */ pIF->pTxDescList->ui32Write++; if(pIF->pTxDescList->ui32Write == NUM_TX_DESCRIPTORS) { pIF->pTxDescList->ui32Write = 0; } /* If this is the last descriptor, mark it as the end of the packet. */ if(!ui32NumChained) { pDesc->Desc.ui32CtrlStatus |= (DES0_TX_CTRL_LAST_SEG | DES0_TX_CTRL_INTERRUPT); /* Tag the descriptor with the original pbuf pointer. */ pDesc->pBuf = p; } else { /* Set the lsb of the pbuf pointer. We use this as a signal that * we should not free the pbuf when we are walking the descriptor * list while processing the transmit interrupt. We only free the * pbuf when processing the last descriptor used to transmit its * chain. */ pDesc->pBuf = (struct pbuf *)((uint32_t)p + 1); } DRIVER_STATS_INC(TXBufQueuedCount); /* Hand the descriptor over to the hardware. */ pDesc->Desc.ui32CtrlStatus |= DES0_TX_CTRL_OWN; } /* Tell the transmitter to start (in case it had stopped). */ EMACTxDMAPollDemand(EMAC0_BASE); /* Update lwIP statistics */ LINK_STATS_INC(link.xmit); SYS_ARCH_UNPROTECT(lev); return(ERR_OK); }
Only UDP was implemented with the chip, so other protocols were not tested.
Please review the feasibility.
Thank you for your support.