科研要求,使用手上的DE2-115开发板实现千兆以太网的数据发送
千兆以太网使用的时钟频率为125MHz,一般的GMII接口由于收发数据所使用的数据线为8根即一个时钟周期的上升沿可以发送8bit数据,而DE2-115开发板所使用的接口为RGMII,收发数据所使用的数据线为4根,所以需要在一个时钟周期的上升沿和下降沿都进行数据的传输。如下图TX_DATA和RX_DATA。

接下来就是具体的verilog代码的编写了,在这里参考了黑金开发板百兆网口的代码。以太网一帧的数据并不只包括数据,还有以太网协议用来检验是否为一帧数据的开始、结束、传输协议、校验码等参数,所以在发送一帧数据前需要发送这些参数,CRC校验码在一帧数据的最后被发送,这些具体格式可以去网上参考。

module ethernet#(//开发板MAC地址 00-11-22-33-44-65parameter BOARD_MAC = 48'h00_11_22_33_44_65,       //开发板IP地址 192.168.1.12    parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd12},   //目的MAC地址 2C_56_DC_19_01_F9  parameter  DES_MAC  = 48'h2C_56_DC_19_01_F9, //目的IP地址 192.168.1.13     parameter  DES_IP   = {8'd192,8'd168,8'd1,8'd13} )

首先设置源IP地址,源MAC地址,目的IP地址以及目的MAC地址。

//上升沿发送改为上升沿和下降沿都发送
always @(posedge eth_tx_clk_250m or negedge rst_n)beginif(!rst_n)cnt           <= 1'b1;else if(!cnt_1)beginif(cnt)begincnt            <= 1'b0;eth_tx_data <= eth_tx_data_s[3:0];//eth_rx_data_s[3:0] <= eth_rx_data;endelse if(!cnt) begincnt            <= 1'b1;eth_tx_data <= eth_tx_data_s[7:4];//eth_rx_data_s[7:4] <= eth_rx_data;endend
end

在这里使用了250MHz的时钟作为RGMII接口在125MHZ时钟的上升沿和下降沿都进行数据的发送。这里本来可以使用quartus提供的IP核Addioout将单沿数据转换为双沿数据,但在实际使用过程采用SignalTap进行观测的时候发现抓取不到该IP核输出的数据,而使用ModelSim进行仿真的时候是有输出的。最后在网上查到有帖子说是SignalTap抓取不到双沿数据,但是本着科学探索的精神,使用WireShark在PC上抓取传输上来的数据包,结果发现问题比较复杂,遂放弃……

/***********************************************///Project Name : UDP_Send
//Email        :
//Create Time   : 2021/01/09 13:36
//Editor       : Liu
//Version       : Rev1.0.0/***********************************************/module udp_send#(//开发板MAC地址 00-11-22-33-44-65parameter BOARD_MAC = 48'h00_11_22_33_44_65,      //开发板IP地址 192.168.1.12     parameter BOARD_IP  = {8'd192,8'd168,8'd1,8'd12},   //目的MAC地址 2C_56_DC_19_01_F9parameter DES_MAC   = 48'h2C_56_DC_19_01_F9, //目的IP地址 192.168.1.13     parameter DES_IP    = {8'd192,8'd168,8'd1,8'd13} )
(input                clk,              //时钟信号input                     rst_n,           //复位信号,低电平有效     input                      tx_start_en,    //以太网开始发送数据信号input           [31:0] tx_data,             //以太网待发送数据input        [15:0]  tx_byte_num,    //以太网发送的有效字节数input        [31:0]  crc_data   ,    //CRC校验数据input        [3:0]   crc_next   ,    //CRC下次校验完成数据output  reg          tx_done    ,    //以太网发送完成信号output  reg          tx_req     ,    //读数据请求信号output  reg          eth_tx_en  ,    //MII输出数据有效信号output  reg  [7:0]   eth_tx_data_s,  //MIIH输出数据output  reg          crc_en     ,    //CRC开始校验使能output  reg          crc_clr        //CRC数据复位信号
);//状态机
localparam  st_idle      = 7'b0000001;   //初始状态,等待开始发送信号
localparam  st_check_sum = 7'b0000010;   //IP首部校验和
localparam  st_preamble  = 7'b0000100;   //发送前导码+帧起始界定符
localparam  st_eth_head  = 7'b0001000;   //发送以太网帧头
localparam  st_ip_head   = 7'b0010000;   //发送IP首部+UDP首部
localparam  st_tx_data   = 7'b0100000;   //发送数据
localparam  st_crc       = 7'b1000000;   //发送CRC校验值localparam  ETH_TYPE     = 16'h0800;     //以太网协议类型 IP协议
//以太网数据最小46个字节,IP首部20个字节+UDP首部8个字节
//所以数据至少46-20-8=18个字节
localparam  MIN_DATA_NUM = 16'd18;//reg define
reg    [6:0]       cur_state        ;
reg    [6:0]       next_state       ;reg    [7:0]       preamble[7:0]    ;     //前导码
reg    [7:0]       eth_head[13:0]   ;     //以太网首部
reg    [31:0]      ip_head[6:0]     ;     //IP首部 + UDP首部reg                start_en_d0      ;
reg                start_en_d1      ;
reg    [15:0]      tx_data_num      ;     //发送的有效数据字节个数
reg    [15:0]      total_num        ;     //总字节数
reg    [15:0]      udp_num          ;     //UDP字节数
reg                skip_en          ;     //控制状态跳转使能信号
reg    [4:0]       cnt              ;
reg    [31:0]      check_buffer     ;     //首部校验和
reg    [2:0]       tx_bit_sel       ;
reg    [15:0]      data_cnt         ;     //发送数据个数计数器
reg                tx_done_t        ;
reg    [4:0]       real_add_cnt     ;     //以太网数据实际多发的字节数//wire define
wire               pos_start_en     ;     //开始发送数据上升沿
wire   [15:0]      real_tx_data_num ;     //实际发送的字节数(以太网最少字节要求)assign  pos_start_en = (~start_en_d1) & start_en_d0;
assign  real_tx_data_num = (tx_data_num >= MIN_DATA_NUM) ? tx_data_num : MIN_DATA_NUM; //采tx_start_en的上升沿
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginstart_en_d0 <= 1'b0;start_en_d1 <= 1'b0;end    else beginstart_en_d0 <= tx_start_en;start_en_d1 <= start_en_d0;end
end //寄存数据有效字节
always @(posedge clk or negedge rst_n) beginif(!rst_n) begintx_data_num <= 16'd0;total_num <= 16'd0;udp_num <= 16'd0;endelse beginif(pos_start_en && cur_state == st_idle) begin//数据长度tx_data_num <= 16'd4;   //tx_byte_num;        //IP长度:有效数据+IP首部长度            total_num <= 16'd4 + 16'd28;  //tx_byte_num//UDP长度:有效数据+UDP首部长度            udp_num <= 16'd4 + 16'd8;     //tx_byte_num          end    end
end   always @(posedge clk or negedge rst_n) beginif(!rst_n)cur_state <= st_idle;  elsecur_state <= next_state;
endalways @(*) beginnext_state = st_idle;case(cur_state)st_idle     : begin                               //等待发送数据if(skip_en)                next_state = st_check_sum;elsenext_state = st_idle;end  st_check_sum: begin                               //IP首部校验if(skip_en)next_state = st_preamble;elsenext_state = st_check_sum;    end                             st_preamble : begin                               //发送前导码+帧起始界定符if(skip_en)next_state = st_eth_head;elsenext_state = st_preamble;      endst_eth_head : begin                               //发送以太网首部if(skip_en)next_state = st_ip_head;elsenext_state = st_eth_head;      end              st_ip_head : begin                                //发送IP首部+UDP首部               if(skip_en)next_state = st_tx_data;elsenext_state = st_ip_head;      endst_tx_data : begin                                //发送数据                  if(skip_en)next_state = st_crc;elsenext_state = st_tx_data;      endst_crc: begin                                     //发送CRC校验值if(skip_en)next_state = st_idle;elsenext_state = st_crc;      enddefault : next_state = st_idle;   endcase
end    //发送数据
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginskip_en <= 1'b0; cnt <= 5'd0;check_buffer <= 32'd0;ip_head[1][31:16] <= 16'd0;tx_bit_sel <= 3'b0;crc_en <= 1'b0;eth_tx_en <= 1'b0;eth_tx_data_s <= 8'd0;tx_req <= 1'b0;tx_done_t <= 1'b0; data_cnt <= 16'd0;real_add_cnt <= 5'd0;//初始化数组    //前导码 7个8'h55 + 1个8'hd5preamble[0] <= 8'h55;                 preamble[1] <= 8'h55;preamble[2] <= 8'h55;preamble[3] <= 8'h55;preamble[4] <= 8'h55;preamble[5] <= 8'h55;preamble[6] <= 8'h55;preamble[7] <= 8'hd5;//目的MAC地址eth_head[0] <= DES_MAC[47:40];eth_head[1] <= DES_MAC[39:32];eth_head[2] <= DES_MAC[31:24];eth_head[3] <= DES_MAC[23:16];eth_head[4] <= DES_MAC[15:8];eth_head[5] <= DES_MAC[7:0];//源MAC地址eth_head[6] <= BOARD_MAC[47:40];eth_head[7] <= BOARD_MAC[39:32];eth_head[8] <= BOARD_MAC[31:24];eth_head[9] <= BOARD_MAC[23:16];eth_head[10] <= BOARD_MAC[15:8];eth_head[11] <= BOARD_MAC[7:0];//以太网类型eth_head[12] <= ETH_TYPE[15:8];eth_head[13] <= ETH_TYPE[7:0];        endelse beginskip_en <= 1'b0;tx_req <= 1'b0;crc_en <= 1'b0;eth_tx_en <= 1'b0;tx_done_t <= 1'b0;case(cur_state)st_idle     : beginif(pos_start_en)skip_en <= 1'b1; if(skip_en) begin//版本号:4 首部长度:5(单位:32bit,20byte/4=5)//IPV4:4 IPV6:6  ip_head[0] <= {8'h45, 8'h00, total_num};  //16位标识,每次发送累加1      ip_head[1][31:16] <= ip_head[1][31:16] + 1'b1; //bit[15:13]: 010表示不分片ip_head[1][15:0] <= 16'h4000;    //协议:17(udp)                  ip_head[2] <= {8'h40,8'd17,16'h0};//8'd17 = 8'h11   //源IP地址               ip_head[3] <= BOARD_IP;    //目的IP地址                        ip_head[4] <= DES_IP;       //16位源端口号:1234  16位目的端口号:1234                      ip_head[5] <= {16'd1234,16'd1234};  //16位udp长度,16位udp校验和              ip_head[6] <= {udp_num,16'h0000};                   end                                                   endst_check_sum: begin                           //IP首部校验cnt <= cnt + 5'd1;if(cnt == 5'd0) begin                   check_buffer <=   ip_head[0][31:16] + ip_head[0][15:0]+ ip_head[1][31:16] + ip_head[1][15:0]+ ip_head[2][31:16] + ip_head[2][15:0]+ ip_head[3][31:16] + ip_head[3][15:0]+ ip_head[4][31:16] + ip_head[4][15:0];endelse if(cnt == 5'd1)                      //可能出现进位,累加一次check_buffer <= check_buffer[31:16] + check_buffer[15:0];else if(cnt == 5'd2) begin                //可能再次出现进位,累加一次check_buffer <= check_buffer[31:16] + check_buffer[15:0];skip_en <= 1'b1;end                             else if(cnt == 5'd3) begin                //按位取反 cnt <= 5'd0;            ip_head[2][15:0] <= ~check_buffer[15:0];end    end st_preamble : begin                           //发送前导码+帧起始界定符eth_tx_en <= 1'b1;eth_tx_data_s <= preamble[cnt][7:0];    if(cnt == 5'd6)                       skip_en <= 1'b1; if(skip_en)cnt <= 5'd0;elsecnt <= cnt + 5'd1;endst_eth_head : begin                           //发送以太网首部eth_tx_en <= 1'b1;crc_en <= 1'b1;eth_tx_data_s <= eth_head[cnt][7:0];if(cnt == 5'd12)skip_en <= 1'b1;if(skip_en)cnt <= 5'd0;                                 elsecnt <= cnt + 5'd1;end st_ip_head  : begin                           //发送IP首部 + UDP首部crc_en <= 1'b1;eth_tx_en <= 1'b1; tx_bit_sel <= tx_bit_sel + 3'd1; if(tx_bit_sel == 3'd1)eth_tx_data_s <= ip_head[cnt][31:24];else if(tx_bit_sel == 3'd2)eth_tx_data_s <= ip_head[cnt][23:16];else if(tx_bit_sel == 3'd3)begineth_tx_data_s <= ip_head[cnt][15:8];if(cnt == 5'd6)skip_en <= 1'b1;end           else if(tx_bit_sel == 3'd4) begineth_tx_data_s <= ip_head[cnt][7:0];tx_bit_sel <= 3'd1; if(cnt == 5'd6) begintx_bit_sel <= 3'd0;//提前读请求数据,等待数据有效时发送tx_req <= 1'b1;cnt <= 5'd0;end elsecnt <= cnt + 5'd1;    end                            endst_tx_data  : begin                           //发送数据crc_en <= 1'b1;eth_tx_en <= 1'b1;tx_bit_sel <= tx_bit_sel + 3'd1;  if(tx_bit_sel[0] == 1'b0) beginif(data_cnt < tx_data_num - 16'd1)data_cnt <= data_cnt + 16'd1;                        else if(data_cnt == tx_data_num - 16'd1)begin//如果发送的有效数据少于18个字节,在后面填补充位//补充的值为最后一次发送的有效数据if(data_cnt + real_add_cnt < real_tx_data_num - 16'd1)real_add_cnt <= real_add_cnt + 5'd1;  elseskip_en <= 1'b1;end        end        if(tx_bit_sel == 3'd0)eth_tx_data_s <= tx_data[31:24];else if(tx_bit_sel == 3'd1)eth_tx_data_s <= tx_data[23:16];else if(tx_bit_sel == 3'd2)eth_tx_data_s <= tx_data[15:8];else if(tx_bit_sel == 3'd3) begineth_tx_data_s <= tx_data[7:0];tx_bit_sel <= 3'd0; if(data_cnt != tx_data_num - 16'd1)tx_req <= 1'b1;  end                                                                                                                                                   if(skip_en) begindata_cnt <= 16'd0;real_add_cnt <= 5'd0;tx_bit_sel <= 3'd0;end                                                          end  st_crc      : begin                          //发送CRC校验值eth_tx_en <= 1'b1;tx_bit_sel <= tx_bit_sel + 3'd1;if(tx_bit_sel == 3'd0)//注意是crc_nexteth_tx_data_s <= {~crc_data[24],~crc_data[25],~crc_data[26],~crc_data[27], ~crc_next[0], ~crc_next[1], ~crc_next[2], ~crc_next[3]};else if(tx_bit_sel == 3'd1)eth_tx_data_s <= {~crc_data[16],~crc_data[17],~crc_data[18],~crc_data[19],~crc_data[20],~crc_data[21],~crc_data[22], ~crc_data[23]};else if(tx_bit_sel == 3'd2)eth_tx_data_s <= {~crc_data[8],~crc_data[9],~crc_data[10],~crc_data[11], ~crc_data[12],~crc_data[13],~crc_data[14], ~crc_data[15]};else if(tx_bit_sel == 3'd3)begineth_tx_data_s <= {~crc_data[0],~crc_data[1],~crc_data[2],~crc_data[3], ~crc_data[4],~crc_data[5],~crc_data[6], ~crc_data[7]}; skip_en <= 1'b1;tx_done_t <= 1'b1;tx_bit_sel <= 3'd0;end                                                                                                                                                                         end                          default :;  endcase                                             end
end            //发送完成信号及crc值复位信号
always @(posedge clk or negedge rst_n) beginif(!rst_n) begintx_done <= 1'b0;crc_clr <= 1'b0;endelse begintx_done <= tx_done_t;crc_clr <= tx_done_t;end
end
endmodule

上边就是整个数据发送的代码,主要包括一个有限状态机。这块需要自己看代码理解,配合SignalTap观察具体状态,可以配合WireShark抓包,根据抓取到的数据分析问题。
千兆以太网发送代码
verilog代码
百度云链接
链接:https://pan.baidu.com/s/13ErHI6FA_ibCeTBW2vQAiw
提取码:oq2s
百度云链接为最新代码

FPGA实现千兆以太网发送相关推荐

  1. 基于FPGA的千兆以太网设计

    今天给大侠带来基于FPGA的千兆以太网设计,话不多说,上货. 一.设计概述 由于设计比较复杂,本篇带来设计流程以及设计思路,仅供各位大侠参考. 本篇通过管理数据输入输出MDIO配置PHY寄存器,使其工 ...

  2. 简谈基于FPGA的千兆以太网设计

    简谈基于FPGA的千兆以太网设计 今天给大侠带来简谈基于FPGA的千兆以太网设计,话不多说,上货. 今天我们来简单的聊一聊以太网,以太网在FPGA学习中属于比较高级的内容了,有些大侠肯定会感觉以太网学 ...

  3. 基于FPGA的千兆以太网的实现(1)

    基于FPGA的以太网图片接收 项目简述 UDP协议讲解 V3学院的上位机传送图像数据的数据流 项目的实验框图 跨时钟域处理时序图 Image_ctrl时序图 工程代码 测试模块的代码 测试结果 总结 ...

  4. 基于FPGA的数据采集、通讯和存储系统设计(即FPGA+RTL8211千兆以太网+SD卡存储+RTC+Uart+AD7606数模转换+电流放大采集等硬件设计及程序验证)

    本文主要介绍了学生期间自己做的一个小项目,便于学习初期对fpga的整体把握,涉及了很多常见.常用.常考和面试常问的知识点. 可以作为入门后的拓展学习和应对一些找工作的项目面试. 下面对硬件及软件代码进 ...

  5. 【转】简谈基于FPGA的千兆以太网

    原文地址: http://blog.chinaaet.com/luhui/p/5100052903 大家好,又到了学习时间了,学习使人快乐.今天我们来简单的聊一聊以太网,以太网在FPGA学习中属于比较 ...

  6. 简谈基于FPGA的千兆以太网

    当今,随着互联网技术的迅速发展,采用以太网实现数据采集和控制方面的应用,成为了电子系统设计的热点.以太网具有价格低廉.稳定可靠.传输速度快.传输距离远等特点,以太网技术发展成熟,具有很高的性价比.采用 ...

  7. 【千兆以太网】基于FPGA的千兆以太网MIMO通信系统开发

    1.千兆以太网技术 千兆以太网技术简介 以太网技术是当今应用广泛的网络技术,千兆以太网技术继承了以往以太网技术的许多优点,同时又具有许多新的特性,例如传输介质包括光纤和铜缆,使用8B/10B的编解码方 ...

  8. FPGA驱动千兆以太网PHY但电脑只显示百兆

    前两天公司做了个新板子,ZYNQ7035 + RTL8211E,拿给我测,于是写逻辑代码测试一下数据回环,没想到电脑端网络适配器一直显示是百兆网.查了多方原因,差点想手动配置寄存器了,但是想想又觉得不 ...

  9. 国产FPGA(紫光同创)—— 数据采集及千兆以太网传输(一)

    科研需要,使用国产FPGA(紫光PLG50H)实现数据采集及千兆以太网传输.总体流程如图所示 第一部分先对数据采集部分进行说明. 一.模数转换(ADC芯片-LTC2324) 本项目使用的是LTC232 ...

  10. 国产FPGA(紫光同创)—— 数据采集及千兆以太网传输(二)

    科研需要,使用国产FPGA(紫光PLG50H)实现数据采集及千兆以太网传输.总体流程如图所示 数据采集完成后,第二部分就需要千兆以太网实现数据传输. 一.硬件部分 开发板上通过Realtek RTL8 ...

最新文章

  1. [转载]SSH框架搭建详细图文教程
  2. 5-flutter 布局和列表
  3. nopi设置excel只读
  4. 各主流浏览器内核介绍
  5. 双系统linux进了grub,windows linux双系统开机后进去grub如何修复
  6. 突出重围:Oracle 10.2.0.5应用SCN补丁解决DB Link预警实践
  7. unity3D读取Txt文件中信息
  8. C++和JNI的数据转换
  9. 日常问题解决记录三:记一次Win10安装Oracle11g后遇到的问题
  10. c++删除数组中重复元素_在VBA中如何使用动态数组,以及利用动态数组去除重复值的方法...
  11. 一周二次课(12月12日)
  12. multiprocessing.queue取数据要加锁么_秒杀抢购思路以及高并发下数据安全
  13. 面试题,谈谈一款APP的优缺点,并提出改进建议
  14. 跨平台数据库ODB实战3-Person类的存储、查询、更新和删除
  15. 适合计算机中职生见到打拼音的软件,中职计算机基础教案设计(18页)-原创力文档...
  16. 刘林仙版《薛刚反唐》整理
  17. php如何判断emoji字符串,PHP实现识别带emoji表情的字符串
  18. 上交所几大业务平台简介
  19. 怎么在笔记中加入音频文件?
  20. Python,江湖救急

热门文章

  1. APP的原型制作流程
  2. 机器学习和统计学和数据发掘_面向数据科学和机器学习程序员的5项最佳数学和统计学课程...
  3. 宝藏级UI组件库:FirstUI,微信小程序版+uniapp版更新至1.6.0,完美支持vue3
  4. 简单的五险一金计算器
  5. SpringBoot项目整合JasperReport报表生成PDF并下载
  6. Java Lambda 归约 reduce
  7. C#语言与三菱PLC串口通讯
  8. 这可能是最全的反爬虫及应对方案,再也不怕爬不到数据了
  9. python 反爬虫策略
  10. programer的自我修养