W5100 problem with TX Free Size Register

Hello!

Can anybody explain me the behaviour of w5100 chip?
Whats realy mean the register Sn_TX_FSR (Socket n TX Free Size Register) [R] [0x0420-0x0421.
I read it as 0 every time, should I calulate it as TX Memory Size - Free register?
But my quastion actualy about transmission error after 2-3 thousand of transactions, suddenly the value of register S0_TX_FSR change to 0x2000.
And in the Wireshark software I see the message “TCP Zero Window”. And communication is stopped.

thanks in advance
Arty

Sn_TX_FSR is internally calculated and is the result of the following operation: Bufsize - (Sn_TX_WR - Sn_TX_RD).
No, you do not need to calculate anything, it is already calculated.

Thus if you read 0 then:

  1. you read wrong register/location;
  2. the result of the formula above is 0. It may happen if:
    (a) buffer size is 0. I am not sure it can happen because never checked/used sockets with are not allocated with memory (e.g. you allocate 4K for socket 0, 4K for socket 1 and - not sure what happens with memory pointers for sockets 2 and 3 which do not have any memory left to operate). But you can see if it can be the cause - what value you have in TMSR register?
    (b) you filled the complete buffer with data, and did not instruct W5100 to send it, or W5100 is having problems sending it. For it you need to check status registers.

It would be very useful to know the setup you are running, and what you are trying to do before you get this behavior.

Hello Eugeny!
Thank you for reply.

Yes, about Sn_TX_FSR it was my mistake. Now it’s ok, I see how it changing.
But the problem with “TCP Zero Window” when I send data from MCU to PC still present. First of all i use default value 2k for TMSR and RMSR.
Now I see that it’s enough for my purpose. But I’ve allocated all memory for the socket 0 and it doesn’t help. I see at terminal that Sn_TX_FSR=0x2000, the pointer at tail and head with right value: before loading to the buffer S0_TX_RD=544D, Sn_TX_FSR=2000 S0_TX_WR=144D; after loading: length=13 TX_WR=1460. And after this tramsmisson communication is stopped. But before was sent more than thousand frames.

Artyom, 144D + 13 = 1460. This is correct. I assume you update WR properly (FSR becomes 1FED), and then issue 0x20 or 0x21 command to the W5100 (depending on the mode). What you do after it? You read FSR and it keeps being 1FED?

Note that all pointers and counters are 16-bit. S0_TX_RD=544D — S0_TX_WR=144D sounds strange because it has C000 difference (144D-544D, with buffer size being 2000). If you need to update WR for example with 1800 bytes to send, you add 144D+1800=2C4D and write this 2C4D to the WR (and NOT truncated 0C4D).

If nothing will work from above I am afraid you will need to perform dump of whole register space after it gets stuck to see what is wrong. You say that it works well for some thousands of frames, and then it stops - how it stops, W5100 hangs? it gives timeout error in socket interrupt register? Something gets changed, and you need to check whole config - main and socket - to be sure your application accidentally does not change some vital value within W5100 (e.g. gateway IP address or SHAR).

Hello!

I’ve solved my problem. The main issue was when the pointer S0_RX_RD took the adress back to the begining, after that the register S0_RX_RSR had the value 0x2000. I understand that it is prohibited anything to do with this pointer except add value of readed data.
My algorithm before:

	S0_RX_OFFSET = make16(GetW5100register(S0_RX_RD0), GetW5100register(S0_RX_RD1));
	S0_RX_OFFSET = (S0_RX_OFFSET & S0_RX_MASK);
	S0_RX_Start_Addr = S0_RX_OFFSET + S0_RX_BASE;
	for (n=0; n < length ; n++)   
	{
		if(S0_RX_Start_Addr > (S0_RX_BASE + S0_RX_MASK))
			S0_RX_Start_Addr = S0_RX_BASE;
		*(data + n) = GetW5100register(S0_RX_Start_Addr);
		S0_RX_Start_Addr++;
	}
	S0_RX_OFFSET += length;
	SetW5100register(S0_RX_RD0, make8(S0_RX_OFFSET,1));
	SetW5100register(S0_RX_RD1, make8(S0_RX_OFFSET,0));
	SetW5100register(S0_CR, RECV);

and it only work until S0_RX_RD doesn’t get to the end of buffer (8192/~(15-20) = ~(1000-1500) frames). Now I’m keeping the value of S0_RX_RD and just add the value of received data. It’s works well:

	S0_RX_OFFSET = make16(GetW5100register(S0_RX_RD0), GetW5100register(S0_RX_RD1));
	temp_adr = (S0_RX_OFFSET & S0_RX_MASK);
	S0_RX_Start_Addr = temp_adr + S0_RX_BASE;
	for (n=0; n < length ; n++)   
	{
		if(S0_RX_Start_Addr > (S0_RX_BASE + S0_RX_MASK))
			S0_RX_Start_Addr = S0_RX_BASE;
		*(data + n) = GetW5100register(S0_RX_Start_Addr);
		S0_RX_Start_Addr++;
	}
	S0_RX_OFFSET += length;
	SetW5100register(S0_RX_RD0, make8(S0_RX_OFFSET,1));
	SetW5100register(S0_RX_RD1, make8(S0_RX_OFFSET,0));
	SetW5100register(S0_CR, RECV);