• 以太网ARP测试实验


    1.1 ARP测试整体框架

            当上位机发送ARP请求时,FPGA返回ARP应答数据;当按下FPGA的触摸按键时,FPGA发送ARP请求,上位机返回ARP应答数据。

     PLL时钟对eth_rxc的输入时钟进行相位调整;GMII TO RGMI 模块负责将双沿(DDR)数据和单沿(SDR)数据之间的转换;ARP顶层模块实现了以太网ARP数据包的接收、发送以及CRC校验的功能;ARP控制模块根据输入的按键触摸信号和接收到的ARP请求信号,控制ARP顶层模块发送ARP请求或者ARP应答。

    1. module eth_arp_test(
    2. input sys_rst_n , //系统复位信号,低电平有效
    3. input touch_key , //触摸按键,用于触发开发板发出ARP请求
    4. //以太网RGMII接口
    5. input eth_rxc , //RGMII接收数据时钟
    6. input eth_rx_ctl, //RGMII输入数据有效信号
    7. input [3:0] eth_rxd , //RGMII输入数据
    8. output eth_txc , //RGMII发送数据时钟
    9. output eth_tx_ctl, //RGMII输出数据有效信号
    10. output [3:0] eth_txd , //RGMII输出数据
    11. output eth_rst_n //以太网芯片复位信号,低电平有效
    12. );
    13. //parameter define
    14. //开发板MAC地址 00-11-22-33-44-55
    15. parameter BOARD_MAC = 48'h00_11_22_33_44_55;
    16. //开发板IP地址 192.168.1.10
    17. parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
    18. //目的MAC地址 ff_ff_ff_ff_ff_ff 广播MAC地址,收到上位机的请求或应答后ARP模块会替换成真正的目的MAC地址
    19. parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
    20. //目的IP地址 192.168.1.102 需要与电脑的以太网IP地址一致
    21. parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
    22. //wire define
    23. wire eth_rxc_deg ; //eth_rxc时钟相位偏移
    24. wire gmii_rx_clk ; //GMII接收时钟
    25. wire gmii_rx_dv ; //GMII接收数据有效信号
    26. wire [7:0] gmii_rxd ; //GMII接收数据
    27. wire gmii_tx_clk ; //GMII发送时钟
    28. wire gmii_tx_en ; //GMII发送数据使能信号
    29. wire [7:0] gmii_txd ; //GMII发送数据
    30. wire arp_gmii_tx_en; //ARP GMII输出数据有效信号
    31. wire [7:0] arp_gmii_txd ; //ARP GMII输出数据
    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 arp_tx_en ; //ARP发送使能信号
    37. wire arp_tx_type ; //ARP发送类型 0:请求 1:应答
    38. wire [47:0] des_mac ; //发送的目标MAC地址
    39. wire [31:0] des_ip ; //发送的目标IP地址
    40. wire arp_tx_done ; //ARP发送完成信号
    41. //*****************************************************
    42. //** main code
    43. //*****************************************************
    44. assign des_mac = src_mac; //将收到的目的MAC地址和IP地址,作为FPGA发送时的目的MAC地址和IP地址
    45. assign des_ip = src_ip;
    46. assign eth_rst_n = sys_rst_n;
    47. //PLL
    48. pll u_pll(
    49. .inclk0 (eth_rxc), //相位偏移180度
    50. .c0 (eth_rxc_deg),
    51. .locked ()
    52. );
    53. //GMII接口转RGMII接口
    54. gmii_to_rgmii u_gmii_to_rgmii(
    55. .gmii_rx_clk (gmii_rx_clk ),
    56. .gmii_rx_dv (gmii_rx_dv ),
    57. .gmii_rxd (gmii_rxd ),
    58. .gmii_tx_clk (gmii_tx_clk ),
    59. .gmii_tx_en (gmii_tx_en ),
    60. .gmii_txd (gmii_txd ),
    61. .rgmii_rxc (eth_rxc_deg ),
    62. .rgmii_rx_ctl (eth_rx_ctl ),
    63. .rgmii_rxd (eth_rxd ),
    64. .rgmii_txc (eth_txc ),
    65. .rgmii_tx_ctl (eth_tx_ctl ),
    66. .rgmii_txd (eth_txd )
    67. );
    68. //ARP通信
    69. arp
    70. #(
    71. .BOARD_MAC (BOARD_MAC), //参数例化
    72. .BOARD_IP (BOARD_IP ),
    73. .DES_MAC (DES_MAC ),
    74. .DES_IP (DES_IP )
    75. )
    76. u_arp(
    77. .rst_n (sys_rst_n ),
    78. .gmii_rx_clk (gmii_rx_clk),
    79. .gmii_rx_dv (gmii_rx_dv ),
    80. .gmii_rxd (gmii_rxd ),
    81. .gmii_tx_clk (gmii_tx_clk),
    82. .gmii_tx_en (gmii_tx_en ),
    83. .gmii_txd (gmii_txd ),
    84. .arp_rx_done (arp_rx_done),
    85. .arp_rx_type (arp_rx_type),
    86. .src_mac (src_mac ),
    87. .src_ip (src_ip ),
    88. .arp_tx_en (arp_tx_en ),
    89. .arp_tx_type (arp_tx_type),
    90. .des_mac (des_mac ),
    91. .des_ip (des_ip ),
    92. .tx_done (tx_done )
    93. );
    94. //ARP控制
    95. arp_ctrl u_arp_ctrl(
    96. .clk (gmii_rx_clk),
    97. .rst_n (sys_rst_n),
    98. .touch_key (touch_key),
    99. .arp_rx_done (arp_rx_done),
    100. .arp_rx_type (arp_rx_type),
    101. .arp_tx_en (arp_tx_en),
    102. .arp_tx_type (arp_tx_type)
    103. );
    104. endmodule

    1.2 GMII TO RGMII模块

    1. //实现双沿(DDR)和单沿(SDR)数据之间的转换
    2. module gmii_to_rgmii(
    3. //以太网GMII接口
    4. output gmii_rx_clk , //GMII接收时钟
    5. output gmii_rx_dv , //GMII接收数据有效信号
    6. output [7:0] gmii_rxd , //GMII接收数据
    7. output gmii_tx_clk , //GMII发送时钟
    8. input gmii_tx_en , //GMII发送数据使能信号
    9. input [7:0] gmii_txd , //GMII发送数据
    10. //以太网RGMII接口
    11. input rgmii_rxc , //RGMII接收时钟
    12. input rgmii_rx_ctl , //RGMII接收数据控制信号
    13. input [3:0] rgmii_rxd , //RGMII接收数据
    14. output rgmii_txc , //RGMII发送时钟
    15. output rgmii_tx_ctl , //RGMII发送数据控制信号
    16. output [3:0] rgmii_txd //RGMII发送数据
    17. );
    18. //*****************************************************
    19. //** main code
    20. //*****************************************************
    21. assign gmii_tx_clk = gmii_rx_clk; //将GMII接收时钟赋值给GMII发送时钟,因此GMII的发送时钟和接收时钟为同一时钟
    22. //RGMII接收
    23. rgmii_rx u_rgmii_rx(
    24. .rgmii_rxc (rgmii_rxc ),
    25. .rgmii_rx_ctl (rgmii_rx_ctl),
    26. .rgmii_rxd (rgmii_rxd ),
    27. .gmii_rx_dv (gmii_rx_dv ),
    28. .gmii_rxd (gmii_rxd ),
    29. .gmii_rx_clk (gmii_rx_clk)
    30. );
    31. //RGMII发送
    32. rgmii_tx u_rgmii_tx(
    33. .gmii_tx_clk (gmii_tx_clk ),
    34. .gmii_tx_en (gmii_tx_en ),
    35. .gmii_txd (gmii_txd ),
    36. .rgmii_txc (rgmii_txc ),
    37. .rgmii_tx_ctl (rgmii_tx_ctl),
    38. .rgmii_txd (rgmii_txd )
    39. );
    40. endmodule

    关于 ALTDDIO_IN、ALTDDIO_OUT这两个IP核,可以参考:Altera(Quartus) IP核 ALTDDIO_IN、ALTDDIO_OUT(双倍数据速率I/O,DDIO)的使用和仿真_孤独的单刀的博客-CSDN博客

    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. //以太网GMII接口
    7. output gmii_rx_clk , //GMII接收时钟
    8. output gmii_rx_dv , //GMII接收数据有效信号
    9. output [7:0] gmii_rxd //GMII接收数据
    10. );
    11. //wire define
    12. wire [1:0] gmii_rxdv_t; //两位GMII接收有效信号
    13. //*****************************************************
    14. //** main code
    15. //*****************************************************
    16. assign gmii_rx_clk = rgmii_rxc;
    17. assign gmii_rx_dv = gmii_rxdv_t[0] & gmii_rxdv_t[1];
    18. //通过ALTDDIO_IN这个IP实现双沿(DDR)和单沿(SDR)数据之间的转换
    19. ddi_x4 ddi_x4_inst(
    20. .datain (rgmii_rxd ), //datain,DDR输入端口
    21. .inclock (rgmii_rxc ), //inclock,DDR输入数据的采样时钟信号,在时钟信号的每个时钟边缘上对数据端口进行采样
    22. .dataout_h (gmii_rxd[7:4]), //dataout_h,在inclock时钟上升沿输出的数据
    23. .dataout_l (gmii_rxd[3:0]) //dataout_l,在inclock时钟下降沿输出的数据
    24. );
    25. ddi_x1 ddi_x1_inst(
    26. .datain (rgmii_rx_ctl),
    27. .inclock (rgmii_rxc ),
    28. .dataout_h (gmii_rxdv_t[1]),
    29. .dataout_l (gmii_rxdv_t[0])
    30. );
    31. endmodule

    Width是代表输入端的位宽,RGMII接口是4位输入,所以这里将其设为4
    Asynchronous clear and asynchronous set ports选项是表示端口是否需要复位,这里选择 Not used
    Use'inclocken’ports表示是否使用输入时钟使能,这里选择不使用
    Invert input clock表示输入时钟上升边缘上的第一个数据位是否被捕获。如果不启用,则在输入时钟的下降沿上捕获数据的第一个位,启用表示在输入时钟的上升沿上捕获数据的第一个位,这里选择不使用

    1. module rgmii_tx(
    2. //GMII发送端口
    3. input gmii_tx_clk , //GMII发送时钟
    4. input gmii_tx_en , //GMII输出数据有效信号
    5. input [7:0] gmii_txd , //GMII输出数据
    6. //RGMII发送端口
    7. output rgmii_txc , //RGMII发送数据时钟
    8. output rgmii_tx_ctl, //RGMII输出数据有效信号
    9. output [3:0] rgmii_txd //RGMII输出数据
    10. );
    11. //*****************************************************
    12. //** main code
    13. //*****************************************************
    14. //通过ALTDDIO OUT这个IP实现SDR与DDR之间的转换
    15. ddo_x4 ddo_x4_inst(
    16. .datain_h(gmii_txd[3:0]),
    17. .datain_l(gmii_txd[7:4]),
    18. .outclock(gmii_tx_clk),
    19. .dataout(rgmii_txd)
    20. );
    21. ddo_x1 ddo_x1_inst(
    22. .datain_h(gmii_tx_en),
    23. .datain_l(gmii_tx_en ),
    24. .outclock(gmii_tx_clk),
    25. .dataout(rgmii_tx_ctl)
    26. );
    27. ddo_x1 ddo_x1_clk(
    28. .datain_h(1'b1),
    29. .datain_l(1'b0),
    30. .outclock(gmii_tx_clk),
    31. .dataout(rgmii_txc)
    32. );
    33. endmodule

    1.3 ARP模块 

    1. module arp(
    2. input rst_n , //复位信号,低电平有效
    3. //GMII接口
    4. input gmii_rx_clk, //GMII接收数据时钟
    5. input gmii_rx_dv , //GMII输入数据有效信号
    6. input [7:0] gmii_rxd , //GMII输入数据
    7. input gmii_tx_clk, //GMII发送数据时钟
    8. output gmii_tx_en , //GMII输出数据有效信号
    9. output [7:0] gmii_txd , //GMII输出数据
    10. //用户接口
    11. output arp_rx_done, //ARP接收完成信号
    12. output arp_rx_type, //ARP接收类型 0:请求 1:应答
    13. output [47:0] src_mac , //接收到目的MAC地址
    14. output [31:0] src_ip , //接收到目的IP地址
    15. input arp_tx_en , //ARP发送使能信号
    16. input arp_tx_type, //ARP发送类型 0:请求 1:应答
    17. input [47:0] des_mac , //发送的目标MAC地址
    18. input [31:0] des_ip , //发送的目标IP地址
    19. output tx_done //以太网发送完成信号
    20. );
    21. //parameter define
    22. //开发板MAC地址 00-11-22-33-44-55
    23. parameter BOARD_MAC = 48'h00_11_22_33_44_55;
    24. //开发板IP地址 192.168.1.10
    25. parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
    26. //目的MAC地址 ff_ff_ff_ff_ff_ff 广播MAC地址,收到上位机的请求或应答后ARP模块会替换成真正的目的MAC地址
    27. parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
    28. //目的IP地址 192.168.1.102 需要与电脑的以太网IP地址一致
    29. parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
    30. //wire define
    31. wire crc_en ; //CRC开始校验使能
    32. wire crc_clr ; //CRC数据复位信号
    33. wire [7:0] crc_d8 ; //输入待校验8位数据
    34. wire [31:0] crc_data; //CRC校验数据
    35. wire [31:0] crc_next; //CRC下次校验完成数据
    36. //*****************************************************
    37. //** main code
    38. //*****************************************************
    39. assign crc_d8 = gmii_txd;
    40. //ARP接收模块
    41. arp_rx
    42. #(
    43. .BOARD_MAC (BOARD_MAC), //参数例化
    44. .BOARD_IP (BOARD_IP )
    45. )
    46. u_arp_rx(
    47. .clk (gmii_rx_clk),
    48. .rst_n (rst_n),
    49. .gmii_rx_dv (gmii_rx_dv),
    50. .gmii_rxd (gmii_rxd ),
    51. .arp_rx_done (arp_rx_done),
    52. .arp_rx_type (arp_rx_type),
    53. .src_mac (src_mac ),
    54. .src_ip (src_ip )
    55. );
    56. //ARP发送模块
    57. arp_tx
    58. #(
    59. .BOARD_MAC (BOARD_MAC), //参数例化
    60. .BOARD_IP (BOARD_IP ),
    61. .DES_MAC (DES_MAC ),
    62. .DES_IP (DES_IP )
    63. )
    64. u_arp_tx(
    65. .clk (gmii_tx_clk),
    66. .rst_n (rst_n),
    67. .arp_tx_en (arp_tx_en ),
    68. .arp_tx_type (arp_tx_type),
    69. .des_mac (des_mac ),
    70. .des_ip (des_ip ),
    71. .crc_data (crc_data ),
    72. .crc_next (crc_next[31:24]),
    73. .tx_done (tx_done ),
    74. .gmii_tx_en (gmii_tx_en),
    75. .gmii_txd (gmii_txd ),
    76. .crc_en (crc_en ),
    77. .crc_clr (crc_clr )
    78. );
    79. //以太网发送CRC校验模块
    80. crc32_d8 u_crc32_d8(
    81. .clk (gmii_tx_clk),
    82. .rst_n (rst_n ),
    83. .data (crc_d8 ),
    84. .crc_en (crc_en ),
    85. .crc_clr (crc_clr ),
    86. .crc_data (crc_data ),
    87. .crc_next (crc_next )
    88. );
    89. endmodule

    1.3.1 ARP接收模块 

    1. //ARP接收模块负责解析以太网的数据,判断目的MAC地址和目的IP地址是否为开发板的地址,然后按照ARP协议将数据解析出来
    2. module arp_rx
    3. #(
    4. //开发板MAC地址 00-11-22-33-44-55
    5. parameter BOARD_MAC = 48'h00_11_22_33_44_55,
    6. //开发板IP地址 192.168.1.10
    7. parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10}
    8. )
    9. (
    10. input clk , //时钟信号
    11. input rst_n , //复位信号,低电平有效
    12. input gmii_rx_dv , //GMII输入数据有效信号
    13. input [7:0] gmii_rxd , //GMII输入数据
    14. output reg arp_rx_done, //ARP接收完成信号
    15. output reg arp_rx_type, //ARP接收类型 0:请求 1:应答
    16. output reg [47:0] src_mac , //接收到的源MAC地址
    17. output reg [31:0] src_ip //接收到的源IP地址
    18. );
    19. //parameter define
    20. localparam st_idle = 5'b0_0001; //初始状态,等待接收前导码
    21. localparam st_preamble = 5'b0_0010; //接收前导码状态
    22. localparam st_eth_head = 5'b0_0100; //接收以太网帧头
    23. localparam st_arp_data = 5'b0_1000; //接收ARP数据
    24. localparam st_rx_end = 5'b1_0000; //接收结束
    25. localparam ETH_TPYE = 16'h0806; //以太网帧类型ARP
    26. //reg define
    27. reg [4:0] cur_state ;
    28. reg [4:0] next_state;
    29. reg skip_en ; //控制状态跳转使能信号
    30. reg error_en ; //解析错误使能信号
    31. reg [4:0] cnt ; //解析数据计数器
    32. reg [47:0] des_mac_t ; //接收到的目的MAC地址
    33. reg [31:0] des_ip_t ; //接收到的目的IP地址
    34. reg [47:0] src_mac_t ; //接收到的源MAC地址
    35. reg [31:0] src_ip_t ; //接收到的源IP地址
    36. reg [15:0] eth_type ; //以太网类型
    37. reg [15:0] op_data ; //操作码
    38. reg rx_done_t ; //ARP接收完成信号
    39. //*****************************************************
    40. //** main code
    41. //*****************************************************
    42. //(三段式状态机)同步时序描述状态转移
    43. always @(posedge clk or negedge rst_n) begin
    44. if(!rst_n)
    45. cur_state <= st_idle;
    46. else
    47. cur_state <= next_state;
    48. end
    49. //组合逻辑判断状态转移条件
    50. always @(*) begin
    51. next_state = st_idle; //默认在空闲状态
    52. case(cur_state)
    53. st_idle : begin //等待接收前导码
    54. if(skip_en) //当控制状态跳转使能信号有效,跳转到接收前导码
    55. next_state = st_preamble;
    56. else
    57. next_state = st_idle; //否则一直保持在空闲状态
    58. end
    59. st_preamble : begin //接收前导码
    60. if(skip_en)
    61. next_state = st_eth_head;
    62. else if(error_en)
    63. //在中间状态如前导码错误、MAC地址错误以及IP地址等错误时跳转到st_rx_end状态,而不是跳转到st_idle状态
    64. //因为中间状态在解析到数据错误时,单包数据的接收还没有结束,如果此时跳转到st_idle状态会误把有效数据当成前导码来解析
    65. next_state = st_rx_end;
    66. else
    67. next_state = st_preamble;
    68. end
    69. st_eth_head : begin //接收以太网帧头
    70. if(skip_en)
    71. next_state = st_arp_data;
    72. else if(error_en)
    73. next_state = st_rx_end; //跳转到st_rx_end状态,而非跳转到st_idle状态
    74. else
    75. next_state = st_eth_head;
    76. end
    77. st_arp_data : begin //接收ARP数据
    78. if(skip_en)
    79. next_state = st_rx_end;
    80. else if(error_en)
    81. next_state = st_rx_end; //跳转到st_rx_end状态,而非跳转到st_idle状态
    82. else
    83. next_state = st_arp_data;
    84. end
    85. st_rx_end : begin //接收结束
    86. if(skip_en)
    87. next_state = st_idle;
    88. else
    89. next_state = st_rx_end;
    90. end
    91. default : next_state = st_idle;
    92. endcase
    93. end
    94. //时序电路描述状态输出,解析以太网数据
    95. always @(posedge clk or negedge rst_n) begin
    96. if(!rst_n) begin
    97. skip_en <= 1'b0;
    98. error_en <= 1'b0;
    99. cnt <= 5'd0;
    100. des_mac_t <= 48'd0;
    101. des_ip_t <= 32'd0;
    102. src_mac_t <= 48'd0;
    103. src_ip_t <= 32'd0;
    104. eth_type <= 16'd0;
    105. op_data <= 16'd0;
    106. rx_done_t <= 1'b0;
    107. arp_rx_type <= 1'b0;
    108. src_mac <= 48'd0;
    109. src_ip <= 32'd0;
    110. end
    111. else begin
    112. skip_en <= 1'b0;
    113. error_en <= 1'b0;
    114. rx_done_t <= 1'b0;
    115. case(next_state)
    116. st_idle : begin //检测到第一个8'h55
    117. if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55)) //gmii_rx_dv信号拉高表示此时有输入的数据,根据gmii_rxd来解析数据
    118. skip_en <= 1'b1;
    119. end
    120. st_preamble : begin
    121. if(gmii_rx_dv) begin //解析前导码
    122. cnt <= cnt + 5'd1;
    123. if((cnt < 5'd6) && (gmii_rxd != 8'h55)) //如果不是连续7个字节的0x55则出现错误
    124. error_en <= 1'b1;
    125. else if(cnt==5'd6) begin //计数到6,即连续7个字节的0x55,前导码正确
    126. cnt <= 5'd0;
    127. if(gmii_rxd==8'hd5) //接下来的数据是8'hd5,说明帧起始定界符正确
    128. skip_en <= 1'b1; //前导码和起始定界符解析完毕,可以跳转
    129. else
    130. error_en <= 1'b1;
    131. end
    132. end
    133. end
    134. st_eth_head : begin //解析帧头
    135. if(gmii_rx_dv) begin
    136. cnt <= cnt + 5'b1;
    137. if(cnt < 5'd6) //目的MAC地址小于6个字节,则将GMII输入数据添加到目标MAC地址寄存器
    138. des_mac_t <= {des_mac_t[39:0],gmii_rxd}; //取des_mac_t的低40位与8位的gmii_rxd拼接,将gmii_rxd放在低八位形成左移
    139. else if(cnt == 5'd6) begin
    140. //判断MAC地址是否为开发板MAC地址或者公共地址
    141. if((des_mac_t != BOARD_MAC)
    142. && (des_mac_t != 48'hff_ff_ff_ff_ff_ff))
    143. error_en <= 1'b1;
    144. end
    145. else if(cnt == 5'd12) //计数到12,即该解析类型/长度
    146. eth_type[15:8] <= gmii_rxd;
    147. else if(cnt == 5'd13) begin
    148. eth_type[7:0] <= gmii_rxd;
    149. cnt <= 5'd0;
    150. if(eth_type[15:8] == ETH_TPYE[15:8] //判断是否为ARP协议
    151. && gmii_rxd == ETH_TPYE[7:0])
    152. skip_en <= 1'b1;
    153. else
    154. error_en <= 1'b1;
    155. end
    156. end
    157. end
    158. st_arp_data : begin //解析数据
    159. if(gmii_rx_dv) begin
    160. cnt <= cnt + 5'd1;
    161. if(cnt == 5'd6)
    162. op_data[15:8] <= gmii_rxd; //操作码
    163. else if(cnt == 5'd7)
    164. op_data[7:0] <= gmii_rxd;
    165. else if(cnt >= 5'd8 && cnt < 5'd14) //源MAC地址
    166. src_mac_t <= {src_mac_t[39:0],gmii_rxd};
    167. else if(cnt >= 5'd14 && cnt < 5'd18) //源IP地址
    168. src_ip_t<= {src_ip_t[23:0],gmii_rxd};
    169. else if(cnt >= 5'd24 && cnt < 5'd28) //目标IP地址
    170. des_ip_t <= {des_ip_t[23:0],gmii_rxd};
    171. else if(cnt == 5'd28) begin
    172. cnt <= 5'd0;
    173. if(des_ip_t == BOARD_IP) begin //判断目的IP地址和OP操作码是否正确
    174. if((op_data == 16'd1) || (op_data == 16'd2)) begin
    175. skip_en <= 1'b1;
    176. rx_done_t <= 1'b1;
    177. src_mac <= src_mac_t;
    178. src_ip <= src_ip_t;
    179. src_mac_t <= 48'd0;
    180. src_ip_t <= 32'd0;
    181. des_mac_t <= 48'd0;
    182. des_ip_t <= 32'd0;
    183. if(op_data == 16'd1)
    184. arp_rx_type <= 1'b0; //ARP请求
    185. else
    186. arp_rx_type <= 1'b1; //ARP应答
    187. end
    188. else
    189. error_en <= 1'b1;
    190. end
    191. else
    192. error_en <= 1'b1;
    193. end
    194. end
    195. end
    196. st_rx_end : begin
    197. cnt <= 5'd0;
    198. //单包数据接收完成
    199. if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
    200. skip_en <= 1'b1;
    201. end
    202. default : ;
    203. endcase
    204. end
    205. end
    206. 当解析到正确的ARP数据包后,拉高arp_rx_done信号,持续一个时钟周期
    207. always @(posedge clk or negedge rst_n) begin
    208. if(!rst_n)
    209. arp_rx_done <= 1'b0;
    210. else
    211. arp_rx_done <= rx_done_t;
    212. end
    213. endmodule

    1.3.2 ARP发送模块 

     

    1. //ARP发送模块根据以太网格式和ARP协议发送ARP请求或者ARP应答数据
    2. module arp_tx(
    3. input clk , //时钟信号
    4. input rst_n , //复位信号,低电平有效
    5. input arp_tx_en , //ARP发送使能信号
    6. input arp_tx_type, //ARP发送类型 0:请求 1:应答
    7. input [47:0] des_mac , //发送的目标MAC地址
    8. input [31:0] des_ip , //发送的目标IP地址
    9. input [31:0] crc_data , //CRC校验数据
    10. input [7:0] crc_next , //CRC下次校验完成数据
    11. output reg tx_done , //以太网发送完成信号
    12. output reg gmii_tx_en , //GMII输出数据有效信号
    13. output reg [7:0] gmii_txd , //GMII输出数据
    14. output reg crc_en , //CRC开始校验使能
    15. output reg crc_clr //CRC数据复位信号
    16. );
    17. //parameter define
    18. //开发板MAC地址 00-11-22-33-44-55
    19. parameter BOARD_MAC = 48'h00_11_22_33_44_55;
    20. //开发板IP地址 192.168.1.10
    21. parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
    22. //目的MAC地址 ff_ff_ff_ff_ff_ff
    23. parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
    24. //目的IP地址 192.168.1.102
    25. parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
    26. localparam st_idle = 5'b0_0001; //初始状态,等待开始发送信号
    27. localparam st_preamble = 5'b0_0010; //发送前导码+帧起始界定符
    28. localparam st_eth_head = 5'b0_0100; //发送以太网帧头
    29. localparam st_arp_data = 5'b0_1000; //发送arp数据
    30. localparam st_crc = 5'b1_0000; //发送CRC校验值
    31. localparam ETH_TYPE = 16'h0806 ; //以太网帧类型 ARP协议
    32. localparam HD_TYPE = 16'h0001 ; //硬件类型 以太网
    33. localparam PROTOCOL_TYPE= 16'h0800 ; //上层协议为IP协议
    34. //以太网数据最小为46个字节,不足部分填充数据
    35. localparam MIN_DATA_NUM = 16'd46 ;
    36. //reg define
    37. reg [4:0] cur_state ;
    38. reg [4:0] next_state ;
    39. reg [7:0] preamble[7:0] ; //前导码+SFD
    40. reg [7:0] eth_head[13:0]; //以太网首部
    41. reg [7:0] arp_data[27:0]; //ARP数据
    42. reg tx_en_d0 ; //arp_tx_en信号延时
    43. reg tx_en_d1 ;
    44. reg skip_en ; //控制状态跳转使能信号
    45. reg [5:0] cnt ;
    46. reg [4:0] data_cnt ; //发送数据个数计数器
    47. reg tx_done_t ;
    48. //wire define
    49. wire pos_tx_en ; //arp_tx_en信号上升沿
    50. //*****************************************************
    51. //** main code
    52. //*****************************************************
    53. assign pos_tx_en = (~tx_en_d1) & tx_en_d0;
    54. //对arp_tx_en信号延时打拍两次,用于采arp_tx_en的上升沿
    55. always @(posedge clk or negedge rst_n) begin
    56. if(!rst_n) begin
    57. tx_en_d0 <= 1'b0;
    58. tx_en_d1 <= 1'b0;
    59. end
    60. else begin
    61. tx_en_d0 <= arp_tx_en;
    62. tx_en_d1 <= tx_en_d0;
    63. end
    64. end
    65. //(三段式状态机)同步时序描述状态转移
    66. always @(posedge clk or negedge rst_n) begin
    67. if(!rst_n)
    68. cur_state <= st_idle;
    69. else
    70. cur_state <= next_state;
    71. end
    72. //组合逻辑判断状态转移条件
    73. always @(*) begin
    74. next_state = st_idle;
    75. case(cur_state)
    76. st_idle : begin //空闲状态
    77. if(skip_en)
    78. next_state = st_preamble;
    79. else
    80. next_state = st_idle;
    81. end
    82. st_preamble : begin //发送前导码+帧起始界定符
    83. if(skip_en)
    84. next_state = st_eth_head;
    85. else
    86. next_state = st_preamble;
    87. end
    88. st_eth_head : begin //发送以太网首部
    89. if(skip_en)
    90. next_state = st_arp_data;
    91. else
    92. next_state = st_eth_head;
    93. end
    94. st_arp_data : begin //发送ARP数据
    95. if(skip_en)
    96. next_state = st_crc;
    97. else
    98. next_state = st_arp_data;
    99. end
    100. st_crc: begin //发送CRC校验值
    101. if(skip_en)
    102. next_state = st_idle;
    103. else
    104. next_state = st_crc;
    105. end
    106. default : next_state = st_idle;
    107. endcase
    108. end
    109. //时序电路描述状态输出,发送以太网数据
    110. always @(posedge clk or negedge rst_n) begin
    111. if(!rst_n) begin
    112. skip_en <= 1'b0;
    113. cnt <= 6'd0;
    114. data_cnt <= 5'd0;
    115. crc_en <= 1'b0;
    116. gmii_tx_en <= 1'b0;
    117. gmii_txd <= 8'd0;
    118. tx_done_t <= 1'b0;
    119. //在复位时初始化数组
    120. //前导码 7个8'h55 + 1个8'hd5
    121. preamble[0] <= 8'h55;
    122. preamble[1] <= 8'h55;
    123. preamble[2] <= 8'h55;
    124. preamble[3] <= 8'h55;
    125. preamble[4] <= 8'h55;
    126. preamble[5] <= 8'h55;
    127. preamble[6] <= 8'h55;
    128. preamble[7] <= 8'hd5;
    129. //以太网帧头
    130. eth_head[0] <= DES_MAC[47:40]; //目的MAC地址
    131. eth_head[1] <= DES_MAC[39:32];
    132. eth_head[2] <= DES_MAC[31:24];
    133. eth_head[3] <= DES_MAC[23:16];
    134. eth_head[4] <= DES_MAC[15:8];
    135. eth_head[5] <= DES_MAC[7:0];
    136. eth_head[6] <= BOARD_MAC[47:40]; //源MAC地址
    137. eth_head[7] <= BOARD_MAC[39:32];
    138. eth_head[8] <= BOARD_MAC[31:24];
    139. eth_head[9] <= BOARD_MAC[23:16];
    140. eth_head[10] <= BOARD_MAC[15:8];
    141. eth_head[11] <= BOARD_MAC[7:0];
    142. eth_head[12] <= ETH_TYPE[15:8]; //以太网帧类型
    143. eth_head[13] <= ETH_TYPE[7:0];
    144. //ARP数据
    145. arp_data[0] <= HD_TYPE[15:8]; //硬件类型
    146. arp_data[1] <= HD_TYPE[7:0];
    147. arp_data[2] <= PROTOCOL_TYPE[15:8]; //上层协议类型
    148. arp_data[3] <= PROTOCOL_TYPE[7:0];
    149. arp_data[4] <= 8'h06; //硬件地址长度,6
    150. arp_data[5] <= 8'h04; //协议地址长度,4
    151. arp_data[6] <= 8'h00; //OP,操作码 8'h01:ARP请求 8'h02:ARP应答
    152. arp_data[7] <= 8'h01;
    153. arp_data[8] <= BOARD_MAC[47:40]; //发送端(源)MAC地址
    154. arp_data[9] <= BOARD_MAC[39:32];
    155. arp_data[10] <= BOARD_MAC[31:24];
    156. arp_data[11] <= BOARD_MAC[23:16];
    157. arp_data[12] <= BOARD_MAC[15:8];
    158. arp_data[13] <= BOARD_MAC[7:0];
    159. arp_data[14] <= BOARD_IP[31:24]; //发送端(源)IP地址
    160. arp_data[15] <= BOARD_IP[23:16];
    161. arp_data[16] <= BOARD_IP[15:8];
    162. arp_data[17] <= BOARD_IP[7:0];
    163. arp_data[18] <= DES_MAC[47:40]; //接收端(目的)MAC地址
    164. arp_data[19] <= DES_MAC[39:32];
    165. arp_data[20] <= DES_MAC[31:24];
    166. arp_data[21] <= DES_MAC[23:16];
    167. arp_data[22] <= DES_MAC[15:8];
    168. arp_data[23] <= DES_MAC[7:0];
    169. arp_data[24] <= DES_IP[31:24]; //接收端(目的)IP地址
    170. arp_data[25] <= DES_IP[23:16];
    171. arp_data[26] <= DES_IP[15:8];
    172. arp_data[27] <= DES_IP[7:0];
    173. end
    174. else begin
    175. skip_en <= 1'b0;
    176. crc_en <= 1'b0;
    177. gmii_tx_en <= 1'b0;
    178. tx_done_t <= 1'b0;
    179. case(next_state)
    180. st_idle : begin
    181. if(pos_tx_en) begin
    182. skip_en <= 1'b1;
    183. //如果目标MAC地址和IP地址已经更新,则发送正确的地址
    184. if((des_mac != 48'b0) || (des_ip != 32'd0)) begin //根据输入的发送类型、目的MAC地址和IP地址,更新数组里的值
    185. eth_head[0] <= des_mac[47:40];
    186. eth_head[1] <= des_mac[39:32];
    187. eth_head[2] <= des_mac[31:24];
    188. eth_head[3] <= des_mac[23:16];
    189. eth_head[4] <= des_mac[15:8];
    190. eth_head[5] <= des_mac[7:0];
    191. arp_data[18] <= des_mac[47:40];
    192. arp_data[19] <= des_mac[39:32];
    193. arp_data[20] <= des_mac[31:24];
    194. arp_data[21] <= des_mac[23:16];
    195. arp_data[22] <= des_mac[15:8];
    196. arp_data[23] <= des_mac[7:0];
    197. arp_data[24] <= des_ip[31:24];
    198. arp_data[25] <= des_ip[23:16];
    199. arp_data[26] <= des_ip[15:8];
    200. arp_data[27] <= des_ip[7:0];
    201. end
    202. if(arp_tx_type == 1'b0)
    203. arp_data[7] <= 8'h01; //ARP请求
    204. else
    205. arp_data[7] <= 8'h02; //ARP应答
    206. end
    207. end
    208. st_preamble : begin //发送前导码+帧起始界定符
    209. gmii_tx_en <= 1'b1;
    210. gmii_txd <= preamble[cnt];
    211. if(cnt == 6'd7) begin
    212. skip_en <= 1'b1;
    213. cnt <= 1'b0;
    214. end
    215. else
    216. cnt <= cnt + 1'b1;
    217. end
    218. st_eth_head : begin //发送以太网首部
    219. gmii_tx_en <= 1'b1;
    220. crc_en <= 1'b1;
    221. gmii_txd <= eth_head[cnt];
    222. if (cnt == 6'd13) begin
    223. skip_en <= 1'b1;
    224. cnt <= 1'b0;
    225. end
    226. else
    227. cnt <= cnt + 1'b1;
    228. end
    229. st_arp_data : begin //发送ARP数据
    230. crc_en <= 1'b1;
    231. gmii_tx_en <= 1'b1;
    232. //至少发送46个字节
    233. if (cnt == MIN_DATA_NUM - 1'b1) begin
    234. skip_en <= 1'b1;
    235. cnt <= 1'b0;
    236. data_cnt <= 1'b0;
    237. end
    238. else
    239. cnt <= cnt + 1'b1;
    240. if(data_cnt <= 6'd27) begin
    241. data_cnt <= data_cnt + 1'b1;
    242. gmii_txd <= arp_data[data_cnt];
    243. end
    244. else
    245. gmii_txd <= 8'd0; //Padding,填充0
    246. end
    247. st_crc : begin //发送CRC校验值
    248. gmii_tx_en <= 1'b1;
    249. cnt <= cnt + 1'b1;
    250. if(cnt == 6'd0) //将输入的crc的计算结果每4位高低位互换
    251. gmii_txd <= {~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
    252. ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
    253. else if(cnt == 6'd1)
    254. gmii_txd <= {~crc_data[16], ~crc_data[17], ~crc_data[18],
    255. ~crc_data[19], ~crc_data[20], ~crc_data[21],
    256. ~crc_data[22],~crc_data[23]};
    257. else if(cnt == 6'd2) begin
    258. gmii_txd <= {~crc_data[8], ~crc_data[9], ~crc_data[10],
    259. ~crc_data[11],~crc_data[12], ~crc_data[13],
    260. ~crc_data[14],~crc_data[15]};
    261. end
    262. else if(cnt == 6'd3) begin
    263. gmii_txd <= {~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
    264. ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
    265. tx_done_t <= 1'b1;
    266. skip_en <= 1'b1;
    267. cnt <= 1'b0;
    268. end
    269. end
    270. default :;
    271. endcase
    272. end
    273. end
    274. //发送完成信号及crc值复位信号
    275. always @(posedge clk or negedge rst_n) begin
    276. if(!rst_n) begin
    277. tx_done <= 1'b0;
    278. crc_clr <= 1'b0;
    279. end
    280. else begin
    281. tx_done <= tx_done_t;
    282. crc_clr <= tx_done_t;
    283. end
    284. end
    285. endmodule

    1.3.3 CRC校验模块 

    1. //CRC校验模块是对ARP发送模块的数据(不包括前导码和起始界定符)做校验,把校验结果值拼在以太网顿格式的FCS字段
    2. //如果CRC校验值计算错误或者没有的话,那么电脑网卡会直接丢弃该倾导致收不到数据
    3. //CRC32校验在FPGA实现的原理是LFSR (Linear Feedback Shift Register,线性反馈移位寄存器)
    4. //其思想是各个寄存器储存着上一次CRC32运算的结果,寄存器的输出即为CRC32的值
    5. //本次实验只对发送模块做校验,没有对接收模块做校验。
    6. //因为可以直接通过解析出的数据来大致判断接收是否正确,而发送模块必须发送正确的校验数据,否则发送的数据直接丢弃,导致ARP请求或者应答失败
    7. //可通过http://www.easics.com/webtools/crctool生成CRC校验的源代码
    8. module crc32_d8(
    9. input clk , //时钟信号
    10. input rst_n , //复位信号,低电平有效
    11. input [7:0] data , //输入待校验8位数据
    12. input crc_en , //crc使能,开始校验标志
    13. input crc_clr , //crc数据复位信号
    14. output reg [31:0] crc_data, //CRC校验数据
    15. output [31:0] crc_next //CRC下次校验完成数据
    16. );
    17. //*****************************************************
    18. //** main code
    19. //*****************************************************
    20. //输入待校验8位数据,需要先将高低位互换
    21. wire [7:0] data_t;
    22. assign data_t = {data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]};
    23. //CRC32的生成多项式为:G(x)= x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11
    24. //+ x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
    25. assign crc_next[0] = crc_data[24] ^ crc_data[30] ^ data_t[0] ^ data_t[6];
    26. assign crc_next[1] = crc_data[24] ^ crc_data[25] ^ crc_data[30] ^ crc_data[31]
    27. ^ data_t[0] ^ data_t[1] ^ data_t[6] ^ data_t[7];
    28. assign crc_next[2] = crc_data[24] ^ crc_data[25] ^ crc_data[26] ^ crc_data[30]
    29. ^ crc_data[31] ^ data_t[0] ^ data_t[1] ^ data_t[2] ^ data_t[6]
    30. ^ data_t[7];
    31. assign crc_next[3] = crc_data[25] ^ crc_data[26] ^ crc_data[27] ^ crc_data[31]
    32. ^ data_t[1] ^ data_t[2] ^ data_t[3] ^ data_t[7];
    33. assign crc_next[4] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28]
    34. ^ crc_data[30] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[4]
    35. ^ data_t[6];
    36. assign crc_next[5] = crc_data[24] ^ crc_data[25] ^ crc_data[27] ^ crc_data[28]
    37. ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[0]
    38. ^ data_t[1] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[6]
    39. ^ data_t[7];
    40. assign crc_next[6] = crc_data[25] ^ crc_data[26] ^ crc_data[28] ^ crc_data[29]
    41. ^ crc_data[30] ^ crc_data[31] ^ data_t[1] ^ data_t[2] ^ data_t[4]
    42. ^ data_t[5] ^ data_t[6] ^ data_t[7];
    43. assign crc_next[7] = crc_data[24] ^ crc_data[26] ^ crc_data[27] ^ crc_data[29]
    44. ^ crc_data[31] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5]
    45. ^ data_t[7];
    46. assign crc_next[8] = crc_data[0] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27]
    47. ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
    48. assign crc_next[9] = crc_data[1] ^ crc_data[25] ^ crc_data[26] ^ crc_data[28]
    49. ^ crc_data[29] ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5];
    50. assign crc_next[10] = crc_data[2] ^ crc_data[24] ^ crc_data[26] ^ crc_data[27]
    51. ^ crc_data[29] ^ data_t[0] ^ data_t[2] ^ data_t[3] ^ data_t[5];
    52. assign crc_next[11] = crc_data[3] ^ crc_data[24] ^ crc_data[25] ^ crc_data[27]
    53. ^ crc_data[28] ^ data_t[0] ^ data_t[1] ^ data_t[3] ^ data_t[4];
    54. assign crc_next[12] = crc_data[4] ^ crc_data[24] ^ crc_data[25] ^ crc_data[26]
    55. ^ crc_data[28] ^ crc_data[29] ^ crc_data[30] ^ data_t[0]
    56. ^ data_t[1] ^ data_t[2] ^ data_t[4] ^ data_t[5] ^ data_t[6];
    57. assign crc_next[13] = crc_data[5] ^ crc_data[25] ^ crc_data[26] ^ crc_data[27]
    58. ^ crc_data[29] ^ crc_data[30] ^ crc_data[31] ^ data_t[1]
    59. ^ data_t[2] ^ data_t[3] ^ data_t[5] ^ data_t[6] ^ data_t[7];
    60. assign crc_next[14] = crc_data[6] ^ crc_data[26] ^ crc_data[27] ^ crc_data[28]
    61. ^ crc_data[30] ^ crc_data[31] ^ data_t[2] ^ data_t[3] ^ data_t[4]
    62. ^ data_t[6] ^ data_t[7];
    63. assign crc_next[15] = crc_data[7] ^ crc_data[27] ^ crc_data[28] ^ crc_data[29]
    64. ^ crc_data[31] ^ data_t[3] ^ data_t[4] ^ data_t[5] ^ data_t[7];
    65. assign crc_next[16] = crc_data[8] ^ crc_data[24] ^ crc_data[28] ^ crc_data[29]
    66. ^ data_t[0] ^ data_t[4] ^ data_t[5];
    67. assign crc_next[17] = crc_data[9] ^ crc_data[25] ^ crc_data[29] ^ crc_data[30]
    68. ^ data_t[1] ^ data_t[5] ^ data_t[6];
    69. assign crc_next[18] = crc_data[10] ^ crc_data[26] ^ crc_data[30] ^ crc_data[31]
    70. ^ data_t[2] ^ data_t[6] ^ data_t[7];
    71. assign crc_next[19] = crc_data[11] ^ crc_data[27] ^ crc_data[31] ^ data_t[3] ^ data_t[7];
    72. assign crc_next[20] = crc_data[12] ^ crc_data[28] ^ data_t[4];
    73. assign crc_next[21] = crc_data[13] ^ crc_data[29] ^ data_t[5];
    74. assign crc_next[22] = crc_data[14] ^ crc_data[24] ^ data_t[0];
    75. assign crc_next[23] = crc_data[15] ^ crc_data[24] ^ crc_data[25] ^ crc_data[30]
    76. ^ data_t[0] ^ data_t[1] ^ data_t[6];
    77. assign crc_next[24] = crc_data[16] ^ crc_data[25] ^ crc_data[26] ^ crc_data[31]
    78. ^ data_t[1] ^ data_t[2] ^ data_t[7];
    79. assign crc_next[25] = crc_data[17] ^ crc_data[26] ^ crc_data[27] ^ data_t[2] ^ data_t[3];
    80. assign crc_next[26] = crc_data[18] ^ crc_data[24] ^ crc_data[27] ^ crc_data[28]
    81. ^ crc_data[30] ^ data_t[0] ^ data_t[3] ^ data_t[4] ^ data_t[6];
    82. assign crc_next[27] = crc_data[19] ^ crc_data[25] ^ crc_data[28] ^ crc_data[29]
    83. ^ crc_data[31] ^ data_t[1] ^ data_t[4] ^ data_t[5] ^ data_t[7];
    84. assign crc_next[28] = crc_data[20] ^ crc_data[26] ^ crc_data[29] ^ crc_data[30]
    85. ^ data_t[2] ^ data_t[5] ^ data_t[6];
    86. assign crc_next[29] = crc_data[21] ^ crc_data[27] ^ crc_data[30] ^ crc_data[31]
    87. ^ data_t[3] ^ data_t[6] ^ data_t[7];
    88. assign crc_next[30] = crc_data[22] ^ crc_data[28] ^ crc_data[31] ^ data_t[4] ^ data_t[7];
    89. assign crc_next[31] = crc_data[23] ^ crc_data[29] ^ data_t[5];
    90. always @(posedge clk or negedge rst_n) begin
    91. if(!rst_n)
    92. crc_data <= 32'hff_ff_ff_ff;
    93. else if(crc_clr) //CRC校验值复位
    94. crc_data <= 32'hff_ff_ff_ff;
    95. else if(crc_en)
    96. crc_data <= crc_next;
    97. end
    98. endmodule

    1.4 ARP控制模块

    ARP控制模块首先检测输入触摸按键的上升沿,当检测到上升沿之后,触发ARP顶层模块发起 ARP请求信号;同时检测输入的arp_rx_done和arp_rx_type_信号,当接收上位机的ARP请求信号后,触发ARP顶层模块发送ARP应答信号,将开发板的MAC地址发送给上位机。

    1. module arp_ctrl(
    2. input clk , //输入时钟
    3. input rst_n , //复位信号,低电平有效
    4. input touch_key , //触摸按键,用于触发开发板发出ARP请求
    5. input arp_rx_done, //ARP接收完成信号
    6. input arp_rx_type, //ARP接收类型 0:请求 1:应答
    7. output reg arp_tx_en , //ARP发送使能信号
    8. output reg arp_tx_type //ARP发送类型 0:请求 1:应答
    9. );
    10. //reg define
    11. reg touch_key_d0;
    12. reg touch_key_d1;
    13. //wire define
    14. wire pos_touch_key; //touch_key信号上升沿
    15. //*****************************************************
    16. //** main code
    17. //*****************************************************
    18. assign pos_touch_key = ~touch_key_d1 & touch_key_d0;
    19. //对arp_tx_en信号延时打拍两次,用于采touch_key的上升沿
    20. always @(posedge clk or negedge rst_n) begin
    21. if(!rst_n) begin
    22. touch_key_d0 <= 1'b0;
    23. touch_key_d1 <= 1'b0;
    24. end
    25. else begin
    26. touch_key_d0 <= touch_key;
    27. touch_key_d1 <= touch_key_d0;
    28. end
    29. end
    30. //为arp_tx_en和arp_tx_type赋值
    31. always @(posedge clk or negedge rst_n) begin
    32. if(!rst_n) begin
    33. arp_tx_en <= 1'b0;
    34. arp_tx_type <= 1'b0;
    35. end
    36. else begin
    37. if(pos_touch_key == 1'b1) begin //检测到输入触摸按键上升沿
    38. arp_tx_en <= 1'b1;
    39. arp_tx_type <= 1'b0;
    40. end
    41. //接收到ARP请求,开始控制ARP发送模块应答
    42. else if((arp_rx_done == 1'b1) && (arp_rx_type == 1'b0)) begin
    43. arp_tx_en <= 1'b1;
    44. arp_tx_type <= 1'b1;
    45. end
    46. else
    47. arp_tx_en <= 1'b0;
    48. end
    49. end
    50. endmodule

    参考文献:

    《开拓者之FPGA开发指南》

    《FPGA Verilog开发实战指南》

  • 相关阅读:
    NANK南卡和松下护眼台灯哪款好?最全面对比两款热门护眼台灯
    智慧电网解决方案-最新全套文件
    数据集笔记:Beijing-BRT-dataset
    基于.NET6的简单三层管理系统
    【时间预测+速度规划+ADRC】移动机器人高精度轨迹跟踪控制
    Docker Compose使用教程
    自动化运维工具——ansible概述和部署
    扩展的以太网
    【牛客刷题】——Python入门 06 条件语句
    Java的集合框架
  • 原文地址:https://blog.csdn.net/STATEABC/article/details/132159648