This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS570LC4357: EMAC RX IRQ stops being called

Part Number: TMS570LC4357
Other Parts Discussed in Thread: HALCOGEN

Hello,

we currently have a platform where we run LwIP, with changes from the example provided by TI, essentially to prevent the processing of the IP requests by the RX IRQ. The processing is done instead in the while loop in the function main().

What we observe is that we can communicate for hours, sometimes even days, with the platform, until the RX IRQ stops being called. When this happens, we can see:

  • MACSTATUS has a value of 0x80000000 (system is idle, no error)
  • RXCONTROL has a value of 0x00000001 (rx is enable)
  • RXINTMASKSET has a value of 0x00000001 (the RX IRQ is enable)
  • MACINVECTOR has a value of 0x00000001 (there is a pending RX IRQ)
  • The global IRQs are enable (the register CPSR has a good value and other IRQs can be seen being executed)

and even so, the RX IRQ is not called. It feels like we executed an IRQ that wasn't acknowledged, but we acknowledge it at every call (see handler below).

If we manually acknowledge the RX IRQ outside the IRQ (using the debugger or a periodic function), boom, the RX IRQ is called again.

We suspect that multiple messages might arrive at the same time in our setup, but couldn't see how we end up in this scenario.

Any clues on how to explain this behavior?

Many thanks

Kind regards,

Frederic.

void hdkif_rx_inthandler(struct netif * netif)
{
  hdkif_t *psHdkif = netif->state;
  rxch_t *psRxch = &(psHdkif->rxchptr);
  volatile emac_rx_bd_t *psCurrBd, *psCurrTail, *psProcessedBd;

  struct pbuf *spBuf;
  uint16_t u16PacketLen, u16PayloadOffset, u16BdLen, u16BdOffset;
  const void *pBdData;

  /* Get the buffer descriptor which
  * contains the start of the most recent received packet
  * */
  psCurrBd = psRxch->active_head;
  psProcessedBd = psRxch->active_tail;
  /*
  * Process the descriptors as long as data is available
  * when the DMA is receiving data, SOP flag will be set
  */
  while((hdkif_swizzle_data(psCurrBd->flags_pktlen) & EMAC_BUF_DESC_SOP) == EMAC_BUF_DESC_SOP) {
    /* Start processing once the packet is loaded */
    if((hdkif_swizzle_data(psCurrBd->flags_pktlen) & EMAC_BUF_DESC_OWNER) != EMAC_BUF_DESC_OWNER) {
      /* this bd chain will be freed after processing */
      psRxch->free_head = psCurrBd;
      u16PacketLen = hdkif_swizzle_data(psCurrBd->flags_pktlen) & 0xFFFF;
      spBuf = pbuf_alloc(PBUF_RAW, u16PacketLen, PBUF_POOL);
      /* start processing the descriptors until EOP (end of packet) is met.*/
      u16PayloadOffset = 0;
      while((hdkif_swizzle_data(psCurrBd->flags_pktlen) & EMAC_BUF_DESC_EOP) != EMAC_BUF_DESC_EOP)
      {
        u16BdOffset = (uint16_t) (hdkif_swizzle_data(psCurrBd->bufoff_len) & 0xFFFF0000) >> 16;
        u16BdLen = (uint16_t) hdkif_swizzle_data(psCurrBd->bufoff_len) & 0xFFFF;
        pBdData = (const void*) (hdkif_swizzle_data(psCurrBd->bufptr) + u16BdOffset);
        /*Copy ethernet frame into the pbuf chain if pbuf was allocated (if the pool is big enough)
        * The condition has to be here because we need to iterate through all the descriptors anyway.
        * */
        if ((spBuf != NULL) && (pbuf_take_at(spBuf, pBdData, u16BdLen, u16PayloadOffset) == ERR_MEM))
        {
          /* pbuf too small, drop packet */
          pbuf_free(spBuf);
        }
        u16PayloadOffset += u16BdLen;

        psCurrBd->flags_pktlen = (uint32_t) hdkif_swizzle_data(EMAC_BUF_DESC_OWNER);
        psCurrBd->bufoff_len = (uint32_t) hdkif_swizzle_data(MAX_TRANSFER_UNIT);
        psProcessedBd = psCurrBd;

        /* process the next buffer descriptor that is still part of this packet*/
        psCurrBd = psCurrBd->next;
      }

      /* Updating the last descriptor (which contained the EOP flag) */
      pBdData = (const void*) hdkif_swizzle_data(psCurrBd->bufptr);
      u16BdLen = (uint16_t) hdkif_swizzle_data(psCurrBd->bufoff_len);
      if ((spBuf != NULL) && (pbuf_take_at(spBuf, pBdData, u16BdLen, u16PayloadOffset) == ERR_MEM))
      {
        /* pbuf too small, drop packet */
        pbuf_free(spBuf);
      }
      psCurrBd->flags_pktlen = (uint32)hdkif_swizzle_data(EMAC_BUF_DESC_OWNER);
      psCurrBd->bufoff_len = (uint32)hdkif_swizzle_data(MAX_TRANSFER_UNIT);
      psProcessedBd = psCurrBd;
      psCurrBd = hdkif_swizzle_rxp(psCurrBd->next);

      /* acknowledge to the EMAC that the BD was processed */
      EMACRxCPWrite(psHdkif->emac_base, (uint32_t)EMAC_CHANNELNUMBER, (uint32_t)psProcessedBd);

      /* add the newly created pbuf to the queue for polling */
      if ((spBuf != NULL) && (_lwIPQueueAdd(spBuf) == 0xFFUL))
      {
        /* queue is full, free the pbuf */
        pbuf_free(spBuf);
      }

      /* The next buffer descriptor is the new head of the linked list. */
      psRxch->active_head = psCurrBd;
      /* The processed descriptor is now the tail of the linked list. */
      psCurrTail = psRxch->active_tail;
      psCurrTail->next = hdkif_swizzle_rxp(psRxch->free_head);

      /* The last element in the already processed Rx descriptor chain is now the end of list. */
      psProcessedBd->next = NULL;

      /**
      * 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((hdkif_swizzle_data(psCurrTail->flags_pktlen) & EMAC_BUF_DESC_EOQ) == EMAC_BUF_DESC_EOQ) {
        EMACRxHdrDescPtrWrite(psHdkif->emac_base, (uint32)(psRxch->free_head), (uint32)EMAC_CHANNELNUMBER);
      }
      psRxch->free_head = psCurrBd;
      psRxch->active_tail = psProcessedBd;
    }
  }
  EMACCoreIntAck(psHdkif->emac_base, EMAC_INT_CORE0_RX);
}

  • Hello,

    The EMAC will not generate interrupt pulse until the original pulse has been acknowledged by the application software. The RX and TX interrupt handlers acknowledge the EMAC interrupt by writing RXnCP and EMAC control module interrupt by programming MACEOIVECTOR register.

    Frederic Desy said:
    until the RX IRQ stops being called.

     

    What does this mean in your post?

  • Hello QJ,

    thank you for your reply.

    In my RX IRQ hander, the functions EMACRxCPWrite (to clear RX0CP) and EMACCoreIntAck (to write 0x01 to MACEIOVECTOR) from HalCoGen are called to acknowledge the EMAC interrupt at every call of the IRQ. 

    Best regards,

    Frederic

  • Hi Frederic,

    Can you post your test code which generates the issue?

  • Hi QJ,

    it will be difficult to send it as is, as it is running on our platform - it is not running on the TI dev kit. I will see what I can send.

    In the meantime, could you please confirm what happens if a message is received between the time where an IRQ is acknowledged (through the call fo the function "EMACCoreIntAck") and the end of the actual execution of the IRQ (there are a few returns of functions after this function call)? Would the IRQ handler be called right away? Or would the IRQ be kept pending?

    Best regards,

    Frederic.

  • Hello Frederic,

    After entering the IRQ ISR, the IRQ is disabled. When new message is received before IRQ ISR has completed, the new IRQ interrupt will be in pending.