[Oberon] Wiznet W5500 code for Oberon System[?]

Skulski, Wojciech skulski at pas.rochester.edu
Wed Mar 16 02:23:41 CET 2022


  thank you for the code. It is really low level! The kind of stuff that every EE will love. We will love it too!

I am looking forward to seeing Joerg's higher level code. Polished or unpolished, it will be very useful and relevant to us. 

Thank you,
From: Oberon [oberon-bounces at lists.inf.ethz.ch] on behalf of Paul Reed [paulreed at paddedcell.com]
Sent: Saturday, March 12, 2022 8:56 AM
To: ETH Oberon and related systems
Subject: [EXT] Re: [Oberon] Wiznet W5500 code for Oberon System[?]

Hi Wojtek,

> I remember some remarks concerning the Wiznet W5500 support for Oberon
> System... if we could use the existing code...

I remember some discussions, and some wishful thinking, but I don't
remember anyone producing any code.

So given there was no answer, I grabbed a WIZ850io module (a W5500
breakout) and wired it to the GPIO port on an Oberon system (Spartan-3
Starter Kit, as it happens). My working test code is below in case it

> ...SPI-to-Ethernet translator. It helps to avoid the burden of
> maintaining the TCP/IP stack within the Oberon System.

So it's intended as an SPI-to-TCP/IP translator, more accurately. You
open a socket and go. However as you'll see from my code I wasn't really
interested in that and preferred to put it in MACRAW mode (socket 0
only, see datasheet) so I could look at the Ethernet frames on my test

Obviously, only use this code on a network that you control, or where
you have permission to look at raw frames: if they haven't heard of
Oberon, say "WireShark". ;-)

Status0 reads (peeks, not removes) the frames captured in the RX buffer
of socket 0 as it fills up.

Interestingly, Init doesn't reset the chip, it just re-opens the socket
without closing it, but doing this you'll then get a new buffer to peek

Even if someone had produced more elaborate code in Oberon, it may not
even have been helpful, because this is an application-level protocol,
it doesn't work at the driver level in any normal sense.

So how someone else had used it may prejudice how you might want to use
it. Searching I found sample code in BASIC and in Python, but it wasn't
very useful except to confirm some of the concepts in the datasheet.

You probably want to dedicate an SPI port to it, that seems to be the
consensus (especially since it may be able to run at 33-80MHz). Of
course, with an FPGA that's relatively easy. I bit-banged the SPI over
GPIO because that was the easiest way to connect the module just as a
test without too much investment.


MODULE W5500;  (* PDR 11.3.22      W5500.Init     W5500.Status0*)
   IMPORT SYSTEM, Texts, Oberon;

CONST gpio= -32; gpoc = -28;  (* general-purpose I/O data and output
control *)
   MISObit = 0; MOSI = 2; SCK = 4; CS = 8;  (* W5500 module pin
assignments *)
   COMMON = 0; SOCKREG0 = 8; SOCKRX0 = 24; WRITE = 4;  (* W5500 control
byte *)

VAR W: Texts.Writer;
   init: BOOLEAN; rcvd: INTEGER;

(* bit-bang the W5500 SPI interface using GPIO *)

PROCEDURE bits(x, n: INTEGER); (*adapted from NW PICL.Mod*)
BEGIN (*send n bits of x, MSB first*)
   REPEAT DEC(n); b := ROR(x, n) MOD 2 * MOSI;
     SYSTEM.PUT(gpio, b); rcvd := rcvd * 2 + ORD(SYSTEM.BIT(gpio,
     SYSTEM.PUT(gpio, b + SCK)
   UNTIL n = 0
END bits;

SYSTEM.PUT(gpio, 0)
END begin;

BEGIN SYSTEM.PUT(gpio, CS); SYSTEM.PUT(gpoc, 0); SYSTEM.PUT(gpio, 0)
END end;

PROCEDURE get(adr, ctrl, n: INTEGER);
BEGIN begin; bits(adr, 16); bits(ctrl, 8); rcvd := 0; bits(0, n); end
END get;

PROCEDURE set(adr, ctrl, x, n: INTEGER);
BEGIN begin; bits(adr, 16); bits(ctrl + WRITE, 8); bits(x, n); end
END set;

BEGIN get(39H, COMMON, 8);
   Texts.WriteString(W, "W5500 version byte (reg 39H) = ");
Texts.WriteHex(W, rcvd);
   IF rcvd = 4 THEN get(2EH, COMMON, 8);
     Texts.WriteString(W, "  PHYCFGR"); Texts.WriteHex(W, rcvd);
     IF ODD(rcvd DIV 4) THEN Texts.WriteString(W, ": full") ELSE
Texts.WriteString(W, ": half") END;
     Texts.WriteString(W, " duplex, 10");
     IF ODD(rcvd DIV 2) THEN Texts.Write(W, "0") END;
     Texts.WriteString(W, "Mb, link ");
     IF ODD(rcvd) THEN Texts.WriteString(W, "up") ELSE
Texts.WriteString(W, "down") END;
     IF rcvd DIV 8 # 17H THEN
       Texts.WriteString(W,  "  Unexpected config, not HW auto");
     set(0, SOCKREG0, 4, 8); (*raw*)
     set(1, SOCKREG0, 1, 8); (*open*)
     init := TRUE
   ELSE Texts.WriteString(W, "04 expected, initialisation failed")
   Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
END Init;

PROCEDURE Hex(VAR W: Texts.Writer; x, n: INTEGER);
BEGIN (*n MOD 4 = 0, n >= 4*)
   REPEAT DEC(n, 4);
     c := ROR(x, n) MOD 10H;
     IF c > 9 THEN c := c - 10 + ORD("A") ELSE c := c + ORD("0") END;
     Texts.Write(W, CHR(c))
   UNTIL n <= 0
END Hex;

   VAR rx, len, i: INTEGER;
   get(3, SOCKREG0, 8);
   Texts.WriteString(W, "Socket 0 status"); Texts.WriteHex(W, rcvd);
   get(26H, SOCKREG0, 16); rx := rcvd;
   Texts.WriteString(W, ", rx size "); Texts.WriteInt(W, rx, 1);
   Texts.WriteLn(W); i := 0;
   WHILE i < rx DO
     get(i+0, SOCKRX0, 16); len := rcvd; Texts.WriteInt(W, len, 1);
Texts.Write(W, " ");
     get(i+2, SOCKRX0, 32); Texts.WriteHex(W, rcvd);
     get(i+6, SOCKRX0, 16); Hex(W, rcvd, 16); (*dst MAC*)
     get(i+8, SOCKRX0, 32); Texts.WriteHex(W, rcvd);
     get(i+12, SOCKRX0, 16); Hex(W, rcvd, 16); (*src MAC*)
     get(i+14, SOCKRX0, 16); Texts.WriteHex(W, rcvd); (*ether type*)
     i := i + len
   END; Texts.Append(Oberon.Log, W.buf)
END Status0;

BEGIN end; Texts.OpenWriter(W); init := FALSE
END W5500.
Oberon at lists.inf.ethz.ch mailing list for ETH Oberon and related systems

More information about the Oberon mailing list