Networking

The network stack — Ethernet, ARP, IPv4, ICMP, UDP, TCP (client and server), DHCP and DNS — is arch-neutral and built from scratch. The same source runs on all five targets.

One stack, several NICs

TargetNIC
x86_64e1000
riscv64 / aarch64virtio-net
RV32 (Pico 2 W)CYW43439 Wi-Fi
ARMv6-M (Pico W)CYW43439 Wi-Fi

The stack never names a card. It uses what the HAL provides — transmit a frame, poll for one, release it back to the ring, plus the MAC address and the receive-waiter slot the driver publishes — and any backend that implements them plugs in. The same stack runs over wired Ethernet or over the radio.

The Wi-Fi driver and its dependencies

The CYW43439 driver is Zig, and no vendor SDK is compiled in. Its gSPI bus program and its bus and join command sequences are based on the pico-sdk and cyw43-driver sources.

And the radio itself runs vendor firmware: three binary blobs — firmware (about 224 KB), NVRAM and CLM — are programmed into flash and streamed into the chip at boot. That is a property of the part; every CYW43 driver loads the same three.

The kernel owns the NIC; scripts drive it

The layering is the same two-layer split as everything else: the kernel owns the network card, and scripts drive it through FFIs.

On riscv64 the boundary carries a bidirectional buffer ABI. A U-mode actor passes a pointer and a length; the kernel walks the page tables to prove every page of that pointer is user-mapped before it dereferences it, then reads or writes it. So a script sends and receives arbitrary byte payloads — a custom message, an HTTP response — not just integers across the boundary.

aarch64 uses the same mechanism for file I/O only. There is no TCP FFI there.

On x86 and the MCU profiles the FFI boundary is all-integer today. The buffer ABI has not been wired.

Interrupt-driven receive (x86 only)

On x86, receive is handled on the interrupt, not a poll cadence. The e1000's legacy INTx is routed through the interrupt controller to a vector; the interrupt-service routine de-asserts the line, sends the end-of-interrupt, and wakes the network task. A frame is processed when it arrives.

Every other target polls. The virtio-net drivers have an interrupt hook, but nothing routes the interrupt controller to it. The CYW43439 has no receive interrupt wired to the CPU at all — a limit of the chip. Its frames come off the bus when the driver asks for them.

TCP

Client (active open) and server (passive open — the OS serves a small HTTP page). Three-way handshake, data transfer, FIN close. Sequence and acknowledgement numbers wrap safely.

Simultaneous connectionsThree. Each owns its endpoints, sequence state, retransmit queue, send queue and window
Connection matchFull 4-tuple, RFC 793. An incoming SYN takes any free slot
RetransmissionUnacknowledged segments held and re-sent. RTO 400 ms, doubling to a 3 s cap, then gives up
Fast retransmitThree duplicate acknowledgements resend the missing segment at once and halve the window
Congestion controlPer-connection cwnd and ssthresh: a segment per RTT out of slow start, then a segment per window. A timeout halves the threshold and restarts slow start
Flow controlData is queued, segmented to the MSS and paced by min(congestion window, the peer's advertised window)
SACKNegotiated both ways. Out-of-order arrivals are held, the acknowledgement names what arrived, and an incoming SACK stops us resending what the peer already has
Window scalingOffered, so a peer may advertise past 64 KiB. This end advertises a shift of zero on purpose — it keeps no large receive buffer
Idle timeout20 s without a segment from the peer

Checksums

SendReceive
TCPcomputed over the pseudo-headerverified; a segment that does not clear is dropped
UDPcomputed over the pseudo-headerverified when the sender supplied one (0 means omitted, which IPv4 permits)
IPv4 header, ICMPcomputedverified

The IPv4 version, header length, total length and destination IP are validated, and the UDP length bounds checked. The release build has no bounds checks, so a malformed packet that got past validation would read out of bounds; malformed traffic is dropped at the door.

Distributed, over the wire

Above the stack sits the actor layer's location transparency: a remote actor message travels as a UDP payload and arrives in a local mailbox.

On real silicon. On the Pico 2 W (RP2350, no MMU) this stack runs as a privileged task beside unprivileged, PMP-confined actors. It joins Wi-Fi, takes a real DHCP lease, resolves DNS, and carries an actor's message across a real LAN to an x86 node. All of it in one image.

The stack is addressable, not merely outbound: it answers ARP for its own address, and off-link traffic goes to the gateway. So a peer can open the conversation, and a peer on another subnet is reachable.

On-link vs off-link is a bitwise test against the netmask. The mask defaults to /24 and is replaced by the DHCP-leased netmask when the server sends one, so a subnet that is not a /24 routes correctly.

The details are on the Actors page.