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

Paul Reed paulreed at paddedcell.com
Sat Mar 12 14:56:38 CET 2022


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 
helps.

> ...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 
network.

*******
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 
at.

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.

Cheers,
Paul


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*)
   VAR b: INTEGER;
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, 
MISObit));
     SYSTEM.PUT(gpio, b + SCK)
   UNTIL n = 0
END bits;

PROCEDURE begin;
BEGIN SYSTEM.PUT(gpio, CS); SYSTEM.PUT(gpoc, MOSI+SCK+CS); 
SYSTEM.PUT(gpio, 0)
END begin;

PROCEDURE end;
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;

PROCEDURE Init*;
BEGIN get(39H, COMMON, 8);
   Texts.WriteString(W, "W5500 version byte (reg 39H) = "); 
Texts.WriteHex(W, rcvd);
   Texts.WriteLn(W);
   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;
     Texts.WriteLn(W);
     IF rcvd DIV 8 # 17H THEN
       Texts.WriteString(W,  "  Unexpected config, not HW auto");
       Texts.WriteLn(W);
     END;
     set(0, SOCKREG0, 4, 8); (*raw*)
     set(1, SOCKREG0, 1, 8); (*open*)
     init := TRUE
   ELSE Texts.WriteString(W, "04 expected, initialisation failed")
   END;
   Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
END Init;

PROCEDURE Hex(VAR W: Texts.Writer; x, n: INTEGER);
   VAR c: 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;

PROCEDURE Status0*;
   VAR rx, len, i: INTEGER;
BEGIN IF ~init THEN Init END;
   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*)
     Texts.WriteLn(W);
     i := i + len
   END; Texts.Append(Oberon.Log, W.buf)
END Status0;

BEGIN end; Texts.OpenWriter(W); init := FALSE
END W5500.


More information about the Oberon mailing list