newAM / w5500-rs

Embedded rust support for the Wiznet W5500 SPI internet offload chip
MIT License
36 stars 14 forks source link

UDP Binding stuck in infinite loop #280

Open adsnaider opened 1 year ago

adsnaider commented 1 year ago

Describe the Problem

I'm building an application with the w5500-evb-pico. Upon setting up the SPI and initializing the registers, I used the Udp::udp_bind call to bind Sn0 to some port. However, this often blocks forever (or until the watchdog resets the chip). I've narrowed it down to

while self.sn_sr(sn)? != Ok(SocketStatus::Udp) {}

in udp_bind being the problematic loop and the reason is that the SocketStatus is Closed for some reason. This happens more often than not and I'm not sure what the issue may be here. I've tried with 2 different wiznets and both experience the same problem.

Expected Behaviour

udp_bind doesn't block.

Steps to Reproduce

Setup a w5500-evb-pico, connected over ethernet to a computer. Initialize the w5500 eh0 vdm

I can share some source code as well but it's not much different from what's here: https://github.com/newAM/w5500-issue-252/blob/main/src/main.rs, but I can try running that specifically to see if I have the same issue on that source.

newAM commented 1 year ago

Hmm. A quick fix would be to rebind in the loop, but this shouldn't occur in the first place.

Are you able to try out the code here? If it is intermittent it could be the chip missing the chip-select: https://github.com/newAM/w5500-rs/issues/252#issuecomment-1670593061

adsnaider commented 1 year ago

Oh, that's really odd, that seems to be working :O

Edit: Fyi, that specifically works when I have

            spi_cs.set_high().unwrap();
            w5500_hl::ll::eh0::reset(&mut w5500_reset, &mut delay).unwrap();

when I set the SPI

newAM commented 1 year ago

:/ Your chip probably has a fast clock speed and the default HAL implementation doesn't assert chip select low for long enough. I haven't merged that "fix" yet because I don't have a good solution for embedded-hal 1.0 yet.

adsnaider commented 1 year ago

Interesting find. Do you know if that will cause a problem with networking as well?

newAM commented 1 year ago

We've only seen it in these two locations so far, but it will cause problems with any accesses over the SPI bus given enough time.

kiemlicz commented 5 months ago

I'm experiencing something similar. My setup:

Cargo.toml relevant section

panic-halt = "0.2.0"
embedded-hal = "1.0"
ufmt = "0.2.0"
nb = "1.1.0"

w5500-ll = { version = "0.12.0", features = ["eh0"] }
w5500-hl = { version = "0.11.0" }
w5500-dhcp = "0.6.0"
w5500-mqtt = { version = "0.3.0" }

[dependencies.embedded-hal-v0]
version = "0.2.3"
package = "embedded-hal"

[dependencies.arduino-hal]
git = "https://github.com/rahix/avr-hal"
rev = "3e362624547462928a219c40f9ea8e3a64f21e5f"
features = ["arduino-mega2560"]

main.rs

//...

#[arduino_hal::entry]
fn main() -> ! {
    let dp = arduino_hal::Peripherals::take().unwrap_or_else(|| panic!());
    let pins = arduino_hal::pins!(dp);

    let mut serial = arduino_hal::default_serial!(dp, pins, 57600);

    let mut d10 = pins.d10.into_output(); 
    let mut d53 = pins.d53.into_output();
    let mut d4 = pins.d4.into_output();
    d4.set_high();
    d10.set_low();

    let (mut spi, mut cs) = arduino_hal::Spi::new(
        dp.SPI,
        pins.d52.into_output(),
        pins.d51.into_output(),
        pins.d50.into_pull_up_input(),
        d53,
        spi::Settings {mode: embedded_hal::spi::MODE_0, ..spi::Settings::default()}
    );

    cs.set_high().unwrap(); //trying this as a suggestion from GH issue - no effect
    let mut w5500 = W5500::new(spi, cs);

    //below are setup_socket and udp_bind calls

    // dhcp.setup_socket(&mut w5500).unwrap();
    let simr: u8 = w5500.simr().unwrap();
    w5500.set_simr(LOCAL_SOCKET.bitmask() | simr).unwrap();
    const MASK: SocketInterruptMask = SocketInterruptMask::ALL_MASKED.unmask_recv();
    w5500.set_sn_imr(LOCAL_SOCKET, MASK).unwrap();
    w5500.close(LOCAL_SOCKET).unwrap();
    w5500.set_sipr(&Ipv4Addr::UNSPECIFIED).unwrap();

    // w5500.udp_bind(LOCAL_SOCKET, 68).unwrap();
    ufmt::uwriteln!(&mut serial, "W5500 bind1\r").unwrap_infallible();
    w5500.set_sn_cr(LOCAL_SOCKET, SocketCommand::Close).unwrap();
    ufmt::uwriteln!(&mut serial, "W5500 bind2\r").unwrap_infallible();
    while w5500.sn_sr(LOCAL_SOCKET).unwrap() != Ok(SocketStatus::Closed) {
        match w5500.sn_sr(LOCAL_SOCKET).unwrap() {
            Ok(status) => {
                ufmt::uwriteln!(&mut serial, "W5500 waiting1: {}\r", (status as u8)).unwrap_infallible();
            }
            Err(f) => {
                ufmt::uwriteln!(&mut serial, "W5500 waiting1 err: {}\r", f).unwrap_infallible();
            }
        }
        // w5500.write(0x0, 0x0, &[0x80]).unwrap();
        // arduino_hal::delay_ms(1000);
    }
    ufmt::uwriteln!(&mut serial, "W5500 bind3\r").unwrap_infallible();
    w5500.set_sn_port(LOCAL_SOCKET, 68).unwrap();
    ufmt::uwriteln!(&mut serial, "W5500 bind4\r").unwrap_infallible();
    const MODE: SocketMode = SocketMode::DEFAULT.set_protocol(Protocol::Udp);
    ufmt::uwriteln!(&mut serial, "W5500 bind5\r").unwrap_infallible();
    w5500.set_sn_mr(LOCAL_SOCKET, MODE).unwrap();
    ufmt::uwriteln!(&mut serial, "W5500 bind6\r").unwrap_infallible();
    w5500.set_sn_cr(LOCAL_SOCKET, SocketCommand::Open).unwrap();
    // This will not hang, the socket status will always change to Udp
    // after a open command with SN_MR set to UDP.
    // (unless you do somthing silly like holding the W5500 in reset)
    ufmt::uwriteln!(&mut serial, "W5500 bind7\r").unwrap_infallible();
    while w5500.sn_sr(LOCAL_SOCKET).unwrap() != Ok(SocketStatus::Udp) {
        match w5500.sn_sr(LOCAL_SOCKET).unwrap() {
            Ok(status) => {
                ufmt::uwriteln!(&mut serial, "W5500 waiting2: {}\r", (status as u8)).unwrap_infallible();
            }
            Err(f) => {
                ufmt::uwriteln!(&mut serial, "W5500 waiting2 err: {}\r", f).unwrap_infallible();
            }
        }

        // w5500.set_sn_port(LOCAL_SOCKET, 68).unwrap();
        // w5500.set_sn_mr(LOCAL_SOCKET, MODE).unwrap();
        // w5500.set_sn_cr(LOCAL_SOCKET, SocketCommand::Open).unwrap();
        // w5500.write(0x0, 0x0, &[0x80]).unwrap();
        // arduino_hal::delay_ms(1000);
    }     //HANGS

This

while w5500.sn_sr(LOCAL_SOCKET).unwrap() != Ok(SocketStatus::Udp) 

hangs yielding

Starting
W5500 bind1
W5500 bind2
W5500 bind3
W5500 bind4
W5500 bind5
W5500 bind6
W5500 bind7
W5500 waiting2: 0
W5500 waiting2: 0
W5500 waiting2: 0
W5500 waiting2 err: 37   <-------- happens sometimes, rather rarely
W5500 waiting2: 0
W5500 waiting2: 0
W5500 waiting2: 0
...

Is there any chance to get this working?

newAM commented 5 months ago

it is "fixed" (read: worked around) for EH0 on the main branch. I have been meaning to make a release, I'll do that today.

For EH1 there's no control for me, so it's up to the HAL authors to keep CS high long enough to meet the hold times. The W5500 base trait can also just be implemented directly if changing the HAL isn't easy.

kiemlicz commented 5 months ago

Thanks

I'm pasting updated snippet.

Cargo.toml

[dependencies]
panic-halt = "0.2.0"
embedded-hal = "1.0.0"
ufmt = "0.2.0"
nb = "1.1.0"

w5500-ll = { version = "0.13.0", features = ["eh0"] }
w5500-hl = { version = "0.12.0" }
w5500-dhcp = "0.7.0"
w5500-mqtt = { version = "0.4.0" }
#[arduino_hal::entry]
fn main() -> ! {
    let dp = arduino_hal::Peripherals::take().unwrap_or_else(|| panic!());
    let pins = arduino_hal::pins!(dp);

    let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
    let mut d10 = pins.d10.into_output();
    let seed: u64 = 872398473984;

    d10.set_low();
    let (mut spi, mut cs) = arduino_hal::Spi::new(
        dp.SPI,
        pins.d52.into_output(),
        pins.d51.into_output(),
        pins.d50.into_pull_up_input(),
        pins.d53.into_output(),
        spi::Settings {mode: embedded_hal::spi::MODE_0, ..spi::Settings::default()}
        // spi::Settings::default()
    );

    ufmt::uwriteln!(&mut serial, "Starting\r").unwrap_infallible();
    cs.set_high().unwrap();
    let mut w5500 = W5500::new(spi, cs);
    let mut dhcp: Client = Client::new(LOCAL_SOCKET, seed, LOCAL_MAC, HOSTNAME);

    ufmt::uwriteln!(&mut serial, "W5500 socket_setup\r").unwrap_infallible();
    dhcp.setup_socket(&mut w5500).unwrap();

It is still stuck, should it work?

newAM commented 5 months ago

Yeah, that should work. I have been running a DHCP client device for a little over a year now without issues.

What is the system frequency and SPI bus frequency of your device?