基于FPGA的千兆以太网UDP 硬件协议栈
发布者:jackzhang
时间:2016-03-29 13:37:25
原文:http://bbs.eetop.cn/thread-593390-1-2.html
目前TCP协议大多由cpu跑代码实现, 这次用FPGA的纯逻辑实现 , System Verilog编写,
下面给大家粗略讲一下我的实现方法,
下面是工程的示意图.
这个工程由几部分组成, 外部使用了88e1111千兆以太网phy。FPGA内部有几个大的模块,
顶层模块:
- //////////////////////////////////////////////////////////////////////
- //// ////
- //// 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,
-
- //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, 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的。 下面是接口列表.
- 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,
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协议可以基本全速运行,但是有丢包的情况,需要有个确认机制。
tcp协议只实现了最基本的功能,能够通信。窗口管理和慢启动,拥塞避免等特性还在完善中,速度只能达到200多M。
对这个工程的介绍就到这里了,希望对大家有用。