Я беру недорогой гигабитный 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:
Модуль 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:
И этот же пакет можно увидеть уже принятым в самой FPGA с помощью Gowin Analyzer Oscilloscope:
Здесь видно начало пакета, преамбула из 7 байт 0x55, затем разделитель Start-of-Frame Delimeter, один байт 0xD5 и далее MAC адрес назначения (6 байт 0xFF), MAC адрес ноутбука (0xE8, 0x03, 0x9A..) и так далее. Пакет довольно длинный, поэтому его окончание на следующем скриншоте:
Здесь уже видны полезные данные принятые в пакете, сигнал 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 в нашем интернет магазине!
Подробнее...