陳家揚
$ 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
Start vivado in ubuntu
$ /tools/Xilinx/Vivado/2024.1/bin/vivado
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.
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)
Near-end Loopback
Far Loopback
Mode Control/Status Register
to “1”.Connector Loopback
pynq | LAN8720 |
---|---|
A20 | TX1 |
W9 | NC |
B19 | TX-EN |
Y8 | TX0 |
B20 | RX0 |
Y7 | RX1 |
Y17 | CLK |
Y16 | CRS |
F19 | MDIO |
W10 | MDC |
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
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
data_fifo
module until the total amount exceeds the payload size.preamble
field of the packet is transmitted.SFD
field of the packet is transmitted.HEADER
field of the packet is transmitted.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
localparam HEADER_LENGTH = HEADER_BYTES*8/RMII_WIDTH;
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;
Illustrate the test plan and procedures.
I plan to use the following tools to capture the packets generated by the Ethernet module.
$ 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