Hi,
On the TMS570LS3137, we are currently seeing an issue where the EMAC gets stuck in the below loop. in the Rx interrupt handler. As long as the packet data rate is low, and the application services the packets fast enough, the execution remains fine, but if the data rate is increased, i.e. we send burst of packets back to back to the ECU then we run into a stall, where we are stuck in this Loop.
We use LWiP as the IP stack, and due to the application servicing other tasks, its possible that the PBUF is not freed immediately, as soon as the callback is called. We store the Pbuf pointer in queue, service it after sometime and then free it.
I am unable to determine, why the EMAC runs into a stall in this while loop.
On Inspection in GDB, we find that curr_bd has become equal to 0x0 (NULL).
struct TiPhyData* hdkif; struct ReceiveChannel* receive_channel; volatile struct EmacRxBuffer *curr_bd, *processed_bd, *curr_tail, *last_bd; volatile struct pbuf *pbuf, *q, *new_pbuf; uint32_t ex_len = 0, len_to_alloc = 0; u16_t tot_len; err_t rc; hdkif = netif->state; receive_channel = &(hdkif->receive_channel); // Get the bd which contains the earliest filled data curr_bd = receive_channel->active_head; last_bd = receive_channel->active_tail; //* Process the descriptors as long as data is available // when the DMA is receiving data, SOP flag will be set
while (curr_bd->flags_pktlen & EMAC_BUF_DESC_SOP) { -----------------------------------> We Get Stuck in this Loop ex_len = 0; len_to_alloc = 0; // Start processing once the packet is loaded if ((curr_bd->flags_pktlen & EMAC_BUF_DESC_OWNER) != EMAC_BUF_DESC_OWNER) { if (receive_channel->free_head == NULL) { // this bd chain will be freed after processing receive_channel->free_head = curr_bd; } // Get the total length of the packet. curr_bd points to the start // of the packet. tot_len = (curr_bd->flags_pktlen) & 0xFFFF; // Get the start of the pbuf queue q = curr_bd->pbuf; do { // Get the pbuf pointer which is associated with the current bd pbuf = curr_bd->pbuf; // If the earlier pbuf ended, update the chain if (pbuf->next == NULL) { pbuf->next = (struct pbuf*)(curr_bd->next)->pbuf; } len_to_alloc += pbuf->len; pbuf->len = (curr_bd->buffer_offset) & 0xFFFF; pbuf->tot_len = tot_len - ex_len; processed_bd = curr_bd; ex_len += pbuf->len; curr_bd = curr_bd->next; } while ((processed_bd->flags_pktlen & EMAC_BUF_DESC_EOP) != EMAC_BUF_DESC_EOP); //* Close the chain for this pbuf. A full packet is received in // this pbuf chain. Now this pbuf can be given to upper layers for // processing. The start of the pbuf chain is now 'q'. pbuf->next = NULL; // Adjust the link statistics LINK_STATS_INC(link.recv); // Process the packet // check if platform has registered a cb and call it.. else call // the standard eth input function if (plat_eth_rx_cb) {
// application callback which processes the packet through LWip and frees the PBUF rc = plat_eth_rx_cb(hdkif_data->inst_num, netif, (struct pbuf*)q); } else { rc = ethernet_input((struct pbuf*)q, netif); } if (rc != ERR_OK) { // Adjust the link statistics LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); } EMACRxCPWrite(hdkif->emac_base, 0, (unsigned int)processed_bd); receive_channel->active_head = curr_bd; // The earlier pbuf chain is freed from the upper layer. So, we need to // allocate a new pbuf chain and update the descriptors with the pbuf info. // To support chaining, the total length freed by the upper layer is tracked. // Care should be taken even if the allocation fails. // now len_to_alloc will contain the length of the pbuf which was freed // from the upper layer receive_channel->freed_pbuf_len += len_to_alloc; new_pbuf = pbuf_alloc(PBUF_RAW, (receive_channel->freed_pbuf_len), PBUF_POOL); if (new_pbuf != NULL) { curr_bd = receive_channel->free_head; for (q = new_pbuf; (q != NULL) && (curr_bd != receive_channel->active_head); q = q->next) { curr_bd->buffer_pointer = (uint32_t)(q->payload); curr_bd->buffer_offset = (q->len) & 0xFFFF; curr_bd->flags_pktlen = EMAC_BUF_DESC_OWNER; receive_channel->freed_pbuf_len -= q->len; // Save the pbuf curr_bd->pbuf = q; last_bd = curr_bd; curr_bd = curr_bd->next; } // At this point either pbuf expired or no rxbd to allocate. If // there are no, enough rx bds to allocate all pbufs in the chain, // free the rest of the pbuf if (q != NULL) { pbuf_free((struct pbuf*)q); } curr_tail = receive_channel->active_tail; last_bd->next = NULL; curr_tail->next = receive_channel->free_head; // Check if the reception has ended. If the EOQ flag is set, the NULL // Pointer is taken by the DMA engine. So we need to write the RX HDP // with the next descriptor. if (curr_tail->flags_pktlen & EMAC_BUF_DESC_EOQ) { EMACRxHdrDescPtrWrite(hdkif->emac_base, (uint32_t)(receive_channel->free_head), 0); } receive_channel->free_head = curr_bd; receive_channel->active_tail = last_bd; } } curr_bd = receive_channel->active_head; } EMACCoreIntAck(hdkif->emac_base, EMAC_INT_CORE0_RX);
}
I have seen a bunch of threads on this forum, with similar issues with the EMAC, does Ti have fix or potential workaround to this issue.
Another Thread on the forum, that has an almost identical issue: (https://e2e.ti.com/support/microcontrollers/hercules/f/312/t/446947)
Also on this thread Ti Mentions that a new driver will be available in 1Q 2019: (https://e2e.ti.com/support/microcontrollers/hercules/f/312/p/734628/2775351