Packet loss in LPC LWIP emac driver

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Packet loss in LPC LWIP emac driver

2,588 Views
ankish_kacktwan
Contributor I

Version:1.0 StartHTML:000000256 EndHTML:000035106 StartFragment:000002712 EndFragment:000035074 StartSelection:000002712 EndSelection:000035074 SourceURL:https://community.nxp.com/discussion/create.jspa?question=true&containerType=14&containerID=1

Hi everyone,

I am using lpc1778 emac driver for lwip with NOS_SYS=0.

But i am experiencing a wierd issue of loosing some packet randomly after some time of running the device

which is an Modbus server which services the Modbus request from a PLC

After debugging the issue it seems the packet is lost after the DMA discriptor is handed over to the ethernet DMA by incrementing the TXProducer index

I tried to log the payload handed to by the lwip to the emac driver which is the right response from the server but is not seen on the Wireshark and thus by the PLC

here is my EMAC driver code

My emac discriptors and the buffers are located in the SDRAM

 /*
 * @brief LPC17xx/40xx LWIP EMAC driver
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2012
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights in the software, without fee, provided that it
 * is used in conjunction with NXP Semiconductors microcontrollers.  This
 * copyright, permission, and disclaimer notice must appear in all copies of
 * this code.
 */
#include "lwip/opt.h"
#include "lwip/sys.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
//#include "lwip/diag.h"
#include "lwip/snmp.h"
#include "netif/etharp.h"
//#include "netif/ppp_oe.h"

#include "lpc_17xx40xx_emac_config.h"
#include "lpc17xx_40xx_emac.h"
#include "hal/enet_17xx_40xx.h"
#include "eepromDefines.h"
#include <string.h>
extern void msDelay(uint32_t ms);
int int_rx_cnt = 0;
int int_tx_cnt = 0;
int pak_ip_cnt = 0;
typedef struct {
 UCHAR    aucTCPBuf[256+7];
} __attribute__ ((packed)) data_buff;
data_buff  data[7];
int cnt = 0;
#if LPC_NUM_BUFF_TXDESCS < 2
#error LPC_NUM_BUFF_TXDESCS must be at least 2
#endif
#if LPC_NUM_BUFF_RXDESCS < 3
#error LPC_NUM_BUFF_RXDESCS must be at least 3
#endif
/** @ingroup NET_LWIP_LPC17XX40XX_EMAC_DRIVER
 * @{
 */
/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/
#if NO_SYS == 0
/**
 * @brief Driver transmit and receive thread priorities
 * Thread priorities for receive thread and TX cleanup thread. Alter
 * to prioritize receive or transmit bandwidth. In a heavily loaded
 * system or with LWIP_DEBUG enabled, the priorities might be better
 * the same. */
#define tskRECPKT_PRIORITY   (DEFAULT_THREAD_PRIO + 4)
#define tskTXCLEAN_PRIORITY  (DEFAULT_THREAD_PRIO + 5)
// #define tskTXCLEAN_PRIORITY  (TCPIP_THREAD_PRIO - 1) // FIXME
// #define tskRECPKT_PRIORITY   (TCPIP_THREAD_PRIO - 1) // FIXME
#endif
/** @brief Debug output formatter lock define
 * When using FreeRTOS and with LWIP_DEBUG enabled, enabling this
 * define will allow RX debug messages to not interleave with the
 * TX messages (so they are actually readable). Not enabling this
 * define when the system is under load will cause the output to
 * be unreadable. There is a small tradeoff in performance for this
 * so use it only for debug. */
// #define LOCK_RX_THREAD
#if NO_SYS == 0
/** @brief Receive group interrupts
 */
#define RXINTGROUP (ENET_INT_RXOVERRUN | ENET_INT_RXERROR | ENET_INT_RXDONE)
/** @brief Transmit group interrupts
 */
#define TXINTGROUP (ENET_INT_TXUNDERRUN | ENET_INT_TXERROR | ENET_INT_TXDONE)
#else
#define RXINTGROUP 0
#define TXINTGROUP 0
#endif
/* LPC EMAC driver data structure */
typedef struct {
 /* prxs must be 8 byte aligned! */
 ENET_RXSTAT_T prxs[LPC_NUM_BUFF_RXDESCS]; /**< Pointer to RX statuses */
 ENET_RXDESC_T prxd[LPC_NUM_BUFF_RXDESCS]; /**< Pointer to RX descriptor list */
 ENET_TXSTAT_T ptxs[LPC_NUM_BUFF_TXDESCS]; /**< Pointer to TX statuses */
 ENET_TXDESC_T ptxd[LPC_NUM_BUFF_TXDESCS]; /**< Pointer to TX descriptor list */
 struct netif *pnetif;      /**< Reference back to LWIP parent netif */
 struct pbuf *rxb[LPC_NUM_BUFF_RXDESCS];  /**< RX pbuf pointer list, zero-copy mode */
 u32_t rx_fill_desc_index;     /**< RX descriptor next available index */
 volatile u32_t rx_free_descs;    /**< Count of free RX descriptors */
 struct pbuf *txb[LPC_NUM_BUFF_TXDESCS];  /**< TX pbuf pointer list, zero-copy mode */
 u32_t lpc_last_tx_idx;      /**< TX last descriptor index, zero-copy mode */
#if NO_SYS == 0
 sys_sem_t rx_sem;       /**< RX receive thread wakeup semaphore */
 sys_sem_t tx_clean_sem;      /**< TX cleanup thread wakeup semaphore */
 sys_mutex_t tx_lock_mutex;     /**< TX critical section mutex */
 sys_mutex_t rx_lock_mutex;     /**< RX critical section mutex */
 xSemaphoreHandle xtx_count_sem;    /**< TX free buffer counting semaphore */
#endif
} lpc_enetdata_t;
/** \brief  LPC EMAC driver work data
 */
ALIGNED(8) lpc_enetdata_t lpc_enetdata;
/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/
/*****************************************************************************
 * Private functions
 ****************************************************************************/
/* Queues a pbuf into the RX descriptor list */
static void lpc_rxqueue_pbuf(lpc_enetdata_t *lpc_enetif, struct pbuf *p)
{
 u32_t idx;
 /* Get next free descriptor index */
 idx = lpc_enetif->rx_fill_desc_index;
 /* Setup descriptor and clear statuses */
 lpc_enetif->prxd[idx].Control = ENET_RCTRL_INT | ((u32_t) ENET_RCTRL_SIZE(p->len));
 lpc_enetif->prxd[idx].Packet = (u32_t) p->payload;
 lpc_enetif->prxs[idx].StatusInfo = 0xFFFFFFFF;
 lpc_enetif->prxs[idx].StatusHashCRC = 0xFFFFFFFF;
 /* Save pbuf pointer for push to network layer later */
 lpc_enetif->rxb[idx] = p;
 /* Wrap at end of descriptor list */
 idx++;
 if (idx >= LPC_NUM_BUFF_RXDESCS) {
  idx = 0;
 }
 /* Queue descriptor(s) */
 lpc_enetif->rx_free_descs -= 1;
 lpc_enetif->rx_fill_desc_index = idx;
 LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
    ("lpc_rxqueue_pbuf: pbuf packet queued: %p (free desc=%lu)\n", p,
     lpc_enetif->rx_free_descs));
}
/* Sets up the RX descriptor ring buffers. */
static err_t lpc_rx_setup(lpc_enetdata_t *lpc_enetif)
{
 /* Setup pointers to RX structures */
 Chip_ENET_InitRxDescriptors(LPC_ETHERNET, lpc_enetif->prxd, lpc_enetif->prxs, LPC_NUM_BUFF_RXDESCS);
 lpc_enetif->rx_free_descs = LPC_NUM_BUFF_RXDESCS;
 lpc_enetif->rx_fill_desc_index = 0;
 /* Build RX buffer and descriptors */
 lpc_rx_queue(lpc_enetif->pnetif);
 return ERR_OK;
}
// Enthaelt Informationen zum Grund fuer das letzte Droppen eines Ethernet-Frames.
// Sind die Bits 11-17 '0' enthaelt 'status_info' das 'Receive status information word'
// des Ethernet-Controllers.
//uint32_t status_info;
uint32_t status_has_crc;
void get_lwip_stat(uint32_t *xmit, uint32_t *recv)
{
 static uint32_t last_xmit = 0, last_recv = 0;
 *xmit = lwip_stats.link.xmit - last_xmit;
 last_xmit = lwip_stats.link.xmit;
 *recv = lwip_stats.link.recv - last_recv;
 last_recv = lwip_stats.link.recv;
}
uint32_t get_link_drop(void)
{
 return lwip_stats.link.drop;
}
/* Allocates a pbuf and returns the data from the incoming packet */
static struct pbuf *lpc_low_level_input(struct netif *netif) {
 lpc_enetdata_t *lpc_enetif = netif->state;
 struct pbuf *p = NULL;
 u32_t idx, length;
#ifdef LOCK_RX_THREAD
#if NO_SYS == 0
 /* Get exclusive access */
 sys_mutex_lock(&lpc_enetif->rx_lock_mutex);
#endif
#endif
 /* Monitor RX overrun status. This should never happen unless
    (possibly) the internal bus is behing held up by something.
    Unless your system is running at a very low clock speed or
    there are possibilities that the internal buses may be held
    up for a long time, this can probably safely be removed. */
 if (Chip_ENET_GetIntStatus(LPC_ETHERNET) & ENET_INT_RXOVERRUN) {
  LINK_STATS_INC(link.err);
  LINK_STATS_INC(link.drop);
//  status_info = GET_STATUS_DEFINE(1);
  /* Temporarily disable RX */
  Chip_ENET_RXDisable(LPC_ETHERNET);
  /* Reset the RX side */
  Chip_ENET_ResetRXLogic(LPC_ETHERNET);
  Chip_ENET_ClearIntStatus(LPC_ETHERNET, ENET_INT_RXOVERRUN);
  /* De-allocate all queued RX pbufs */
  for (idx = 0; idx < LPC_NUM_BUFF_RXDESCS; idx++) {
   if (lpc_enetif->rxb[idx] != NULL) {
    pbuf_free(lpc_enetif->rxb[idx]);
    lpc_enetif->rxb[idx] = NULL;
   }
  }
  /* Start RX side again */
  lpc_rx_setup(lpc_enetif);
  /* Re-enable RX */
  Chip_ENET_RXEnable(LPC_ETHERNET);
#ifdef LOCK_RX_THREAD
#if NO_SYS == 0
  sys_mutex_unlock(&lpc_enetif->rx_lock_mutex);
#endif
#endif
  return NULL;
 }
 /* Determine if a frame has been received */
 length = 0;
 idx = Chip_ENET_GetRXConsumeIndex(LPC_ETHERNET);
 if (!Chip_ENET_IsRxEmpty(LPC_ETHERNET)) {
  /* Handle errors */
  if (lpc_enetif->prxs[idx].StatusInfo & (ENET_RINFO_CRC_ERR |
            ENET_RINFO_SYM_ERR | ENET_RINFO_ALIGN_ERR | ENET_RINFO_LEN_ERR)) {
#if LINK_STATS
   if (lpc_enetif->prxs[idx].StatusInfo & (ENET_RINFO_CRC_ERR |
             ENET_RINFO_SYM_ERR | ENET_RINFO_ALIGN_ERR)) {
    LINK_STATS_INC(link.chkerr);
   }
   if (lpc_enetif->prxs[idx].StatusInfo & ENET_RINFO_LEN_ERR) {
    LINK_STATS_INC(link.lenerr);
   }
#endif
   /* Drop the frame */
   LINK_STATS_INC(link.drop);
//   status_info = lpc_enetif->prxs[idx].StatusInfo;
   status_has_crc = lpc_enetif->prxs[idx].StatusHashCRC;
   /* Re-queue the pbuf for receive */
   lpc_enetif->rx_free_descs++;
   p = lpc_enetif->rxb[idx];
   lpc_enetif->rxb[idx] = NULL;
   lpc_rxqueue_pbuf(lpc_enetif, p);
   LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
      ("lpc_low_level_input: Packet dropped with errors (0x%lx)\n",
       lpc_enetif->prxs[idx].StatusInfo));
   p = NULL;
  }
  else {
   /* A packet is waiting, get length */
   length = ENET_RINFO_SIZE(lpc_enetif->prxs[idx].StatusInfo) - 4; /* Remove FCS */
   /* Zero-copy */
   p = lpc_enetif->rxb[idx];
   p->len = (u16_t) length;
   /* Free pbuf from desriptor */
   lpc_enetif->rxb[idx] = NULL;
   lpc_enetif->rx_free_descs++;
   /* Queue new buffer(s) */
   if (lpc_rx_queue(lpc_enetif->pnetif) == 0) {
    /* Re-queue the pbuf for receive */
    lpc_rxqueue_pbuf(lpc_enetif, p);
    /* Drop the frame */
    LINK_STATS_INC(link.drop);
//    status_info = GET_STATUS_DEFINE(2);
    LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
       ("lpc_low_level_input: Packet dropped since it could not allocate Rx Buffer\n"));
    p = NULL;
   }
   else {
    LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
       ("lpc_low_level_input: Packet received: %p, size %lu (index=%lu)\n",
        p, length, idx));
    /* Save size */
    p->tot_len = (u16_t) length;
    LINK_STATS_INC(link.recv);
   }
  }
  /* Update Consume index */
  Chip_ENET_IncRXConsumeIndex(LPC_ETHERNET);
 }
#ifdef LOCK_RX_THREAD
#if NO_SYS == 0
 sys_mutex_unlock(&lpc_enetif->rx_lock_mutex);
#endif
#endif
 return p;
}
/* Determine if the passed address is usable for the ethernet DMA controller */
static s32_t lpc_packet_addr_notsafe(void *addr) {
#if defined(CHIP_LPC175X_6X)
 /* Check for legal address ranges */
 if ((((u32_t) addr >= 0x10000000) && ((u32_t) addr < 0x10008000)) /* 32kB local SRAM */
  || (((u32_t) addr >= 0x1FFF0000) && ((u32_t) addr < 0x1FFF2000)) /* 8kB ROM */
  || (((u32_t) addr >= 0x2007C000) && ((u32_t) addr < 0x20084000)) /* 32kB AHB SRAM */
  ) {
  return 0;
 }
 return 1;
#else
 /* Check for legal address ranges */
 if ((((u32_t) addr >= 0x20000000) && ((u32_t) addr < 0x20008000)) ||
  (((u32_t) addr >= 0x80000000) && ((u32_t) addr < 0xF0000000))) {
  return 0;
 }
 return 1;
#endif 
}
/* Sets up the TX descriptor ring buffers */
static err_t lpc_tx_setup(lpc_enetdata_t *lpc_enetif)
{
 s32_t idx;
 /* Build TX descriptors for local buffers */
 for (idx = 0; idx < LPC_NUM_BUFF_TXDESCS; idx++) {
  lpc_enetif->ptxd[idx].Control = 0;
  lpc_enetif->ptxs[idx].StatusInfo = 0xFFFFFFFF;
 }
 /* Setup pointers to TX structures */
 Chip_ENET_InitTxDescriptors(LPC_ETHERNET, lpc_enetif->ptxd, lpc_enetif->ptxs, LPC_NUM_BUFF_TXDESCS);
 lpc_enetif->lpc_last_tx_idx = 0;
 return ERR_OK;
}
/* Free TX buffers that are complete */
static void lpc_tx_reclaim_st(lpc_enetdata_t *lpc_enetif, u32_t cidx)
{
#if NO_SYS == 0
 /* Get exclusive access */
 sys_mutex_lock(&lpc_enetif->tx_lock_mutex);
#endif
 while (cidx != lpc_enetif->lpc_last_tx_idx) {
  if (lpc_enetif->txb[lpc_enetif->lpc_last_tx_idx] != NULL) {
   LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
      ("lpc_tx_reclaim_st: Freeing packet %p (index %lu)\n",
       lpc_enetif->txb[lpc_enetif->lpc_last_tx_idx],
       lpc_enetif->lpc_last_tx_idx));
   pbuf_free(lpc_enetif->txb[lpc_enetif->lpc_last_tx_idx]);
   lpc_enetif->txb[lpc_enetif->lpc_last_tx_idx] = NULL;
  }
#if NO_SYS == 0
  xSemaphoreGive(lpc_enetif->xtx_count_sem);
#endif
  lpc_enetif->lpc_last_tx_idx++;
  if (lpc_enetif->lpc_last_tx_idx >= LPC_NUM_BUFF_TXDESCS) {
   lpc_enetif->lpc_last_tx_idx = 0;
  }
 }
#if NO_SYS == 0
 /* Restore access */
 sys_mutex_unlock(&lpc_enetif->tx_lock_mutex);
#endif
}
/* Low level output of a packet. Never call this from an interrupt context,
 * as it may block until TX descriptors become available. */
static err_t lpc_low_level_output(struct netif *netif, struct pbuf *p)
{
 lpc_enetdata_t *lpc_enetif = netif->state;
 struct pbuf *q;
#if LPC_TX_PBUF_BOUNCE_EN == 1
 u8_t *dst;
 struct pbuf *np;
#endif
 u32_t idx;
 u32_t dn, notdmasafe = 0;
 /* Zero-copy TX buffers may be fragmented across mutliple payload
    chains. Determine the number of descriptors needed for the
    transfer. The pbuf chaining can be a mess! */
 dn = (u32_t) pbuf_clen(p);
 /* Test to make sure packet addresses are DMA safe. A DMA safe
    address is once that uses external memory or periphheral RAM.
    IRAM and FLASH are not safe! */
 for (q = p; q != NULL; q = q->next) {
  notdmasafe += lpc_packet_addr_notsafe(q->payload);
 }
#if LPC_TX_PBUF_BOUNCE_EN == 1
 /* If the pbuf is not DMA safe, a new bounce buffer (pbuf) will be
    created that will be used instead. This requires an copy from the
    non-safe DMA region to the new pbuf */
 if (notdmasafe) {
  /* Allocate a pbuf in DMA memory */
  np = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
  if (np == NULL) {
   LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
      ("lpc_low_level_output: could not allocate TX pbuf\n"));
   return ERR_MEM;
  }
  /* This buffer better be contiguous! */
  LWIP_ASSERT("lpc_low_level_output: New transmit pbuf is chained",
     (pbuf_clen(np) == 1));
  /* Copy to DMA safe pbuf */
  dst = (u8_t *) np->payload;
  for (q = p; q != NULL; q = q->next) {
   /* Copy the buffer to the descriptor's buffer */
   MEMCPY(dst, (u8_t *) q->payload, q->len);
   dst += q->len;
  }
  np->len = p->tot_len;
  LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
     ("lpc_low_level_output: Switched to DMA safe buffer, old=%p, new=%p\n",
      q, np));
  /* use the new buffer for descrptor queueing. The original pbuf will
     be de-allocated outsuide this driver. */
  p = np;
  dn = 1;
 }
#else
 if (notdmasafe) {
  LWIP_ASSERT("lpc_low_level_output: Not a DMA safe pbuf",
     (notdmasafe == 0));
 }
#endif
 /* Wait until enough descriptors are available for the transfer. */
 /* THIS WILL BLOCK UNTIL THERE ARE ENOUGH DESCRIPTORS AVAILABLE */
 while (dn > lpc_tx_ready(netif)) {
#if NO_SYS == 0
  xSemaphoreTake(lpc_enetif->xtx_count_sem, 0);
#else
  msDelay(1);
#endif
 }
 /* Get free TX buffer index */
 idx = Chip_ENET_GetTXProduceIndex(LPC_ETHERNET);
#if NO_SYS == 0
 /* Get exclusive access */
 sys_mutex_lock(&lpc_enetif->tx_lock_mutex);
#endif
 /* Prevent LWIP from de-allocating this pbuf. The driver will
    free it once it's been transmitted. */
 if (!notdmasafe) {
  pbuf_ref(p);
 }
 /* Setup transfers */
 q = p;
 while (dn > 0) {
  dn--;
  /* Only save pointer to free on last descriptor */
  if (dn == 0) {
   /* Save size of packet and signal it's ready */
   lpc_enetif->ptxd[idx].Control = ENET_TCTRL_SIZE(q->len) | ENET_TCTRL_INT |
           ENET_TCTRL_LAST;
   lpc_enetif->txb[idx] = p;
  }
  else {
   /* Save size of packet, descriptor is not last */
   lpc_enetif->ptxd[idx].Control = ENET_TCTRL_SIZE(q->len) | ENET_TCTRL_INT;
   lpc_enetif->txb[idx] = NULL;
  }
  LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
     ("lpc_low_level_output: pbuf packet(%p) sent, chain#=%lu,"
      " size = %d (index=%lu)\n", q->payload, dn, q->len, idx));
  lpc_enetif->ptxd[idx].Packet = (u32_t) q->payload;
  if(cnt>4)
  cnt = 0;
  else
  cnt++;
  memcpy(data[cnt].aucTCPBuf,q->payload,sizeof(data[cnt].aucTCPBuf));
  q = q->next;
  idx = Chip_ENET_IncTXProduceIndex(LPC_ETHERNET);
 }
 LINK_STATS_INC(link.xmit);
#if NO_SYS == 0
 /* Restore access */
 sys_mutex_unlock(&lpc_enetif->tx_lock_mutex);
#endif
 return ERR_OK;
}
#if NO_SYS == 0
/* Packet reception task for FreeRTOS */
static portTASK_FUNCTION(vPacketReceiveTask, pvParameters)
{
 lpc_enetdata_t *lpc_enetif = pvParameters;
 while (1) {
  /* Wait for receive task to wakeup */
  sys_arch_sem_wait(&lpc_enetif->rx_sem, 0);
  /* Process packets until all empty */
  while (!Chip_ENET_IsRxEmpty(LPC_ETHERNET)) {
   lpc_enetif_input(lpc_enetif->pnetif);
  }
 }
}
/* Transmit cleanup task for FreeRTOS */
static portTASK_FUNCTION(vTransmitCleanupTask, pvParameters)
{
 lpc_enetdata_t *lpc_enetif = pvParameters;
 s32_t idx;
 while (1) {
  /* Wait for transmit cleanup task to wakeup */
  sys_arch_sem_wait(&lpc_enetif->tx_clean_sem, 0);
  /* Error handling for TX underruns. This should never happen unless
     something is holding the bus or the clocks are going too slow. It
     can probably be safely removed. */
  if (Chip_ENET_GetIntStatus(LPC_ETHERNET) & ENET_INT_TXUNDERRUN) {
   LINK_STATS_INC(link.err);
   LINK_STATS_INC(link.drop);
//   status_info = GET_STATUS_DEFINE(3);
#if NO_SYS == 0
   /* Get exclusive access */
   sys_mutex_lock(&lpc_enetif->tx_lock_mutex);
#endif
   /* Reset the TX side */
   Chip_ENET_ResetTXLogic(LPC_ETHERNET);
   Chip_ENET_ClearIntStatus(LPC_ETHERNET, ENET_INT_TXUNDERRUN);
   /* De-allocate all queued TX pbufs */
   for (idx = 0; idx < LPC_NUM_BUFF_TXDESCS; idx++) {
    if (lpc_enetif->txb[idx] != NULL) {
     pbuf_free(lpc_enetif->txb[idx]);
     lpc_enetif->txb[idx] = NULL;
    }
   }
#if NO_SYS == 0
   /* Restore access */
   sys_mutex_unlock(&lpc_enetif->tx_lock_mutex);
#endif
   /* Start TX side again */
   lpc_tx_setup(lpc_enetif);
  }
  else {
   /* Free TX buffers that are done sending */
   lpc_tx_reclaim(lpc_enetdata.pnetif);
  }
 }
}
#endif
/* Low level init of the MAC and PHY */
static err_t low_level_init(struct netif *netif)
{
 lpc_enetdata_t *lpc_enetif = netif->state;
 err_t err = ERR_OK;
 /* Save station address */
 Chip_ENET_SetADDR(LPC_ETHERNET, netif->hwaddr);
 /* Setup transmit and receive descriptors */
 if (lpc_tx_setup(lpc_enetif) != ERR_OK) {
  return ERR_BUF;
 }
 if (lpc_rx_setup(lpc_enetif) != ERR_OK) {
  return ERR_BUF;
 }
 /* Enable packet reception */
#if IP_SOF_BROADCAST_RECV
 Chip_ENET_EnableRXFilter(LPC_ETHERNET, ENET_RXFILTERCTRL_APE | ENET_RXFILTERCTRL_ABE);
#else
 Chip_ENET_EnableRXFilter(ENET_RXFILTERCTRL_APE);
#endif
 /* Clear and enable rx/tx interrupts */
 Chip_ENET_EnableInt(LPC_ETHERNET, RXINTGROUP | TXINTGROUP);
 /* Enable RX and TX */
 Chip_ENET_TXEnable(LPC_ETHERNET);
 Chip_ENET_RXEnable(LPC_ETHERNET);
 return err;
}
/* This function is the ethernet packet send function. It calls
 * etharp_output after checking link status. */
 static err_t lpc_etharp_output(struct netif *netif, struct pbuf *q,
          ip_addr_t *ipaddr)
{
 /* Only send packet is link is up */
 if (netif->flags & NETIF_FLAG_LINK_UP) {
  return etharp_output(netif, q, ipaddr);
 }
 return ERR_CONN;
}
/*****************************************************************************
 * Public functions
 ****************************************************************************/
/* Attempt to allocate and requeue a new pbuf for RX */
s32_t lpc_rx_queue(struct netif *netif)
{
 lpc_enetdata_t *lpc_enetif = netif->state;
 struct pbuf *p;
 s32_t queued = 0;
 /* Attempt to requeue as many packets as possible */
 while (lpc_enetif->rx_free_descs > 0) {
  /* Allocate a pbuf from the pool. We need to allocate at the
     maximum size as we don't know the size of the yet to be
     received packet. */
  p = pbuf_alloc(PBUF_RAW, (u16_t) ENET_ETH_MAX_FLEN, PBUF_RAM);
  if (p == NULL) {
   LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
      ("lpc_rx_queue: could not allocate RX pbuf (free desc=%lu)\n",
       lpc_enetif->rx_free_descs));
   return queued;
  }
  /* pbufs allocated from the RAM pool should be non-chained. */
  LWIP_ASSERT("lpc_rx_queue: pbuf is not contiguous (chained)",
     pbuf_clen(p) <= 1);
  /* Queue packet */
  lpc_rxqueue_pbuf(lpc_enetif, p);
  /* Update queued count */
  queued++;
 }
 return queued;
}
/* Attempt to read a packet from the EMAC interface */
void lpc_enetif_input(struct netif *netif)
{
 struct eth_hdr *ethhdr;
 struct pbuf *p;
 /* move received packet into a new pbuf */
 p = lpc_low_level_input(netif);
 if (p == NULL) {
  return;
 }
 /* points to packet payload, which starts with an Ethernet header */
 ethhdr = p->payload;
 switch (htons(ethhdr->type)) {
 case ETHTYPE_IP:
 case ETHTYPE_ARP:
#if PPPOE_SUPPORT
 case ETHTYPE_PPPOEDISC:
 case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
  /* full packet send to tcpip_thread to process */
  pak_ip_cnt++;
  if (netif->input(p, netif) != ERR_OK) {
   LWIP_DEBUGF(NETIF_DEBUG, ("lpc_enetif_input: IP input error\n"));
   /* Free buffer */
   pbuf_free(p);
  }
  break;
 default:
  /* Return buffer */
  pbuf_free(p);
  break;
 }
}
/* Call for freeing TX buffers that are complete */
void lpc_tx_reclaim(struct netif *netif)
{
 lpc_tx_reclaim_st((lpc_enetdata_t *) netif->state,
       Chip_ENET_GetTXConsumeIndex(LPC_ETHERNET));
}
/* Polls if an available TX descriptor is ready */
s32_t lpc_tx_ready(struct netif *netif)
{
 u32_t pidx, cidx;
 cidx = Chip_ENET_GetTXConsumeIndex(LPC_ETHERNET);
 pidx = Chip_ENET_GetTXProduceIndex(LPC_ETHERNET);
 return Chip_ENET_GetFreeDescNum(LPC_ETHERNET, pidx, cidx, LPC_NUM_BUFF_TXDESCS);
}
/**
 * @brief EMAC interrupt handler
 * @return Nothing
 * @note This function handles the transmit, receive, and error interrupt of
 * the LPC17xx/40xx. This is meant to be used when NO_SYS=0.
 */
void ETH_IRQHandler(void)
{
#if NO_SYS == 1
 /* Interrupts are not used without an RTOS */
 NVIC_DisableIRQ(ETHERNET_IRQn);
#else
 signed portBASE_TYPE xRecTaskWoken = pdFALSE, XTXTaskWoken = pdFALSE;
 uint32_t ints;
 /* Interrupts are of 2 groups - transmit or receive. Based on the
    interrupt, kick off the receive or transmit (cleanup) task */
 /* Get pending interrupts */
 ints = Chip_ENET_GetIntStatus(LPC_ETHERNET);
 if (ints & RXINTGROUP) {
  /* RX group interrupt(s) */
  /* Give semaphore to wakeup RX receive task. Note the FreeRTOS
     method is used instead of the LWIP arch method. */
  int_rx_cnt++;
  xSemaphoreGiveFromISR(lpc_enetdata.rx_sem, &xRecTaskWoken);
 }
 if (ints & TXINTGROUP) {
  /* TX group interrupt(s) */
  /* Give semaphore to wakeup TX cleanup task. Note the FreeRTOS
     method is used instead of the LWIP arch method. */
  int_tx_cnt++;
  xSemaphoreGiveFromISR(lpc_enetdata.tx_clean_sem, &XTXTaskWoken);
 }
 /* Clear pending interrupts */
 Chip_ENET_ClearIntStatus(LPC_ETHERNET, ints);
 /* Context switch needed? */
 portEND_SWITCHING_ISR(xRecTaskWoken || XTXTaskWoken);
#endif
}
/* Set up the MAC interface duplex */
void lpc_emac_set_duplex(int full_duplex)
{
 if (full_duplex) {
  Chip_ENET_SetFullDuplex(LPC_ETHERNET);
 }
 else {
  Chip_ENET_SetHalfDuplex(LPC_ETHERNET);
 }
}
/* Set up the MAC interface speed */
void lpc_emac_set_speed(int mbs_100)
{
 if (mbs_100) {
  Chip_ENET_Set100Mbps(LPC_ETHERNET);
 }
 else {
  Chip_ENET_Set10Mbps(LPC_ETHERNET);
 }
}
#define EMBEDDED_SWITCH
/* LWIP 17xx/40xx EMAC initialization function */
err_t lpc_enetif_init(struct netif *netif)
{
 err_t err;
 LWIP_ASSERT("netif != NULL", (netif != NULL));
 lpc_enetdata.pnetif = netif;
 /* set MAC hardware address */
 extern EEPROM_DATA_STATIC  eepromDataStat;
 memcpy(netif->hwaddr, eepromDataStat.mac_address, 6);
 netif->hwaddr_len = ETHARP_HWADDR_LEN;
 /* maximum transfer unit */
 netif->mtu = 1500;
 /* device capabilities */
 netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_UP |
       NETIF_FLAG_ETHERNET;
#ifdef EMBEDDED_SWITCH
 netif->flags |= NETIF_FLAG_LINK_UP;
#endif
 /* Initialize the hardware */
 netif->state = &lpc_enetdata;
 err = low_level_init(netif);
 if (err != ERR_OK) {
  return err;
 }
#if LWIP_NETIF_HOSTNAME
 /* Initialize interface hostname */
 netif->hostname = "lwiplpc";
#endif /* LWIP_NETIF_HOSTNAME */
 netif->name[0] = 'e';
 netif->name[1] = 'n';
 netif->output = lpc_etharp_output;
 netif->linkoutput = lpc_low_level_output;
 /* For FreeRTOS, start tasks */
#if NO_SYS == 0
 lpc_enetdata.xtx_count_sem = xSemaphoreCreateCounting(LPC_NUM_BUFF_TXDESCS,
                LPC_NUM_BUFF_TXDESCS);
 LWIP_ASSERT("xtx_count_sem creation error",
    (lpc_enetdata.xtx_count_sem != NULL));
 err = sys_mutex_new(&lpc_enetdata.tx_lock_mutex);
 LWIP_ASSERT("tx_lock_mutex creation error", (err == ERR_OK));
 err = sys_mutex_new(&lpc_enetdata.rx_lock_mutex);
 LWIP_ASSERT("rx_lock_mutex creation error", (err == ERR_OK));
 /* Packet receive task */
 LWIP_DEBUGF(EMAC_DEBUG, ("launching receive task\n"));
 err = sys_sem_new(&lpc_enetdata.rx_sem, 0);
 LWIP_ASSERT("rx_sem creation error", (err == ERR_OK));
 sys_thread_new("receive_thread", vPacketReceiveTask, netif->state,
       DEFAULT_THREAD_STACKSIZE, tskRECPKT_PRIORITY);
 LWIP_DEBUGF(EMAC_DEBUG, ("launched receive task\n"));
 /* Transmit cleanup task */
 LWIP_DEBUGF(EMAC_DEBUG, ("launching transmit task\n"));
 err = sys_sem_new(&lpc_enetdata.tx_clean_sem, 0);
 LWIP_ASSERT("tx_clean_sem creation error", (err == ERR_OK));
 sys_thread_new("txclean_thread", vTransmitCleanupTask, netif->state,
       DEFAULT_THREAD_STACKSIZE, tskTXCLEAN_PRIORITY);
 LWIP_DEBUGF(EMAC_DEBUG, ("launched transmit task\n"));
#endif
 return ERR_OK;
}
/**
 * @}
 */
Is this some known issue on the lwip lpc port?
Please help me as i am struggling with this issue for atleast a month now
Labels (1)
0 Kudos
5 Replies

2,173 Views
ankish_kacktwan
Contributor I

While debugging the lost packet i discovered that on some random packets after transferring the ownership to the TX hardware discriptors the sending on wire is interrupted resulting in incomplete packet in wireshark.

and thus the packet is dropped by the Physical which is an ksz8863 chip.

Does someone facing the same issue with the lpc1778 device in RMII mode.

Can someone help me on this please as i am struggling with this for quite long.

Total size of the packet is 303 bytes but only 62 bytes are sent by the MAC on the wire.

0 Kudos

2,173 Views
soledad
NXP Employee
NXP Employee

Hi,

LWIP and the EMAC driver will handle fragment packets up to a total size of about 10KBytes without any changes. If you need to transfer larger fragmented packet chains beyond that size, try changing the following LWIP configuration options.


#define IP_REASS_MAXAGE 3    /* 3 seconds should be ok for most everything unless the transfer rate is very low.*/

#define IP_REASS_MAX_PBUFS              5 /* About 40KBytes of buffers requires about 40K/1500 pbufs */

After investigating this we found what seems to be a bug on the driver in LPCOpen that causes a failure of the memory assignment from heap when MCU receiving. We are working on fixing the driver but have no estimated date for this.


I hope this helps, have a nice day!

Regards

Soledad

0 Kudos

2,173 Views
ankish_kacktwan
Contributor I

I dont think the issue i am facing is related to the receiving path as i can see the packet has been delivered to the mac from the stack.

Also the settings for the mentioned define are correct as frames from PLC are no longer than 100 bytes or so

#define IP_REASS_MAXAGE 3    /* 3 seconds should be ok for most everything unless the transfer rate is very low.*/

#define IP_REASS_MAX_PBUFS              5 /* About 40KBytes of buffers requires about 40K/1500 pbufs */ 

I'll try to summarize:
1) PLC sends request
2) lwip receives request 
3) payload is passed to our application
4) application triggers transmission of response
5) response is sent
6) microcontroller generates a Tx interrupt, indicating everything is fine. 
7) Wireshark on PC does not show the answer frame. 

when i am trying to log the payload handed over from the lwip stack to driver it shows the write response with right sequence number and aknowledgment number but never passes out from the MAC and thus to the wire.

0 Kudos

2,173 Views
soledad
NXP Employee
NXP Employee

Hi,

Could you please try the following:

In the tcp_impl.h file change the definition of TCP_MSL to the following:

 

#define TCP_MSL 100 //1000 //60000UL /* The maximum segment lifetime in milliseconds */

Please let me know if this helps,

Have a nice day!

Regards

Soledad

0 Kudos

2,173 Views
ankish_kacktwan
Contributor I

Its already been defined to 

#define TCP_MSL 100UL /* The maximum segment lifetime in milliseconds */

Also in some random packets shows wrong checksum as some of the data in the payload been changed by the mac when sending the packet on the wire.

I am logging the  packets send to the driver from the tcpip stack and it shows the data in the buffer is not corrupted but the data in the wireshark is corrupt on some random packets.

while (dn > 0) {
        dn--;

        /* Only save pointer to free on last descriptor */
        if (dn == 0) {
            /* Save size of packet and signal it's ready */
            lpc_enetif->ptxd[idx].Control = ENET_TCTRL_SIZE(q->len) | ENET_TCTRL_INT |
                                            ENET_TCTRL_LAST;
            lpc_enetif->txb[idx] = p;
        }
        else {
            /* Save size of packet, descriptor is not last */
            lpc_enetif->ptxd[idx].Control = ENET_TCTRL_SIZE(q->len) | ENET_TCTRL_INT;
            lpc_enetif->txb[idx] = NULL;
        }

        LWIP_DEBUGF(EMAC_DEBUG | LWIP_DBG_TRACE,
                    ("lpc_low_level_output: pbuf packet(%p) sent, chain#=%lu,"
                     " size = %d (index=%lu)\n", q->payload, dn, q->len, idx));

        lpc_enetif->ptxd[idx].Packet = (u32_t) q->payload;
        if(cnt>4)
            cnt=0;
        else
            cnt++;
        memcpy(&buf_data[cnt].aucTCPBuf,q->payload,256+7); logging the payload to check data corruption.
        q = q->next;

        idx = Chip_ENET_IncTXProduceIndex(LPC_ETHERNET);
    }
    LINK_STATS_INC(link.xmit);

#if NO_SYS == 0
    /* Restore access */
    sys_mutex_unlock(&lpc_enetif->tx_lock_mutex);
#endif

    return ERR_OK;
}
0 Kudos