本帖最后由 正点原子运营 于 2024-1-2 17:29 编辑
1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
3)购买链接:https://detail.tmall.com/item_o.htm?id=692712955836
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)FPGA技术交流QQ群:435699340
1.4 程序设计根据实验任务,我们可以大致规划出系统的控制流程:首先我们需要完成RGMII接口数据和GMII接口数据的转换,以方便数据的采集和解析,在数据采集过程中所用到的延时原语参考时钟由锁相环输出的时钟提供;其次整个以太网帧格式与ARP协议的实现由ARP顶层模块完成;ARP控制模块负责检测输入的触摸按键是否被按下,控制ARP顶层模块发起请求与产生应答等操作。由此画出系统的功能框图如下图所示: GMII TO RGMII模块负责将双沿(DDR)数据和单沿(SDR)数据之间的转换;ARP顶层模块实现了以太网ARP数据包的接收、发送以及CRC校验的功能;ARP控制模块根据输入的按键触摸信号和接收到的ARP请求信号,控制ARP顶层模块发送ARP请求或者ARP应答。 FPGA顶层模块例化了以下四个模块,网口复位模块(reset_dly)、GMII TO RGMII模块(gmii_to_rgmii)、ARP顶层模块(arp)和ARP控制模块(arp_ctrl),实现了各模块之间的数据交互。 其中ARP顶层模块和GMII TO RGMII模块内部也例化了多个其它模块,这样设计的目的是为了方便模块的重用。 顶层模块的代码如下: - 1 module eth_arp_test(
- 2 input sys_clk , //系统时钟
- 3 input sys_rst_n , //系统复位信号,低电平有效
- 4 input touch_key , //触摸按键,用于触发开发板发出ARP请求
- 5 //以太网RGMII接口
- 6 input eth_rxc , //RGMII接收数据时钟
- 7 input eth_rx_ctl, //RGMII输入数据有效信号
- 8 input [3:0 eth_rxd ,//RGMII输入数据
- 9 output eth_txc , //RGMII发送数据时钟
- 10 output eth_tx_ctl, //RGMII输出数据有效信号
- 11 output [3:0 eth_txd ,//RGMII输出数据
- 12 output eth_rst_n //以太网芯片复位信号,低电平有效
- 13 );
- 14
- 15 //parameter define
- 16 //开发板MAC地址00-11-22-33-44-55
- 17 parameter BOARD_MAC = 48'h00_11_22_33_44_55;
- 18 //开发板IP地址192.168.1.10
- 19 parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
- 20 //目的MAC地址ff_ff_ff_ff_ff_ff
- 21 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
- 22 //目的IP地址192.168.1.102
- 23 parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
- 24
- 25 //wire define
- 26 wire [7:0 gmii_txd ; //GMII发送数据
- 27 wire gmii_tx_en ; //GMII发送数据使能信号
- 28 wire gmii_tx_clk ; //GMII发送时钟
- 29 wire [7:0 gmii_rxd ; //GMII接收数据
- 30 wire gmii_rx_dv ; //GMII接收数据有效信号
- 31 wire gmii_rx_clk ; //GMII接收时钟
- 32
- 33 wire arp_rx_done ; //ARP接收完成信号
- 34 wire arp_rx_type ; //ARP接收类型 0:请求 1:应答
- 35 wire [47:0 src_mac ; //接收到目的MAC地址
- 36 wire [31:0 src_ip ; //接收到目的IP地址
- 37 wire arp_tx_en ;//ARP发送使能信号
- 38 wire arp_tx_type ; //ARP发送类型 0:请求 1:应答
- 39 wire tx_done ; //ARP发送完成信号
- 40 wire [47:0 des_mac ; //发送的目标IP地址
- 41 wire [31:0 des_ip ; //以太网发送完成信号
- 42 //*****************************************************
- 43 //** main code
- 44 //*****************************************************
- 45 assign des_mac = src_mac;
- 46 assign des_ip = src_ip ;
- 47
- 48 //输出网口复位信号
- 49 reset_dly delay_u0(
- 50 .clk (sys_clk ),
- 51 .rst_n (sys_rst_n),
- 52 .rst_n_dly (eth_rst_n)
- 53 );
- 54
- 55 gmii_to_rgmii u_gmii_to_rgmii(
- 56 .rgmii_txd (eth_txd ),
- 57 .rgmii_tx_ctl (eth_tx_ctl),
- 58 .rgmii_txc (eth_txc ),
- 59 .rgmii_rxd (eth_rxd ),
- 60 .rgmii_rx_ctl (eth_rx_ctl),
- 61 .rgmii_rxc (eth_rxc ),
- 62
- 63 .gmii_rx_clk (gmii_rx_clk),
- 64 .gmii_txd (gmii_txd ),
- 65 .gmii_tx_en (gmii_tx_en ),
- 66 .gmii_tx_clk (gmii_tx_clk),
- 67 .gmii_rxd (gmii_rxd ),
- 68 .gmii_rx_dv (gmii_rx_dv )
- 69 );
- 70
- 71 //ARP通信
- 72 arp
- 73 #(
- 74 .BOARD_MAC (BOARD_MAC ), //参数例化
- 75 .BOARD_IP (BOARD_IP ),
- 76 .DES_MAC (DES_MAC ),
- 77 .DES_IP (DES_IP )
- 78 )
- 79 u_arp(
- 80 .rst_n (sys_rst_n ),
- 81
- 82 .gmii_rx_clk (gmii_rx_clk),
- 83 .gmii_rx_dv (gmii_rx_dv ),
- 84 .gmii_rxd (gmii_rxd ),
- 85 .gmii_tx_clk (gmii_tx_clk),
- 86 .gmii_tx_en (gmii_tx_en ),
- 87 .gmii_txd (gmii_txd ),
- 88
- 89 .arp_rx_done (arp_rx_done),
- 90 .arp_rx_type (arp_rx_type),
- 91 .src_mac (src_mac ),
- 92 .src_ip (src_ip ),
- 93 .arp_tx_en (arp_tx_en ),
- 94 .arp_tx_type (arp_tx_type),
- 95 .des_mac (des_mac ),
- 96 .des_ip (des_ip ),
- 97 .tx_done (tx_done )
- 98 );
- 99
- 100 //ARP控制
- 101 arp_ctrl u_arp_ctrl(
- 102 .clk (gmii_rx_clk),
- 103 .rst_n (sys_rst_n ),
- 104
- 105 .touch_key (touch_key ),
- 106 .arp_rx_done (arp_rx_done),
- 107 .arp_rx_type (arp_rx_type),
- 108 .arp_tx_en (arp_tx_en ),
- 109 .arp_tx_type (arp_tx_type)
- 110 );
- 111
- 112 endmodule
复制代码顶层模块主要完成对其余模块的例化。在程序的第16行至第23行代码定义了开发板的MAC地址、IP地址、默认的目的MAC地址和目的IP地址。开发板的MAC地址为00:11:22:33:44:55;可开发板的IP地址为192.168.1.10;默认目的MAC地址为ff:ff:ff:ff:ff:ff,这是一个广播MAC地址,在收到上位机的请求或者应答之后,ARP模块会替换成实际的目的MAC地址。目的IP地址这里设置为192.168.1.102,因此大家在做本次实验时,需要把电脑的以太网的IP地址改成192.168.1.102,或者将代码中定义的DES_IP改成电脑的IP地址。 程序的第45行和46行代码将收到的对端设备MAC地址和目的IP地址,作为开发板发送时的目的MAC地址和IP地址。 gmii_to_rgmii模块代码如下: - 1 module gmii_to_rgmii(
- 2 //以太网GMII接口
- 3 output gmii_rx_clk , //GMII接收时钟
- 4 output gmii_rx_dv , //GMII接收数据有效信号
- 5 output [7:0 gmii_rxd , //GMII接收数据
- 6 output gmii_tx_clk , //GMII发送时钟
- 7 input gmii_tx_en , //GMII发送数据使能信号
- 8 input [7:0 gmii_txd , //GMII发送数据
- 9 //以太网RGMII接口
- 10 input rgmii_rxc , //RGMII接收时钟
- 11 input rgmii_rx_ctl, //RGMII接收数据控制信号
- 12 input [3:0 rgmii_rxd , //RGMII接收数据
- 13 output rgmii_txc , //RGMII发送时钟
- 14 output rgmii_tx_ctl, //RGMII发送数据控制信号
- 15 output [3:0 rgmii_txd //RGMII发送数据
- 16 );
- 17 //wire
- 18 wire pll_lock ;
- 19 wire gmii_tx_er;
- 20 //*****************************************************
- 21 //** main code
- 22 //*****************************************************
- 23 assign gmii_tx_clk = gmii_rx_clk;
- 24 //RGMII接收
- 25 rgmii_rx u_rgmii_rx(
- 26 .rgmii_rxc (rgmii_rxc ),
- 27 .rgmii_rx_ctl (rgmii_rx_ctl ),
- 28 .rgmii_rxd (rgmii_rxd ),
- 29
- 30 .gmii_rx_clk (gmii_rx_clk ),
- 31 .gmii_rx_dv (gmii_rx_dv ),
- 32 .gmii_rxd (gmii_rxd ),
- 33 .gmii_tx_clk_deg (gmii_tx_clk_deg),
- 34 .pll_lock (pll_lock )
- 35 );
- 36
- 37 //RGMII发送
- 38 rgmii_tx u_rgmii_tx(
- 39 .reset (1'b0 ),
- 40
- 41 .gmii_tx_er (1'b0 ),
- 42 .gmii_tx_clk (gmii_tx_clk ),
- 43 .gmii_tx_en (gmii_tx_en ),
- 44 .gmii_txd (gmii_txd ),
- 45 .gmii_tx_clk_deg (gmii_tx_clk_deg),
- 46
- 47 .rgmii_txc (rgmii_txc ),
- 48 .rgmii_tx_ctl (rgmii_tx_ctl ),
- 49 .rgmii_txd (rgmii_txd )
- 50 );
- 51
- 52 endmodule
复制代码由该模块的端口可知,该模块实现了双沿(DDR)数据和单沿(SDR)数据之间的转换。程序中第23行将GMII接收时钟赋值给GMII发送时钟,因此GMII的发送时钟和接收时钟实际上为同一个时钟。GMII TO RGMII模块例化了rgmii_rx模块和rgmii_tx模块。 rgmii_rx模块代码如下所示: - 1 module rgmii_rx(
- 2 //以太网RGMII接口
- 3 input rgmii_rxc , //RGMII接收时钟
- 4 input rgmii_rx_ctl , //RGMII接收数据控制信号
- 5 input [3:0 rgmii_rxd , //RGMII接收数据
- 6
- 7 //以太网GMII接口
- 8 output gmii_rx_clk , //GMII接收时钟
- 9 output gmii_rx_dv , //GMII接收数据有效信号
- 10 output [7:0 gmii_rxd , //GMII接收数据
- 11 output gmii_tx_clk_deg,
- 12 output pll_lock
- 13 );
- 14 //define reg
- 15 reg [ 7:0 gmii_rxd;
- 16 reg gmii_rx_dv;
- 17 //define wire
- 18 wire gmii_rx_dv_s;
- 19 wire [ 7:0 gmii_rxd_s;
- 20 //*****************************************************
- 21 //** main code
- 22 //*****************************************************
- 23 pll_sft U_pll_phase_shift(
- 24 .clkout0 (gmii_rx_clk ), //125MHz
- 25 .clkout1 (gmii_tx_clk_deg),
- 26 .clkin1 (rgmii_rxc ),
- 27 .clkfb (gmii_rx_clk ),
- 28 .pll_rst (1'b0 ),
- 29 .pll_lock (pll_lock )
- 30 );
- 31
- 32
- 33 always @(posedge gmii_rx_clk)
- 34 begin
- 35 gmii_rxd = gmii_rxd_s;
- 36 gmii_rx_dv = gmii_rx_dv_s;
- 37 end
- 38
- 39 wire [5:0 nc1;
- 40 GTP_ISERDES #(
- 41 .ISERDES_MODE ("IDDR"),
- 42 //"IDDR","IMDDR","IGDES4","IMDES4","IGDES7","IGDES8","IMDES8"
- 43 .GRS_EN ("TRUE"), //"TRUE"; "FALSE"
- 44 .LRS_EN ("TRUE") //"TRUE"; "FALSE"
- 45 ) igddr1(
- 46 .DI (rgmii_rxd[0]),
- 47 .ICLK (1'd0 ),
- 48 .DESCLK (gmii_rx_clk ),
- 49 .RCLK (gmii_rx_clk ),
- 50 .WADDR (3'd0 ),
- 51 .RADDR (3'd0 ),
- 52 .RST (1'b0 ),
- 53 .DO ({gmii_rxd_s[4],gmii_rxd_s[0],nc1})
- 54 );
- 55
- 56 wire [5:0 nc2;
- 57 GTP_ISERDES #(
- 58 .ISERDES_MODE ("IDDR"),
- 59 //"IDDR","IMDDR","IGDES4","IMDES4","IGDES7","IGDES8","IMDES8"
- 60 .GRS_EN ("TRUE"), //"TRUE"; "FALSE"
- 61 .LRS_EN ("TRUE") //"TRUE"; "FALSE"
- 62 ) igddr2(
- 63 .DI (rgmii_rxd[1]),
- 64 .ICLK (1'd0 ),
- 65 .DESCLK (gmii_rx_clk ),
- 66 .RCLK (gmii_rx_clk ),
- 67 .WADDR (3'd0 ),
- 68 .RADDR (3'd0 ),
- 69 .RST (1'b0 ),
- 70 .DO ({gmii_rxd_s[5],gmii_rxd_s[1],nc2})
- 71 );
- 72
- 73 wire [5:0 nc3;
- 74 GTP_ISERDES #(
- 75 .ISERDES_MODE ("IDDR"),
- 76 //"IDDR","IMDDR","IGDES4","IMDES4","IGDES7","IGDES8","IMDES8"
- 77 .GRS_EN ("TRUE"), //"TRUE"; "FALSE"
- 78 .LRS_EN ("TRUE") //"TRUE"; "FALSE"
- 79 ) igddr3(
- 80 .DI (rgmii_rxd[2]),
- 81 .ICLK (1'd0 ),
- 82 .DESCLK (gmii_rx_clk ),
- 83 .RCLK (gmii_rx_clk ),
- 84 .WADDR (3'd0 ),
- 85 .RADDR (3'd0 ),
- 86 .RST (1'b0 ),
- 87 .DO ({gmii_rxd_s[6],gmii_rxd_s[2],nc3})
- 88 );
- 89
- 90 wire [5:0 nc4;
- 91 GTP_ISERDES #(
- 92 .ISERDES_MODE ("IDDR"),
- 93 //"IDDR","IMDDR","IGDES4","IMDES4","IGDES7","IGDES8","IMDES8"
- 94 .GRS_EN ("TRUE"), //"TRUE"; "FALSE"
- 95 .LRS_EN ("TRUE") //"TRUE"; "FALSE"
- 96 ) igddr4(
- 97 .DI (rgmii_rxd[3]),
- 98 .ICLK (1'd0 ),
- 99 .DESCLK (gmii_rx_clk ),
- 100 .RCLK (gmii_rx_clk ),
- 101 .WADDR (3'd0 ),
- 102 .RADDR (3'd0 ),
- 103 .RST (1'b0 ),
- 104 .DO ({gmii_rxd_s[7],gmii_rxd_s[3],nc4})
- 105 );
- 106
- 107 wire [5:0 nc5;
- 108 GTP_ISERDES #(
- 109 .ISERDES_MODE ("IDDR"),
- 110 //"IDDR","IMDDR","IGDES4","IMDES4","IGDES7","IGDES8","IMDES8"
- 111 .GRS_EN ("TRUE"), //"TRUE"; "FALSE"
- 112 .LRS_EN ("TRUE") //"TRUE"; "FALSE"
- 113 ) igddr5(
- 114 .DI (rgmii_rx_ctl),
- 115 .ICLK (1'd0 ),
- 116 .DESCLK (gmii_rx_clk ),
- 117 .RCLK (gmii_rx_clk ),
- 118 .WADDR (3'd0 ),
- 119 .RADDR (3'd0 ),
- 120 .RST (1'b0 ),
- 121 .DO ({rgmii_rx_ctl_s,gmii_rx_dv_s,nc5})
- 122 );
- 123
- 124 endmodule
复制代码该模块通过调用GTP_ISERDES原语,实现了RGMII接口输入的DDR数据到SDR数据的转换,输入的rgmii_rx_ctl控制信号的转换方法同样类似。rgmii_rx模块信号转换示意图如下图所示: 图 50.4.2 rgmii_rx模块信号转换示意图 时钟专用引脚输入的rgmii_rxc时钟经过PLL后,得到rgmii_rx_clk。 rgmii_rx_ctl控制信号和4位rgmii_rxd数据先经过GTP_ISERDES(IDDR模式)将双沿1位数据转换成单沿两位数据。 另外,在程序的第40行至122行代码是对GTP_ISERDES的五次例化。 rgmii_tx模块代码如下所示: - 1 module rgmii_tx(
- 2 input reset,
- 3 //GMII发送端口
- 4 input gmii_tx_er ,
- 5 input gmii_tx_clk , //GMII发送时钟
- 6 input gmii_tx_en , //GMII输出数据有效信号
- 7 input [7:0 gmii_txd , //GMII输出数据
- 8 input gmii_tx_clk_deg, //GMII发送时钟相位偏移45度
- 9 //RGMII发送端口
- 10 output rgmii_txc , //RGMII发送数据时钟
- 11 output rgmii_tx_ctl , //RGMII输出数据有效信号
- 12 output [3:0 rgmii_txd //RGMII输出数据
- 13 );
- 14 // registers
- 15 reg tx_reset_d1 ;
- 16 reg tx_reset_sync ;
- 17 reg rx_reset_d1 ;
- 18
- 19 reg [7:0 gmii_txd_r ;
- 20 reg [7:0 gmii_txd_r_d1 ;
- 21
- 22 reg gmii_tx_en_r ;
- 23 reg gmii_tx_en_r_d1;
- 24
- 25 reg gmii_tx_er_r ;
- 26
- 27 reg rgmii_tx_ctl_r ;
- 28 reg [3:0 gmii_txd_low ;
- 29
- 30 // wire
- 31 wire padt1 ;
- 32 wire padt2 ;
- 33 wire padt3 ;
- 34 wire padt4 ;
- 35 wire padt5 ;
- 36 wire padt6 ;
- 37 wire stx_txc ;
- 38 wire stx_ctr ;
- 39 wire [3:0 stxd_rgm;
- 40 //*****************************************************
- 41 //** main code
- 42 //*****************************************************
- 43 always @(posedge gmii_tx_clk) begin
- 44 tx_reset_d1 <=reset;
- 45 tx_reset_sync <= tx_reset_d1;
- 46 end
- 47
- 48 always @(posedge gmii_tx_clk) begin
- 49 if (tx_reset_sync == 1'b1) begin
- 50 gmii_txd_r <=8'h0;
- 51 gmii_tx_en_r <= 1'b0;
- 52 gmii_tx_er_r <= 1'b0;
- 53 end
- 54 else
- 55 begin
- 56 gmii_txd_r <= gmii_txd;
- 57 gmii_tx_en_r <= gmii_tx_en;
- 58 gmii_tx_er_r <= gmii_tx_er;
- 59 gmii_txd_r_d1 <=gmii_txd_r;
- 60 gmii_tx_en_r_d1 <= gmii_tx_en_r;
- 61 end
- 62 end
- 63
- 64 always @(posedge gmii_tx_clk)
- 65 begin
- 66 rgmii_tx_ctl_r = gmii_tx_en_r ^ gmii_tx_er_r;
- 67 gmii_txd_low =gmii_txd_r[7:4];
- 68 end
- 69
- 70 //输出双沿采样寄存器 (rgmii_txd)
- 71 GTP_OSERDES #(
- 72 .OSERDES_MODE ("ODDR" ),
- 73 //"ODDR","OMDDR","OGSER4","OMSER4","OGSER7","OGSER8",OMSER8"
- 74 .WL_EXTEND ("FALSE"), //"TRUE"; "FALSE"
- 75 .GRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 76 .LRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 77 .TSDDR_INIT (1'b0 ) //1'b0;1'b1
- 78 ) gtp_ogddr6(
- 79 .DO (stx_txc ),
- 80 .TQ (padt6 ),
- 81 .DI ({7'd0,1'b1} ),
- 82 .TI (4'd0 ),
- 83 .RCLK (gmii_tx_clk_deg),
- 84 .SERCLK (gmii_tx_clk_deg),
- 85 .OCLK (1'd0 ),
- 86 .RST (tx_reset_sync )
- 87 );
- 88 GTP_OUTBUFT gtp_outbuft6
- 89 (
- 90 .I (stx_txc ),
- 91 .T (padt6 ),
- 92 .O (rgmii_txc)
- 93 );
- 94
- 95
- 96 GTP_OSERDES #(
- 97 .OSERDES_MODE ("ODDR" ),
- 98 //"ODDR","OMDDR","OGSER4","OMSER4","OGSER7","OGSER8",OMSER8"
- 99 .WL_EXTEND ("FALSE"), //"TRUE"; "FALSE"
- 100 .GRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 101 .LRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 102 .TSDDR_INIT (1'b0 ) //1'b0;1'b1
- 103 ) gtp_ogddr2(
- 104 .DO (stxd_rgm[3 ),
- 105 .TQ (padt2 ),
- 106 .DI ({6'd0,gmii_txd_low[3],gmii_txd_r_d1[3]}),
- 107 .TI (4'd0 ),
- 108 .RCLK (gmii_tx_clk ),
- 109 .SERCLK (gmii_tx_clk ),
- 110 .OCLK (1'd0 ),
- 111 .RST (tx_reset_sync)
- 112 );
- 113 GTP_OUTBUFT gtp_outbuft2
- 114 (
- 115 .I (stxd_rgm[3]),
- 116 .T (padt2 ),
- 117 .O (rgmii_txd[3])
- 118 );
- 119
- 120
- 121 GTP_OSERDES #(
- 122 .OSERDES_MODE ("ODDR" ),
- 123 //"ODDR","OMDDR","OGSER4","OMSER4","OGSER7","OGSER8",OMSER8"
- 124 .WL_EXTEND ("FALSE"), //"TRUE"; "FALSE"
- 125 .GRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 126 .LRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 127 .TSDDR_INIT (1'b0 ) //1'b0;1'b1
- 128 ) gtp_ogddr3(
- 129 .DO (stxd_rgm[2 ),
- 130 .TQ (padt3 ),
- 131 .DI ({6'd0,gmii_txd_low[2],gmii_txd_r_d1[2]}),
- 132 .TI (4'd0 ),
- 133 .RCLK (gmii_tx_clk ),
- 134 .SERCLK (gmii_tx_clk ),
- 135 .OCLK (1'd0 ),
- 136 .RST (tx_reset_sync)
- 137 );
- 138 GTP_OUTBUFT gtp_outbuft3
- 139 (
- 140 .I (stxd_rgm[2]),
- 141 .T (padt3 ),
- 142 .O (rgmii_txd[2])
- 143 );
- 144
- 145
- 146 GTP_OSERDES #(
- 147 .OSERDES_MODE ("ODDR" ),
- 148 //"ODDR","OMDDR","OGSER4","OMSER4","OGSER7","OGSER8",OMSER8"
- 149 .WL_EXTEND ("FALSE"), //"TRUE"; "FALSE"
- 150 .GRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 151 .LRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 152 .TSDDR_INIT (1'b0 ) //1'b0;1'b1
- 153 ) gtp_ogddr4(
- 154 .DO (stxd_rgm[1 ),
- 155 .TQ (padt4 ),
- 156 .DI ({6'd0,gmii_txd_low[1],gmii_txd_r_d1[1]}),
- 157 .TI (4'd0 ),
- 158 .RCLK (gmii_tx_clk ),
- 159 .SERCLK (gmii_tx_clk ),
- 160 .OCLK (1'd0 ),
- 161 .RST (tx_reset_sync)
- 162 );
- 163 GTP_OUTBUFT gtp_outbuft4
- 164 (
- 165 .I (stxd_rgm[1]),
- 166 .T (padt4 ),
- 167 .O (rgmii_txd[1])
- 168 );
- 169
- 170
- 171 GTP_OSERDES #(
- 172 .OSERDES_MODE ("ODDR" ),
- 173 //"ODDR","OMDDR","OGSER4","OMSER4","OGSER7","OGSER8",OMSER8"
- 174 .WL_EXTEND ("FALSE"), //"TRUE"; "FALSE"
- 175 .GRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 176 .LRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 177 .TSDDR_INIT (1'b0 ) //1'b0;1'b1
- 178 ) gtp_ogddr5(
- 179 .DO (stxd_rgm[0 ),
- 180 .TQ (padt5 ),
- 181 .DI ({6'd0,gmii_txd_low[0],gmii_txd_r_d1[0]}),
- 182 .TI (4'd0 ),
- 183 .RCLK (gmii_tx_clk ),
- 184 .SERCLK (gmii_tx_clk ),
- 185 .OCLK (1'd0 ),
- 186 .RST (tx_reset_sync)
- 187 );
- 188 GTP_OUTBUFT gtp_outbuft5
- 189 (
- 190 .I (stxd_rgm[0]),
- 191 .T (padt5 ),
- 192 .O (rgmii_txd[0])
- 193 );
- 194
- 195
- 196 //输出双沿采样寄存器 (rgmii_tx_ctl)
- 197 GTP_OSERDES #(
- 198 .OSERDES_MODE ("ODDR" ),
- 199 //"ODDR","OMDDR","OGSER4","OMSER4","OGSER7","OGSER8",OMSER8"
- 200 .WL_EXTEND ("FALSE"), //"TRUE"; "FALSE"
- 201 .GRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 202 .LRS_EN ("TRUE" ), //"TRUE"; "FALSE"
- 203 .TSDDR_INIT (1'b0 ) //1'b0;1'b1
- 204 ) gtp_ogddr1(
- 205 .DO (stx_ctr ),
- 206 .TQ (padt1 ),
- 207 .DI ({6'd0,rgmii_tx_ctl_r,gmii_tx_en_r_d1}),
- 208 .TI (4'd0 ),
- 209 .RCLK (gmii_tx_clk ),
- 210 .SERCLK (gmii_tx_clk ),
- 211 .OCLK (1'd0 ),
- 212 .RST (tx_reset_sync)
- 213 );
- 214 GTP_OUTBUFT gtp_outbuft1
- 215 (
- 216 .I (stx_ctr ),
- 217 .T (padt1 ),
- 218 .O (rgmii_tx_ctl)
- 219 );
- 220
- 221 endmodule
复制代码该模块通过调用ODDR原语将输入的单沿8位数据(gmii_txd)转换成双沿采样的4位数据(rgmii_txd),gmii_tx_en和rgmii_tx_ctl信号的处理方法同样类似。rgmii_tx模块信号转换示意图如下图所示: 图 50.4.3 rgmii_tx模块信号转换示意图 gmii_tx_en数据使能信号、gmii_tx_er输出数据有效信号和8位gmii_txd数据经过GTP_OSERDES的ODDR模式将单沿2位数据转换成双沿1位数据。GTP_OSERDES通常跟GTP_OUTBUF一起使用。 ARP顶层模块实现了整个以太网帧格式与ARP协议的功能,ARP顶层模块例化了ARP接收模块(arp_rx)、ARP发送模块(arp_tx)和CRC校验模块(crc32_d8)。 ARP接收模块(arp_rx):ARP接收模块负责解析以太网的数据,判断目的MAC地址和目的IP地址是否为开发板的地址,然后按照ARP协议将数据解析出来。当解析到正确的ARP数据包后,拉高arp_rx_done信号,持续一个时钟周期。arp_rx_type用于表示ARP数据包的类型,0表示收到ARP请求包,1表示收到ARP应答包。src_mac和src_ip分别是解析出的对端设备MAC地址和IP地址。 ARP发送模块(arp_tx):ARP发送模块根据以太网帧格式和ARP协议发送ARP请求或者ARP应答数据。arp_tx_en和arp_tx_type分别表示ARP发送模块的使能信号和发送ARP类型。dec_mac和dec_ip分别设置对端设备MAC地址和IP地址。 CRC校验模块(crc32_d8):CRC校验模块是对ARP发送模块的数据(不包括前导码和帧起始界定符)做校验,把校验结果值拼在以太网帧格式的FCS字段,如果CRC校验值计算错误或者没有的话,那么电脑网卡会直接丢弃该帧导致收不到数据(有些网卡是可以设置不做校验的)。CRC32校验在FPGA实现的原理是LFSR(Linear Feedback Shift Register,线性反馈移位寄存器),其思想是各个寄存器储存着上一次CRC32运算的结果,寄存器的输出即为CRC32的值。需要说明的是,本次实验只对发送模块做校验,没有对接收模块做校验。这是由于我们可以直接通过解析出的数据来大致判断接收是否正确,而发送模块必须发送正确的校验数据,否则发送的数据直接被电脑的网卡丢弃,导致ARP请求或者应答失败。 在简介部分我们向大家介绍过,ARP的数据包格式包括前导码+SFD、以太网帧头、ARP数据(包括填充部分数据)和CRC校验。在接收以太网数据的过程中,这些不同部分的数据可以刚好对应状态机的不同状态位,因此我们可以通过状态机来解析以太网的数据。 ARP接收模块通过状态机来解析数据,其状态跳转图如下图所示: 接收模块使用三段式状态机来解析以太网包,从上图可以比较直观的看到每个状态实现的功能以及跳转到下一个状态的条件。这里需要注意的一点是,在中间状态如前导码错误、MAC地址错误以及IP地址等错误时跳转到st_rx_end状态,而不是跳转到st_idle状态。因为中间状态在解析到数据错误时,单包数据的接收还没有结束,如果此时跳转到st_idle状态会误把有效数据当成前导码来解析,所以状态跳转到st_rx_end。而gmii_rx_dv信号为0时,单包数据才算接收结束,所以st_rx_end跳转到st_idle的条件是eth_rxdv=0,准备接收下一包数据。因为代码较长,只粘贴了第三段状态机的接收ARP数据状态和接收结束状态源代码,代码如下: - 186 st_arp_data : begin
- 187 if(gmii_rx_dv) begin
- 188 cnt <= cnt + 5'd1;
- 189 if(cnt == 5'd6)
- 190 op_data[15:8] <= gmii_rxd; //操作码
- 191 else if(cnt == 5'd7)
- 192 op_data[7:0] <= gmii_rxd;
- 193 else if(cnt >= 5'd8 && cnt < 5'd14) //源MAC地址
- 194 src_mac_t <= {src_mac_t[39:0],gmii_rxd};
- 195 else if(cnt >= 5'd14 && cnt < 5'd18) //源IP地址
- 196 src_ip_t<= {src_ip_t[23:0],gmii_rxd};
- 197 else if(cnt >= 5'd24 && cnt < 5'd28) //目标IP地址
- 198 des_ip_t <= {des_ip_t[23:0],gmii_rxd};
- 199 else if(cnt == 5'd28) begin
- 200 cnt <= 5'd0;
- 201 if(des_ip_t == BOARD_IP) begin //判断目的IP地址和操作码
- 202 if((op_data == 16'd1) || (op_data == 16'd2)) begin
- 203 skip_en <= 1'b1;
- 204 rx_done_t <= 1'b1;
- 205 src_mac <= src_mac_t;
- 206 src_ip <= src_ip_t;
- 207 src_mac_t <= 48'd0;
- 208 src_ip_t <= 32'd0;
- 209 des_mac_t <= 48'd0;
- 210 des_ip_t <= 32'd0;
- 211 if(op_data == 16'd1)
- 212 arp_rx_type <= 1'b0; //ARP请求
- 213 else
- 214 arp_rx_type <= 1'b1; //ARP应答
- 215 end
- 216 else
- 217 error_en <= 1'b1;
- 218 end
- 219 else
- 220 error_en <= 1'b1;
- 221 end
- 222 end
- 223 end
- 224 st_rx_end : begin
- 225 cnt <= 5'd0;
- 226 //单包数据接收完成
- 227 if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
- 228 skip_en <= 1'b1;
- 229 end
复制代码st_arp_data状态根据ARP协议解析数据,在程序的第201行至第202行代码判断目的IP地址和OP操作码是否正确,如果错误,则丢弃该包数据。在程序的第211行至第214行代码根据操作码为arp_rx_type(接收到的ARP数据包类型)赋值,当接收到ARP请求包时,arp_rx_type等于0;当接收到ARP应答包时,arp_rx_type等于1。 ARP接收过程中仿真的波形图如图 50.4.5所示,gmii_rx_dv信号拉高表示此时输入的数据有效,根据gmii_rxd的值来解析数据。从图中可以看出,发送端的MAC地址和ip地址,以及当前接收到的以太网数据包类型为ARP(0x0806)。在接收完ARP数据包之后,拉高arp_rx_done信号表示接收完成,图中arp_rx_type信号为低电平,表示当前接收到的是ARP请求数据包。 arp的仿真代码如下图所示: - 1 module tb_arp;
- 2
- 3 //parameter define
- 4 parameter T = 8; //时钟周期为8ns
- 5 parameter OP_CYCLE = 100; //操作周期
- 6
- 7 //开发板MAC地址00-11-22-33-44-55
- 8 parameter BOARD_MAC = 48'h00_11_22_33_44_55;
- 9 //开发板IP地址192.168.1.10
- 10 parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
- 11 //目的MAC地址ff_ff_ff_ff_ff_ff
- 12 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
- 13 //目的IP地址192.168.1.10
- 14 parameter DES_IP = {8'd192,8'd168,8'd1,8'd10};
- 15
- 16 //reg define
- 17 reg gmii_clk; //时钟信号
- 18 reg sys_rst_n; //复位信号
- 19
- 20 reg arp_tx_en ; //ARP发送使能信号
- 21 reg arp_tx_type; //ARP发送类型 0:请求 1:应答
- 22 reg [3:0 flow_cnt ;
- 23 reg [13:0 delay_cnt ;
- 24
- 25 wire gmii_rx_clk; //GMII接收时钟
- 26 wire gmii_rx_dv ; //GMII接收数据有效信号
- 27 wire [7:0 gmii_rxd ; //GMII接收数据
- 28 wire gmii_tx_clk; //GMII发送时钟
- 29 wire gmii_tx_en ; //GMII发送数据使能信号
- 30 wire [7:0 gmii_txd ; //GMII发送数据
- 31
- 32 wire arp_rx_done; //ARP接收完成信号
- 33 wire arp_rx_type; //ARP接收类型 0:请求 1:应答
- 34 wire [47:0 src_mac ; //接收到目的MAC地址
- 35 wire [31:0 src_ip ; //接收到目的IP地址
- 36 wire [47:0 des_mac ; //发送的目标MAC地址
- 37 wire [31:0 des_ip ; //发送的目标IP地址
- 38 wire tx_done ; //以太网发送完成信号
- 39
- 40 //*****************************************************
- 41 //** main code
- 42 //*****************************************************
- 43
- 44 assign gmii_rx_clk = gmii_clk ;
- 45 assign gmii_tx_clk = gmii_clk ;
- 46 assign gmii_rx_dv = gmii_tx_en ;
- 47 assign gmii_rxd = gmii_txd ;
- 48
- 49 assign des_mac = src_mac;
- 50 assign des_ip = src_ip;
- 51
- 52 //给输入信号初始值
- 53 initial begin
- 54 gmii_clk = 1'b0;
- 55 sys_rst_n = 1'b0; //复位
- 56 #(T+1) sys_rst_n = 1'b1; //在第(T+1)ns的时候复位信号信号拉高
- 57 end
- 58
- 59 //125Mhz的时钟,周期则为1/125Mhz=8ns,所以每4ns,电平取反一次
- 60 always #(T/2) gmii_clk = ~gmii_clk;
- 61
- 62 always @(posedge gmii_clk or negedge sys_rst_n) begin
- 63 if(!sys_rst_n) begin
- 64 arp_tx_en <= 1'b0;
- 65 arp_tx_type <= 1'b0;
- 66 delay_cnt <= 1'b0;
- 67 flow_cnt <= 1'b0;
- 68 end
- 69 else begin
- 70 case(flow_cnt)
- 71 'd0 : flow_cnt <= flow_cnt + 1'b1;
- 72 'd1 : begin
- 73 arp_tx_en <= 1'b1;
- 74 arp_tx_type <= 1'b0; //发送ARP请求
- 75 flow_cnt <= flow_cnt + 1'b1;
- 76 end
- 77 'd2 : begin
- 78 arp_tx_en <= 1'b0;
- 79 flow_cnt <= flow_cnt + 1'b1;
- 80 end
- 81 'd3 : begin
- 82 if(tx_done)
- 83 flow_cnt <= flow_cnt + 1'b1;
- 84 end
- 85 'd4 : begin
- 86 delay_cnt <= delay_cnt + 1'b1;
- 87 if(delay_cnt == OP_CYCLE - 1'b1)
- 88 flow_cnt <= flow_cnt + 1'b1;
- 89 end
- 90 'd5 : begin
- 91 arp_tx_en <= 1'b1;
- 92 arp_tx_type <= 1'b1; //发送ARP应答
- 93 flow_cnt <= flow_cnt + 1'b1;
- 94 end
- 95 'd6 : begin
- 96 arp_tx_en <= 1'b0;
- 97 flow_cnt <= flow_cnt + 1'b1;
- 98 end
- 99 'd7 : begin
- 100 if(tx_done)
- 101 flow_cnt <= flow_cnt + 1'b1;
- 102 end
- 103 default:;
- 104 endcase
- 105 end
- 106 end
- 107
- 108 //ARP通信
- 109 arp
- 110 #(
- 111 .BOARD_MAC (BOARD_MAC), //参数例化
- 112 .BOARD_IP (BOARD_IP ),
- 113 .DES_MAC (DES_MAC ),
- 114 .DES_IP (DES_IP )
- 115 )
- 116 u_arp(
- 117 .rst_n (sys_rst_n ),
- 118
- 119 .gmii_rx_clk (gmii_rx_clk),
- 120 .gmii_rx_dv (gmii_rx_dv ),
- 121 .gmii_rxd (gmii_rxd ),
- 122 .gmii_tx_clk (gmii_tx_clk),
- 123 .gmii_tx_en (gmii_tx_en ),
- 124 .gmii_txd (gmii_txd ),
- 125
- 126 .arp_rx_done (arp_rx_done),
- 127 .arp_rx_type (arp_rx_type),
- 128 .src_mac (src_mac ),
- 129 .src_ip (src_ip ),
- 130 .arp_tx_en (arp_tx_en ),
- 131 .arp_tx_type (arp_tx_type),
- 132 .des_mac (des_mac ),
- 133 .des_ip (des_ip ),
- 134 .tx_done (tx_done )
- 135 );
- 136
- 137 endmodule
复制代码第14行代码将目的ip地址改写成与开发板的ip地址一样,因为仿真的原理是arp的环回,既将arp的发送数据直接发送给arp的接收数据,如果两个ip地址不一样error_en信号会拉高报错。 ARP发送模块则是根据以太网帧格式是ARP协议发送数据,也就是接收模块的逆过程,同样也非常适合使用状态机来完成发送数据的功能,状态跳转图如下图所示: 发送模块和接收模块有很多相似之处,同样使用三段式状态机来发送以太网包,从上图可以比较直观的看到每个状态实现的功能以及跳转到下一个状态的条件。 发送模块的代码中定义了数组来存储前导码+帧头、以太网的帧头、ARP数据,在复位时初始化数组的值,部分源代码如下。 - 66 reg [7:0] preamble[7:0] ; //前导码+SFD
- 67 reg [7:0] eth_head[13:0]; //以太网首部
- 68 reg [7:0] arp_data[27:0]; //ARP数据
复制代码省略部分代码…… - 155 //初始化数组
- 156 //前导码 7个8'h55 + 1个8'hd5
- 157 preamble[0] <= 8'h55;
- 158 preamble[1] <= 8'h55;
- 159 preamble[2] <= 8'h55;
- 160 preamble[3] <= 8'h55;
- 161 preamble[4] <= 8'h55;
- 162 preamble[5] <= 8'h55;
- 163 preamble[6] <= 8'h55;
- 164 preamble[7] <= 8'hd5;
- 165 //以太网帧头
- 166 eth_head[0] <= DES_MAC[47:40]; //目的MAC地址
- 167 eth_head[1] <= DES_MAC[39:32];
- 168 eth_head[2] <= DES_MAC[31:24];
- 169 eth_head[3] <= DES_MAC[23:16];
- 170 eth_head[4] <= DES_MAC[15:8];
- 171 eth_head[5] <= DES_MAC[7:0];
- 172 eth_head[6] <= BOARD_MAC[47:40]; //源MAC地址
- 173 eth_head[7] <= BOARD_MAC[39:32];
- 174 eth_head[8] <= BOARD_MAC[31:24];
- 175 eth_head[9] <= BOARD_MAC[23:16];
- 176 eth_head[10] <= BOARD_MAC[15:8];
- 177 eth_head[11] <= BOARD_MAC[7:0];
- 178 eth_head[12] <= ETH_TYPE[15:8]; //以太网帧类型
- 179 eth_head[13] <= ETH_TYPE[7:0];
- 180 //ARP数据
- 181 arp_data[0] <= HD_TYPE[15:8]; //硬件类型
- 182 arp_data[1] <= HD_TYPE[7:0];
- 183 arp_data[2] <= PROTOCOL_TYPE[15:8]; //上层协议类型
- 184 arp_data[3] <= PROTOCOL_TYPE[7:0];
- 185 arp_data[4] <= 8'h06; //硬件地址长度,6
- 186 arp_data[5] <= 8'h04; //协议地址长度,4
- 187 arp_data[6] <= 8'h00; //OP,操作码 8'h01:ARP请求 8'h02:ARP应答
- 188 arp_data[7] <= 8'h01;
- 189 arp_data[8] <= BOARD_MAC[47:40]; //发送端(源)MAC地址
- 190 arp_data[9] <= BOARD_MAC[39:32];
- 191 arp_data[10] <= BOARD_MAC[31:24];
- 192 arp_data[11] <= BOARD_MAC[23:16];
- 193 arp_data[12] <= BOARD_MAC[15:8];
- 194 arp_data[13] <= BOARD_MAC[7:0];
- 195 arp_data[14] <= BOARD_IP[31:24]; //发送端(源)IP地址
- 196 arp_data[15] <= BOARD_IP[23:16];
- 197 arp_data[16] <= BOARD_IP[15:8];
- 198 arp_data[17] <= BOARD_IP[7:0];
- 199 arp_data[18] <= DES_MAC[47:40]; //接收端(目的)MAC地址
- 200 arp_data[19] <= DES_MAC[39:32];
- 201 arp_data[20] <= DES_MAC[31:24];
- 202 arp_data[21] <= DES_MAC[23:16];
- 203 arp_data[22] <= DES_MAC[15:8];
- 204 arp_data[23] <= DES_MAC[7:0];
- 205 arp_data[24] <= DES_IP[31:24]; //接收端(目的)IP地址
- 206 arp_data[25] <= DES_IP[23:16];
- 207 arp_data[26] <= DES_IP[15:8];
- 208 arp_data[27] <= DES_IP[7:0];
- 以上代码在复位时对数组进行初始化。
- 216 st_idle : begin
- 217 if(pos_tx_en) begin
- 218 skip_en <= 1'b1;
- 219 //如果目标MAC地址和IP地址已经更新,则发送正确的地址
- 220 if((des_mac != 48'b0) || (des_ip != 32'd0)) begin
- 221 eth_head[0] <= des_mac[47:40];
- 222 eth_head[1] <= des_mac[39:32];
- 223 eth_head[2] <= des_mac[31:24];
- 224 eth_head[3] <= des_mac[23:16];
- 225 eth_head[4] <= des_mac[15:8];
- 226 eth_head[5] <= des_mac[7:0];
- 227 arp_data[18] <= des_mac[47:40];
- 228 arp_data[19] <= des_mac[39:32];
- 229 arp_data[20] <= des_mac[31:24];
- 230 arp_data[21] <= des_mac[23:16];
- 231 arp_data[22] <= des_mac[15:8];
- 232 arp_data[23] <= des_mac[7:0];
- 233 arp_data[24] <= des_ip[31:24];
- 234 arp_data[25] <= des_ip[23:16];
- 235 arp_data[26] <= des_ip[15:8];
- 236 arp_data[27] <= des_ip[7:0];
- 237 end
- 238 if(arp_tx_type == 1'b0)
- 239 arp_data[7] <= 8'h01; //ARP请求
- 240 else
- 241 arp_data[7] <= 8'h02; //ARP应答
- 242 end
- 243 end
复制代码在程序的第220行至241行代码,根据输入的发送类型、目的MAC地址和IP地址,重新更新数组里的值。 - 265 st_arp_data : begin //发送ARP数据
- 266 crc_en <= 1'b1;
- 267 gmii_tx_en <= 1'b1;
- 268 //至少发送46个字节
- 269 if (cnt == MIN_DATA_NUM - 1'b1) begin
- 270 skip_en <= 1'b1;
- 271 cnt <= 1'b0;
- 272 data_cnt <= 1'b0;
- 273 end
- 274 else
- 275 cnt <= cnt + 1'b1;
- 276 if(data_cnt <=6'd27) begin
- 277 data_cnt <= data_cnt + 1'b1;
- 278 gmii_txd <= arp_data[data_cnt];
- 279 end
- 280 else
- 281 gmii_txd <= 8'd0; //Padding,填充0
- 282 end
复制代码程序第265行至第282行代码为发送ARP数据的状态。我们前面讲过以太网帧格式的数据部分最少是46个字节,ARP数据只有28个字节,因此在发送完ARP数据之后补充发送18个字节,填充的数据为0。 - 283 st_crc : begin //发送CRC校验值
- 284 gmii_tx_en <= 1'b1;
- 285 cnt <= cnt + 1'b1;
- 286 if(cnt == 6'd0)
- 287 gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
- 288 ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
- 289 else if(cnt == 6'd1)
- 290 gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],
- 291 ~crc_data[19], ~crc_data[20], ~crc_data[21],
- 292 ~crc_data[22],~crc_data[23]};
- 293 else if(cnt == 6'd2) begin
- 294 gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],
- 295 ~crc_data[11],~crc_data[12], ~crc_data[13],
- 296 ~crc_data[14],~crc_data[15]};
- 297 end
- 298 else if(cnt == 6'd3) begin
- 299 gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
- 300 ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
- 301 tx_done_t <= 1'b1;
- 302 skip_en <= 1'b1;
- 303 cnt <= 1'b0;
- 304 end
- 305 end
复制代码程序的第283行至305行代码为发送CRC校验值状态,发送模块的CRC校验是由crc32_d8模块完成的,发送模块将输入的crc的计算结果每4位高低位互换,按位取反发送出去,crc计算部分在后面阐述。 ARP发送过程中采集的波形图如图 50.4.7所示,arp_tx_en信号作为开始发送ARP数据包的触发信号,arp_tx_type为高电平,表示发送ARP应答数据包。ARP应答数据包中的目的MAC地址和目的IP地址从ARP接收数据包中获取,gmii_tx_en拉高,表示gmii_txd数据有效,在发送完ARP数据包后,输出一个脉冲信号(tx_done),表示发送完成。 CRC校验模块主要完成对ARP发送模块数据的校验,由于代码较长,这里不再贴出代码。CRC32校验在FPGA实现的原理是线性反馈移位寄存器,其思想是各个寄存器储存着上一次CRC32运算的结果,寄存器的输出即为CRC32的值。CRC32的原理与公式推导较复杂,在此可不必深究。CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 +x^7 + x^5 + x^4 + x^2 + x^1 + 1,只需稍作修改就可以直接使用。 ARP控制模块的代码如下: - 1 module arp_ctrl(
- 2 input clk , //输入时钟
- 3 input rst_n , //复位信号,低电平有效
- 4
- 5 input touch_key , //触摸按键,用于触发开发板发出ARP请求
- 6 input arp_rx_done, //ARP接收完成信号
- 7 input arp_rx_type, //ARP接收类型 0:请求 1:应答
- 8 output reg arp_tx_en , //ARP发送使能信号
- 9 output reg arp_tx_type //ARP发送类型0:请求 1:应答
- 10 );
- 11
- 12 //reg define
- 13 reg touch_key_d0;
- 14 reg touch_key_d1;
- 15
- 16 //wire define
- 17 wire pos_touch_key; //touch_key信号上升沿
- 18
- 19 //*****************************************************
- 20 //** main code
- 21 //*****************************************************
- 22
- 23 assign pos_touch_key = ~touch_key_d1 & touch_key_d0;
- 24
- 25 //对arp_tx_en信号延时打拍两次,用于采touch_key的上升沿
- 26 always @(posedge clk or negedge rst_n) begin
- 27 if(!rst_n) begin
- 28 touch_key_d0 <= 1'b0;
- 29 touch_key_d1 <= 1'b0;
- 30 end
- 31 else begin
- 32 touch_key_d0 <= touch_key;
- 33 touch_key_d1 <= touch_key_d0;
- 34 end
- 35 end
- 36
- 37 //为arp_tx_en和arp_tx_type赋值
- 38 always @(posedge clk or negedge rst_n) begin
- 39 if(!rst_n) begin
- 40 arp_tx_en <= 1'b0;
- 41 arp_tx_type <= 1'b0;
- 42 end
- 43 else begin
- 44 if(pos_touch_key == 1'b1) begin //检测到输入触摸按键上升沿
- 45 arp_tx_en <= 1'b1;
- 46 arp_tx_type <= 1'b0;
- 47 end
- 48 //接收到ARP请求,开始控制ARP发送模块应答
- 49 else if((arp_rx_done == 1'b1) && (arp_rx_type == 1'b0)) begin
- 50 arp_tx_en <= 1'b1;
- 51 arp_tx_type <= 1'b1;
- 52 end
- 53 else
- 54 arp_tx_en <= 1'b0;
- 55 end
- 56 end
- 57
- 58 endmodule
复制代码ARP控制模块的代码较简单,首先检测输入触摸按键的上升沿,当检测到上升沿之后,触发ARP顶层模块发起ARP请求信号;同时检测输入的arp_rx_done和arp_rx_type信号,当接收上位机的ARP请求信号后,触发ARP顶层模块发送ARP应答信号,将开发板的MAC地址发送给上位机。
1.5 下载验证编译工程并生成比特流.sbit文件后,此时将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,将网线一端连接开发板的网口,另一端连接电脑的网口或者路由器,接下来连接电源线,并打开开发板的电源开关,网口的位置如下图所示。 点击PDS工具栏的下载按钮,在弹出的Fabric Configuration界面中双击“Boundary Scan”,我们将生成好的sbit流文件下载到开发板中去。 程序下载完成后,PHY芯片会和电脑网卡进行通信(自协商),如果程序下载正确并且硬件连接无误的话,我们点击电脑右下角的网络图标,会看到本地连接刚开始显示的是正在识别,一段时间之后显示未识别的网络,打开方式如下图所示(WIN7和WIN10操作可能存在差异,但基本相同)。 点击图 50.5.2中的“未识别的网络(无Internet)”,弹出如下图所示界面。 点击“更改适配器”选项,弹出如下图所示界面。 如果看到上图“以太网”显示未识别的网络之后,说明硬件连接和程序都是没有问题的,接下来设置以太网的IP地址,改成代码中设置的目的IP地址,顶层模块参数定义如下: - //目的IP地址 192.168.1.102
- parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
复制代码因此接下来将电脑以太网的IP地址设置成192.168.1.102。鼠标右击图 50.5.4中的以太网,如下图所示: 点击“属性”,弹出如下图所示界面。 鼠标双击“Internet协议版本4(TCP/IPv4)”,弹出如下图所示界面。 在“Internet协议版本4(TCP/IPv4)”属性界面中,选择使用下面的IP地址,IP地址设置成192.168.1.102,并点击确定完成设置。 接下来以管理员身份打开电脑的命令的DOS命令窗口(注意必须以管理员身份打开),打开方式如下: 打开DOS命令窗口后,在命令行中输入“arp -a”,如下图所示: 输入完成后,按下键盘的回车键,此时会弹出电脑中所有网络接口的ARP缓存表,我们只需要关注以太网接口的ARP缓存表(IP地址为192.168.1.102),如下图所示: 可以发现,此时ARP缓存表中还没有开发板的MAC地址和IP地址,此时我们按下开发板的触摸按键(TPAD)。按下后,开发板会向电脑发起ARP请求,并且电脑会返回自己的MAC地址到开发板。 需要说明的是,在开发板发起ARP请求时,会将开发板的MAC地址和IP地址都发给电脑,此时电脑就已经获取到了开发板的MAC地址和IP地址,并更新至ARP的缓存表中,我们重新在DOS命令中输入“arp -a”,如图 50.5.11和图 50.5.12所示。 图 50.5.12 开发板的MAC地址更新至缓存表中 由上图可知,此时以太网接口的ARP缓存表中已经添加了开发板的IP地址(192.168.1.10)和MAC地址(00-11-22-33-44-55),说明开发板发送ARP请求成功。如果大家操作失败,请检查开发板的网口是否通过网线连接电脑的网口,并且此时开发板已经下载程序以及通过按下触摸按键(TPAD)来触发ARP请求。另外,如果电脑网口不支持千兆网通信,那么也会导致ARP操作失败。 接下来我们再来通过电脑发起ARP请求,验证开发板有没有正确返回ARP应答。我们先从以太网ARP缓存表中删除开发板的MAC地址,删除方法是在DOS命令中输入“arp -d”,并按下按键的回车键,如下图所示: 接下来重新在DOS命令中输入“arp -a”,来验证是否删除成功(如果没有以管理员运行会导致删除失败),输入完成后,如下图所示; 此时我们之前获取的开发板MAC地址已经删除成功,接下来在DOS命令中输入“ping 192.168.1.10”,来让电脑发起ARP请求,如下图所示。 需要说明的是,ping是一个十分强大的TCP/IP工具,它可以用来检测网络的连通情况和分析网络速度。ping命令是一个固定格式的ICMP(Internet控制报文协议)请求数据包,之后会发起ARP请求命令,所以我们这里是通过ping命令来间接发起ARP请求。由于开发板并没有实现ICMP协议,因此在ping时会请求超时,但是在ping的过程中发起的ARP请求,开发板会响应并返回ARP应答数据。 接下来再次在DOS命令中输入“arp -a”,查询是否成功获取到开发板MAC地址,如下图所示: 由上图可知,电脑正确获取到开发板的MAC地址,并更新至ARP缓存表中。到这里,开发板实现的ARP协议就已经全部验证成功了。 接下来介绍一个以太网通信时经常使用的抓包软件,该软件位于开发板所随附的资料“6_软件资料/1_软件/Wireshark”目录下,也可以直接在网上搜索下载,我们现在打开Wireshark,界面如下图所示: 双击上图所示的以太网或者先选中以太网,再点击上方红框选中的蓝色按钮,即可开始抓取本地连接的数据包,抓取界面如下图所示: 图 50.5.18 wireshark以太网打开界面 从上图可以看到,已经抓取到其它应用程序使用以太网发送的数据包,但是这些数据包并不是开发板发送的数据包,我们这个时候按下开发板的触摸按键(TPAD),就可以在wireshark中抓取到数据包了,抓取到的数据包如下图所示: 图 50.5.19 wireshark抓取到的数据包 上图中第38行数据包是开发板发送给电脑的ARP请求包,第39行数据包是电脑发送给开发板的ARP应答包,此时双击第38行即可看到开发板发送的详细数据,如下图所示: 图 50.5.20 wireshark抓取到的详细数据 上图中下方红框为开发板发送的16进制数据(去掉前导码、SFD和CRC值),可以看到,后面的18个0就是我们在发送时填充的18个字节数据。需要说明的是,当打开第39行电脑返回的ARP请求包时,看不到填充的0,这是由于后面填充的数据是网卡自动填充的,因此wireshark中会看不到。
|