【【通信协议之UDP的FPGA实现】】

通信协议之UDP的FPGA实现

其中的 FIFO 和 clock_wizard 是官方IP - 200Mhz
并且其中的 icmp 用了一部分的原语

arp_rx.v

module arp_rx
  #(
     //开发板MAC地址 00-11-22-33-44-55
     parameter BOARD_MAC = 48'h00_11_22_33_44_55,
     //开发板IP地址 192.168.1.10
     parameter BOARD_IP = {
   8'd192,8'd168,8'd1,8'd10}
   )
   (
     input                clk        , //时钟信号
     input                rst_n      , //复位信号,低电平有效

     input                gmii_rx_dv , //GMII输入数据有效信号
     input        [7:0]   gmii_rxd   , //GMII输入数据
     output  reg          arp_rx_done, //ARP接收完成信号
     output  reg          arp_rx_type, //ARP接收类型 0:请求  1:应答
     output  reg  [47:0]  src_mac    , //接收到的源MAC地址
     output  reg  [31:0]  src_ip       //接收到的源IP地址
   );

  //parameter define
  localparam  st_idle     = 5'b0_0001; //初始状态,等待接收前导码
  localparam  st_preamble = 5'b0_0010; //接收前导码状态
  localparam  st_eth_head = 5'b0_0100; //接收以太网帧头
  localparam  st_arp_data = 5'b0_1000; //接收ARP数据
  localparam  st_rx_end   = 5'b1_0000; //接收结束

  localparam  ETH_TPYE = 16'h0806;     //以太网帧类型 ARP

  //reg define
  reg    [4:0]   cur_state ;
  reg    [4:0]   next_state;

  reg            skip_en   ; //控制状态跳转使能信号
  reg            error_en  ; //解析错误使能信号
  reg    [4:0]   cnt       ; //解析数据计数器
  reg    [47:0]  des_mac_t ; //接收到的目的MAC地址
  reg    [31:0]  des_ip_t  ; //接收到的目的IP地址
  reg    [47:0]  src_mac_t ; //接收到的源MAC地址
  reg    [31:0]  src_ip_t  ; //接收到的源IP地址
  reg    [15:0]  eth_type  ; //以太网类型
  reg    [15:0]  op_data   ; //操作码

  //*****************************************************
  //**                    main code
  //*****************************************************

  //(三段式状态机)同步时序描述状态转移
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cur_state <= st_idle;
    else
      cur_state <= next_state;
  end

  //组合逻辑判断状态转移条件
  always @(*)
  begin
    next_state = st_idle;
    case(cur_state)
      st_idle :
      begin                     //等待接收前导码
        if(skip_en)
          next_state = st_preamble;
        else
          next_state = st_idle;
      end
      st_preamble :
      begin                 //接收前导码
        if(skip_en)
          next_state = st_eth_head;
        else if(error_en)
          next_state = st_rx_end;
        else
          next_state = st_preamble;
      end
      st_eth_head :
      begin                 //接收以太网帧头
        if(skip_en)
          next_state = st_arp_data;
        else if(error_en)
          next_state = st_rx_end;
        else
          next_state = st_eth_head;
      end
      st_arp_data :
      begin                  //接收ARP数据
        if(skip_en)
          next_state = st_rx_end;
        else if(error_en)
          next_state = st_rx_end;
        else
          next_state = st_arp_data;
      end
      st_rx_end :
      begin                   //接收结束
        if(skip_en)
          next_state = st_idle;
        else
          next_state = st_rx_end;
      end
      default :
        next_state = st_idle;
    endcase
  end

  //时序电路描述状态输出,解析以太网数据
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
    begin
      skip_en <= 1'b0;
      error_en <= 1'b0;
      cnt <= 5'd0;
      des_mac_t <= 48'd0;
      des_ip_t <= 32'd0;
      src_mac_t <= 48'd0;
      src_ip_t <= 32'd0;
      eth_type <= 16'd0;
      op_data <= 16'd0;
      arp_rx_done <= 1'b0;
      arp_rx_type <= 1'b0;
      src_mac <= 48'd0;
      src_ip <= 32'd0;
    end
    else
    begin
      skip_en <= 1'b0;
      error_en <= 1'b0;
      arp_rx_done <= 1'b0;
      case(next_state)
        st_idle :
        begin                                  //检测到第一个8'h55
          if((gmii_rx_dv == 1'b1) && (gmii_rxd == 8'h55))
            skip_en <= 1'b1;
        end
        st_preamble :
        begin
          if(gmii_rx_dv)
          begin                         //解析前导码
            cnt <= cnt + 5'd1;
            if((cnt < 5'd6) && (gmii_rxd != 8'h55))  //7个8'h55
              error_en <= 1'b1;
            else if(cnt==5'd6)
            begin
              cnt <= 5'd0;
              if(gmii_rxd==8'hd5)                  //1个8'hd5
                skip_en <= 1'b1;
              else
                error_en <= 1'b1;
            end
          end
        end
        st_eth_head :
        begin
          if(gmii_rx_dv)
          begin
            cnt <= cnt + 5'b1;
            if(cnt < 5'd6)
              des_mac_t <= {
   des_mac_t[39:0],gmii_rxd};
            else if(cnt == 5'd6)
            begin
              //判断MAC地址是否为开发板MAC地址或者公共地址
              if((des_mac_t != BOARD_MAC)
                  && (des_mac_t != 48'hff_ff_ff_ff_ff_ff))
                error_en <= 1'b1;
            end
            else if(cnt == 5'd12)
              eth_type[15:8] <= gmii_rxd;          //以太网协议类型
            else if(cnt == 5'd13)
            begin
              eth_type[7:0] <= gmii_rxd;
              cnt <= 5'd0;
              if(eth_type[15:8] == ETH_TPYE[15:8]  //判断是否为ARP协议
                  && gmii_rxd == ETH_TPYE[7:0])
                skip_en <= 1'b1;
              else
                error_en <= 1'b1;
            end
          end
        end
        st_arp_data :
        begin
          if(gmii_rx_dv)
          begin
            cnt <= cnt + 5'd1;
            if(cnt == 5'd6)
              op_data[15:8] <= gmii_rxd;           //操作码
            else if(cnt == 5'd7)
              op_data[7:0] <= gmii_rxd;
            else if(cnt >= 5'd8 && cnt < 5'd14)      //源MAC地址
              src_mac_t <= {
   src_mac_t[39:0],gmii_rxd};
            else if(cnt >= 5'd14 && cnt < 5'd18)     //源IP地址
              src_ip_t<= {
   src_ip_t[23:0],gmii_rxd};
            else if(cnt >= 5'd24 && cnt < 5'd28)     //目标IP地址
              des_ip_t <= {
   des_ip_t[23:0],gmii_rxd};
            else if(cnt == 5'd28)
            begin
              cnt <= 5'd0;
              if(des_ip_t == BOARD_IP)
              begin       //判断目的IP地址和操作码
                if((op_data == 16'd1) || (op_data == 16'd2))
                begin
                  skip_en <= 1'b1;
                  arp_rx_done <= 1'b1;
                  src_mac <= src_mac_t;
                  src_ip <= src_ip_t;
                  src_mac_t <= 48'd0;
                  src_ip_t <= 32'd0;
                  des_mac_t <= 48'd0;
                  des_ip_t <= 32'd0;
                  if(op_data == 16'd1)
                    arp_rx_type <= 1'b0;     //ARP请求
                  else
                    arp_rx_type <= 1'b1;     //ARP应答
                end
                else
                  error_en <= 1'b1;
              end
              else
                error_en <= 1'b1;
            end
          end
        end
        st_rx_end :
        begin
          cnt <= 5'd0;
          //单包数据接收完成
          if(gmii_rx_dv == 1'b0 && skip_en == 1'b0)
            skip_en <= 1'b1;
        end
        default :
          ;
      endcase
    end
  end

endmodule

arp_tx.v

module arp_tx(
    input                clk        , //时钟信号
    input                rst_n      , //复位信号,低电平有效

    input                arp_tx_en  , //ARP发送使能信号
    input                arp_tx_type, //ARP发送类型 0:请求  1:应答
    input        [47:0]  des_mac    , //发送的目标MAC地址
    input        [31:0]  des_ip     , //发送的目标IP地址
    input        [31:0]  crc_data   , //CRC校验数据
    input         [7:0]  crc_next   , //CRC下次校验完成数据
    output  reg          tx_done    , //以太网发送完成信号
    output  reg          gmii_tx_en , //GMII输出数据有效信号
    output  reg  [7:0]   gmii_txd   , //GMII输出数据
    output  reg          crc_en     , //CRC开始校验使能
    output  reg          crc_clr      //CRC数据复位信号
  );

  //parameter define
  //开发板MAC地址 00-11-22-33-44-55
  parameter BOARD_MAC = 48'h00_11_22_33_44_55;
  //开发板IP地址 192.168.1.10
  parameter BOARD_IP  = {
   8'd192,8'd168,8'd1,8'd10};
  //目的MAC地址 ff_ff_ff_ff_ff_ff
  parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
  //目的IP地址 192.168.1.102
  parameter  DES_IP    = {
   8'd192,8'd168,8'd1,8'd102};

  localparam  st_idle      = 5'b0_0001; //初始状态,等待开始发送信号
  localparam  st_preamble  = 5'b0_0010; //发送前导码+帧起始界定符
  localparam  st_eth_head  = 5'b0_0100; //发送以太网帧头
  localparam  st_arp_data  = 5'b0_1000; //
  localparam  st_crc       = 5'b1_0000; //发送CRC校验值

  localparam  ETH_TYPE     = 16'h0806 ; //以太网帧类型 ARP协议
  localparam  HD_TYPE      = 16'h0001 ; //硬件类型 以太网
  localparam  PROTOCOL_TYPE= 16'h0800 ; //上层协议为IP协议
  //以太网数据最小为46个字节,不足部分填充数据
  localparam  MIN_DATA_NUM = 16'd46   ;

  //reg define
  reg  [4:0]  cur_state     ;
  reg  [4:0]  next_state    ;

  reg  [7:0]  preamble[7:0] ; //前导码+SFD
  reg  [7:0]  eth_head[13:0]; //以太网首部
  reg  [7:0]  arp_data[27:0]; //ARP数据

  reg         tx_en_d0      ; //arp_tx_en信号延时
  reg         tx_en_d1      ;
  reg         skip_en       ; //控制状态跳转使能信号
  reg  [5:0]  cnt           ;
  reg  [4:0]  data_cnt      ; //发送数据个数计数器
  reg         tx_done_t     ;

  //wire define
  wire        pos_tx_en     ; //arp_tx_en信号上升沿

  //*****************************************************
  //**                    main code
  //*****************************************************

  assign  pos_tx_en = (~tx_en_d1) & tx_en_d0;

  //对arp_tx_en信号延时打拍两次,用于采arp_tx_en的上升沿
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
    begin
      tx_en_d0 <= 1'b0;
      tx_en_d1 <= 1'b0;
    end
    else
    begin
      tx_en_d0 <= arp_tx_en;
      tx_en_d1 <= tx_en_d0;
    end
  end

  //(三段式状态机)同步时序描述状态转移
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cur_state <= st_idle;
    else
      cur_state <= next_state;
  end

  //组合逻辑判断状态转移条件
  always @(*)
  begin
    next_state = st_idle;
    case(cur_state)
      st_idle :
      begin                     //空闲状态
        if(skip_en)
          next_state = st_preamble;
        else
          next_state = st_idle;
      end
      st_preamble :
      begin                 //发送前导码+帧起始界定符
        if(skip_en)
          next_state = st_eth_head;
        else
          next_state = st_preamble;
      end
      st_eth_head :
      begin                 //发送以太网首部
        if(skip_en)
          next_state = st_arp_data;
        else
          next_state = st_eth_head;
      end
      st_arp_data :
      begin                 //发送ARP数据
        if(skip_en)
          next_state = st_crc;
        else
          next_state = st_arp_data;
      end
      st_crc:
      begin                       //发送CRC校验值
        if(skip_en)
          next_state = st_idle;
        else
          next_state = st_crc;
      end
      default :
        next_state = st_idle;
    endcase
  end

  //时序电路描述状态输出,发送以太网数据
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
    begin
      skip_en <= 1'b0;
      cnt <= 6'd0;
      data_cnt <= 5'd0;
      crc_en <= 1'b0;
      gmii_tx_en <= 1'b0;
      gmii_txd <= 8'd0;
      tx_done_t <= 1'b0;

      //初始化数组
      //前导码 7个8'h55 + 1个8'hd5
      preamble[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;
      //以太网帧头
      eth_head[0] <= DES_MAC[47:40];      //目的MAC地址
      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];
      eth_head[6] <= BOARD_MAC[47:40];    //源MAC地址
      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];
      //ARP数据
      arp_data[0] <= HD_TYPE[15:8];       //硬件类型
      arp_data[1] <= HD_TYPE[7:0];
      arp_data[2] <= PROTOCOL_TYPE[15:8]; //上层协议类型
      arp_data[3] <= PROTOCOL_TYPE[7:0];
      arp_data[4] <= 8'h06;               //硬件地址长度,6
      arp_data[5] <= 8'h04;               //协议地址长度,4
      arp_data[6] <= 8'h00;               //OP,操作码 8'h01:ARP请求 8'h02:ARP应答
      arp_data[7] <= 8'h01;
      arp_data[8] <= BOARD_MAC[47:40];    //发送端(源)MAC地址
      arp_data[9] <= BOARD_MAC[39:32];
      arp_data[10] <= BOARD_MAC[31:24];
      arp_data[11] <= BOARD_MAC[23:16];
      arp_data[12] <= BOARD_MAC[15:8];
      arp_data[13] <= BOARD_MAC[7:0];
      arp_data[14] <= BOARD_IP[31:24];    //发送端(源)IP地址
      arp_data[15] <= BOARD_IP[23:16];
      arp_data[16] <= BOARD_IP[15:8];
      arp_data[17] <= BOARD_IP[7:0];
      arp_data[18] <= DES_MAC[47:40];     //接收端(目的)MAC地址
      arp_data[19] <= DES_MAC[39:32];
      arp_data[20] <= DES_MAC[31:24];
      arp_data[21] <= DES_MAC[23:16];
      arp_data[22] <= DES_MAC[15:8];
      arp_data[23] <= DES_MAC[7:0];
      arp_data[24] <= DES_IP[31:24];      //接收端(目的)IP地址
      arp_data[25] <= DES_IP[23:16];
      arp_data[26] <= DES_IP[15:8];
      arp_data[27] <= DES_IP[7:0];
    end
    else
    begin
      skip_en <= 1'b0;
      crc_en <= 1'b0;
      gmii_tx_en <= 1'b0;
      tx_done_t <= 1'b0;
      case(next_state)
        st_idle :
        begin
          if(pos_tx_en)
          begin
            skip_en <= 1'b1;
            //如果目标MAC地址和IP地址已经更新,则发送正确的地址
            if((des_mac != 48'b0) || (des_ip != 32'd0))
            begin
              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];
              arp_data[18] <= des_mac[47:40];
              arp_data[19] <= des_mac[39:32];
              arp_data[20] <= des_mac[31:24];
              arp_data[21] <= des_mac[23:16];
              arp_data[22] <= des_mac[15:8];
              arp_data[23] <= des_mac[7:0];
              arp_data[24] <= des_ip[31:24];
              arp_data[25] <= des_ip[23:16];
              arp_data[26] <= des_ip[15:8];
              arp_data[27] <= des_ip[7:0];
            end
            if(arp_tx_type == 1'b0)
              arp_data[7] <= 8'h01;            //ARP请求
            else
              arp_data[7] <= 8'h02;            //ARP应答
          end
        end
        st_preamble :
        begin                          //发送前导码+帧起始界定符
          gmii_tx_en <= 1'b1;
          gmii_txd <= preamble[cnt];
          if(cnt == 6'd7)
          begin
            skip_en <= 1'b1;
            cnt <= 1'b0;
          end
          else
            cnt <= cnt + 1'b1;
        end
        st_eth_head :
        begin                          //发送以太网首部
          gmii_tx_en <= 1'b1;
          crc_en <= 1'b1;
          gmii_txd <= eth_head[cnt];
          if (cnt == 6'd13)
          begin
            skip_en <= 1'b1;
            cnt <= 1'b0;
          end
          else
            cnt <= cnt + 1'b1;
        end
        st_arp_data :
        begin                          //发送ARP数据
          crc_en <= 1'b1;
          gmii_tx_en <= 1'b1;
          //至少发送46个字节
          if (cnt == MIN_DATA_NUM - 1'b1)
          begin
            skip_en <= 1'b1;
            cnt <= 1'b0;
            data_cnt <= 1'b0;
          end
          else
            cnt <= cnt + 1'b1;
          if(data_cnt <= 6'd27)
          begin
            data_cnt <= data_cnt + 1'b1;
            gmii_txd <= arp_data[data_cnt];
          end
          else
            gmii_txd <= 8'd0;                    //Padding,填充0
        end
        st_crc      :
        begin                          //发送CRC校验值
          gmii_tx_en <= 1'b1;
          cnt <= cnt + 1'b1;
          if(cnt == 6'd0)
            gmii_txd <= {
   ~crc_next[0], ~crc_next[1], ~crc_next[2],~crc_next[3],
                         ~crc_next[4], ~crc_next[5], ~crc_next[6],~crc_next[7]};
          else if(cnt == 6'd1)
            gmii_txd <= {
   ~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(cnt == 6'd2)
          begin
            gmii_txd <= {
   ~crc_data[8], ~crc_data[9], ~crc_data[10],
                         ~crc_data[11],~crc_data[12], ~crc_data[13],
                         ~crc_data[14],~crc_data[15]};
          end
          else if(cnt == 6'd3)
          begin
            gmii_txd <= {
   ~crc_data[0], ~crc_data[1], ~crc_data[2],~crc_data[3],
                         ~crc_data[4], ~crc_data[5], ~crc_data[6],~crc_data[7]};
            tx_done_t <= 1'b1;
            skip_en <= 1'b1;
            cnt <= 1'b0;
          end
        end
        default :
          ;
      endcase
    end
  end

  //发送完成信号及crc值复位信号
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
    begin
      tx_done <= 1'b0;
      crc_clr <= 1'b0;
    end
    else
    begin
      tx_done <= tx_done_t;
      crc_clr <= tx_done_t;
    end
  end

endmodule

arp.v

module arp(
    input                rst_n      , //复位信号,低电平有效
    //GMII接口
    input                gmii_rx_clk, //GMII接收数据时钟
    input                gmii_rx_dv , //GMII输入数据有效信号
    input        [7:0]   gmii_rxd   , //GMII输入数据
    input                gmii_tx_clk, //GMII发送数据时钟
    output               gmii_tx_en , //GMII输出数据有效信号
    output       [7:0]   gmii_txd   , //GMII输出数据

    //用户接口
    output               arp_rx_done, //ARP接收完成信号
    output               arp_rx_type, //ARP接收类型 0:请求  1:应答
    output       [47:0]  src_mac    , //接收到目的MAC地址
    output       [31:0]  src_ip     , //接收到目的IP地址
    input                arp_tx_en  , //ARP发送使能信号
    input                arp_tx_type, //ARP发送类型 0:请求  1:应答
    input        [47:0]  des_mac    , //发送的目标MAC地址
    input        [31:0]  des_ip     , //发送的目标IP地址
    output               tx_done      //以太网发送完成信号
  );

  //parameter define
  //开发板MAC地址 00-11-22-33-44-55
  parameter BOARD_MAC = 48'h00_11_22_33_44_55;
  //开发板IP地址 192.168.1.10
  parameter BOARD_IP  = {
   8'd192,8'd168,8'd1,8'd10};
  //目的MAC地址 ff_ff_ff_ff_ff_ff
  parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;
  //目的IP地址 192.168.1.102
  parameter  DES_IP    = {
   8'd192,8'd168,8'd1,8'd102};

  //wire define
  wire           crc_en  ; //CRC开始校验使能
  wire           crc_clr ; //CRC数据复位信号
  wire   [7:0]   crc_d8  ; //输入待校验8位数据
  wire   [31:0]  crc_data; //CRC校验数据
  wire   [31:0]  crc_next; //CRC下次校验完成数据

  //*****************************************************
  //**                    main code
  //*****************************************************

  assign  crc_d8 = gmii_txd;

  //ARP接收模块
  arp_rx
    #(
      .BOARD_MAC       (BOARD_MAC),         //参数例化
      .BOARD_IP        (BOARD_IP )
    )
    u_arp_rx(
      .clk             (gmii_rx_clk),
      .rst_n           (rst_n),

      .gmii_rx_dv      (gmii_rx_dv),
      .gmii_rxd        (gmii_rxd  ),
      .arp_rx_done     (arp_rx_done),
      .arp_rx_type     (arp_rx_type),
      .src_mac         (src_mac    ),
      .src_ip          (src_ip     )
    );

  //ARP发送模块
  arp_tx
    #(
      .BOARD_MAC       (BOARD_MAC),         //参数例化
      .BOARD_IP        (BOARD_IP ),
      .DES_MAC         (DES_MAC  ),
      .DES_IP          (DES_IP   )
    )
    u_arp_tx(
      .clk             (gmii_tx_clk),
      .rst_n           (rst_n),

      .arp_tx_en       (arp_tx_en ),
      .arp_tx_type     (arp_tx_type),
      .des_mac         (des_mac   ),
      .des_ip          (des_ip    ),
      .crc_data        (crc_data  ),
      .crc_next        (crc_next[31:24]),
      .tx_done         (tx_done   ),
      .gmii_tx_en      (gmii_tx_en),
      .gmii_txd        (gmii_txd  ),
      .crc_en          (crc_en    ),
      .crc_clr         (crc_clr   )
    );

  //以太网发送CRC校验模块
  crc32_d8   u_crc32_d8(
               .clk             (gmii_tx_clk),
               .rst_n           (rst_n      ),
               .data            (crc_d8     ),
               .crc_en          (crc_en     ),
               .crc_clr         (crc_clr    ),
               .crc_data        (crc_data   ),
               .crc_next        (crc_next   )
             );

endmodule

crc_32_d8.v

module crc32_d8(
    input                 clk     ,  //时钟信号
    input                 rst_n   ,  //复位信号,低电平有效
    input         
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值