S0_RX_WR not returning to RX_RD after overflow


#1

Dear support,

I am using a W5200 with a PIC32MX360F512L to implement an UDP communication between my embedded application and a PC. Everything is going perfectly fine until the RX memory overflow (2048 in my configuration)

Indeed, after the overflow, the RX_WR pointer does not return to the RX_RD value, which I compute perfectly well.
For exemple, If the W5200 have already received 2000 bytes that I have read and acknowledge, I have both RX_WR et RX_RD equal to this value; pretty normal. But if I send 50 more bytes, i will of course detect the overflow and compute RX_RD = 2050%2048 = 2. I write it in the register (I am sure it is correctly written as I have checked by reading back the RX_RD register at the correct address 0x4028). Nevertheless, The RX_WR goes to 2050. Therefore, the next received data are written in socket 1 memory and the RX_RSR register indicates 2048 received bytes (RX_WR-RX_RD = 2050-2 = 2048!).

I have of course tried to write “manually” the RX_WR register when the overflow is detected but his register seems to be read-only, even if it is indicated as [R/W] in the datasheet.

Therefore, my question is: how do I get the RX_WR pointer the correct value at the RX memory overflow?

Here you will find my receiving alogrithm.

  • Read S0_RX_RD
  • Compute address offset and physical address
  • Read the header (8 bytes) and compute the data length (overflow correctly detected and processed)
  • Read the data (overflow also correctly detected and processed)
  • Compute new value of S0_RX_RD = (S0_RX_RD + 8 + DataLength) & 2047
  • Write it in the W5200 register 0x4028 and 0x4029
  • Proceed with the RCV command
  • Clear the Sn_IR register by writting 1 in the according bits

I have already checked all my registers address and the my Write/REad functions. They work perfectly.
Moreover, I have already read all that was to read in the documentation and the forum.

So please, tell me what I am doing wrong ?

I have been on this problem for almost a entire week and the project allowed time is near its end. Therefore, a very quick answer would be greatly appreciated :slight_smile:

Thanks in advance!


#2

Hi, crod.

You should mask Sn_RX_WR also.

It is physical address. 2050 is logical address.

If you don’t understand, see reference code in WIZnet’s web.

Best regards.


#3

Hi crod,

You seem to be misunderstanding the mechanisam of buffer handling of W5100.
Sn_RX_RD, Sn_RX_WR, Sn_TX_RD and Sn_TX_WR registers are used as another purpose internally.
So, every pointer registers have the range to 4G byte map of 32bits length address internally.
But W5100 has 2K bytes physical Tx and Rx buffer per socket respectively.
in other words, we need memory mapping from logical address to physical address.
But this doesn’t mean that you should remap all these registers to 2K address range by force.
You have not to change the value of all pointer register to value within address range of 2K size.

Instead of this, in order to get data properly you have to follow below procedure.

First, you should not remap the value of Sn_RX_RD itself to real address.
For example, when Sn_RX_RD should be changed to 2050 after receiving 50 bytes, don’t update its value to 2(= 2050 - 2048).
Instead, update your function handling data receipt like below.

[code]void recv_data_processing(SOCKET s, uint8 *data, uint16 len)
{
uint16 ptr;
uint16 size;
uint16 src_mask;
uint8 * src_ptr;

ptr = IINCHIP_READ(Sn_RX_RD0(s));
ptr = ((ptr & 0x00ff) << 8) + IINCHIP_READ(Sn_RX_RD0(s) + 1);

#ifdef DEF_IINCHIP_DBG
printf(" ISR_RX: rd_ptr : %.4x\r\n", ptr);
#endif

src_mask = (uint32)ptr & getIINCHIP_RxMASK(s);
src_ptr = (uint8 *)(getIINCHIP_RxBASE(s) + src_mask);

if( (src_mask + len) > getIINCHIP_RxMAX(s) )
{
size = getIINCHIP_RxMAX(s) - src_mask;
IINCHIP_READ_BLOCK((uint32)src_ptr, (uint8*)data,size);
data += size;
size = len - size;
src_ptr = (uint8 )(getIINCHIP_RxBASE(s));
IINCHIP_READ_BLOCK((uint32)src_ptr, (uint8
) data,size);
}
else
{
IINCHIP_READ_BLOCK((uint32)src_ptr, (uint8*) data,len);
}

ptr += len;
IINCHIP_WRITE(Sn_RX_RD0(s),(uint8)((ptr & 0xff00) >> 8));
IINCHIP_WRITE((Sn_RX_RD0(s) + 1),(uint8)(ptr & 0x00ff));
}
[/code]

This is a function in the driver of W5200.
If you see this function, you will be able to find what you did wrong and should change in your code.

1)First, it reads Sn_RX_RD
2)Then, it reads RxMASK. This can be changed according to users’ memory allocation
3)Then, it reads its Physical address’ base address
4)And it calculates its physical address to read data from its peer
For example, I suppose that Socket 0, 2K bytes RX buffer, current S0_RX_RD is 0x07CF(= 2000 in decimal) and Socket 0 received 50 bytes.
Because RxMASK is 0x7FFF, src_mask above code is 0x07CF(0x07CF & 0x7FFF).
And src_ptr is 0xC7CF(= RxBASE(0xC000) + src_mask(0x07CF))
At this case, src_mask + len exceeds RxMAX size.
So, we have to read data in two step.
5) Read data from RX buffer
6) Finally, it updates S0_RX_RD pointer register with its original value added by the length of data.

ptr += len; IINCHIP_WRITE(Sn_RX_RD0(s),(uint8)((ptr & 0xff00) >> 8)); IINCHIP_WRITE((Sn_RX_RD0(s) + 1),(uint8)(ptr & 0x00ff));

In case of our example, S0_RX_RD will be 0x0802, not 0x0002.(S0_RX_WR is also 0x0802)
If Socket 0 receives another 10 bytes, then S0_RX_WR will be changed to 0x080C.

Please refer to the driver of W5200 on WIZnet’s website.

Thanks.

javakys


#4

Thanks, that works!!! :smiley:

Thank you so much for the help, I really appreciate it! :wink:

Best regards,

Christian


#5

You’re welcome.
It’s my pleasure. :smiley: