Try   HackMD

Ethernet device implemented in Chisel

陳家揚

Environment

$ lscpu
Architecture:             x86_64
  CPU op-mode(s):         32-bit, 64-bit
  Address sizes:          46 bits physical, 48 bits virtual
  Byte Order:             Little Endian
CPU(s):                   22
  On-line CPU(s) list:    0-21
Vendor ID:                GenuineIntel
  Model name:             Intel(R) Core(TM) Ultra 7 155H
    CPU family:           6
    Model:                170
    Thread(s) per core:   2
    Core(s) per socket:   16
    Socket(s):            1
    Stepping:             4
    CPU(s) scaling MHz:   21%
    CPU max MHz:          4800.0000
    CPU min MHz:          400.0000
    BogoMIPS:             5990.40

  • vivado 2024.1

Start vivado in ubuntu

$ /tools/Xilinx/Vivado/2024.1/bin/vivado

pynq-z2

Access to PHY module (Ethernet port) with PL

  • Ethernet PHY

    The PYNQ-Z2 has a Realtek RTL8211E-VL PHY supporting 10/100/1000 Ethernet.
    The PHY is connected to the Zynq RGMII controller. The auxiliary interrupt (INTB) and reset (PHYRSTB) signals connect to MIO pins MIO10 and MIO9, respectively.
    One of the Zynq PS Ethernet controllers can be connected to the appropriate MIO pins to control the Ethernet port.

    The Zynq does not need to be configured for the PHY to establish a connection. After power-up the PHY starts with Auto Negotiation enabled, advertising 10/100/1000 link speeds and full duplex. The PHY will automatically establishes a link if there is an Ethernet-capable partner connected.

Since the Ethernet interface of the PYNQ-Z2 is connected to the PS (Processing System) side, it is necessary to externally connect an Ethernet module to the PL (Programmable Logic) side. The module I chose is the LAN8720, which supports RMII (Reduced Media Independent Interface) interface.

LAN8720

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

pin

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

MAC interface (rmii)

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • support 10Mbps / 100Mbps data rates

  • A single clock reference is used for both transmit and receive

  • 2-bit (di-bit) wide transmit and receive data path

    transmit data - TXD[1:0]
    transmit strobe - TXEN
    receive data - RXD[1:0]
    receive error - RXER (Optional)
    carrier sense - CRS_DV
    Reference Clock - (RMII references usually define this signal as REF_CLK)

Loop back

  • Near-end Loopback

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

  • Far Loopback

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

    The far loopback mode is enabled by setting the FARLOOPBACK bit of the Mode Control/Status Register to “1”.
    In this mode, data that is received from the link partner on the MDI is
    looped back out to the link partner.

  • Connector Loopback

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

eth-module

pin map

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

pynq LAN8720
A20 TX1
W9 NC
B19 TX-EN
Y8 TX0
B20 RX0
Y7 RX1
Y17 CLK
Y16 CRS
F19 MDIO
W10 MDC

constraint file

set_property -dict { PACKAGE_PIN F19   IOSTANDARD LVCMOS33 } [get_ports { MDIO }]; #IO_L12N_T1_MRCC_34 Sch=rpio_08_r

set_property -dict { PACKAGE_PIN W10   IOSTANDARD LVCMOS33 } [get_ports { MDC }]; #IO_L16P_T2_13 Sch=rpio_11_r
set_property -dict { PACKAGE_PIN B20   IOSTANDARD LVCMOS33 } [get_ports { RX0 }]; #IO_L1N_T0_AD0N_35 Sch=rpio_12_r

set_property -dict { PACKAGE_PIN B19   IOSTANDARD LVCMOS33 } [get_ports { TX-EN }]; #IO_L2P_T0_AD8P_35 Sch=rpio_16_r

set_property -dict { PACKAGE_PIN Y8    IOSTANDARD LVCMOS33 } [get_ports { TX0 }]; #IO_L14N_T2_SRCC_13 Sch=rpio_19_r
set_property -dict { PACKAGE_PIN A20   IOSTANDARD LVCMOS33 } [get_ports { TX1 }]; #IO_L2N_T0_AD8N_35 Sch=rpio_20_r

set_property -dict { PACKAGE_PIN Y7    IOSTANDARD LVCMOS33 } [get_ports { RX1 }]; #IO_L13P_T2_MRCC_13 Sch=rpio_24_r

set_property -dict { PACKAGE_PIN W9    IOSTANDARD LVCMOS33 } [get_ports { NC }]; #IO_L16N_T2_13 Sch=rpio_26_r
set_property -dict { PACKAGE_PIN Y16   IOSTANDARD LVCMOS33 } [get_ports { CRS }]; #IO_L7P_T1_34 Sch=rpio_sd_r
set_property -dict { PACKAGE_PIN Y17   IOSTANDARD LVCMOS33 } [get_ports { CLK }]; #IO_L7N_T1_34 Sch=rpio_sc_r

Netlist

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

rst_gen

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

eth_rst_gen

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

gen_50M

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

packet_gen

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

data of the packets

In this Eth-packet generator, a timer is used to generate (simulate) the content of the data payload.

The value of packet_timer is then assigned to the 8-bit wide s_axis_tdata, which serves as the input to data_fifo module.

logic [63:0] packet_timer;

// increment the timer and create an enable pulse when reaching max
   always_ff@(posedge eth_clk) begin
      if (eth_rst == 1) begin
         packet_timer <= 0;
         packet_enable <= 0;

      end
      else begin
         packet_enable <= 0;

         if (packet_timer == packet_max) begin
            packet_timer <= 0;
            packet_enable <= 1;

         end
         else begin
            packet_timer <= packet_timer + 1;
         end
      end
   end

finite state machine

  • IDLE
    • At this stage, the module continues accumulating data from the data_fifo module until the total amount exceeds the payload size.
  • PREAMBLE
    • At this stage, the preamble field of the packet is transmitted.
  • SFD
    • At this stage, the SFD field of the packet is transmitted.
  • HEADER
    • At this stage, the HEADER field of the packet is transmitted.
  • DATA
    • At this stage, data transmission occurs.
  • FCS
    • Transmit the frame check sequence.
  • WAIT
    • Waiting for the next transmission.
state tx_valid tx_data fcs_en fcs_rst
IDLE 0 0 0 1
PREAMBLE 1 preamble_buffer[RMII_WIDTH-1:0] 0 0
SFD 1 sfd_buffer[RMII_WIDTH-1:0] 0 0
HEADER 1 header_buffer[RMII_WIDTH-1:0] 1 0
DATA 1 data_buffer[RMII_WIDTH-1:0] 1 0
FCS 1 fcs_buffer[RMII_WIDTH-1:0] 0 0
WAIT 0 0 0 0

Since the RMII data width is 2 bits, transmitting a header requires

HEADER_BYTES8RMII_WIDTH iterations, where RMII_WIDTH = 2.

localparam HEADER_LENGTH = HEADER_BYTES*8/RMII_WIDTH;

data_fifo

image

crc_gen

image

ether_header_gen

image

typedef struct      packed {
    // Ethernet Frame Header
    // no FCS, added later
    logic [1:0][7:0] eth_type_length;
    logic [5:0][7:0] mac_source;
    logic [5:0][7:0] mac_destination;
} ethernet_header;

test script

Illustrate the test plan and procedures.

I plan to use the following tools to capture the packets generated by the Ethernet module.

  • scapy
  • wireshark
$ ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: wlp85s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DORMANT group default qlen 1000
    link/ether 6c:f6:da:84:33:ed brd ff:ff:ff:ff:ff:ff
4: enx00e04c684eaf: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:e0:4c:68:4e:af brd ff:ff:ff:ff:ff:ff

test.py

from scapy.all import Ether, sendp, sniff, Raw

pc_mac = "00:e0:4c:68:4e:af"
fpga_mac = "00:11:22:33:44:55"

# Construct eth-frame
pkt = Ether(dst=fpga_mac, src=pc_mac) / b"Hello FPGA!"
# Transmit to certain network interface
sendp(pkt, iface="enx00e04c684eaf")
# Construct eth-frame with raw payload
pkt = Ether(dst=fpga_mac, src=pc_mac) / Raw(b"Hello FPGA!")

# Transmit to certain network interface
sendp(pkt, iface="enx00e04c684eaf")

sni.py

sniff(iface="enx00e04c684eaf", count=2, prn=lambda x: x.summary())
$ sudo python3 test.py
$ sudo python3 sni.py

result