Тест шилда Ethernet 1G

 gige test

Я беру недорогой гигабитный Ethernet коммутатор и подключаю к нему UTP5 патч кордами мой ноутбук и нашу FPGA плату Марсоход3GW2 (Gowin FPGA). Сделаем простой тестовый FPGA проект с шилдом Ethernet. Попробуем принимать и отправлять пакеты данных. 

Исходники этой работы лежат на гитхабе.

Для этого проекта я не стал ничего особо выдумывать, просто взял пару модулей из своих старых архивов, объединил их, кое-что исправил. Когда-то у нас уже была платка расширения Ethernet на микросхеме Realtek 8201BL и мы что-то делали для неё. Сейчас мы выпустили другую плату и микросхема PHY теперь другая, Realtek 8211E. Интерфейсы этих микросхем довольно похожи, но главное отличие это конечно скорость передачи. Из микросхемы RTL8201 в ПЛИС шла частота 25МГц и данные поступали 4х битные. Вот и получалась скорость передачи 4*25=100Мбит/секунду. А сейчас с PHY RTL8211E шина данных всё еще 4-х битная, но частота передачи в ПЛИС идёт уже 125МГц, ну и ещё плюс к этому используется передача DDR - и по фронту и по спаду тактовой частоты. То есть передается 4*125*2=1000Мбит/секунду. Получается гигабитный Ethernet.

Итак, топ модуль написан на Verilog HDL и выглядит следующим образом:

// 1G Ethernet rec&send example 
module gig_e(
    input  clk,key0,key1,
    input Rx_dv,
    input [3:0]Rx_D,
    input Rx_clk,
    output Tx_clk,
    output [3:0]Tx_D,
    output Tx_ena,
    output PHYRST,
    output MDC,
    inout MDIO,
    output XTAL,
    output [7:0] led
);

assign MDC = 1'b0;
assign PHYRST = 1'b1;

reg [25:0] cnt;
always @(posedge clk)
    cnt <=  cnt + 1'b1 ;

assign XTAL = cnt[1];

reg [25:0] R_cnt;
always @(posedge Rx_clk)
    R_cnt <=  R_cnt + 1'b1 ;

wire clkout;
wire clkoutp;
Gowin_rPLL inst1(
    .clkin(Rx_clk), //input clkin
    .clkout(clkout), //output clkout
    .clkoutp(clkoutp) //output clkout
);

wire r_clk = clkoutp;
wire Rx_dv_p,Rx_dv_pp;
wire [7:0] ddr_in;
Gowin_DDR  inst2(
  .din({Rx_dv,Rx_D}),
  .clk(r_clk),
  .q({Rx_dv_p,ddr_in[7:4],Rx_dv_pp,ddr_in[3:0]})
);

wire [127:0]rx_data;
wire [10:0]byte_count_;
wire my_packet_;
wire my_data_;
wire [5:0]err_cnt;
rec_packet inst3(
	.clk(r_clk),
	.data_valid(Rx_dv_p),
	.data(ddr_in),		
	.rx_data(rx_data),
    .my_packet(my_packet_),
    .my_data(my_data_),
    .byte_count(byte_count_),
    .err_cnt(err_cnt)
);

assign led[7:0]  = key0 ? rx_data [7:0] : { err_cnt, R_cnt[25], cnt[25] };

//////////////////////////////////////////////////////////////
assign Tx_clk = clkout;
reg [25:0] T_cnt;
always @(posedge Tx_clk)
    T_cnt <= key1? 26'b0: T_cnt + 1'b1 ;

wire [9:0] ram_rd_adr;
wire [7:0] t_data;
wire t_ena;
send_packet inst4(
	.clk(Tx_clk),
	.send(&{T_cnt[18:11]|rx_data [15:8],T_cnt[10:0]}),
	.padding(1'b0),
	.data({6'b0,ram_rd_adr}),
	.tx_en(t_ena),			
	.ram_adr(ram_rd_adr),
	.tx_data(t_data));

ddr_out inst5(
	.din({t_ena,t_data[7:4],t_ena,t_data[3:0]}), //input [9:0] din
	.clk(Tx_clk), //input clk
	.q({Tx_ena,Tx_D}) //output [4:0] q
);

endmodule

 

Verilog модуль send_packet inst4 передает широковещательный пакет, в котором 1024 байта данных. Данные, это просто увеличивающийся 9-ти битный счетчик. Прередача пакетов идет при нажатой кнопке "key1", эта кнопка находится дальше от разъёма HDMI платы Марсоход3GW2. Передаваемые платой пакеты можно посмотреть на компьютере каким-нибудь сниффером, например Wireshark:

FPGA board sends UDP packet

Модуль rec_packet inst3 принимает из сети пакеты с 16 байтами данных. В проекте есть каталог python, и в нем есть тестовые программы на питоне для отправки пакетов в сеть. Питоновский скрипт send_pkt.py довольно короткий:

import sys
import socket

num_args = len(sys.argv)
if num_args<3 :
	print("Need 2 or more arguments: 1) this host IP address, 2) number of UDP packets sents 3) optional up to 16 int values for packet bytes")
	sys.exit()

host_IP = sys.argv[1]
num_pkts = int(sys.argv[2])
if num_pkts <=0 :
	num_pkts = 1

packet = bytearray([0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F])

i=0
while i<num_args-3 :
	val = int(sys.argv[i+3],0)
	packet[i]=val & 0xFF
	i=i+1

print("My Host IP",host_IP)
print("Number of packets for send ",num_pkts)
print("Packet:")
print(packet)

sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind((host_IP,0))
sock.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)

i=0
while i<num_pkts:
	sock.sendto(packet,('255.255.255.255',26985))
	i=i+1

print ('Sent UDP packets:',num_pkts)

 

Пакеты этим скриптом передаются широковещательные на сокет 26985. Вот пример запуска тестовой программы для отправки UDP пакетов:

d:\altera\GOWIN\Marsohod3GW\_gig_eth_test\python>python send_pkt.py 10.8.0.11 5 0x33 0xFF 0xBB 0x66
My Host IP 10.8.0.11
Number of packets for send 5
Packet:
bytearray(b'3\xff\xbbfDEFGHIJKLMNO')
Sent UDP packets: 5

Этой программе требуется минимум 2 параметра или больше:

  • IP адрес интерфейса с которого ведется отправка пакета, это нужно, если в компьютере есть несколько сетевых интерфейсов, как у меня Ethernet, WiFi, и еще другие типа VPN-Tune, VBoxEthernet;
  • количество одинаковых UDP пакетов, которые будут отправлены;
  • последовательность значений байтов данных передаваемого пакета, в данном примере данные будут 0x33, 0xFF, 0xBB, 0x66..

Этот пакет так же легко увидеть с помощью Wireshark:

PC sends UDP packet

И этот же пакет можно увидеть уже принятым в самой FPGA с помощью Gowin Analyzer Oscilloscope:

 gao cap1

Здесь видно начало пакета, преамбула из 7 байт 0x55, затем разделитель Start-of-Frame Delimeter, один байт 0xD5 и далее MAC адрес назначения (6 байт 0xFF), MAC адрес ноутбука (0xE8, 0x03, 0x9A..) и так далее. Пакет довольно длинный, поэтому его окончание на следующем скриншоте:

gao cap2

Здесь уже видны полезные данные принятые в пакете, сигнал ddr_in[7:0] -> 0x33, 0xFF, 0xBB, 0x66 и далее. Они как раз выделены сигналом my_data_.

Еще полезно обратить внимание на подсчёт контрольной суммы принятого пакета, на её проверку, а так же итоговый сигнал crc32_ok. Подсчёт контрольной суммы Ethernet пакета мы уже однажды описали в этой статье. Там довольно подробно всё расписано. Сейчас у нас микросхема Realtek выбрана другая, а алгоритм подсчета контрольной суммы конечно остался такой же, как и был раньше. Сейчас я сделал отдельный регистр для подсчёта ошибок контрольной суммы при приёме. Его содержимое даже можно увидеть, если нажать кнопку платы key0 (ближе к разъёму HDMI).

Если кнопка key0 не нажата, то светодиоды отображают первый байт данных в пакете.

По нажатию этой кнопки на светодиодах отображаются:

  • младшие два бита это выходы 26-ти битных счетчиков cnt и R_cnt. Счётчик cnt работает от сигнала тактирования платы входной частоты 100МГц, а счётчик R_cnt работает от входного тактового сигнала из микросхемы RTL8211E. Здесь должно быть 125МГц, если установлено гигабитное соединение. То есть led[1] должен моргать немного чаще, чем led[0];
  • оставшиеся светодиоды led[7:2] как раз будут отображать количество ошибок приёма, обнаруженные через проверку контрольной суммы crc32 пакета при нажатой кнопке key0.

В моём проекте я вижу, что пакеты принимаются весьма надёжно и ошибок crc32 нет, но я смог добиться этого только подобрав фазу тактирующей частоты. Она идет из Realtek чипа, 125МГц, а потом в ПЛИС подаётся на Gowin_rPLL inst1 и выходят сигналы clkout и clkoutp те же самые 125МГц. Но с помощью Gowin_rPLL я подобрал фазу сдвига этой частоты clkoutp, чтобы захватывать данные надёжно модулем Gowin_DDR inst2.

Что ещё можно рассказать? В самом проекте используется только первые два байта данных из пакета.

Первый байт выведен на 8 бит светодиодов, второй управляет частотой передачи пакетов модулем  send_packet.v, где  "FF" - максимальная частота, а "00" - минимальная. 

И кстати, у нас есть еще один питоновский скрипт, который непрерывно посылает UDP в сеть, но первый байт данных в пакете это растущеие значения 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF и назад убывающие значения. Плата принимает эти байты и отображает на светодиодах. Получается красивая анимация передачи пакетов по сети Ethernet. Постараюсь снять про это демо ролик.

В следующей моей статье я реализовал более сложный проект, который уже откликается не на бродкасты, на на конкретный свой заданный статический IP адрес. Для этого в проекте реализован ARP протокол. Читайте от этом в статье "Управление шаговым мотором по сети Ethernet".

Если вы захотите повторить или просто испытать этот проект, то проще всего приобрести плату FPGA Марсоход3GW2 в нашем интернет магазине!

buy button

 

 


Добавить комментарий