千兆以太网 TCP, UDP协议, FPGA实现

    xiaoxiao2021-03-25  92

    

    目前TCP协议大多由cpu跑代码实现, 这次用FPGA的纯逻辑实现 , System Verilog编写,

    下面给大家粗略讲一下我的实现方法,

    下面是工程的示意图.

    这个工程由几部分组成, 外部使用了88e1111千兆以太网phy。FPGA内部有几个大的模块,

    顶层模块:

    [plain]   view plain  copy //                                                                          tcpip_hw                                                                                                                              Description                                                            top module                                                                                                                        Author(s):                                                             - bin qiu, qiubin@opencores.org or  chat1@126.com                                                                                                  Copyright (C) 2015                              //     `include "tcpip_hw_defines.sv"          module tcpip_hw(             input clk,         input rst_n,                  output mdc,         inout  mdio,         output phy_rst_n,         output is_link_up,              input [7:0] rx_data,         output logic [7:0] tx_data,              input rx_clk,         input rx_data_valid,         output logic gtx_clk,         input  tx_clk,           output logic tx_en,              er interface       input [7:0] wr_data,           input wr_clk,           input wr_en,           output wr_full,                  output [7:0] rd_data,           input rd_clk,           input rd_en,           output rd_empty     );          wire clk8;     wire clk50;     wire clk125;     wire clk125out;     wire is_1000m;     logic eth_mode;               always @(posedge clk50)         eth_mode <= is_1000m;              assign gtx_clk = clk125out;               pll pll_inst(         .inclk0(clk),         .c0(clk50),         .c1(clk125out),         .c2(clk8)     );          wire mdio_out;     wire mdio_oe;     wire reset_n;          assign mdio = mdio_oe == 1'b1 ? mdio_out : 1'bz;      rst_ctrl rst_ctrl_inst(         .ext_rst(rst_n),         .rst(reset_n),         .clk(clk50)      );          wire is_100m;     wire is_10m;          wire [7:0] tse_addr;     wire tse_wr;     wire [31:0] tse_wr_data;     wire tse_rd;     wire [31:0] tse_rd_data;     wire tse_busy;               headers_if if_headers_rx(clk50);     headers_if if_headers_tx(clk50);     ff_tx_if if_tx(clk50);     ff_rx_if if_rx(clk50);          frame_type_t rx_type;     frame_type_t tx_type;          logic rx_done;     logic [31:0] data_recv;     logic [15:0] data_recv_len;     logic data_recv_valid;     logic data_recv_start;     logic rx_done_clear;     u32_t cur_ripaddr;     u16_t cur_rport;     u16_t rx_header_checksum;     logic need_ack;     logic [7:0] flags;          logic tx_start;     logic [13:0] tx_dword_count;     logic fifo_rdreq;     logic [31:0] fifo_q;     logic fifo_empty;     logic pkt_send_eop;     logic [15:0] data_send_len;          logic [10:0] wr_addr_synced;     rx_ram_in_if if_rx_ram_in();     rx_ram_out_if if_rx_ram_out();     tx_ram_out_if if_tx_ram_out();     tx_ram_in_if if_tx_ram_in();          logic [31:0] local_ipaddr;     logic [31:0] remote_ipaddr;     logic [31:0] remote_port_local_port;     logic [1:0] op_mode;     logic init_done;     simple_mac_top simple_mac_top(           .clk(clk50),         .rst_n(reset_n),                  .mdc(mdc),         .mdio_in(mdio),         .mdio_out(mdio_out),         .mdio_oe(mdio_oe),         .phy_rst_n(phy_rst_n),         .eth_mode(eth_mode),                  .rx_data(rx_data[7:0]),         .tx_data(tx_data[7:0]),         .rx_clk(rx_clk),         .tx_clk(tx_clk),         .clk125out(clk125out),         .rx_data_valid(rx_data_valid),         .tx_en(tx_en),         .reg_addr(tse_addr),         .reg_wr(tse_wr),         .reg_wr_data(tse_wr_data),         .reg_rd(tse_rd),         .reg_rd_data(tse_rd_data),         .reg_busy(tse_busy),                  .ff_rx_clk(clk50),         .ff_rx_data(if_rx.ff_rx_data),         .ff_rx_eop(if_rx.ff_rx_eop),         .ff_rx_sop(if_rx.ff_rx_sop),         .rx_err(if_rx.rx_err),         .ff_rx_dval(if_rx.ff_rx_dval),         .ff_rx_rdy(if_rx.ff_rx_rdy),              .ff_tx_clk(clk50),         .ff_tx_data(if_tx.ff_tx_data),         .ff_tx_eop(if_tx.ff_tx_eop),         .ff_tx_sop(if_tx.ff_tx_sop),         .ff_tx_wren(if_tx.ff_tx_wren),         .ff_tx_rdy(if_tx.ff_tx_rdy),         .*     );               data_source data_source_inst(         .rst_n(reset_n),         .wr_data(wr_data),         .wr_clk(wr_clk),         .wr_en(wr_en),         .wr_full(wr_full),              .rd_data(rd_data),         .rd_clk(rd_clk),         .rd_en(rd_en),         .rd_empty(rd_empty),                  .*     );          rx_ram rx_ram_inst(         .rst_n(reset_n),         .*     );          tx_ram tx_ram_inst(         .rst_n(reset_n),         .overflow_flag(),         .in_flush(),         .*     );          mac_config mac_config_inst (         .clk(clk50),         .rst_n(reset_n),         .*     );          assign pkt_send_eop = if_tx.ff_tx_eop;     logic [3:0] next_parse_state;               mac_rx_path mac_rx_path_inst(         .rst_n(reset_n),         .*     );          mac_tx_path  mac_tx_path_inst (         .rst_n(reset_n),          .next_state(),         .*     );          eth_fsm eth_fsm_inst(         .clk(clk50),         .rst_n(reset_n),         .state_counter(),        .*     );          endmodule     1.与外部phy芯片通信的模块,simple_mac模块。主要功能是通过mdio配置phy, 给发送帧打包(加入preamble,padding和crc32),和接收帧解包。 下面是顶层代码: [plain]   view plain  copy //                                                                          simple_mac_top                                                                                                                        Description                                                            top module of simple mac                                                                                                          Author(s):                                                             - bin qiu, qiubin@opencores.org or  chat1@126.com                                                                                                  Copyright (C) 2015                              //               module simple_mac_top(           input clk,         input rst_n,                  output mdc,         input  mdio_in,         output mdio_out,         output mdio_oe,         output phy_rst_n,              input [7:0] rx_data,         output logic [7:0] tx_data,                  input eth_mode,         input rx_clk,         input tx_clk,         input clk125out,         output tx_en,         input  rx_data_valid,              input [7:0] reg_addr,         input reg_wr,         input [31:0] reg_wr_data,         input reg_rd,         output [31:0] reg_rd_data,         output reg_busy,                  input  ff_rx_clk,         output [31:0] ff_rx_data,         output ff_rx_eop,         output ff_rx_sop,         output rx_err,         output ff_rx_dval,         input  ff_rx_rdy,              input ff_tx_clk,         input [31:0] ff_tx_data,         input ff_tx_eop,         input ff_tx_sop,         input ff_tx_wren,         output ff_tx_rdy     );     assign phy_rst_n = rst_n;          wire [7:0] rx_data_mac;     wire rx_data_valid_mac;     wire rx_sop_mac;     wire tx_data_valid_mac;     wire [7:0] tx_data_mac;               wire [31:0] ff_tx_data0;     wire ff_tx_eop0;     wire ff_tx_sop0;     wire ff_tx_wren0;          wire [31:0] ff_rx_data0;     wire ff_rx_eop0;     wire ff_rx_sop0;     wire ff_rx_dval0;          wire tx_data_en;          tx_header_align32 tx_header_align32_inst(         .ff_tx_clk(ff_tx_clk),         .rst_n(rst_n),         .ff_tx_data0(ff_tx_data),         .ff_tx_eop0(ff_tx_eop),         .ff_tx_sop0(ff_tx_sop),         .ff_tx_wren0(ff_tx_wren),              .ff_tx_data(ff_tx_data0),         .ff_tx_eop(ff_tx_eop0),         .ff_tx_sop(ff_tx_sop0),         .ff_tx_wren(ff_tx_wren0)     );               rx_header_align32 rx_header_align32_inst(         .ff_rx_clk(ff_rx_clk),         .rst_n(rst_n),         .ff_rx_data0(ff_rx_data0),         .ff_rx_eop0(ff_rx_eop0),         .ff_rx_sop0(ff_rx_sop0),         .ff_rx_dval0(ff_rx_dval0),              .ff_rx_data(ff_rx_data),         .ff_rx_eop(ff_rx_eop),         .ff_rx_sop(ff_rx_sop),         .ff_rx_dval(ff_rx_dval)     );     wire mac_tx_clk;          assign mac_tx_clk = clk125out;          simple_mac_rx_gmii rx_gmii_inst(         .rx_clk(rx_clk),         .rst_n(rst_n),         .eth_mode(eth_mode),         .rx_data(rx_data),         .rx_data_valid(rx_data_valid),              .rx_data_mac(rx_data_mac),         .rx_data_valid_mac(rx_data_valid_mac),         .rx_sop_mac(rx_sop_mac)     );          simple_mac_tx_gmii gmii_tx_inst(         .rst_n(rst_n),         .eth_mode(eth_mode),         .tx_data_mac(tx_data_mac),         .tx_data_valid_mac(tx_data_valid_mac),         .tx_data_en(tx_data_en),              .tx_clk(mac_tx_clk),         .tx_en(tx_en),         .tx_data(tx_data)     );          logic [47:0] mac_addr;     simple_mac_rx_path simple_mac_rx_path_inst(         .rx_clk(rx_clk),         .rst_n(rst_n),         .mac_addr(mac_addr),         .rx_data_mac(rx_data_mac),         .rx_data_valid_mac(rx_data_valid_mac),         .rx_sop_mac(rx_sop_mac),             .ff_rx_clk(ff_rx_clk),        .ff_rx_data(ff_rx_data0),        .ff_rx_eop(ff_rx_eop0),        .ff_rx_sop(ff_rx_sop0),        .rx_err(rx_err),        .ff_rx_dval(ff_rx_dval0),        .ff_rx_rdy(ff_rx_rdy)     );          simple_mac_tx_path simple_mac_tx_path_inst(         .ff_tx_clk(ff_tx_clk),         .rst_n(rst_n),         .eth_mode(eth_mode),         .ff_tx_data(ff_tx_data0),         .ff_tx_eop(ff_tx_eop0),         .ff_tx_sop(ff_tx_sop0),         .ff_tx_wren(ff_tx_wren0),         .ff_tx_rdy(ff_tx_rdy),              .tx_clk_mac(mac_tx_clk),         .tx_data_mac(tx_data_mac),         .tx_data_valid_mac(tx_data_valid_mac),         .tx_data_en(tx_data_en),         .pkt_send_num()          );          wire mdio_busy;     wire [15:0] mdio_rd_data;     simple_mac_phy_mdio phy_mdio(         .clk(clk),         .rst_n(rst_n),         .mdc(mdc),         .mdin(mdio_in),         .mdout(mdio_out),         .mdoe(mdio_oe),              .phy_addr(5'b10000),         .data_in(reg_wr_data[15:0]),         .reg_addr(reg_addr),         .wr(reg_wr),         .rd(reg_rd),         .data_out(mdio_rd_data),         .busy(mdio_busy)         );          wire [31:0] regs_rd_data;     wire regs_busy;     simple_mac_regs simple_mac_regs_inst(         .clk(clk),         .rst_n(rst_n),         .data_in(reg_wr_data),         .reg_addr(reg_addr),         .wr(reg_wr),         .rd(reg_rd),         .data_out(regs_rd_data),         .busy(regs_busy),              .mac_addr(mac_addr),         .*     );          simple_mac_bus_arb simple_mac_bus_arb_inst(         .reg_addr(reg_addr),             .mdio_busy(mdio_busy),         .mdio_rd_data(mdio_rd_data),         .regs_busy(regs_busy),         .regs_rd_data(regs_rd_data),         .reg_busy(reg_busy),         .reg_rd_data(reg_rd_data)     );               endmodule    

    2.mac_config

    这个模块主要是配置phy芯片寄存器的。

    3.Rx Path

    这个模块负责从simple_mac接收数据,然后提交给eth_fsm的。 下面是接口列表.

    [plain]   view plain  copy input rst_n,     ff_rx_if.s if_rx,     headers_if if_headers_rx,      output frame_type_t rx_type,     output logic rx_done,          output logic [31:0] data_recv,     output logic data_recv_start,     output logic data_recv_valid,     output logic [15:0] data_recv_len,     output u32_t cur_ripaddr,     output u16_t cur_rport,          input rx_done_clear,          input [31:0] local_ipaddr,     input [31:0] remote_port_local_port  接口列表里有2个interface,   

    接口列表里有2个interface, 

    if_rx是与simple_mac连接的接口。 

    if_headers_rx是保存各种header并提供给eth_fsm的,如mac_header, arp_header,ip_header,udp_header,tcp_header。 

    rx_done是一帧接收完的信号并提供给eth_fsm。

    中间一段用来从一帧中提取数据并提供给eth_fsm 。

    下面是配置ip地址和收发端口号的。

    4.Tx Path

    这个模块从eth_fsm取得数据和各种header,并发送给simple_mac, 下面是接口

    [plain]   view plain  copy input rst_n,     ff_tx_if.s if_tx,     headers_if if_headers_tx,     input frame_type_t tx_type,     input tx_start,     input [13:0] tx_dword_count,     output logic fifo_rdreq,     input [31:0] fifo_q,    

    其中if_tx是与simple_mac的接口, if_headers_tx是从eth_fsm来的各种header, 

    tx_type是帧的类型,目前支持ARP, ICMP,TCP,UDP。

    tx_start是一帧传输开始的信号。

    tx_dword_count是发送的字节数除以4 。

    fifo_rdreq和fifo_q是从eth_fsm来的数据。

    5.eth_fsm

    这是整个工程的核心, 是处理协议的状态机和控制数据的流动,下面是接口

    [plain]   view plain  copy input clk,     input rst_n,     input is_link_up,          headers_if if_headers_rx,     input frame_type_t rx_type,     input rx_done,          headers_if if_headers_tx,     output frame_type_t tx_type,     output logic tx_start,          input [31:0] data_recv,     input [15:0] data_recv_len,     input data_recv_valid,     input data_recv_start,     output logic rx_done_clear,     input u32_t cur_ripaddr,     input u16_t cur_rport,     rx_ram_in_if.m if_rx_ram_in,     tx_ram_out_if.m if_tx_ram_out,     input [31:0] remote_port_local_port,     input [31:0] local_ipaddr,     input fifo_rdreq,     output [31:0] fifo_q,     input pkt_send_eop,          output logic [13:0]  tx_dword_count,     output logic init_done     由于这个模块过于复杂,就不介绍了。

    6.data_source

    这个模块提供了与用户模块的接口   [plain]   view plain  copy     input rst_n,         input init_done,              input [7:0] wr_data,         input wr_clk,         input wr_en,         output wr_full,              output [7:0] rd_data,         input rd_clk,         input rd_en,         output rd_empty,                  tx_ram_in_if.m if_tx_ram_in,         rx_ram_out_if.s if_rx_ram_out     )    

    其中wr开头和rd开头的都是对外提供的fifo接口,  分别用来写和读内部的发送FIFO和接收FIFO.

    目前实现情况

    目前udp协议可以基本全速运行,由于全速达到了100MB/s以上,如果处理速度慢,会有丢包的情况。

    目前tcp协议只实现了最基本的功能,能够通信。窗口管理和慢启动,拥塞避免等特性还在完善中,速度只能达到200多M。

    对这个工程的介绍就到这里了,希望对大家有用。

    udp版本可以是开源的,但是商用需要license费。

    tcp版本不开源,也是收费的。

    需要的朋友可联系我qq:1517642772

    转载请注明原文地址: https://ju.6miu.com/read-14682.html

    最新回复(0)