W3150A+ UDP receive issue

Hi Wiznet!

I ran into a weird thing on UDP reception. I wrote my own simple driver with the instructions of the datasheet, in which I use the value of RX_RSR register to determine the received data size. I also resolve the data size from the UDP header, but use the RX_RSR value minus the UDP header size (8) to compute the size to read. It works in most cases, but randomly fails. On fail, the UDP header seems to contain the expected size, but the RX_RSR value is much smaller – in a subsequent reading the remaining data can be read without UDP header…

E.g.: TFTP server implementation, receiving frames with 516 bytes of data (RX_RSR=524). Works fine, but after a while the RX_RSR value in a case will be only 12, but the UDP header contains 516. I read this 12 bytes (fearing of over-reading the buffer), and when I check RX_RSR next time, it is 512.

I have figured out that the example code in the datasheet uses the size in UDP header for reading, but it is safe? How can be RX_RSR smaller than this data size part in UDP header?

Thank you in advance.

Best regards,

Gábor

Hi, btprg.

First, test that your interface is collect. Reading version register many times or writing & reading some registers which are available to read&write

I recommend that you read RX_RSR twice. If each value is same, try to read receive buffer data.
For example,

uint16_t getSn_RX_RSR(uint8_t sn)
{
   uint16_t val=0,val1=0;
   do
   {
      val1 = WIZCHIP_READ(Sn_RX_RSR(sn));
      val1 = (val1 << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_RSR(sn),1));
      if (val1 != 0)
      {
        val = WIZCHIP_READ(Sn_RX_RSR(sn));
        val = (val << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_RSR(sn),1));
      }
   }while (val != val1);
   return val;
}

If problem is not resolved, please contact us again.
And if you can open your code, please share it.

Thank you.

Hi dkay,

The random register read/write operations work normally.

My original code was this:

/**************************************************************************//**
 * @brief
 *   Get received data from a UDP socket.
 * @param[in] socket
 *   Number of socket (0..3).
 * @param[out] recvdata
 *   Address of the buffer which receives the data.
 * @param[in] recvmax
 *   Maximum number of bytes to read (practically the destination buffer
 *   size). If the amount of data is more than this parameter, the first
 *   recvmax bytes will be copied into the buffer, the others will be
 *   discarded. Set to zero to disable this feature.
 * @param[out] info
 *   Points to a receive info structure which will be filled up with
 *   information about the action.
 * @return
 *   TRUE on success, otherwise false.
 *****************************************************************************/
bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
  uint16_t sbaddr = getSocketRegBaseAddress(socket);
  uint16_t recvlen, rx_rd, start, data_ahead;
  uint8_t data[UDP_HEADER_SIZE];

  /* check for errors */
  if (rxMemory[socket].size == 0) return false;   // socket is unusable
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, data, 2);   // read received data size
  recvlen = (data[0] << 8) + data[1];   // (big-endian)
  if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false;   // no data or error

  /* initialize */
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // read read pointer
  rx_rd = (data[0] << 8) + data[1];   // (big-endian)
  start = rxMemory[socket].base + rx_rd % rxMemory[socket].size;   // compute starting address

  /* read header (circular read from the Rx buffer) */
  /*   the 8-byte header structure:
   *     offset 0..3:  source IP address
   *     offset 4..5:  source port number
   *     offset 6..7:  data size (in bytes)
   */
  data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < UDP_HEADER_SIZE) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead);   // read the first part of the header from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead);   // read the remaining part of header restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE);   // read all
  }
  start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // update starting address

  /* process header */
  memcpy(info->srcip, data, 4);   // source IP address
  info->srcport = (data[4] << 8) + data[5];   // source port number (big-endian)
  data_ahead = (data[6] << 8) + data[7];   // data length (big-endian), data_ahead re-used
  info->rxlen = data_ahead;
  info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);

  /* read data (circular read from the Rx buffer) */
  if (recvmax != 0)   // determine the actual number of data to read
    info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
    else
    info->rxlen_act = recvlen - UDP_HEADER_SIZE;
  data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < info->rxlen_act) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead);   // read the first part of data from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead);   // read the remaining part of data restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act);   // read all
  }

  /* finalize and indicate that Rx is processed */
  rx_rd += recvlen;
  data[0] = rx_rd >> 8;
  data[1] = rx_rd & 0xFF;
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // re-write read pointer
  data[0] = SCR_RECV;    // receive processed indicator command
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);

  return true;
} /* W3150AP_udpRecv */

Before reading your message, I implemented a version that uses the UDP header size parameter (as the datasheet suggests) if the RX_RSR value is smaller than it. This works fine.

bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
  uint16_t sbaddr = getSocketRegBaseAddress(socket);
  uint16_t recvlen, rx_rd, start, data_ahead;
  uint8_t data[UDP_HEADER_SIZE];

  /* check for errors */
  if (rxMemory[socket].size == 0) return false;   // socket is unusable
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, data, 2);   // read received data size
  recvlen = (data[0] << 8) + data[1];   // (big-endian)
  if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false;   // no data or error

  /* initialize */
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // read read pointer
  rx_rd = (data[0] << 8) + data[1];   // (big-endian)
  start = rxMemory[socket].base + rx_rd % rxMemory[socket].size;   // compute starting address

  /* read header (circular read from the Rx buffer) */
  /*   the 8-byte header structure:
   *     offset 0..3:  source IP address
   *     offset 4..5:  source port number
   *     offset 6..7:  data size (in bytes)
   */
  data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < UDP_HEADER_SIZE) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead);   // read the first part of the header from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead);   // read the remaining part of header restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE);   // read all
  }
  start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // update starting address

  /* process header */
  memcpy(info->srcip, data, 4);   // source IP address
  info->srcport = (data[4] << 8) + data[5];   // source port number (big-endian)
  data_ahead = (data[6] << 8) + data[7];   // data length (big-endian), data_ahead re-used
  info->rxlen = data_ahead;
  info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);

  /* read data (circular read from the Rx buffer) */
  if (info->herror < 0) {    // XXX workaround for a possible bug: sometimes RX_RSR is smaller than the size encoded in the UDP header
    if (recvmax != 0)   // determine the actual number of data to read
      info->rxlen_act = (info->rxlen > recvmax) ? recvmax : info->rxlen;
      else
      info->rxlen_act = info->rxlen;
  } else {   // normal operation
    if (recvmax != 0)   // determine the actual number of data to read
      info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
      else
      info->rxlen_act = recvlen - UDP_HEADER_SIZE;
  }
  data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < info->rxlen_act) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead);   // read the first part of data from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead);   // read the remaining part of data restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act);   // read all
  }

  /* finalize and indicate that Rx is processed */
  if (info->herror < 0)   // XXX workaround for a possible bug: sometimes RX_RSR is smaller than the size encoded in the UDP header
    rx_rd += info->rxlen_act + UDP_HEADER_SIZE;
    else   // normal operation
    rx_rd += recvlen;
  data[0] = rx_rd >> 8;
  data[1] = rx_rd & 0xFF;
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // re-write read pointer
  data[0] = SCR_RECV;    // receive processed indicator command
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);

  return true;
} /* W3150AP_udpRecv */

Then I implemented the multiple RX_RSR reading suggested by you, and it also works well.

bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
  uint16_t sbaddr = getSocketRegBaseAddress(socket);
  uint16_t recvlen, rx_rd, start, data_ahead;
  uint8_t data[UDP_HEADER_SIZE];

  /* check for errors */
  if (rxMemory[socket].size == 0) return false;   // socket is unusable
  {
    uint16_t x1, x2;
    SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, (uint8_t*) &x1, 2);
    do {
      x2 = x1;
      SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, (uint8_t*) &x1, 2);
    } while (x2 != x1);
    recvlen = (((uint8_t*) &x1)[0] << 8) + ((uint8_t*) &x1)[1];
  }
  if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false;   // no data or error

  /* initialize */
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // read read pointer
  rx_rd = (data[0] << 8) + data[1];   // (big-endian)
  start = rxMemory[socket].base + rx_rd % rxMemory[socket].size;   // compute starting address

  /* read header (circular read from the Rx buffer) */
  /*   the 8-byte header structure:
   *     offset 0..3:  source IP address
   *     offset 4..5:  source port number
   *     offset 6..7:  data size (in bytes)
   */
  data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < UDP_HEADER_SIZE) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead);   // read the first part of the header from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead);   // read the remaining part of header restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE);   // read all
  }
  start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // update starting address

  /* process header */
  memcpy(info->srcip, data, 4);   // source IP address
  info->srcport = (data[4] << 8) + data[5];   // source port number (big-endian)
  data_ahead = (data[6] << 8) + data[7];   // data length (big-endian), data_ahead re-used
  info->rxlen = data_ahead;
  info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);

  /* read data (circular read from the Rx buffer) */
  if (recvmax != 0)   // determine the actual number of data to read
    info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
    else
    info->rxlen_act = recvlen - UDP_HEADER_SIZE;
  data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < info->rxlen_act) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead);   // read the first part of data from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead);   // read the remaining part of data restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act);   // read all
  }

  /* finalize and indicate that Rx is processed */
  rx_rd += recvlen;
  data[0] = rx_rd >> 8;
  data[1] = rx_rd & 0xFF;
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // re-write read pointer
  data[0] = SCR_RECV;    // receive processed indicator command
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);

  return true;
} /* W3150AP_udpRecv */

So my problem is solved, thank you :slightly_smiling_face: But I am wondering what is the reason of this thing that RX_RSR sometimes contains too small number…

If you are interested, I could send the whole driver. It only supports UDP, but it is much less complicated than the example driver provided by WIZnet.

Best wishes.