Skip to content

extmod/network_wiznet5k: Switch to upstream ioLibrary_Driver. - #19713

Open
wiznet-grace wants to merge 3 commits into
micropython:masterfrom
WIZnet-ioNIC:wiznet5k-iolibrary
Open

wiznet-grace wants to merge 3 commits into
micropython:masterfrom
WIZnet-ioNIC:wiznet5k-iolibrary

Conversation

@wiznet-grace

Copy link
Copy Markdown

Summary

Point lib/wiznet5k at WIZnet's ioLibrary_Driver instead of a fork that carries MicroPython-specific patches and has drifted from the library WIZnet maintains and tests, and rewrite extmod/network_wiznet5k.c against it. The library is vendored unchanged. WIZnet unified it last year, so one codebase now covers W5100 through W6300, which is what lets the later PRs add the W6xxx parts without a second driver.

The API is unchanged: same network.WIZNET5K class, same MICROPY_PY_NETWORK_WIZNET5K values, same wiznet5k_poll() / wiznet5k_deinit() entry points. No board files are touched, and the port build files only lose the fork-only defines.

The rewrite also carries a few fixes the driver needed anyway:

  • the DHCP wait drives the library's one-second tick, so a lost request is retransmitted rather than stalling until the overall timeout
  • the MAC address and the network configuration both survive active(False) / active(True)
  • on lwIP, a failed send no longer takes the interface down, which nothing brought back up

The W6100 and W6300 register branches are in the driver already, but nothing selects those chips until the boards PR, so that code is not reachable yet.

First of the PRs splitting up #18035, which stays open for now. Once this is merged:

  1. the new W5100S/W5500/W6100 EVB boards, which need no PIO
  2. a general-purpose machine.PIOSPI on the rp2 port, plus the W6300 and W55RP20 boards, which talk to the chip over PIO and need it
  3. socket timeout and setsockopt, which the old driver never had

Testing

Hardware, on W5100S-EVB-Pico and W5500-EVB-Pico, each built both with lwIP and with the WIZnet stack (MICROPY_PY_LWIP=0):

  • DHCP, ifconfig(), status(), DNS
  • TCP connect / send / recv, UDP sendto / recvfrom
  • the MAC address and the configured address both survive active(False) / active(True)
  • WIZnet stack only: Ctrl-C breaks out of a blocked recv(), which is what the second commit is for
  • WIZnet stack only: a 32 KB send to a deliberately slow peer, to force the retry loop that replaces the library's blocking wait

Builds, with no warnings and no warning suppression:

  • rp2: W5100S-EVB-Pico, W5500-EVB-Pico, SIL_RP2040_SHIM
  • stm32: PYBV10, PYBV11, PYBLITEV10 (NETWORK variant), GARATRONIC_PYMATE_CORE8ADI8DOSC — build-tested only

The warnings the library's own headers used to raise were fixed upstream in Wiznet/ioLibrary_Driver#186, which the submodule is pinned to.

Known gaps on the WIZnet stack, the same before and after this change, so not regressions:

  • socket.settimeout() is unimplemented
  • recv() raises OSError(7) after the peer closes instead of returning b""
  • a DNS lookup that gets no answer never returns, because DNS_run() only gives up on a one-second tick that the driver has never driven. gethostbyname() now refuses the lookup when the interface has no address, which is the way that is easy to hit by accident, but a DNS server that simply stays silent still hangs.

All three need the same waiting machinery, so they go with the socket timeout PR at the end of the series.

Trade-offs and Alternatives

  • The alternative is to keep the fork and extend it for the W6xxx parts, which means maintaining the W6100 and W6300 register logic separately from the library WIZnet ships.
  • The submodule at lib/wiznet5k now points at a different repository, so existing checkouts need git submodule sync --recursive before git submodule update --init --recursive.
  • network.WIZNET5K keeps its name even though the driver now covers the W6xxx parts too, so existing user code is unaffected.
  • The fork called a yield hook from the blocking loops in socket.c, which upstream has no equivalent for. Adding one to ioLibrary_Driver was the obvious fix, but that library has plenty of other users and shouldn't carry a MicroPython hook. The second commit opens the sockets non-blocking and waits in the driver instead, which the library already supports and which is also where a socket timeout has to live.

@codecov

codecov Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.59%. Comparing base (a129b2f) to head (4788edb).

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #19713      +/-   ##
==========================================
+ Coverage   98.55%   98.59%   +0.03%     
==========================================
  Files         182      182              
  Lines       23346    23346              
  Branches        5        5              
==========================================
+ Hits        23009    23017       +8     
+ Misses        336      328       -8     
  Partials        1        1              
Flag Coverage Δ
unix-coverage-32bit 98.59% <ø> (+0.03%) ⬆️
unix-coverage-64bit 98.52% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

Code size report:

Reference:  tools/mpremote: Add tell() support for remote mounted files. [a129b2f]
Comparison: docs/library/network.WIZNET5K: Update for the new driver. [merge of 4788edb]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@projectgus projectgus left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @wiznet-grace for splitting the big PR up and submitting this.

Generally this looks really good, we would like to use the latest upstream Wiznet library.

I have not done any testing yet, but I have some comments below about removing some of the patches we previously had in our MicroPython fork. Most of these were solving real problems that users experienced with MicroPython+Wiznet, so we don't want to have any regressions if we switch to the upstream.

Comment thread extmod/network_wiznet5k.c Outdated
#endif
}
int8_t wizchip_socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag) {
return socket(sn, protocol, port, flag);

@projectgus projectgus Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the problems we ran into in the past is having ioLibrary_Driver provide these libc names (socket, close, listen, recv). This is a problem if we also link lwIP TCP/IP stack into the binary, for example (because the symbol name clashes).

Wrapping them like this in this file doesn't solve the problem, because the symbol socket is still defined by the ioLibrary_Driver module and it'll be part of the final linker input.

The previous approach we used for MicroPython was this one:
PR commit

Would you consider applying this change? Otherwise, you could add a simpler optional macro in ioLibrary_Driver that changes the names socket -> wizchip_socket on the library side if set. But the other name has to not be compiled into the binary, otherwise it doesn't solve the problem.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: Fixed commit link

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the wrappers didn't help. Fixed in the library instead: Wiznet/ioLibrary_Driver#187 (merged) adds an opt-in WIZCRight, the wrappers didn't help.
Fixed in the library instead: Wiznet/ioLibrary_Driver#187 (merged) adds an opt-in WIZCHIP_PREFIXED_EXPORTS that renames those functions to wizchip_* in the library itself. The submodule now includes it, the build sets it to 1 again, and the wrappers are gone. nm shows no unprefixed names left on any of the affected boards.
HIP_PREFIXED_EXPORTS that renames those functions to wizchip_* in the library itself. The submodule now includes it, the build sets it to 1 again, and the wrappers are gone. nm shows no unprefixed names left on any of the affected boards.

Comment thread extmod/extmod.mk
CFLAGS_THIRDPARTY += -DWIZCHIP_PREFIXED_EXPORTS=1
ifeq ($(MICROPY_PY_LWIP),1)
# When using MACRAW mode (with lwIP), maximum buffer space must be used for the raw socket
CFLAGS_THIRDPARTY += -DWIZCHIP_USE_MAX_BUFFER

@projectgus projectgus Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was added to set the buffer sizes to 16KB when using lwIP, see this PR commit.

Looking at the upstream library I couldn't see any change to the macraw buffer size for W5200 (didn't check the others). Is this problem solved in some other way?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: fixed commit link

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The driver handles this now. Before opening the MACRAW socket, wiznet5k_init() gives socket 0 all the buffer memory the other sockets would have had, so no library patch is needed:

https://github.com/WIZnet-ioNIC/WIZnet-EVB-Pico-micropython/blob/4788edb7b/extmod/network_wiznet5k.c#L237-L245

    // Configure buffers for fast MACRAW
    #if _WIZCHIP_ < W5200
    uint8_t sn_size[8] = {8, 0, 0, 0, 8, 0, 0, 0};
    #elif (_WIZCHIP_ == W5200 || _WIZCHIP_ == W5300 || _WIZCHIP_ == W5500 || _WIZCHIP_ == W6100)
    uint8_t sn_size[16] = {16, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0};
    #elif (_WIZCHIP_ == W6300)
    uint8_t sn_size[16] = {32, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0};
    #endif
    ctlwizchip(CW_INIT_WIZCHIP, sn_size);

The first half of the array is the TX size per socket and the second half the RX size, so socket 0 gets 16 KB each way on the W5500 and the other sockets get none.

Comment thread extmod/network_wiznet5k.c
mp_hal_pin_high(wiznet5k_obj.cs);
}

void mpy_wiznet_yield(void) {

@projectgus projectgus Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This yield function was added in the MicroPython fork to improve MicroPython performance when waiting on Wiznet chips in socket mode. PR commit here.

There doesn't seem to be any equivalent feature in the upstream library. Would you consider adding it? It doesn't change anything if the WIZNET_YIELD macro is not set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EDIT: Fixed commit link

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is covered by the second commit: the driver opens sockets in non-blocking mode and waits in its own loop with mp_event_handle_nowait(), so Ctrl-C and callbacks still run (tested on W5100S/W5500). The one exception is the SENDOK wait after a UDP sendto() inside the library, which is bounded by the chip timeout (~1.8 s on W5500).

Point the submodule at WIZnet's ioLibrary_Driver instead of a fork that
carries MicroPython-specific patches and has drifted from upstream.

The driver is rewritten in the same commit, as the old one relies on the
fork's WIZCHIP_EXPORT() macro.  WIZCHIP_PREFIXED_EXPORTS is now provided
upstream and stays set; the other fork-only build defines go, as the
driver sets the socket buffer sizes itself.  No user-visible change.

Signed-off-by: wiznet-grace <[email protected]>
The fork called a yield hook from the blocking loops in socket.c.
ioLibrary_Driver has none, so a blocked recv() could not be interrupted
with Ctrl-C.  Open the sockets non-blocking instead and wait in the
driver, calling mp_event_handle_nowait() between attempts.

Only the WIZnet TCP stack uses this code; lwIP builds are unaffected.

Signed-off-by: wiznet-grace <[email protected]>
List every chip the driver now supports rather than just W5200 and W5500,
and show the no-argument constructor used on boards that have the chip
fitted, alongside the existing external-module example.

Signed-off-by: wiznet-grace <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib Relates to lib/ directory in source port-rp2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants