Skip to content

ENC28J60 Ethernet

The FPGC has 10 Mbit Ethernet provided by a Microchip ENC28J60 controller. The chip integrates a MAC, a 10BASE-T PHY, and an 8 KiB packet buffer behind a single SPI port; from the FPGA's point of view it is just another SPI peripheral on SPI4. The magnetics and RJ-45 jack on the PCB connect directly to the chip's TPIN/TPOUT differential pairs.

The driver running on the FPGC is a thin layer that pushes raw Ethernet frames in and out of the on-chip buffer; everything above the link layer (FNP, ARP, …) is handled by software.

Bus shape

Pin Role
CS chip select (active low)
CLK serial clock
SI host → chip (MOSI)
SO chip → host (MISO)
INT open-drain interrupt to host
RST active-low reset
WOL wake-on-LAN (unused)
CLKOUT programmable clock output (unused)

The chip needs a 25 MHz crystal of its own (separate from the FPGA clock domain) for the PHY. The SPI clock is independent of that and runs at the same divider as the rest of the FPGA's slow-side SPI peripherals — well below the chip's 20 MHz SPI maximum.

INT is wired to the FPGA's interrupt controller and surfaces as a dedicated CPU interrupt line. The driver enables only the packet-pending interrupt; everything else (link change, TX done, errors) is polled when needed.

Memory model

The ENC28J60 has 8 KiB of internal SRAM that is shared between the RX and TX paths. Software partitions it with a pair of pointers:

  • RX buffer — a circular region delimited by ERXST and ERXND. The chip writes incoming frames here and advances its own write pointer. The host reads frames out by streaming from the chip's read pointer and then advancing it.
  • TX buffer — the rest of the SRAM, used as a scratch pad for outgoing frames. The host loads a frame, writes the start and end pointers, and tells the chip to transmit.

Each received frame is preceded by a 6-byte status vector: the next-packet pointer (used by the circular RX bookkeeping), the received byte count, and a status word with CRC-OK and other flags. Each transmitted frame is followed by a 7-byte status vector with the on-air byte count and the TX result.

There is one register page that is always visible (the special function registers — control, status, interrupts) plus a paged set of MAC and PHY registers reached through bank-select bits in the control register. Driver code switches banks before each access to a banked register and clears them afterwards.

Command set

The ENC28J60 SPI command set is intentionally tiny — eight instructions, identified by the top three bits of the opcode byte:

Opcode (top 3 bits) Mnemonic Purpose
000 RCR Read control register
001 RBM Read buffer memory
010 WCR Write control register
011 WBM Write buffer memory
100 BFS Bit-field set in a control register
101 BFC Bit-field clear in a control register
111 SRC Soft reset

The remaining five bits of the opcode byte select the target register, except for RBM/WBM which use a fixed encoding and operate on the buffer memory pointed to by ERDPT/EWRPT.

Reading and writing the chip's SRAM is just the RBM/WBM opcode followed by an arbitrary number of data bytes. The chip auto-increments its internal pointer per byte, so a single SPI transaction can move an entire frame.

Init

After power-up the chip needs:

  1. A soft reset (SRC).
  2. A short wait — the spec calls out an OST (Oscillator Start-up Timer) that must elapse before the chip becomes responsive.
  3. Programming the RX buffer pointers (ERXST/ERXND/ERXRDPT) to carve out the partition between RX and TX.
  4. MAC configuration: enable receive, full or half duplex, link-level addressing, and the receive filter. The FPGC enables broadcast + unicast filters; multicast and promiscuous modes are off.
  5. Programming the local MAC address. The FPGC derives a per-board address from the unique ID of one of the SPI flash chips; the OUI half is fixed.
  6. PHY configuration: link enable, full or half duplex, and the LED blink mode.
  7. Enabling the packet-pending interrupt and the global interrupt master.

After all of that the chip starts accepting frames into the RX buffer and the host starts taking interrupts.

RX path

When a frame arrives the chip writes it into the RX buffer behind the current write pointer and asserts INT. The host responds by:

  1. Reading EPKTCNT to find out how many frames are waiting.
  2. Setting ERDPT to the saved next-packet pointer.
  3. Streaming the 6-byte status vector and the frame payload out via RBM.
  4. Updating ERXRDPT to release the buffer slot.
  5. Decrementing EPKTCNT (by writing the PKTDEC bit in ECON2).

The status vector's next-packet pointer chains the frames together; the host always knows where the next one starts because the chip wrote it as part of the previous frame's header.

When the host falls behind, the chip's RX buffer eventually fills. The chip then drops new frames and asserts the RXERIF flag.

TX path

Sending a frame is the symmetric flow:

  1. Write the frame into the TX region of the buffer with WBM.
  2. Set ETXST and ETXND to delimit the frame.
  3. Write the TXRTS bit in ECON1 to start the transmission.
  4. Wait until the chip clears TXRTS (or until ESTAT.TXABRT is set, indicating an error).
  5. Read the 7-byte transmit status vector out of the buffer to find out the on-air byte count and any collision/abort information.

Chained transmissions are not used; the FPGC sends one frame at a time.

DMA path

Both buffer-memory operations (RBM for RX, WBM for TX) are streamed through the DMA engine's SPI burst port. A typical RX moves the 6-byte status vector through the per-byte CPU path, then hands the payload over to the DMA. A TX is the same in reverse: the CPU writes the destination pointers and the start opcode, then DMA streams the payload.

The DMA path keeps the CPU free for handling FNP-level work (checksums, ARP, ACK timing) while a packet is moving across the SPI bus.

Interrupt

The chip's INT pin is wired to a dedicated CPU interrupt line. The handler reads EIR to find out what happened, services any pending packets, and clears the interrupt by writing the appropriate bits. When the SPI bus is busy with another transaction at the moment the interrupt fires, the driver defers the work to a short timer-driven retry rather than blocking inside the ISR.

Network protocol

The frames the FPGC actually puts on the wire are not IP. The project uses FNP, a lightweight custom L2 protocol carried in raw Ethernet frames with EtherType 0xB4B4. FNP defines its own file-transfer and remote-keyboard message types and runs entirely between two FPGCs on the same Ethernet segment. There is no IP stack on board; bridging to a real network requires a separate host running an FNP-to-IP gateway.