W5500 in MACRAW mode doesn't always update S0_TX_WR properly

I’m using the W5500 with lwIP in MACRAW mode and I’m running into a problem with transmitted packets getting corrupted.

It seems that what’s happening is that the wiz_send_data() function is writing data to the socket 0 TX buffer and updating S0_TX_WR with the new buffer position, but when it goes to add the next pbuf to the buffer, it reads back S0_TX_WR and it has the original buffer position still, so the additional data overwrites the first portion and clobbers the header.

I’ve got a logic analyzer capture showing this. The sequence is:
TX: 00 24 0C 4B A5
RX: 01 02 03 00 00
(20 microseconds later)
TX: 00 24 08 00 00
RX: 01 02 03 4B 24

So this is a variable data mode write to socket 0 register 0x24, followed by a variable data mode read starting at 0x24, but the value read back doesn’t match.

What am I missing here? Why isn’t the value getting updated?

Thanks,

Scott

0C is not VDM write to be precise. It is 2 bytes burst read.
Did you issue SEND command to initiate sending? Did you wait until CR gets reset (command was accepted)? I am afraid until you issue SEND command pointer is not internally updated, because chip waits for more data to come into buffer starting the original location.

Hi Eugeny,

According to section 2.3 in the datasheet, OM[1:0] = 00 for VDM mode. 0C is BSB = 1, RWB = 1, OM = 0, meaning it’s a variable-length write to the Socket 0 register, no?

Per the procedure on page 56, the sequence for sending data goes like this:

  1. Read S0_TX_WR to get TX buffer starting address
  2. Write packet data starting at that address
  3. Update S0_TX_WR, incrementing by number of bytes written
  4. Execute SEND command (which updates S0_TX_RD to equal S0_TX_WR)

Now, the data coming from lwIP comes as a linked list of pbuf buffer objects. I didn’t write this part of the driver - it’s the one by Peter Borisenko. It repeats calls to wiz_send_data(), looping through steps 1-3 to copy the entire outbound packet into the buffer before executing SEND.

The problem is that sometimes updates to S0_TX_WR don’t “stick”. Is Borisenko’s approach wrong? Are you unable to write a single packet in multiple fragments?

Thanks,

Scott

Right, 0c is 1100, I confused it with 0a. Shame.

Can not comment, did not see the code. There must be step 5 waiting for CR to clear to 0 after you perform send. Look at the W5100 datasheet chapters 5.2.4/5.2.2 (p. 55).

Unable as you perform it. TX_WR is not the register to hold intermediate data. You must cache original TX_WR until you write all the data, and then update TX_WR with final value and perform SEND.

There must be step 5 waiting for CR to clear to 0 after you perform send. Look at the W5100 datasheet chapters 5.2.4/5.2.2 (p. 55).

It’s the W5500 datasheet I’m working from. I don’t see any mention of a CR register or bit.

You must cache original TX_WR until you write all the data, and then update TX_WR with final value and perform SEND.

You mean cache everything in a buffer on the host before doing a single write? If that’s the case, then the driver I’ve been using is just totally wrong - see around line 117 here: https://github.com/PeterBorisenko/w5500-lwip-freertos/blob/master/device/wizchip/wizchip_port.c

As I look at this closer, I’m also seeing that the transmit is happening while the W5500 interrupt is still asserted. I don’t know if that makes any difference. The ISR sets a semaphore and the SPI RX task wakes up, receives packets, sends them to the stack, which in this case is passing them on to my WebSockets server, and then the WebSockets server (running from the lwIP task context) sends its response in two calls, one for the constant header and one with dynamic data.

It’s that second call that’s overwriting the socket 0 TX buffer. The portion with the Ethernet and IP headers gets overwritten and it only sends the contents of the dynamic WebSocket data on the wire as a raw Ethernet frame.

It’s only after the send is complete that control passes back to the SPI RX task, which clears the W5500’s interrupt register and the interrupt ends.

Do you know if there’s some prohibition on sending while the interrupt is active? I’m not sure why my HTTP server seems to work normally when it operates in nearly the same way, except I think HTTP responses get handed off to another task.

Is there some known-good reference implementation for a W5500 lwIP netif driver? I’ve already submitted one significant bugfix for this one. Maybe I should be starting elsewhere.

Thanks,

Scott

Include W5100 into the list.

Page 46?

No. TX_WR for read and for write are different physical registers internally. The value you write to TX_WR address will only be readable after SEND is complete (W5500 will change the register value for reading), before this you read old value. So you must read TX_WR and use this read value until you issue SEND. you can not store some value in TX_WR location and hope to read stored value back for continuation.

No. Interrupt is a signal for the driver, and it is a responsibility to driver to clear it in time and use it appropriately. The common sense at the entrance to the ISR is to disable interrupts, clear interrupt, work out through the buffers, and exit ISR enabling interrupt - if there will be more data coming ISR will be called again.

The workflow is explained in the W5100 datasheet and holds true for w5500. Deviation from it is not recommended.

Ah, CR is the socket command register. The W5500 datasheet doesn’t say to wait for it to clear - it says it’s automatically cleared and you have to check Sn_IR or Sn_SR for completion of the command.

What’s the relationship between the W5100 and W5500? How do I know which parts of the W5100 datasheet apply?

No. TX_WR for read and for write are different physical registers internally. The value you write to TX_WR address will only be readable after SEND is complete

OK, that makes sense. So the implementation I’m using is wrong. Is that documented somewhere so I can point it out to the author when I submit a fix?

Moving on, assuming I keep track of my actual write location, can I still write my outgoing packet using more than one VDM transaction? Or does my pbuf output loop need to keep CS asserted so it can send all of the pieces in a single transaction? Either way it looks like I’ll have to modify w5500.c, unless I want to allocate an MTU’s worth of RAM for a buffer in the pbuf output loop and copy all of the fragments there.

Thanks,

Scott

They have similar internals and datasheet does not have things others have. So it is better to cross-check them.

Explicitly no, but it is being assumed by the workflow. Workflow does not mention reading registers (RX or TX) after they were read first time and before SEND/RECV are issued.

Of course. You just keep correct address in the requests.

Of course. You just keep correct address in the requests.

Good to hear. I was planning to do that as an optimization anyway. I already made some others - notably, it was checking the free space in the TX buffer before every write. I modified it to keep a local copy and only poll the module for the actual free space if the local copy says there might not be room. Nothing else will cause the TX buffer free space to decrease aside from writing packet data to it, right?

It is again compromise to the workflow. Free size is calculated internally basing on RD and WR pointers, and you must read it once like RD and WR. Sorry did not mention it before, and glad you said that. Again - look into the datasheet for the workflow, you must stick to it exactly.

Nothing else is causing the buffer space to decrease though, right? I’m running in MACRAW mode with all 16 kB allocated to socket 0 so there’s no extra added ACK pointer.

TX pointers? No. Only you and SEND command control them.

1 Like

OK, perfect!

My driver seems to be working well now. Thank you so much for your help! I thought it was going to be a real long shot to get an answer on this. Now if I could just find someone to answer all of my lwIP questions! It’s like they gave up trying to document it 10 years ago and it’s driving me nuts.

Thanks again,

Scott

1 Like