天天看点

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

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

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

下面是工程的示意图.

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

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

顶层模块:

//  
                                                                    
      tcpip_hw                                                      
                                                                    
      Description                                                   
          top module                                                
                                                                    
      Author(s):                                                    
          - bin qiu, [email protected] or  [email protected]         
                                                                    
                       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,  
          
//user 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),和接收帧解包。 下面是顶层代码:

//  
                                                                    
      simple_mac_top                                                
                                                                    
      Description                                                   
          top module of simple mac                                  
                                                                    
      Author(s):                                                    
          - bin qiu, [email protected] or  [email protected]         
                                                                    
                       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的。 下面是接口列表.

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, 下面是接口

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

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

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

这个模块提供了与用户模块的接口  

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, UDP协议, FPGA实现

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

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

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

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

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