Adapt rgmii ethernet core from Alex Forencich for Ariane on Genesys2

This commit is contained in:
Jonathan Richard Robert Kimmitt 2019-01-23 13:53:50 +00:00
parent a3044dbfce
commit ef37d20fad
42 changed files with 11724 additions and 305 deletions

View file

@ -10,7 +10,6 @@ ips := xlnx_axi_clock_converter.xci \
xlnx_axi_quad_spi.xci \
xlnx_axi_gpio.xci \
xlnx_clk_gen.xci \
xlnx_axi_ethernetlite.xci \
xlnx_mig_7_ddr3.xci
ips := $(addprefix $(work-dir)/, $(ips))

View file

@ -9,6 +9,8 @@
// specific language governing permissions and limitations under the License.
// Xilinx Peripehrals
`default_nettype none
module ariane_peripherals #(
parameter int AxiAddrWidth = -1,
parameter int AxiDataWidth = -1,
@ -40,7 +42,7 @@ module ariane_peripherals #(
output wire eth_txctl ,
output wire [3:0] eth_txd ,
output wire eth_rst_n ,
input logic phy_tx_clk_i , // 25 MHz Clock
input logic phy_tx_clk_i , // 125 MHz Clock
// MDIO Interface
inout wire eth_mdio ,
output logic eth_mdc ,
@ -484,310 +486,76 @@ module ariane_peripherals #(
// ---------------
// 4. Ethernet
// ---------------
assign ethernet.b_user = 1'b0;
assign ethernet.r_user = 1'b0;
if (InclEthernet) begin : gen_ethernet
AXI_BUS #(
.AXI_ADDR_WIDTH ( AxiAddrWidth ),
.AXI_DATA_WIDTH ( AxiDataWidth ),
.AXI_ID_WIDTH ( AxiIdWidth ),
.AXI_USER_WIDTH ( AxiUserWidth )
) axi_ethernet_cdc();
logic clk_200_int, clk_rgmii, clk_rgmii_quad;
logic eth_en, eth_we, eth_int_n, eth_pme_n, eth_mdio_i, eth_mdio_o, eth_mdio_oen;
logic [AxiAddrWidth-1:0] eth_addr;
logic [AxiDataWidth-1:0] eth_wrdata, eth_rdata;
logic [AxiDataWidth/8-1:0] eth_be;
logic s_eth_rst_n;
axi2mem #(
.AXI_ID_WIDTH ( AxiIdWidth ),
.AXI_ADDR_WIDTH ( AxiAddrWidth ),
.AXI_DATA_WIDTH ( AxiDataWidth ),
.AXI_USER_WIDTH ( AxiUserWidth )
) i_axi2rom (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.slave ( ethernet ),
.req_o ( eth_en ),
.we_o ( eth_we ),
.addr_o ( eth_addr ),
.be_o ( eth_be ),
.data_o ( eth_wrdata ),
.data_i ( eth_rdata )
);
logic [31:0] s_axi_eth_awaddr;
logic [7:0] s_axi_eth_awlen;
logic [2:0] s_axi_eth_awsize;
logic [1:0] s_axi_eth_awburst;
logic [3:0] s_axi_eth_awcache;
logic s_axi_eth_awvalid;
logic s_axi_eth_awready;
logic [31:0] s_axi_eth_wdata;
logic [3:0] s_axi_eth_wstrb;
logic s_axi_eth_wlast;
logic s_axi_eth_wvalid;
logic s_axi_eth_wready;
logic [1:0] s_axi_eth_bresp;
logic s_axi_eth_bvalid;
logic s_axi_eth_bready;
logic [31:0] s_axi_eth_araddr;
logic [7:0] s_axi_eth_arlen;
logic [2:0] s_axi_eth_arsize;
logic [1:0] s_axi_eth_arburst;
logic [3:0] s_axi_eth_arcache;
logic s_axi_eth_arvalid;
logic s_axi_eth_arready;
logic [31:0] s_axi_eth_rdata;
logic [1:0] s_axi_eth_rresp;
logic s_axi_eth_rlast;
logic s_axi_eth_rvalid;
framing_top eth_rgmii
(
.msoc_clk(clk_i),
.core_lsu_addr(eth_addr[14:0]),
.core_lsu_wdata(eth_wrdata),
.core_lsu_be(eth_be),
.ce_d(eth_en),
.we_d(eth_en & eth_we),
.framing_sel(eth_en),
.framing_rdata(eth_rdata),
.rst_int(!rst_ni),
.clk_int(phy_tx_clk_i), // 125 MHz in-phase
.clk90_int(eth_clk_i), // 125 MHz quadrature
.clk_200_int(clk_200MHz_i),
/*
* Ethernet: 1000BASE-T RGMII
*/
.phy_rx_clk(eth_rxck),
.phy_rxd(eth_rxd),
.phy_rx_ctl(eth_rxctl),
.phy_tx_clk(eth_txck),
.phy_txd(eth_txd),
.phy_tx_ctl(eth_txctl),
.phy_reset_n(eth_rst_n),
.phy_int_n(eth_int_n),
.phy_pme_n(eth_pme_n),
.phy_mdc(eth_mdc),
.phy_mdio_i(eth_mdio_i),
.phy_mdio_o(eth_mdio_o),
.phy_mdio_oen(eth_mdio_oen),
.eth_irq(irq_sources[2])
);
rstgen i_rstgen (
.clk_i ( eth_clk_i ),
.rst_ni ( rst_ni ),
.test_mode_i ( test_en ),
.rst_no ( s_eth_rst_n ),
.init_no ( ) // keep open
);
xlnx_axi_clock_converter i_xlnx_axi_clock_converter_ethernet (
.s_axi_aclk ( clk_i ),
.s_axi_aresetn ( rst_ni ),
.s_axi_awid ( ethernet.aw_id ),
.s_axi_awaddr ( ethernet.aw_addr ),
.s_axi_awlen ( ethernet.aw_len ),
.s_axi_awsize ( ethernet.aw_size ),
.s_axi_awburst ( ethernet.aw_burst ),
.s_axi_awlock ( ethernet.aw_lock ),
.s_axi_awcache ( ethernet.aw_cache ),
.s_axi_awprot ( ethernet.aw_prot ),
.s_axi_awregion ( ethernet.aw_region ),
.s_axi_awqos ( ethernet.aw_qos ),
.s_axi_awvalid ( ethernet.aw_valid ),
.s_axi_awready ( ethernet.aw_ready ),
.s_axi_wdata ( ethernet.w_data ),
.s_axi_wstrb ( ethernet.w_strb ),
.s_axi_wlast ( ethernet.w_last ),
.s_axi_wvalid ( ethernet.w_valid ),
.s_axi_wready ( ethernet.w_ready ),
.s_axi_bid ( ethernet.b_id ),
.s_axi_bresp ( ethernet.b_resp ),
.s_axi_bvalid ( ethernet.b_valid ),
.s_axi_bready ( ethernet.b_ready ),
.s_axi_arid ( ethernet.ar_id ),
.s_axi_araddr ( ethernet.ar_addr ),
.s_axi_arlen ( ethernet.ar_len ),
.s_axi_arsize ( ethernet.ar_size ),
.s_axi_arburst ( ethernet.ar_burst ),
.s_axi_arlock ( ethernet.ar_lock ),
.s_axi_arcache ( ethernet.ar_cache ),
.s_axi_arprot ( ethernet.ar_prot ),
.s_axi_arregion ( ethernet.ar_region ),
.s_axi_arqos ( ethernet.ar_qos ),
.s_axi_arvalid ( ethernet.ar_valid ),
.s_axi_arready ( ethernet.ar_ready ),
.s_axi_rid ( ethernet.r_id ),
.s_axi_rdata ( ethernet.r_data ),
.s_axi_rresp ( ethernet.r_resp ),
.s_axi_rlast ( ethernet.r_last ),
.s_axi_rvalid ( ethernet.r_valid ),
.s_axi_rready ( ethernet.r_ready ),
// to size converter
.m_axi_aclk ( eth_clk_i ),
.m_axi_aresetn ( s_eth_rst_n ),
.m_axi_awid ( axi_ethernet_cdc.aw_id ),
.m_axi_awaddr ( axi_ethernet_cdc.aw_addr ),
.m_axi_awlen ( axi_ethernet_cdc.aw_len ),
.m_axi_awsize ( axi_ethernet_cdc.aw_size ),
.m_axi_awburst ( axi_ethernet_cdc.aw_burst ),
.m_axi_awlock ( axi_ethernet_cdc.aw_lock ),
.m_axi_awcache ( axi_ethernet_cdc.aw_cache ),
.m_axi_awprot ( axi_ethernet_cdc.aw_prot ),
.m_axi_awregion ( axi_ethernet_cdc.aw_region ),
.m_axi_awqos ( axi_ethernet_cdc.aw_qos ),
.m_axi_awvalid ( axi_ethernet_cdc.aw_valid ),
.m_axi_awready ( axi_ethernet_cdc.aw_ready ),
.m_axi_wdata ( axi_ethernet_cdc.w_data ),
.m_axi_wstrb ( axi_ethernet_cdc.w_strb ),
.m_axi_wlast ( axi_ethernet_cdc.w_last ),
.m_axi_wvalid ( axi_ethernet_cdc.w_valid ),
.m_axi_wready ( axi_ethernet_cdc.w_ready ),
.m_axi_bid ( axi_ethernet_cdc.b_id ),
.m_axi_bresp ( axi_ethernet_cdc.b_resp ),
.m_axi_bvalid ( axi_ethernet_cdc.b_valid ),
.m_axi_bready ( axi_ethernet_cdc.b_ready ),
.m_axi_arid ( axi_ethernet_cdc.ar_id ),
.m_axi_araddr ( axi_ethernet_cdc.ar_addr ),
.m_axi_arlen ( axi_ethernet_cdc.ar_len ),
.m_axi_arsize ( axi_ethernet_cdc.ar_size ),
.m_axi_arburst ( axi_ethernet_cdc.ar_burst ),
.m_axi_arlock ( axi_ethernet_cdc.ar_lock ),
.m_axi_arcache ( axi_ethernet_cdc.ar_cache ),
.m_axi_arprot ( axi_ethernet_cdc.ar_prot ),
.m_axi_arregion ( axi_ethernet_cdc.ar_region ),
.m_axi_arqos ( axi_ethernet_cdc.ar_qos ),
.m_axi_arvalid ( axi_ethernet_cdc.ar_valid ),
.m_axi_arready ( axi_ethernet_cdc.ar_ready ),
.m_axi_rid ( axi_ethernet_cdc.r_id ),
.m_axi_rdata ( axi_ethernet_cdc.r_data ),
.m_axi_rresp ( axi_ethernet_cdc.r_resp ),
.m_axi_rlast ( axi_ethernet_cdc.r_last ),
.m_axi_rvalid ( axi_ethernet_cdc.r_valid ),
.m_axi_rready ( axi_ethernet_cdc.r_ready )
);
// system-bus is 64-bit, convert down to 32 bit
xlnx_axi_dwidth_converter i_xlnx_axi_dwidth_converter_ethernet (
.s_axi_aclk ( eth_clk_i ),
.s_axi_aresetn ( s_eth_rst_n ),
.s_axi_awid ( axi_ethernet_cdc.aw_id ),
.s_axi_awaddr ( axi_ethernet_cdc.aw_addr[31:0] ),
.s_axi_awlen ( axi_ethernet_cdc.aw_len ),
.s_axi_awsize ( axi_ethernet_cdc.aw_size ),
.s_axi_awburst ( axi_ethernet_cdc.aw_burst ),
.s_axi_awlock ( axi_ethernet_cdc.aw_lock ),
.s_axi_awcache ( axi_ethernet_cdc.aw_cache ),
.s_axi_awprot ( axi_ethernet_cdc.aw_prot ),
.s_axi_awregion ( axi_ethernet_cdc.aw_region ),
.s_axi_awqos ( axi_ethernet_cdc.aw_qos ),
.s_axi_awvalid ( axi_ethernet_cdc.aw_valid ),
.s_axi_awready ( axi_ethernet_cdc.aw_ready ),
.s_axi_wdata ( axi_ethernet_cdc.w_data ),
.s_axi_wstrb ( axi_ethernet_cdc.w_strb ),
.s_axi_wlast ( axi_ethernet_cdc.w_last ),
.s_axi_wvalid ( axi_ethernet_cdc.w_valid ),
.s_axi_wready ( axi_ethernet_cdc.w_ready ),
.s_axi_bid ( axi_ethernet_cdc.b_id ),
.s_axi_bresp ( axi_ethernet_cdc.b_resp ),
.s_axi_bvalid ( axi_ethernet_cdc.b_valid ),
.s_axi_bready ( axi_ethernet_cdc.b_ready ),
.s_axi_arid ( axi_ethernet_cdc.ar_id ),
.s_axi_araddr ( axi_ethernet_cdc.ar_addr[31:0] ),
.s_axi_arlen ( axi_ethernet_cdc.ar_len ),
.s_axi_arsize ( axi_ethernet_cdc.ar_size ),
.s_axi_arburst ( axi_ethernet_cdc.ar_burst ),
.s_axi_arlock ( axi_ethernet_cdc.ar_lock ),
.s_axi_arcache ( axi_ethernet_cdc.ar_cache ),
.s_axi_arprot ( axi_ethernet_cdc.ar_prot ),
.s_axi_arregion ( axi_ethernet_cdc.ar_region ),
.s_axi_arqos ( axi_ethernet_cdc.ar_qos ),
.s_axi_arvalid ( axi_ethernet_cdc.ar_valid ),
.s_axi_arready ( axi_ethernet_cdc.ar_ready ),
.s_axi_rid ( axi_ethernet_cdc.r_id ),
.s_axi_rdata ( axi_ethernet_cdc.r_data ),
.s_axi_rresp ( axi_ethernet_cdc.r_resp ),
.s_axi_rlast ( axi_ethernet_cdc.r_last ),
.s_axi_rvalid ( axi_ethernet_cdc.r_valid ),
.s_axi_rready ( axi_ethernet_cdc.r_ready ),
.m_axi_awaddr ( s_axi_eth_awaddr ),
.m_axi_awlen ( s_axi_eth_awlen ),
.m_axi_awsize ( s_axi_eth_awsize ),
.m_axi_awburst ( s_axi_eth_awburst ),
.m_axi_awlock ( ),
.m_axi_awcache ( s_axi_eth_awcache ),
.m_axi_awprot ( ),
.m_axi_awregion ( ),
.m_axi_awqos ( ),
.m_axi_awvalid ( s_axi_eth_awvalid ),
.m_axi_awready ( s_axi_eth_awready ),
.m_axi_wdata ( s_axi_eth_wdata ),
.m_axi_wstrb ( s_axi_eth_wstrb ),
.m_axi_wlast ( s_axi_eth_wlast ),
.m_axi_wvalid ( s_axi_eth_wvalid ),
.m_axi_wready ( s_axi_eth_wready ),
.m_axi_bresp ( s_axi_eth_bresp ),
.m_axi_bvalid ( s_axi_eth_bvalid ),
.m_axi_bready ( s_axi_eth_bready ),
.m_axi_araddr ( s_axi_eth_araddr ),
.m_axi_arlen ( s_axi_eth_arlen ),
.m_axi_arsize ( s_axi_eth_arsize ),
.m_axi_arburst ( s_axi_eth_arburst ),
.m_axi_arlock ( ),
.m_axi_arcache ( s_axi_eth_arcache ),
.m_axi_arprot ( ),
.m_axi_arregion ( ),
.m_axi_arqos ( ),
.m_axi_arvalid ( s_axi_eth_arvalid ),
.m_axi_arready ( s_axi_eth_arready ),
.m_axi_rdata ( s_axi_eth_rdata ),
.m_axi_rresp ( s_axi_eth_rresp ),
.m_axi_rlast ( s_axi_eth_rlast ),
.m_axi_rvalid ( s_axi_eth_rvalid ),
.m_axi_rready ( s_axi_eth_rready )
);
logic phy_rx_clk;
logic phy_crs;
logic phy_dv;
logic [3:0] phy_rx_data;
logic phy_col;
logic phy_rx_er;
logic phy_rst_n;
logic phy_tx_en;
logic [3:0] phy_tx_data;
logic phy_mdio_i;
logic phy_mdio_o;
logic phy_mdio_t;
logic phy_mdc;
xlnx_axi_ethernetlite i_xlnx_axi_ethernetlite (
.s_axi_aclk ( eth_clk_i ),
.s_axi_aresetn ( s_eth_rst_n ),
.ip2intc_irpt ( irq_sources[2] ),
.s_axi_awaddr ( s_axi_eth_awaddr[12:0] ),
.s_axi_awlen ( s_axi_eth_awlen ),
.s_axi_awsize ( s_axi_eth_awsize ),
.s_axi_awburst ( s_axi_eth_awburst ),
.s_axi_awcache ( s_axi_eth_awcache ),
.s_axi_awvalid ( s_axi_eth_awvalid ),
.s_axi_awready ( s_axi_eth_awready ),
.s_axi_wdata ( s_axi_eth_wdata ),
.s_axi_wstrb ( s_axi_eth_wstrb ),
.s_axi_wlast ( s_axi_eth_wlast ),
.s_axi_wvalid ( s_axi_eth_wvalid ),
.s_axi_wready ( s_axi_eth_wready ),
.s_axi_bresp ( s_axi_eth_bresp ),
.s_axi_bvalid ( s_axi_eth_bvalid ),
.s_axi_bready ( s_axi_eth_bready ),
.s_axi_araddr ( s_axi_eth_araddr[12:0] ),
.s_axi_arlen ( s_axi_eth_arlen ),
.s_axi_arsize ( s_axi_eth_arsize ),
.s_axi_arburst ( s_axi_eth_arburst ),
.s_axi_arcache ( s_axi_eth_arcache ),
.s_axi_arvalid ( s_axi_eth_arvalid ),
.s_axi_arready ( s_axi_eth_arready ),
.s_axi_rdata ( s_axi_eth_rdata ),
.s_axi_rresp ( s_axi_eth_rresp ),
.s_axi_rlast ( s_axi_eth_rlast ),
.s_axi_rvalid ( s_axi_eth_rvalid ),
.s_axi_rready ( s_axi_eth_rready ),
.phy_tx_clk ( phy_tx_clk_i ),
.phy_rx_clk ( phy_rx_clk ),
.phy_crs ( phy_crs ),
.phy_dv ( phy_dv ),
.phy_rx_data ( phy_rx_data ),
.phy_col ( phy_col ),
.phy_rx_er ( phy_rx_er ),
.phy_rst_n ( phy_rst_n ),
.phy_tx_en ( phy_tx_en ),
.phy_tx_data ( phy_tx_data ),
.phy_mdio_i ( phy_mdio_i ),
.phy_mdio_o ( phy_mdio_o ),
.phy_mdio_t ( phy_mdio_t ),
.phy_mdc ( phy_mdc )
);
assign phy_crs = 1'b0;
assign phy_col = 1'b0;
rgmii_to_mii_conv_xilinx i_rgmii_to_mii_conv_xilinx (
.rgmii_phy_txc ( eth_txck ),
.rgmii_phy_txctl ( eth_txctl ),
.rgmii_phy_txd ( eth_txd ),
.rgmii_phy_rxc ( eth_rxck ),
.rgmii_phy_rxctl ( eth_rxctl ),
.rgmii_phy_rxd ( eth_rxd ),
.rgmii_phy_rst_n ( eth_rst_n ),
.rgmii_phy_mdio ( eth_mdio ),
.rgmii_phy_mdc ( eth_mdc ),
.mem_clk_i ( clk_200MHz_i ),
.net_phy_rst_n ( phy_rst_n ),
.net_phy_tx_clk ( phy_tx_clk_i ),
.net_phy_tx_en ( phy_tx_en ),
.net_phy_tx_data ( phy_tx_data ),
.net_phy_rx_clk ( phy_rx_clk ),
.net_phy_dv ( phy_dv ),
.net_phy_rx_data ( phy_rx_data ),
.net_phy_rx_er ( phy_rx_er ),
.net_mdio_i ( phy_mdio_o ),
.net_mdio_o ( phy_mdio_i ),
.net_mdio_t ( phy_mdio_t ),
.net_phy_mdc ( phy_mdc )
);
IOBUF #(
.DRIVE(12), // Specify the output drive strength
.IBUF_LOW_PWR("TRUE"), // Low Power - "TRUE", High Performance = "FALSE"
.IOSTANDARD("DEFAULT"), // Specify the I/O standard
.SLEW("SLOW") // Specify the output slew rate
) IOBUF_inst (
.O(eth_mdio_i), // Buffer output
.IO(eth_mdio), // Buffer inout port (connect directly to top-level port)
.I(eth_mdio_o), // Buffer input
.T(~eth_mdio_oen) // 3-state enable input, high=input, low=output
);
end else begin
assign irq_sources [2] = 1'b0;
@ -838,6 +606,8 @@ module ariane_peripherals #(
logic [1:0] s_axi_gpio_rresp;
logic s_axi_gpio_rlast;
logic s_axi_gpio_rvalid;
logic s_axi_gpio_rready;
// system-bus is 64-bit, convert down to 32 bit
xlnx_axi_dwidth_converter i_xlnx_axi_dwidth_converter_gpio (
.s_axi_aclk ( clk_i ),
@ -949,3 +719,5 @@ module ariane_peripherals #(
assign s_axi_gpio_wlast = 1'b1;
end
endmodule
`default_nettype wire

View file

@ -536,9 +536,9 @@ xlnx_axi_clock_converter i_xlnx_axi_clock_converter_ddr (
);
xlnx_clk_gen i_xlnx_clk_gen (
.clk_out1 ( clk ), // 50MHz
.clk_out2 ( phy_tx_clk ), // 25 MHz
.clk_out3 ( eth_clk ), // 100 MHz
.clk_out1 ( clk ), // 50 MHz
.clk_out2 ( phy_tx_clk ), // 125 MHz (for RGMII PHY)
.clk_out3 ( eth_clk ), // 125 MHz quadrature
.reset ( cpu_reset ),
.locked ( pll_locked ),
.clk_in1 ( ddr_clock_out )

416
fpga/src/arp.sv Normal file
View file

@ -0,0 +1,416 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* ARP block for IPv4, ethernet frame interface
*/
module arp #(
parameter CACHE_ADDR_WIDTH = 9,
parameter REQUEST_RETRY_COUNT = 4,
parameter REQUEST_RETRY_INTERVAL = 125000000*2,
parameter REQUEST_TIMEOUT = 125000000*30
)
(
input wire clk,
input wire rst,
/*
* Ethernet frame input
*/
input wire s_eth_hdr_valid,
output wire s_eth_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [7:0] s_eth_payload_axis_tdata,
input wire s_eth_payload_axis_tvalid,
output wire s_eth_payload_axis_tready,
input wire s_eth_payload_axis_tlast,
input wire s_eth_payload_axis_tuser,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [7:0] m_eth_payload_axis_tdata,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire m_eth_payload_axis_tuser,
/*
* ARP requests
*/
input wire arp_request_valid,
output wire arp_request_ready,
input wire [31:0] arp_request_ip,
output wire arp_response_valid,
input wire arp_response_ready,
output wire arp_response_error,
output wire [47:0] arp_response_mac,
/*
* Configuration
*/
input wire [47:0] local_mac,
input wire [31:0] local_ip,
input wire [31:0] gateway_ip,
input wire [31:0] subnet_mask,
input wire clear_cache
);
localparam [15:0]
ARP_OPER_ARP_REQUEST = 16'h0001,
ARP_OPER_ARP_REPLY = 16'h0002,
ARP_OPER_INARP_REQUEST = 16'h0008,
ARP_OPER_INARP_REPLY = 16'h0009;
wire incoming_frame_valid;
reg incoming_frame_ready;
wire [47:0] incoming_eth_dest_mac;
wire [47:0] incoming_eth_src_mac;
wire [15:0] incoming_eth_type;
wire [15:0] incoming_arp_htype;
wire [15:0] incoming_arp_ptype;
wire [7:0] incoming_arp_hlen;
wire [7:0] incoming_arp_plen;
wire [15:0] incoming_arp_oper;
wire [47:0] incoming_arp_sha;
wire [31:0] incoming_arp_spa;
wire [47:0] incoming_arp_tha;
wire [31:0] incoming_arp_tpa;
/*
* ARP frame processing
*/
arp_eth_rx
arp_eth_rx_inst (
.clk(clk),
.rst(rst),
// Ethernet frame input
.s_eth_hdr_valid(s_eth_hdr_valid),
.s_eth_hdr_ready(s_eth_hdr_ready),
.s_eth_dest_mac(s_eth_dest_mac),
.s_eth_src_mac(s_eth_src_mac),
.s_eth_type(s_eth_type),
.s_eth_payload_axis_tdata(s_eth_payload_axis_tdata),
.s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid),
.s_eth_payload_axis_tready(s_eth_payload_axis_tready),
.s_eth_payload_axis_tlast(s_eth_payload_axis_tlast),
.s_eth_payload_axis_tuser(s_eth_payload_axis_tuser),
// ARP frame output
.m_frame_valid(incoming_frame_valid),
.m_frame_ready(incoming_frame_ready),
.m_eth_dest_mac(incoming_eth_dest_mac),
.m_eth_src_mac(incoming_eth_src_mac),
.m_eth_type(incoming_eth_type),
.m_arp_htype(incoming_arp_htype),
.m_arp_ptype(incoming_arp_ptype),
.m_arp_hlen(incoming_arp_hlen),
.m_arp_plen(incoming_arp_plen),
.m_arp_oper(incoming_arp_oper),
.m_arp_sha(incoming_arp_sha),
.m_arp_spa(incoming_arp_spa),
.m_arp_tha(incoming_arp_tha),
.m_arp_tpa(incoming_arp_tpa),
// Status signals
.busy(),
.error_header_early_termination(),
.error_invalid_header()
);
reg outgoing_frame_valid_reg = 1'b0, outgoing_frame_valid_next;
wire outgoing_frame_ready;
reg [47:0] outgoing_eth_dest_mac_reg = 48'd0, outgoing_eth_dest_mac_next;
reg [15:0] outgoing_arp_oper_reg = 16'd0, outgoing_arp_oper_next;
reg [47:0] outgoing_arp_tha_reg = 48'd0, outgoing_arp_tha_next;
reg [31:0] outgoing_arp_tpa_reg = 32'd0, outgoing_arp_tpa_next;
arp_eth_tx
arp_eth_tx_inst (
.clk(clk),
.rst(rst),
// ARP frame input
.s_frame_valid(outgoing_frame_valid_reg),
.s_frame_ready(outgoing_frame_ready),
.s_eth_dest_mac(outgoing_eth_dest_mac_reg),
.s_eth_src_mac(local_mac),
.s_eth_type(16'h0806),
.s_arp_htype(16'h0001),
.s_arp_ptype(16'h0800),
.s_arp_oper(outgoing_arp_oper_reg),
.s_arp_sha(local_mac),
.s_arp_spa(local_ip),
.s_arp_tha(outgoing_arp_tha_reg),
.s_arp_tpa(outgoing_arp_tpa_reg),
// Ethernet frame output
.m_eth_hdr_valid(m_eth_hdr_valid),
.m_eth_hdr_ready(m_eth_hdr_ready),
.m_eth_dest_mac(m_eth_dest_mac),
.m_eth_src_mac(m_eth_src_mac),
.m_eth_type(m_eth_type),
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser),
// Status signals
.busy()
);
reg cache_query_request_valid_reg = 1'b0, cache_query_request_valid_next;
reg [31:0] cache_query_request_ip_reg = 32'd0, cache_query_request_ip_next;
wire cache_query_response_valid;
wire cache_query_response_error;
wire [47:0] cache_query_response_mac;
reg cache_write_request_valid_reg = 1'b0, cache_write_request_valid_next;
reg [31:0] cache_write_request_ip_reg = 32'd0, cache_write_request_ip_next;
reg [47:0] cache_write_request_mac_reg = 48'd0, cache_write_request_mac_next;
/*
* ARP cache
*/
arp_cache #(
.CACHE_ADDR_WIDTH(CACHE_ADDR_WIDTH)
)
arp_cache_inst (
.clk(clk),
.rst(rst),
// Query cache
.query_request_valid(cache_query_request_valid_reg),
.query_request_ready(),
.query_request_ip(cache_query_request_ip_reg),
.query_response_valid(cache_query_response_valid),
.query_response_ready(1'b1),
.query_response_error(cache_query_response_error),
.query_response_mac(cache_query_response_mac),
// Write cache
.write_request_valid(cache_write_request_valid_reg),
.write_request_ready(),
.write_request_ip(cache_write_request_ip_reg),
.write_request_mac(cache_write_request_mac_reg),
// Configuration
.clear_cache(clear_cache)
);
reg arp_request_operation_reg = 1'b0, arp_request_operation_next;
reg arp_request_ready_reg = 1'b0, arp_request_ready_next;
reg [31:0] arp_request_ip_reg = 32'd0, arp_request_ip_next;
reg arp_response_valid_reg = 1'b0, arp_response_valid_next;
reg arp_response_error_reg = 1'b0, arp_response_error_next;
reg [47:0] arp_response_mac_reg = 48'd0, arp_response_mac_next;
reg [5:0] arp_request_retry_cnt_reg = 6'd0, arp_request_retry_cnt_next;
reg [35:0] arp_request_timer_reg = 36'd0, arp_request_timer_next;
assign arp_request_ready = arp_request_ready_reg;
assign arp_response_valid = arp_response_valid_reg;
assign arp_response_error = arp_response_error_reg;
assign arp_response_mac = arp_response_mac_reg;
always @* begin
incoming_frame_ready = 1'b0;
outgoing_frame_valid_next = outgoing_frame_valid_reg && !outgoing_frame_ready;
outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg;
outgoing_arp_oper_next = outgoing_arp_oper_reg;
outgoing_arp_tha_next = outgoing_arp_tha_reg;
outgoing_arp_tpa_next = outgoing_arp_tpa_reg;
cache_query_request_valid_next = 1'b0;
cache_query_request_ip_next = cache_query_request_ip_reg;
cache_write_request_valid_next = 1'b0;
cache_write_request_mac_next = cache_write_request_mac_reg;
cache_write_request_ip_next = cache_write_request_ip_reg;
arp_request_ready_next = 1'b0;
arp_request_ip_next = arp_request_ip_reg;
arp_request_operation_next = arp_request_operation_reg;
arp_request_retry_cnt_next = arp_request_retry_cnt_reg;
arp_request_timer_next = arp_request_timer_reg;
arp_response_valid_next = arp_response_valid_reg && !arp_response_ready;
arp_response_error_next = 1'b0;
arp_response_mac_next = 48'd0;
// manage incoming frames
incoming_frame_ready = outgoing_frame_ready;
if (incoming_frame_valid && incoming_frame_ready) begin
if (incoming_eth_type == 16'h0806 && incoming_arp_htype == 16'h0001 && incoming_arp_ptype == 16'h0800) begin
// store sender addresses in cache
cache_write_request_valid_next = 1'b1;
cache_write_request_ip_next = incoming_arp_spa;
cache_write_request_mac_next = incoming_arp_sha;
if (incoming_arp_oper == ARP_OPER_ARP_REQUEST) begin
// ARP request
if (incoming_arp_tpa == local_ip) begin
// send reply frame to valid incoming request
outgoing_frame_valid_next = 1'b1;
outgoing_eth_dest_mac_next = incoming_eth_src_mac;
outgoing_arp_oper_next = ARP_OPER_ARP_REPLY;
outgoing_arp_tha_next = incoming_arp_sha;
outgoing_arp_tpa_next = incoming_arp_spa;
end
end else if (incoming_arp_oper == ARP_OPER_INARP_REPLY) begin
// INARP request
if (incoming_arp_tha == local_mac) begin
// send reply frame to valid incoming request
outgoing_frame_valid_next = 1'b1;
outgoing_eth_dest_mac_next = incoming_eth_src_mac;
outgoing_arp_oper_next = ARP_OPER_INARP_REPLY;
outgoing_arp_tha_next = incoming_arp_sha;
outgoing_arp_tpa_next = incoming_arp_spa;
end
end
end
end
// manage ARP lookup requests
if (arp_request_operation_reg) begin
arp_request_ready_next = 1'b0;
cache_query_request_valid_next = 1'b1;
arp_request_timer_next = arp_request_timer_reg - 1;
// if we got a response, it will go in the cache, so when the query succeds, we're done
if (cache_query_response_valid && !cache_query_response_error) begin
arp_request_operation_next = 1'b0;
cache_query_request_valid_next = 1'b0;
arp_response_valid_next = 1'b1;
arp_response_error_next = 1'b0;
arp_response_mac_next = cache_query_response_mac;
end
// timer timeout
if (arp_request_timer_reg == 0) begin
if (arp_request_retry_cnt_reg > 0) begin
// have more retries
// send ARP request frame
outgoing_frame_valid_next = 1'b1;
outgoing_eth_dest_mac_next = 48'hffffffffffff;
outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST;
outgoing_arp_tha_next = 48'h000000000000;
outgoing_arp_tpa_next = arp_request_ip_reg;
arp_request_retry_cnt_next = arp_request_retry_cnt_reg - 1;
if (arp_request_retry_cnt_reg > 1) begin
arp_request_timer_next = REQUEST_RETRY_INTERVAL;
end else begin
arp_request_timer_next = REQUEST_TIMEOUT;
end
end else begin
// out of retries
arp_request_operation_next = 1'b0;
arp_response_valid_next = 1'b1;
arp_response_error_next = 1'b1;
cache_query_request_valid_next = 1'b0;
end
end
end else begin
arp_request_ready_next = !arp_response_valid_next;
if (cache_query_request_valid_reg) begin
cache_query_request_valid_next = 1'b1;
if (cache_query_response_valid) begin
if (cache_query_response_error) begin
arp_request_operation_next = 1'b1;
// send ARP request frame
outgoing_frame_valid_next = 1'b1;
outgoing_eth_dest_mac_next = 48'hffffffffffff;
outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST;
outgoing_arp_tha_next = 48'h000000000000;
outgoing_arp_tpa_next = arp_request_ip_reg;
arp_request_retry_cnt_next = REQUEST_RETRY_COUNT-1;
arp_request_timer_next = REQUEST_RETRY_INTERVAL;
end else begin
cache_query_request_valid_next = 1'b0;
arp_response_valid_next = 1'b1;
arp_response_error_next = 1'b0;
arp_response_mac_next = cache_query_response_mac;
end
end
end else if (arp_request_valid && arp_request_ready) begin
if (~(arp_request_ip | subnet_mask) == 0) begin
// broadcast address
// (all bits in request IP set where subnet mask is clear)
arp_response_valid_next = 1'b1;
arp_response_error_next = 1'b0;
arp_response_mac_next = 48'hffffffffffff;
end else if (((arp_request_ip ^ gateway_ip) & subnet_mask) == 0) begin
// within subnet, look up IP directly
// (no bits differ between request IP and gateway IP where subnet mask is set)
cache_query_request_valid_next = 1'b1;
cache_query_request_ip_next = arp_request_ip;
arp_request_ip_next = arp_request_ip;
end else begin
// outside of subnet, so look up gateway address
cache_query_request_valid_next = 1'b1;
cache_query_request_ip_next = gateway_ip;
arp_request_ip_next = gateway_ip;
end
end
end
end
always @(posedge clk) begin
if (rst) begin
outgoing_frame_valid_reg <= 1'b0;
cache_query_request_valid_reg <= 1'b0;
cache_write_request_valid_reg <= 1'b0;
arp_request_ready_reg <= 1'b0;
arp_request_operation_reg <= 1'b0;
arp_request_retry_cnt_reg <= 6'd0;
arp_request_timer_reg <= 36'd0;
arp_response_valid_reg <= 1'b0;
end else begin
outgoing_frame_valid_reg <= outgoing_frame_valid_next;
cache_query_request_valid_reg <= cache_query_request_valid_next;
cache_write_request_valid_reg <= cache_write_request_valid_next;
arp_request_ready_reg <= arp_request_ready_next;
arp_request_operation_reg <= arp_request_operation_next;
arp_request_retry_cnt_reg <= arp_request_retry_cnt_next;
arp_request_timer_reg <= arp_request_timer_next;
arp_response_valid_reg <= arp_response_valid_next;
end
cache_query_request_ip_reg <= cache_query_request_ip_next;
outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next;
outgoing_arp_oper_reg <= outgoing_arp_oper_next;
outgoing_arp_tha_reg <= outgoing_arp_tha_next;
outgoing_arp_tpa_reg <= outgoing_arp_tpa_next;
cache_write_request_mac_reg <= cache_write_request_mac_next;
cache_write_request_ip_reg <= cache_write_request_ip_next;
arp_request_ip_reg <= arp_request_ip_next;
arp_response_error_reg <= arp_response_error_next;
arp_response_mac_reg <= arp_response_mac_next;
end
endmodule

218
fpga/src/arp_cache.sv Normal file
View file

@ -0,0 +1,218 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* ARP cache
*/
module arp_cache #(
parameter CACHE_ADDR_WIDTH = 9
)
(
input wire clk,
input wire rst,
/*
* Cache query
*/
input wire query_request_valid,
output wire query_request_ready,
input wire [31:0] query_request_ip,
output wire query_response_valid,
input wire query_response_ready,
output wire query_response_error,
output wire [47:0] query_response_mac,
/*
* Cache write
*/
input wire write_request_valid,
output wire write_request_ready,
input wire [31:0] write_request_ip,
input wire [47:0] write_request_mac,
/*
* Configuration
*/
input wire clear_cache
);
reg mem_write = 0;
reg store_query = 0;
reg store_write = 0;
reg query_ip_valid_reg = 0, query_ip_valid_next;
reg [31:0] query_ip_reg = 0;
reg write_ip_valid_reg = 0, write_ip_valid_next;
reg [31:0] write_ip_reg = 0;
reg [47:0] write_mac_reg = 0;
reg [CACHE_ADDR_WIDTH-1:0] wr_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, wr_ptr_next;
reg [CACHE_ADDR_WIDTH-1:0] rd_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, rd_ptr_next;
reg valid_mem[(2**CACHE_ADDR_WIDTH)-1:0];
reg [31:0] ip_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0];
reg [47:0] mac_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0];
reg query_request_ready_reg = 0, query_request_ready_next;
reg query_response_valid_reg = 0, query_response_valid_next;
reg query_response_error_reg = 0, query_response_error_next;
reg [47:0] query_response_mac_reg = 0;
reg write_request_ready_reg = 0, write_request_ready_next;
wire [31:0] query_request_hash;
wire [31:0] write_request_hash;
assign query_request_ready = query_request_ready_reg;
assign query_response_valid = query_response_valid_reg;
assign query_response_error = query_response_error_reg;
assign query_response_mac = query_response_mac_reg;
assign write_request_ready = write_request_ready_reg;
rgmii_lfsr #(
.LFSR_WIDTH(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_CONFIG("GALOIS"),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_WIDTH(32),
.STYLE("AUTO")
)
rd_hash (
.data_in(query_request_ip),
.state_in(32'hffffffff),
.data_out(),
.state_out(query_request_hash)
);
rgmii_lfsr #(
.LFSR_WIDTH(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_CONFIG("GALOIS"),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_WIDTH(32),
.STYLE("AUTO")
)
wr_hash (
.data_in(write_request_ip),
.state_in(32'hffffffff),
.data_out(),
.state_out(write_request_hash)
);
always @* begin
mem_write = 1'b0;
store_query = 1'b0;
store_write = 1'b0;
wr_ptr_next = wr_ptr_reg;
rd_ptr_next = rd_ptr_reg;
query_ip_valid_next = query_ip_valid_reg;
query_request_ready_next = ~query_ip_valid_reg || ~query_request_valid || query_response_ready;
query_response_valid_next = query_response_valid_reg & ~query_response_ready;
query_response_error_next = query_response_error_reg;
if (query_ip_valid_reg && (~query_request_valid || query_response_ready)) begin
query_response_valid_next = 1;
query_ip_valid_next = 0;
if (valid_mem[rd_ptr_reg] && ip_addr_mem[rd_ptr_reg] == query_ip_reg) begin
query_response_error_next = 0;
end else begin
query_response_error_next = 1;
end
end
if (query_request_valid && query_request_ready && (~query_ip_valid_reg || ~query_request_valid || query_response_ready)) begin
store_query = 1;
query_ip_valid_next = 1;
rd_ptr_next = query_request_hash[CACHE_ADDR_WIDTH-1:0];
end
write_ip_valid_next = write_ip_valid_reg;
write_request_ready_next = 1'b1;
if (write_ip_valid_reg) begin
write_ip_valid_next = 0;
mem_write = 1;
end
if (write_request_valid && write_request_ready) begin
store_write = 1;
write_ip_valid_next = 1;
wr_ptr_next = write_request_hash[CACHE_ADDR_WIDTH-1:0];
end
end
always @(posedge clk) begin
if (rst) begin
query_ip_valid_reg <= 1'b0;
query_request_ready_reg <= 1'b0;
query_response_valid_reg <= 1'b0;
write_ip_valid_reg <= 1'b0;
write_request_ready_reg <= 1'b0;
end else begin
query_ip_valid_reg <= query_ip_valid_next;
query_request_ready_reg <= query_request_ready_next;
query_response_valid_reg <= query_response_valid_next;
write_ip_valid_reg <= write_ip_valid_next;
write_request_ready_reg <= write_request_ready_next;
end
query_response_error_reg <= query_response_error_next;
if (store_query) begin
query_ip_reg <= query_request_ip;
end
if (store_write) begin
write_ip_reg <= write_request_ip;
write_mac_reg <= write_request_mac;
end
wr_ptr_reg <= wr_ptr_next;
rd_ptr_reg <= rd_ptr_next;
query_response_mac_reg <= mac_addr_mem[rd_ptr_reg];
if (mem_write) begin
valid_mem[wr_ptr_reg] <= 1'b1;
ip_addr_mem[wr_ptr_reg] <= write_ip_reg;
mac_addr_mem[wr_ptr_reg] <= write_mac_reg;
end
end
endmodule

391
fpga/src/arp_eth_rx.sv Normal file
View file

@ -0,0 +1,391 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* ARP ethernet frame receiver (Ethernet frame in, ARP frame out)
*/
module arp_eth_rx
(
input wire clk,
input wire rst,
/*
* Ethernet frame input
*/
input wire s_eth_hdr_valid,
output wire s_eth_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [7:0] s_eth_payload_axis_tdata,
input wire s_eth_payload_axis_tvalid,
output wire s_eth_payload_axis_tready,
input wire s_eth_payload_axis_tlast,
input wire s_eth_payload_axis_tuser,
/*
* ARP frame output
*/
output wire m_frame_valid,
input wire m_frame_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [15:0] m_arp_htype,
output wire [15:0] m_arp_ptype,
output wire [7:0] m_arp_hlen,
output wire [7:0] m_arp_plen,
output wire [15:0] m_arp_oper,
output wire [47:0] m_arp_sha,
output wire [31:0] m_arp_spa,
output wire [47:0] m_arp_tha,
output wire [31:0] m_arp_tpa,
/*
* Status signals
*/
output wire busy,
output wire error_header_early_termination,
output wire error_invalid_header
);
/*
ARP Frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype (0x0806) 2 octets
HTYPE (1) 2 octets
PTYPE (0x0800) 2 octets
HLEN (6) 1 octets
PLEN (4) 1 octets
OPER 2 octets
SHA Sender MAC 6 octets
SPA Sender IP 4 octets
THA Target MAC 6 octets
TPA Target IP 4 octets
This module receives an Ethernet frame with header fields in parallel and
payload on an AXI stream interface, decodes the ARP packet fields, and
produces the frame fields in parallel.
*/
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_READ_HEADER = 3'd1,
STATE_WAIT_LAST = 3'd2;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_eth_hdr;
reg store_arp_htype_0;
reg store_arp_htype_1;
reg store_arp_ptype_0;
reg store_arp_ptype_1;
reg store_arp_hlen;
reg store_arp_plen;
reg store_arp_oper_0;
reg store_arp_oper_1;
reg store_arp_sha_0;
reg store_arp_sha_1;
reg store_arp_sha_2;
reg store_arp_sha_3;
reg store_arp_sha_4;
reg store_arp_sha_5;
reg store_arp_spa_0;
reg store_arp_spa_1;
reg store_arp_spa_2;
reg store_arp_spa_3;
reg store_arp_tha_0;
reg store_arp_tha_1;
reg store_arp_tha_2;
reg store_arp_tha_3;
reg store_arp_tha_4;
reg store_arp_tha_5;
reg store_arp_tpa_0;
reg store_arp_tpa_1;
reg store_arp_tpa_2;
reg store_arp_tpa_3;
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next;
reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next;
reg m_frame_valid_reg = 1'b0, m_frame_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg [15:0] m_arp_htype_reg = 16'd0;
reg [15:0] m_arp_ptype_reg = 16'd0;
reg [7:0] m_arp_hlen_reg = 8'd0;
reg [7:0] m_arp_plen_reg = 8'd0;
reg [15:0] m_arp_oper_reg = 16'd0;
reg [47:0] m_arp_sha_reg = 48'd0;
reg [31:0] m_arp_spa_reg = 32'd0;
reg [47:0] m_arp_tha_reg = 48'd0;
reg [31:0] m_arp_tpa_reg = 32'd0;
reg busy_reg = 1'b0;
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
reg error_invalid_header_reg = 1'b0, error_invalid_header_next;
assign s_eth_hdr_ready = s_eth_hdr_ready_reg;
assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg;
assign m_frame_valid = m_frame_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign m_arp_htype = m_arp_htype_reg;
assign m_arp_ptype = m_arp_ptype_reg;
assign m_arp_hlen = m_arp_hlen_reg;
assign m_arp_plen = m_arp_plen_reg;
assign m_arp_oper = m_arp_oper_reg;
assign m_arp_sha = m_arp_sha_reg;
assign m_arp_spa = m_arp_spa_reg;
assign m_arp_tha = m_arp_tha_reg;
assign m_arp_tpa = m_arp_tpa_reg;
assign busy = busy_reg;
assign error_header_early_termination = error_header_early_termination_reg;
assign error_invalid_header = error_invalid_header_reg;
always @* begin
state_next = STATE_IDLE;
s_eth_hdr_ready_next = 1'b0;
s_eth_payload_axis_tready_next = 1'b0;
store_eth_hdr = 1'b0;
store_arp_htype_0 = 1'b0;
store_arp_htype_1 = 1'b0;
store_arp_ptype_0 = 1'b0;
store_arp_ptype_1 = 1'b0;
store_arp_hlen = 1'b0;
store_arp_plen = 1'b0;
store_arp_oper_0 = 1'b0;
store_arp_oper_1 = 1'b0;
store_arp_sha_0 = 1'b0;
store_arp_sha_1 = 1'b0;
store_arp_sha_2 = 1'b0;
store_arp_sha_3 = 1'b0;
store_arp_sha_4 = 1'b0;
store_arp_sha_5 = 1'b0;
store_arp_spa_0 = 1'b0;
store_arp_spa_1 = 1'b0;
store_arp_spa_2 = 1'b0;
store_arp_spa_3 = 1'b0;
store_arp_tha_0 = 1'b0;
store_arp_tha_1 = 1'b0;
store_arp_tha_2 = 1'b0;
store_arp_tha_3 = 1'b0;
store_arp_tha_4 = 1'b0;
store_arp_tha_5 = 1'b0;
store_arp_tpa_0 = 1'b0;
store_arp_tpa_1 = 1'b0;
store_arp_tpa_2 = 1'b0;
store_arp_tpa_3 = 1'b0;
frame_ptr_next = frame_ptr_reg;
m_frame_valid_next = m_frame_valid_reg && !m_frame_ready;
error_header_early_termination_next = 1'b0;
error_invalid_header_next = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_ptr_next = 8'd0;
s_eth_hdr_ready_next = !m_frame_valid_next;
if (s_eth_hdr_ready && s_eth_hdr_valid) begin
s_eth_hdr_ready_next = 1'b0;
s_eth_payload_axis_tready_next = 1'b1;
store_eth_hdr = 1'b1;
state_next = STATE_READ_HEADER;
end else begin
state_next = STATE_IDLE;
end
end
STATE_READ_HEADER: begin
// read header state
s_eth_payload_axis_tready_next = 1'b1;
if (s_eth_payload_axis_tvalid) begin
// word transfer in - store it
frame_ptr_next = frame_ptr_reg + 8'd1;
state_next = STATE_READ_HEADER;
case (frame_ptr_reg)
8'h00: store_arp_htype_1 = 1'b1;
8'h01: store_arp_htype_0 = 1'b1;
8'h02: store_arp_ptype_1 = 1'b1;
8'h03: store_arp_ptype_0 = 1'b1;
8'h04: store_arp_hlen = 1'b1;
8'h05: store_arp_plen = 1'b1;
8'h06: store_arp_oper_1 = 1'b1;
8'h07: store_arp_oper_0 = 1'b1;
8'h08: store_arp_sha_5 = 1'b1;
8'h09: store_arp_sha_4 = 1'b1;
8'h0A: store_arp_sha_3 = 1'b1;
8'h0B: store_arp_sha_2 = 1'b1;
8'h0C: store_arp_sha_1 = 1'b1;
8'h0D: store_arp_sha_0 = 1'b1;
8'h0E: store_arp_spa_3 = 1'b1;
8'h0F: store_arp_spa_2 = 1'b1;
8'h10: store_arp_spa_1 = 1'b1;
8'h11: store_arp_spa_0 = 1'b1;
8'h12: store_arp_tha_5 = 1'b1;
8'h13: store_arp_tha_4 = 1'b1;
8'h14: store_arp_tha_3 = 1'b1;
8'h15: store_arp_tha_2 = 1'b1;
8'h16: store_arp_tha_1 = 1'b1;
8'h17: store_arp_tha_0 = 1'b1;
8'h18: store_arp_tpa_3 = 1'b1;
8'h19: store_arp_tpa_2 = 1'b1;
8'h1A: store_arp_tpa_1 = 1'b1;
8'h1B: begin
store_arp_tpa_0 = 1'b1;
state_next = STATE_WAIT_LAST;
end
endcase
if (s_eth_payload_axis_tlast) begin
// end of frame
if (frame_ptr_reg != 8'h1B) begin
// don't have the whole header
error_header_early_termination_next = 1'b1;
end else if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin
// lengths not valid
error_invalid_header_next = 1'b1;
end else begin
// otherwise, transfer tuser
m_frame_valid_next = !s_eth_payload_axis_tuser;
end
s_eth_hdr_ready_next = !m_frame_valid_next;
s_eth_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_READ_HEADER;
end
end
STATE_WAIT_LAST: begin
// wait for end of frame; read and discard
s_eth_payload_axis_tready_next = 1'b1;
if (s_eth_payload_axis_tvalid) begin
if (s_eth_payload_axis_tlast) begin
if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin
// lengths not valid
error_invalid_header_next = 1'b1;
end else begin
// otherwise, transfer tuser
m_frame_valid_next = !s_eth_payload_axis_tuser;
end
s_eth_hdr_ready_next = !m_frame_valid_next;
s_eth_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT_LAST;
end
end else begin
// wait for end of frame; read and discard
state_next = STATE_WAIT_LAST;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 8'd0;
s_eth_payload_axis_tready_reg <= 1'b0;
m_frame_valid_reg <= 1'b0;
busy_reg <= 1'b0;
error_header_early_termination_reg <= 1'b0;
error_invalid_header_reg <= 1'b0;
end else begin
state_reg <= state_next;
s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next;
frame_ptr_reg <= frame_ptr_next;
m_frame_valid_reg <= m_frame_valid_next;
error_header_early_termination_reg <= error_header_early_termination_next;
error_invalid_header_reg <= error_invalid_header_next;
busy_reg <= state_next != STATE_IDLE;
end
// datapath
if (store_eth_hdr) begin
m_eth_dest_mac_reg <= s_eth_dest_mac;
m_eth_src_mac_reg <= s_eth_src_mac;
m_eth_type_reg <= s_eth_type;
end
if (store_arp_htype_0) m_arp_htype_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_arp_htype_1) m_arp_htype_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_arp_ptype_0) m_arp_ptype_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_arp_ptype_1) m_arp_ptype_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_arp_hlen) m_arp_hlen_reg <= s_eth_payload_axis_tdata;
if (store_arp_plen) m_arp_plen_reg <= s_eth_payload_axis_tdata;
if (store_arp_oper_0) m_arp_oper_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_arp_oper_1) m_arp_oper_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_arp_sha_0) m_arp_sha_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_arp_sha_1) m_arp_sha_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_arp_sha_2) m_arp_sha_reg[23:16] <= s_eth_payload_axis_tdata;
if (store_arp_sha_3) m_arp_sha_reg[31:24] <= s_eth_payload_axis_tdata;
if (store_arp_sha_4) m_arp_sha_reg[39:32] <= s_eth_payload_axis_tdata;
if (store_arp_sha_5) m_arp_sha_reg[47:40] <= s_eth_payload_axis_tdata;
if (store_arp_spa_0) m_arp_spa_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_arp_spa_1) m_arp_spa_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_arp_spa_2) m_arp_spa_reg[23:16] <= s_eth_payload_axis_tdata;
if (store_arp_spa_3) m_arp_spa_reg[31:24] <= s_eth_payload_axis_tdata;
if (store_arp_tha_0) m_arp_tha_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_arp_tha_1) m_arp_tha_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_arp_tha_2) m_arp_tha_reg[23:16] <= s_eth_payload_axis_tdata;
if (store_arp_tha_3) m_arp_tha_reg[31:24] <= s_eth_payload_axis_tdata;
if (store_arp_tha_4) m_arp_tha_reg[39:32] <= s_eth_payload_axis_tdata;
if (store_arp_tha_5) m_arp_tha_reg[47:40] <= s_eth_payload_axis_tdata;
if (store_arp_tpa_0) m_arp_tpa_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_arp_tpa_1) m_arp_tpa_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_arp_tpa_2) m_arp_tpa_reg[23:16] <= s_eth_payload_axis_tdata;
if (store_arp_tpa_3) m_arp_tpa_reg[31:24] <= s_eth_payload_axis_tdata;
end
endmodule

340
fpga/src/arp_eth_tx.sv Normal file
View file

@ -0,0 +1,340 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* ARP ethernet frame transmitter (ARP frame in, Ethernet frame out)
*/
module arp_eth_tx
(
input wire clk,
input wire rst,
/*
* ARP frame input
*/
input wire s_frame_valid,
output wire s_frame_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [15:0] s_arp_htype,
input wire [15:0] s_arp_ptype,
input wire [15:0] s_arp_oper,
input wire [47:0] s_arp_sha,
input wire [31:0] s_arp_spa,
input wire [47:0] s_arp_tha,
input wire [31:0] s_arp_tpa,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [7:0] m_eth_payload_axis_tdata,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire m_eth_payload_axis_tuser,
/*
* Status signals
*/
output wire busy
);
/*
ARP Frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype (0x0806) 2 octets
HTYPE (1) 2 octets
PTYPE (0x0800) 2 octets
HLEN (6) 1 octets
PLEN (4) 1 octets
OPER 2 octets
SHA Sender MAC 6 octets
SPA Sender IP 4 octets
THA Target MAC 6 octets
TPA Target IP 4 octets
This module receives an ARP frame with header fields in parallel and
transmits the complete Ethernet payload on an AXI interface.
*/
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_WRITE_HEADER = 2'd1;
reg [1:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_frame;
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
reg [15:0] arp_htype_reg = 16'd0;
reg [15:0] arp_ptype_reg = 16'd0;
reg [15:0] arp_oper_reg = 16'd0;
reg [47:0] arp_sha_reg = 48'd0;
reg [31:0] arp_spa_reg = 32'd0;
reg [47:0] arp_tha_reg = 48'd0;
reg [31:0] arp_tpa_reg = 32'd0;
reg s_frame_ready_reg = 1'b0, s_frame_ready_next;
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg busy_reg = 1'b0;
// internal datapath
reg [7:0] m_eth_payload_axis_tdata_int;
reg m_eth_payload_axis_tvalid_int;
reg m_eth_payload_axis_tready_int_reg = 1'b0;
reg m_eth_payload_axis_tlast_int;
reg m_eth_payload_axis_tuser_int;
wire m_eth_payload_axis_tready_int_early;
assign s_frame_ready = s_frame_ready_reg;
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign busy = busy_reg;
always @* begin
state_next = STATE_IDLE;
s_frame_ready_next = 1'b0;
store_frame = 1'b0;
frame_ptr_next = frame_ptr_reg;
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
m_eth_payload_axis_tdata_int = 8'd0;
m_eth_payload_axis_tvalid_int = 1'b0;
m_eth_payload_axis_tlast_int = 1'b0;
m_eth_payload_axis_tuser_int = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_ptr_next = 8'd0;
s_frame_ready_next = !m_eth_hdr_valid_next;
if (s_frame_ready && s_frame_valid) begin
store_frame = 1'b1;
s_frame_ready_next = 1'b0;
m_eth_hdr_valid_next = 1'b1;
if (m_eth_payload_axis_tready_int_reg) begin
m_eth_payload_axis_tvalid_int = 1'b1;
m_eth_payload_axis_tdata_int = s_arp_htype[15: 8];
frame_ptr_next = 8'd1;
end
state_next = STATE_WRITE_HEADER;
end else begin
state_next = STATE_IDLE;
end
end
STATE_WRITE_HEADER: begin
// read header state
if (m_eth_payload_axis_tready_int_reg) begin
// word transfer out
frame_ptr_next = frame_ptr_reg + 8'd1;
m_eth_payload_axis_tvalid_int = 1'b1;
state_next = STATE_WRITE_HEADER;
case (frame_ptr_reg)
8'h00: m_eth_payload_axis_tdata_int = arp_htype_reg[15: 8];
8'h01: m_eth_payload_axis_tdata_int = arp_htype_reg[ 7: 0];
8'h02: m_eth_payload_axis_tdata_int = arp_ptype_reg[15: 8];
8'h03: m_eth_payload_axis_tdata_int = arp_ptype_reg[ 7: 0];
8'h04: m_eth_payload_axis_tdata_int = 8'd6; // hlen
8'h05: m_eth_payload_axis_tdata_int = 8'd4; // plen
8'h06: m_eth_payload_axis_tdata_int = arp_oper_reg[15: 8];
8'h07: m_eth_payload_axis_tdata_int = arp_oper_reg[ 7: 0];
8'h08: m_eth_payload_axis_tdata_int = arp_sha_reg[47:40];
8'h09: m_eth_payload_axis_tdata_int = arp_sha_reg[39:32];
8'h0A: m_eth_payload_axis_tdata_int = arp_sha_reg[31:24];
8'h0B: m_eth_payload_axis_tdata_int = arp_sha_reg[23:16];
8'h0C: m_eth_payload_axis_tdata_int = arp_sha_reg[15: 8];
8'h0D: m_eth_payload_axis_tdata_int = arp_sha_reg[ 7: 0];
8'h0E: m_eth_payload_axis_tdata_int = arp_spa_reg[31:24];
8'h0F: m_eth_payload_axis_tdata_int = arp_spa_reg[23:16];
8'h10: m_eth_payload_axis_tdata_int = arp_spa_reg[15: 8];
8'h11: m_eth_payload_axis_tdata_int = arp_spa_reg[ 7: 0];
8'h12: m_eth_payload_axis_tdata_int = arp_tha_reg[47:40];
8'h13: m_eth_payload_axis_tdata_int = arp_tha_reg[39:32];
8'h14: m_eth_payload_axis_tdata_int = arp_tha_reg[31:24];
8'h15: m_eth_payload_axis_tdata_int = arp_tha_reg[23:16];
8'h16: m_eth_payload_axis_tdata_int = arp_tha_reg[15: 8];
8'h17: m_eth_payload_axis_tdata_int = arp_tha_reg[ 7: 0];
8'h18: m_eth_payload_axis_tdata_int = arp_tpa_reg[31:24];
8'h19: m_eth_payload_axis_tdata_int = arp_tpa_reg[23:16];
8'h1A: m_eth_payload_axis_tdata_int = arp_tpa_reg[15: 8];
8'h1B: begin
m_eth_payload_axis_tdata_int = arp_tpa_reg[ 7: 0];
m_eth_payload_axis_tlast_int = 1'b1;
s_frame_ready_next = !m_eth_hdr_valid_next;
state_next = STATE_IDLE;
end
endcase
end else begin
state_next = STATE_WRITE_HEADER;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 8'd0;
s_frame_ready_reg <= 1'b0;
m_eth_hdr_valid_reg <= 1'b0;
busy_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
s_frame_ready_reg <= s_frame_ready_next;
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
busy_reg <= state_next != STATE_IDLE;
end
if (store_frame) begin
m_eth_dest_mac_reg <= s_eth_dest_mac;
m_eth_src_mac_reg <= s_eth_src_mac;
m_eth_type_reg <= s_eth_type;
arp_htype_reg <= s_arp_htype;
arp_ptype_reg <= s_arp_ptype;
arp_oper_reg <= s_arp_oper;
arp_sha_reg <= s_arp_sha;
arp_spa_reg <= s_arp_spa;
arp_tha_reg <= s_arp_tha;
arp_tpa_reg <= s_arp_tpa;
end
end
// output datapath logic
reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0;
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
reg m_eth_payload_axis_tlast_reg = 1'b0;
reg m_eth_payload_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0;
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
reg temp_m_eth_payload_axis_tuser_reg = 1'b0;
// datapath control
reg store_eth_payload_int_to_output;
reg store_eth_payload_int_to_temp;
reg store_eth_payload_axis_temp_to_output;
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
store_eth_payload_int_to_output = 1'b0;
store_eth_payload_int_to_temp = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b0;
if (m_eth_payload_axis_tready_int_reg) begin
// input is ready
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_eth_payload_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_eth_payload_int_to_temp = 1'b1;
end
end else if (m_eth_payload_axis_tready) begin
// input is not ready, but output is ready
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_eth_payload_axis_tvalid_reg <= 1'b0;
m_eth_payload_axis_tready_int_reg <= 1'b0;
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
end
// datapath
if (store_eth_payload_int_to_output) begin
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end else if (store_eth_payload_axis_temp_to_output) begin
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
end
if (store_eth_payload_int_to_temp) begin
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end
end
endmodule

453
fpga/src/axis_async_fifo.sv Normal file
View file

@ -0,0 +1,453 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream asynchronous FIFO
*/
module axis_async_fifo #
(
parameter ADDR_WIDTH = 12,
parameter DATA_WIDTH = 8,
parameter KEEP_ENABLE = (DATA_WIDTH>8),
parameter KEEP_WIDTH = (DATA_WIDTH/8),
parameter LAST_ENABLE = 1,
parameter ID_ENABLE = 0,
parameter ID_WIDTH = 8,
parameter DEST_ENABLE = 0,
parameter DEST_WIDTH = 8,
parameter USER_ENABLE = 1,
parameter USER_WIDTH = 1,
parameter FRAME_FIFO = 0,
parameter USER_BAD_FRAME_VALUE = 1'b1,
parameter USER_BAD_FRAME_MASK = 1'b1,
parameter DROP_BAD_FRAME = 0,
parameter DROP_WHEN_FULL = 0
)
(
/*
* Common asynchronous reset
*/
input wire async_rst,
/*
* AXI input
*/
input wire s_clk,
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
input wire m_clk,
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Status
*/
output wire s_status_overflow,
output wire s_status_bad_frame,
output wire s_status_good_frame,
output wire m_status_overflow,
output wire m_status_bad_frame,
output wire m_status_good_frame
);
// check configuration
initial begin
if (FRAME_FIFO && !LAST_ENABLE) begin
$error("Error: FRAME_FIFO set requires LAST_ENABLE set");
$finish;
end
if (DROP_BAD_FRAME && !FRAME_FIFO) begin
$error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set");
$finish;
end
if (DROP_WHEN_FULL && !FRAME_FIFO) begin
$error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set");
$finish;
end
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
$error("Error: Invalid USER_BAD_FRAME_MASK value");
$finish;
end
end
localparam KEEP_OFFSET = DATA_WIDTH;
localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0);
localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0);
localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0);
localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0);
localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0);
reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next;
reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next;
reg [ADDR_WIDTH:0] wr_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_gray_next;
reg [ADDR_WIDTH:0] wr_ptr_cur_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_gray_next;
reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}};
reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next;
reg [ADDR_WIDTH:0] rd_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_gray_next;
reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}};
reg [ADDR_WIDTH:0] wr_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}};
reg [ADDR_WIDTH:0] wr_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}};
reg [ADDR_WIDTH:0] rd_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}};
reg [ADDR_WIDTH:0] rd_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}};
reg s_rst_sync1_reg = 1'b1;
reg s_rst_sync2_reg = 1'b1;
reg s_rst_sync3_reg = 1'b1;
reg m_rst_sync1_reg = 1'b1;
reg m_rst_sync2_reg = 1'b1;
reg m_rst_sync3_reg = 1'b1;
reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0];
reg [WIDTH-1:0] mem_read_data_reg;
reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next;
wire [WIDTH-1:0] s_axis;
reg [WIDTH-1:0] m_axis_reg;
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
// full when first TWO MSBs do NOT match, but rest matches
// (gray code equivalent of first MSB different but rest same)
wire full = ((wr_ptr_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) &&
(wr_ptr_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) &&
(wr_ptr_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0]));
wire full_cur = ((wr_ptr_cur_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) &&
(wr_ptr_cur_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) &&
(wr_ptr_cur_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0]));
// empty when pointers match exactly
wire empty = rd_ptr_gray_reg == wr_ptr_gray_sync2_reg;
// overflow within packet
wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) &&
(wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0]));
// control signals
reg write;
reg read;
reg store_output;
reg drop_frame_reg = 1'b0, drop_frame_next;
reg overflow_reg = 1'b0, overflow_next;
reg bad_frame_reg = 1'b0, bad_frame_next;
reg good_frame_reg = 1'b0, good_frame_next;
reg overflow_sync1_reg = 1'b0;
reg overflow_sync2_reg = 1'b0;
reg overflow_sync3_reg = 1'b0;
reg overflow_sync4_reg = 1'b0;
reg bad_frame_sync1_reg = 1'b0;
reg bad_frame_sync2_reg = 1'b0;
reg bad_frame_sync3_reg = 1'b0;
reg bad_frame_sync4_reg = 1'b0;
reg good_frame_sync1_reg = 1'b0;
reg good_frame_sync2_reg = 1'b0;
reg good_frame_sync3_reg = 1'b0;
reg good_frame_sync4_reg = 1'b0;
assign s_axis_tready = (FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full) && !s_rst_sync3_reg;
generate
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
endgenerate
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0];
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}};
assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}};
assign s_status_overflow = overflow_reg;
assign s_status_bad_frame = bad_frame_reg;
assign s_status_good_frame = good_frame_reg;
assign m_status_overflow = overflow_sync3_reg ^ overflow_sync4_reg;
assign m_status_bad_frame = bad_frame_sync3_reg ^ bad_frame_sync4_reg;
assign m_status_good_frame = good_frame_sync3_reg ^ good_frame_sync4_reg;
// reset synchronization
always @(posedge s_clk or posedge async_rst) begin
if (async_rst) begin
s_rst_sync1_reg <= 1'b1;
s_rst_sync2_reg <= 1'b1;
s_rst_sync3_reg <= 1'b1;
end else begin
s_rst_sync1_reg <= 1'b0;
s_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg;
s_rst_sync3_reg <= s_rst_sync2_reg;
end
end
always @(posedge m_clk or posedge async_rst) begin
if (async_rst) begin
m_rst_sync1_reg <= 1'b1;
m_rst_sync2_reg <= 1'b1;
m_rst_sync3_reg <= 1'b1;
end else begin
m_rst_sync1_reg <= 1'b0;
m_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg;
m_rst_sync3_reg <= m_rst_sync2_reg;
end
end
// Write logic
always @* begin
write = 1'b0;
drop_frame_next = 1'b0;
overflow_next = 1'b0;
bad_frame_next = 1'b0;
good_frame_next = 1'b0;
wr_ptr_next = wr_ptr_reg;
wr_ptr_cur_next = wr_ptr_cur_reg;
wr_ptr_gray_next = wr_ptr_gray_reg;
wr_ptr_cur_gray_next = wr_ptr_cur_gray_reg;
if (s_axis_tready && s_axis_tvalid) begin
// transfer in
if (!FRAME_FIFO) begin
// normal FIFO mode
write = 1'b1;
wr_ptr_next = wr_ptr_reg + 1;
wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1);
end else if (full_cur || full_wr || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_next = 1'b1;
if (s_axis_tlast) begin
// end of frame, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1);
drop_frame_next = 1'b0;
overflow_next = 1'b1;
end
end else begin
write = 1'b1;
wr_ptr_cur_next = wr_ptr_cur_reg + 1;
wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1);
if (s_axis_tlast) begin
// end of frame
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & s_axis_tuser == USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1);
bad_frame_next = 1'b1;
end else begin
// good packet, update write pointer
wr_ptr_next = wr_ptr_cur_reg + 1;
wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1);
good_frame_next = 1'b1;
end
end
end
end
end
always @(posedge s_clk) begin
if (s_rst_sync3_reg) begin
wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_cur_gray_reg <= {ADDR_WIDTH+1{1'b0}};
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b0;
bad_frame_reg <= 1'b0;
good_frame_reg <= 1'b0;
end else begin
wr_ptr_reg <= wr_ptr_next;
wr_ptr_cur_reg <= wr_ptr_cur_next;
wr_ptr_gray_reg <= wr_ptr_gray_next;
wr_ptr_cur_gray_reg <= wr_ptr_cur_gray_next;
drop_frame_reg <= drop_frame_next;
overflow_reg <= overflow_next;
bad_frame_reg <= bad_frame_next;
good_frame_reg <= good_frame_next;
end
if (FRAME_FIFO) begin
wr_addr_reg <= wr_ptr_cur_next;
end else begin
wr_addr_reg <= wr_ptr_next;
end
if (write) begin
mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis;
end
end
// pointer synchronization
always @(posedge s_clk) begin
if (s_rst_sync3_reg) begin
rd_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}};
rd_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}};
end else begin
rd_ptr_gray_sync1_reg <= rd_ptr_gray_reg;
rd_ptr_gray_sync2_reg <= rd_ptr_gray_sync1_reg;
end
end
always @(posedge m_clk) begin
if (m_rst_sync3_reg) begin
wr_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}};
end else begin
wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg;
wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg;
end
end
// status synchronization
always @(posedge s_clk) begin
if (s_rst_sync3_reg) begin
overflow_sync1_reg <= 1'b0;
bad_frame_sync1_reg <= 1'b0;
good_frame_sync1_reg <= 1'b0;
end else begin
overflow_sync1_reg <= overflow_sync1_reg ^ overflow_reg;
bad_frame_sync1_reg <= bad_frame_sync1_reg ^ bad_frame_reg;
good_frame_sync1_reg <= good_frame_sync1_reg ^ good_frame_reg;
end
end
always @(posedge m_clk) begin
if (m_rst_sync3_reg) begin
overflow_sync2_reg <= 1'b0;
overflow_sync3_reg <= 1'b0;
bad_frame_sync2_reg <= 1'b0;
bad_frame_sync3_reg <= 1'b0;
good_frame_sync2_reg <= 1'b0;
good_frame_sync3_reg <= 1'b0;
end else begin
overflow_sync2_reg <= overflow_sync1_reg;
overflow_sync3_reg <= overflow_sync2_reg;
overflow_sync4_reg <= overflow_sync3_reg;
bad_frame_sync2_reg <= bad_frame_sync1_reg;
bad_frame_sync3_reg <= bad_frame_sync2_reg;
bad_frame_sync4_reg <= bad_frame_sync3_reg;
good_frame_sync2_reg <= good_frame_sync1_reg;
good_frame_sync3_reg <= good_frame_sync2_reg;
good_frame_sync4_reg <= good_frame_sync3_reg;
end
end
// Read logic
always @* begin
read = 1'b0;
rd_ptr_next = rd_ptr_reg;
rd_ptr_gray_next = rd_ptr_gray_reg;
mem_read_data_valid_next = mem_read_data_valid_reg;
if (store_output || !mem_read_data_valid_reg) begin
// output data not valid OR currently being transferred
if (!empty) begin
// not empty, perform read
read = 1'b1;
mem_read_data_valid_next = 1'b1;
rd_ptr_next = rd_ptr_reg + 1;
rd_ptr_gray_next = rd_ptr_next ^ (rd_ptr_next >> 1);
end else begin
// empty, invalidate
mem_read_data_valid_next = 1'b0;
end
end
end
always @(posedge m_clk) begin
if (m_rst_sync3_reg) begin
rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
rd_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}};
mem_read_data_valid_reg <= 1'b0;
end else begin
rd_ptr_reg <= rd_ptr_next;
rd_ptr_gray_reg <= rd_ptr_gray_next;
mem_read_data_valid_reg <= mem_read_data_valid_next;
end
rd_addr_reg <= rd_ptr_next;
if (read) begin
mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]];
end
end
// Output register
always @* begin
store_output = 1'b0;
m_axis_tvalid_next = m_axis_tvalid_reg;
if (m_axis_tready || !m_axis_tvalid) begin
store_output = 1'b1;
m_axis_tvalid_next = mem_read_data_valid_reg;
end
end
always @(posedge m_clk) begin
if (m_rst_sync3_reg) begin
m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
end
if (store_output) begin
m_axis_reg <= mem_read_data_reg;
end
end
endmodule

316
fpga/src/axis_fifo.sv Normal file
View file

@ -0,0 +1,316 @@
/*
Copyright (c) 2013-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream FIFO
*/
module axis_fifo #
(
parameter ADDR_WIDTH = 12,
parameter DATA_WIDTH = 8,
parameter KEEP_ENABLE = (DATA_WIDTH>8),
parameter KEEP_WIDTH = (DATA_WIDTH/8),
parameter LAST_ENABLE = 1,
parameter ID_ENABLE = 0,
parameter ID_WIDTH = 8,
parameter DEST_ENABLE = 0,
parameter DEST_WIDTH = 8,
parameter USER_ENABLE = 1,
parameter USER_WIDTH = 1,
parameter FRAME_FIFO = 0,
parameter USER_BAD_FRAME_VALUE = 1'b1,
parameter USER_BAD_FRAME_MASK = 1'b1,
parameter DROP_BAD_FRAME = 0,
parameter DROP_WHEN_FULL = 0
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [DATA_WIDTH-1:0] s_axis_tdata,
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire [ID_WIDTH-1:0] s_axis_tid,
input wire [DEST_WIDTH-1:0] s_axis_tdest,
input wire [USER_WIDTH-1:0] s_axis_tuser,
/*
* AXI output
*/
output wire [DATA_WIDTH-1:0] m_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire [ID_WIDTH-1:0] m_axis_tid,
output wire [DEST_WIDTH-1:0] m_axis_tdest,
output wire [USER_WIDTH-1:0] m_axis_tuser,
/*
* Status
*/
output wire status_overflow,
output wire status_bad_frame,
output wire status_good_frame
);
// check configuration
initial begin
if (FRAME_FIFO && !LAST_ENABLE) begin
$error("Error: FRAME_FIFO set requires LAST_ENABLE set");
$finish;
end
if (DROP_BAD_FRAME && !FRAME_FIFO) begin
$error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set");
$finish;
end
if (DROP_WHEN_FULL && !FRAME_FIFO) begin
$error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set");
$finish;
end
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
$error("Error: Invalid USER_BAD_FRAME_MASK value");
$finish;
end
end
localparam KEEP_OFFSET = DATA_WIDTH;
localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0);
localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0);
localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0);
localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0);
localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0);
reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next;
reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next;
reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}};
reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next;
reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}};
reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0];
reg [WIDTH-1:0] mem_read_data_reg;
reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next;
wire [WIDTH-1:0] s_axis;
reg [WIDTH-1:0] m_axis_reg;
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
// full when first MSB different but rest same
wire full = ((wr_ptr_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) &&
(wr_ptr_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0]));
wire full_cur = ((wr_ptr_cur_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) &&
(wr_ptr_cur_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0]));
// empty when pointers match exactly
wire empty = wr_ptr_reg == rd_ptr_reg;
// overflow within packet
wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) &&
(wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0]));
// control signals
reg write;
reg read;
reg store_output;
reg drop_frame_reg = 1'b0, drop_frame_next;
reg overflow_reg = 1'b0, overflow_next;
reg bad_frame_reg = 1'b0, bad_frame_next;
reg good_frame_reg = 1'b0, good_frame_next;
assign s_axis_tready = FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full;
generate
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
endgenerate
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0];
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}};
assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1;
assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}};
assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}};
assign status_overflow = overflow_reg;
assign status_bad_frame = bad_frame_reg;
assign status_good_frame = good_frame_reg;
// Write logic
always @* begin
write = 1'b0;
drop_frame_next = 1'b0;
overflow_next = 1'b0;
bad_frame_next = 1'b0;
good_frame_next = 1'b0;
wr_ptr_next = wr_ptr_reg;
wr_ptr_cur_next = wr_ptr_cur_reg;
if (s_axis_tready && s_axis_tvalid) begin
// transfer in
if (!FRAME_FIFO) begin
// normal FIFO mode
write = 1'b1;
wr_ptr_next = wr_ptr_reg + 1;
end else if (full_cur || full_wr || drop_frame_reg) begin
// full, packet overflow, or currently dropping frame
// drop frame
drop_frame_next = 1'b1;
if (s_axis_tlast) begin
// end of frame, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
drop_frame_next = 1'b0;
overflow_next = 1'b1;
end
end else begin
write = 1'b1;
wr_ptr_cur_next = wr_ptr_cur_reg + 1;
if (s_axis_tlast) begin
// end of frame
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & s_axis_tuser == USER_BAD_FRAME_VALUE)) begin
// bad packet, reset write pointer
wr_ptr_cur_next = wr_ptr_reg;
bad_frame_next = 1'b1;
end else begin
// good packet, update write pointer
wr_ptr_next = wr_ptr_cur_reg + 1;
good_frame_next = 1'b1;
end
end
end
end
end
always @(posedge clk) begin
if (rst) begin
wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}};
drop_frame_reg <= 1'b0;
overflow_reg <= 1'b0;
bad_frame_reg <= 1'b0;
good_frame_reg <= 1'b0;
end else begin
wr_ptr_reg <= wr_ptr_next;
wr_ptr_cur_reg <= wr_ptr_cur_next;
drop_frame_reg <= drop_frame_next;
overflow_reg <= overflow_next;
bad_frame_reg <= bad_frame_next;
good_frame_reg <= good_frame_next;
end
if (FRAME_FIFO) begin
wr_addr_reg <= wr_ptr_cur_next;
end else begin
wr_addr_reg <= wr_ptr_next;
end
if (write) begin
mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis;
end
end
// Read logic
always @* begin
read = 1'b0;
rd_ptr_next = rd_ptr_reg;
mem_read_data_valid_next = mem_read_data_valid_reg;
if (store_output || !mem_read_data_valid_reg) begin
// output data not valid OR currently being transferred
if (!empty) begin
// not empty, perform read
read = 1'b1;
mem_read_data_valid_next = 1'b1;
rd_ptr_next = rd_ptr_reg + 1;
end else begin
// empty, invalidate
mem_read_data_valid_next = 1'b0;
end
end
end
always @(posedge clk) begin
if (rst) begin
rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
mem_read_data_valid_reg <= 1'b0;
end else begin
rd_ptr_reg <= rd_ptr_next;
mem_read_data_valid_reg <= mem_read_data_valid_next;
end
rd_addr_reg <= rd_ptr_next;
if (read) begin
mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]];
end
end
// Output register
always @* begin
store_output = 1'b0;
m_axis_tvalid_next = m_axis_tvalid_reg;
if (m_axis_tready || !m_axis_tvalid) begin
store_output = 1'b1;
m_axis_tvalid_next = mem_read_data_valid_reg;
end
end
always @(posedge clk) begin
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
end
if (store_output) begin
m_axis_reg <= mem_read_data_reg;
end
end
endmodule

318
fpga/src/axis_gmii_rx.sv Normal file
View file

@ -0,0 +1,318 @@
/*
Copyright (c) 2015-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream GMII frame receiver (GMII in, AXI out)
*/
module axis_gmii_rx
(
input wire clk,
input wire rst,
/*
* GMII input
*/
input wire [7:0] gmii_rxd,
input wire gmii_rx_dv,
input wire gmii_rx_er,
/*
* AXI output
*/
output wire [7:0] m_axis_tdata,
output wire m_axis_tvalid,
output wire m_axis_tlast,
output wire m_axis_tuser,
/*
* Control
*/
input wire clk_enable,
input wire mii_select,
/*
* Status
*/
output wire error_bad_frame,
output wire error_bad_fcs
);
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_PAYLOAD = 3'd1,
STATE_WAIT_LAST = 3'd2;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg reset_crc;
reg update_crc;
reg mii_odd = 1'b0;
reg mii_locked = 1'b0;
reg [7:0] gmii_rxd_d0 = 8'd0;
reg [7:0] gmii_rxd_d1 = 8'd0;
reg [7:0] gmii_rxd_d2 = 8'd0;
reg [7:0] gmii_rxd_d3 = 8'd0;
reg [7:0] gmii_rxd_d4 = 8'd0;
reg gmii_rx_dv_d0 = 1'b0;
reg gmii_rx_dv_d1 = 1'b0;
reg gmii_rx_dv_d2 = 1'b0;
reg gmii_rx_dv_d3 = 1'b0;
reg gmii_rx_dv_d4 = 1'b0;
reg gmii_rx_er_d0 = 1'b0;
reg gmii_rx_er_d1 = 1'b0;
reg gmii_rx_er_d2 = 1'b0;
reg gmii_rx_er_d3 = 1'b0;
reg gmii_rx_er_d4 = 1'b0;
reg [7:0] m_axis_tdata_reg = 8'd0, m_axis_tdata_next;
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next;
reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next;
reg error_bad_frame_reg = 1'b0, error_bad_frame_next;
reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next;
reg [31:0] crc_state = 32'hFFFFFFFF;
wire [31:0] crc_next;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tuser = m_axis_tuser_reg;
assign error_bad_frame = error_bad_frame_reg;
assign error_bad_fcs = error_bad_fcs_reg;
rgmii_lfsr #(
.LFSR_WIDTH(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_CONFIG("GALOIS"),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_WIDTH(8),
.STYLE("AUTO")
)
eth_crc_8 (
.data_in(gmii_rxd_d4),
.state_in(crc_state),
.data_out(),
.state_out(crc_next)
);
always @* begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
update_crc = 1'b0;
m_axis_tdata_next = 8'd0;
m_axis_tvalid_next = 1'b0;
m_axis_tlast_next = 1'b0;
m_axis_tuser_next = 1'b0;
error_bad_frame_next = 1'b0;
error_bad_fcs_next = 1'b0;
if (!clk_enable) begin
// clock disabled - hold state
state_next = state_reg;
end else if (mii_select && !mii_odd) begin
// MII even cycle - hold state
state_next = state_reg;
end else begin
case (state_reg)
STATE_IDLE: begin
// idle state - wait for packet
reset_crc = 1'b1;
if (gmii_rx_dv_d4 && !gmii_rx_er_d4 && gmii_rxd_d4 == ETH_SFD) begin
state_next = STATE_PAYLOAD;
end else begin
state_next = STATE_IDLE;
end
end
STATE_PAYLOAD: begin
// read payload
update_crc = 1'b1;
m_axis_tdata_next = gmii_rxd_d4;
m_axis_tvalid_next = 1'b1;
if (gmii_rx_dv_d4 && gmii_rx_er_d4) begin
// error
m_axis_tlast_next = 1'b1;
m_axis_tuser_next = 1'b1;
error_bad_frame_next = 1'b1;
state_next = STATE_WAIT_LAST;
end else if (!gmii_rx_dv) begin
// end of packet
m_axis_tlast_next = 1'b1;
if (gmii_rx_er_d0 || gmii_rx_er_d1 || gmii_rx_er_d2 || gmii_rx_er_d3) begin
// error received in FCS bytes
m_axis_tuser_next = 1'b1;
error_bad_frame_next = 1'b1;
end else if ({gmii_rxd_d0, gmii_rxd_d1, gmii_rxd_d2, gmii_rxd_d3} == ~crc_next) begin
// FCS good
m_axis_tuser_next = 1'b0;
end else begin
// FCS bad
m_axis_tuser_next = 1'b1;
error_bad_frame_next = 1'b1;
error_bad_fcs_next = 1'b1;
end
state_next = STATE_IDLE;
end else begin
state_next = STATE_PAYLOAD;
end
end
STATE_WAIT_LAST: begin
// wait for end of packet
if (~gmii_rx_dv) begin
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT_LAST;
end
end
endcase
end
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
m_axis_tvalid_reg <= 1'b0;
error_bad_frame_reg <= 1'b0;
error_bad_fcs_reg <= 1'b0;
crc_state <= 32'hFFFFFFFF;
mii_locked <= 1'b0;
mii_odd <= 1'b0;
gmii_rx_dv_d0 <= 1'b0;
gmii_rx_dv_d1 <= 1'b0;
gmii_rx_dv_d2 <= 1'b0;
gmii_rx_dv_d3 <= 1'b0;
gmii_rx_dv_d4 <= 1'b0;
end else begin
state_reg <= state_next;
m_axis_tvalid_reg <= m_axis_tvalid_next;
error_bad_frame_reg <= error_bad_frame_next;
error_bad_fcs_reg <= error_bad_fcs_next;
// datapath
if (reset_crc) begin
crc_state <= 32'hFFFFFFFF;
end else if (update_crc) begin
crc_state <= crc_next;
end
if (clk_enable) begin
if (mii_select) begin
mii_odd <= !mii_odd;
if (mii_locked) begin
mii_locked <= gmii_rx_dv;
end else if (gmii_rx_dv && {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == ETH_SFD) begin
mii_locked <= 1'b1;
mii_odd <= 1'b1;
end
if (mii_odd) begin
gmii_rx_dv_d0 <= gmii_rx_dv & gmii_rx_dv_d0;
gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv;
gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv;
gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv;
gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv;
end else begin
gmii_rx_dv_d0 <= gmii_rx_dv;
end
end else begin
gmii_rx_dv_d0 <= gmii_rx_dv;
gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv;
gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv;
gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv;
gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv;
end
end
end
m_axis_tdata_reg <= m_axis_tdata_next;
m_axis_tlast_reg <= m_axis_tlast_next;
m_axis_tuser_reg <= m_axis_tuser_next;
// delay input
if (clk_enable) begin
if (mii_select) begin
gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]};
if (mii_odd) begin
gmii_rxd_d1 <= gmii_rxd_d0;
gmii_rxd_d2 <= gmii_rxd_d1;
gmii_rxd_d3 <= gmii_rxd_d2;
gmii_rxd_d4 <= gmii_rxd_d3;
gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0;
gmii_rx_er_d1 <= gmii_rx_er_d0;
gmii_rx_er_d2 <= gmii_rx_er_d1;
gmii_rx_er_d3 <= gmii_rx_er_d2;
gmii_rx_er_d4 <= gmii_rx_er_d3;
end else begin
gmii_rx_er_d0 <= gmii_rx_er;
end
end else begin
gmii_rxd_d0 <= gmii_rxd;
gmii_rxd_d1 <= gmii_rxd_d0;
gmii_rxd_d2 <= gmii_rxd_d1;
gmii_rxd_d3 <= gmii_rxd_d2;
gmii_rxd_d4 <= gmii_rxd_d3;
gmii_rx_er_d0 <= gmii_rx_er;
gmii_rx_er_d1 <= gmii_rx_er_d0;
gmii_rx_er_d2 <= gmii_rx_er_d1;
gmii_rx_er_d3 <= gmii_rx_er_d2;
gmii_rx_er_d4 <= gmii_rx_er_d3;
end
end
end
endmodule

381
fpga/src/axis_gmii_tx.sv Normal file
View file

@ -0,0 +1,381 @@
/*
Copyright (c) 2015-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream GMII frame transmitter (AXI in, GMII out)
*/
module axis_gmii_tx #
(
parameter ENABLE_PADDING = 1,
parameter MIN_FRAME_LENGTH = 64
)
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [7:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire s_axis_tuser,
/*
* GMII output
*/
output wire [7:0] gmii_txd,
output wire gmii_tx_en,
output wire gmii_tx_er,
/*
* Control
*/
input wire clk_enable,
input wire mii_select,
/*
* Configuration
*/
input wire [7:0] ifg_delay
);
localparam [7:0]
ETH_PRE = 8'h55,
ETH_SFD = 8'hD5;
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_PREAMBLE = 3'd1,
STATE_PAYLOAD = 3'd2,
STATE_LAST = 3'd3,
STATE_PAD = 3'd4,
STATE_FCS = 3'd5,
STATE_WAIT_END = 3'd6,
STATE_IFG = 3'd7;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg reset_crc;
reg update_crc;
reg [7:0] s_tdata_reg = 8'd0, s_tdata_next;
reg mii_odd_reg = 1'b0, mii_odd_next;
reg [3:0] mii_msn_reg = 4'b0, mii_msn_next;
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
reg [7:0] gmii_txd_reg = 8'd0, gmii_txd_next;
reg gmii_tx_en_reg = 1'b0, gmii_tx_en_next;
reg gmii_tx_er_reg = 1'b0, gmii_tx_er_next;
reg s_axis_tready_reg = 1'b0, s_axis_tready_next;
reg [31:0] crc_state = 32'hFFFFFFFF;
wire [31:0] crc_next;
assign s_axis_tready = s_axis_tready_reg;
assign gmii_txd = gmii_txd_reg;
assign gmii_tx_en = gmii_tx_en_reg;
assign gmii_tx_er = gmii_tx_er_reg;
rgmii_lfsr #(
.LFSR_WIDTH(32),
.LFSR_POLY(32'h4c11db7),
.LFSR_CONFIG("GALOIS"),
.LFSR_FEED_FORWARD(0),
.REVERSE(1),
.DATA_WIDTH(8),
.STYLE("AUTO")
)
eth_crc_8 (
.data_in(s_tdata_reg),
.state_in(crc_state),
.data_out(),
.state_out(crc_next)
);
always @* begin
state_next = STATE_IDLE;
reset_crc = 1'b0;
update_crc = 1'b0;
mii_odd_next = mii_odd_reg;
mii_msn_next = mii_msn_reg;
frame_ptr_next = frame_ptr_reg;
s_axis_tready_next = 1'b0;
s_tdata_next = s_tdata_reg;
gmii_txd_next = 8'd0;
gmii_tx_en_next = 1'b0;
gmii_tx_er_next = 1'b0;
if (!clk_enable) begin
// clock disabled - hold state and outputs
gmii_txd_next = gmii_txd_reg;
gmii_tx_en_next = gmii_tx_en_reg;
gmii_tx_er_next = gmii_tx_er_reg;
state_next = state_reg;
end else if (mii_select && mii_odd_reg) begin
// MII odd cycle - hold state, output MSN
mii_odd_next = 1'b0;
gmii_txd_next = {4'd0, mii_msn_reg};
gmii_tx_en_next = gmii_tx_en_reg;
gmii_tx_er_next = gmii_tx_er_reg;
state_next = state_reg;
end else begin
case (state_reg)
STATE_IDLE: begin
// idle state - wait for packet
reset_crc = 1'b1;
mii_odd_next = 1'b0;
if (s_axis_tvalid) begin
mii_odd_next = 1'b1;
frame_ptr_next = 16'd1;
gmii_txd_next = ETH_PRE;
gmii_tx_en_next = 1'b1;
state_next = STATE_PREAMBLE;
end else begin
state_next = STATE_IDLE;
end
end
STATE_PREAMBLE: begin
// send preamble
reset_crc = 1'b1;
mii_odd_next = 1'b1;
frame_ptr_next = frame_ptr_reg + 16'd1;
gmii_txd_next = ETH_PRE;
gmii_tx_en_next = 1'b1;
if (frame_ptr_reg == 16'd6) begin
s_axis_tready_next = 1'b1;
s_tdata_next = s_axis_tdata;
state_next = STATE_PREAMBLE;
end else if (frame_ptr_reg == 16'd7) begin
// end of preamble; start payload
frame_ptr_next = 16'd0;
if (s_axis_tready_reg) begin
s_axis_tready_next = 1'b1;
s_tdata_next = s_axis_tdata;
end
gmii_txd_next = ETH_SFD;
state_next = STATE_PAYLOAD;
end else begin
state_next = STATE_PREAMBLE;
end
end
STATE_PAYLOAD: begin
// send payload
update_crc = 1'b1;
s_axis_tready_next = 1'b1;
mii_odd_next = 1'b1;
frame_ptr_next = frame_ptr_reg + 16'd1;
gmii_txd_next = s_tdata_reg;
gmii_tx_en_next = 1'b1;
s_tdata_next = s_axis_tdata;
if (s_axis_tvalid) begin
if (s_axis_tlast) begin
s_axis_tready_next = !s_axis_tready_reg;
if (s_axis_tuser) begin
gmii_tx_er_next = 1'b1;
frame_ptr_next = 1'b0;
state_next = STATE_IFG;
end else begin
state_next = STATE_LAST;
end
end else begin
state_next = STATE_PAYLOAD;
end
end else begin
// tvalid deassert, fail frame
gmii_tx_er_next = 1'b1;
frame_ptr_next = 16'd0;
state_next = STATE_WAIT_END;
end
end
STATE_LAST: begin
// last payload word
update_crc = 1'b1;
mii_odd_next = 1'b1;
frame_ptr_next = frame_ptr_reg + 16'd1;
gmii_txd_next = s_tdata_reg;
gmii_tx_en_next = 1'b1;
if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin
s_tdata_next = 8'd0;
state_next = STATE_PAD;
end else begin
frame_ptr_next = 16'd0;
state_next = STATE_FCS;
end
end
STATE_PAD: begin
// send padding
update_crc = 1'b1;
mii_odd_next = 1'b1;
frame_ptr_next = frame_ptr_reg + 16'd1;
gmii_txd_next = 8'd0;
gmii_tx_en_next = 1'b1;
s_tdata_next = 8'd0;
if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin
state_next = STATE_PAD;
end else begin
frame_ptr_next = 16'd0;
state_next = STATE_FCS;
end
end
STATE_FCS: begin
// send FCS
mii_odd_next = 1'b1;
frame_ptr_next = frame_ptr_reg + 16'd1;
case (frame_ptr_reg)
2'd0: gmii_txd_next = ~crc_state[7:0];
2'd1: gmii_txd_next = ~crc_state[15:8];
2'd2: gmii_txd_next = ~crc_state[23:16];
2'd3: gmii_txd_next = ~crc_state[31:24];
endcase
gmii_tx_en_next = 1'b1;
if (frame_ptr_reg < 3) begin
state_next = STATE_FCS;
end else begin
frame_ptr_next = 16'd0;
state_next = STATE_IFG;
end
end
STATE_WAIT_END: begin
// wait for end of frame
reset_crc = 1'b1;
mii_odd_next = 1'b1;
frame_ptr_next = frame_ptr_reg + 16'd1;
s_axis_tready_next = 1'b1;
if (s_axis_tvalid) begin
if (s_axis_tlast) begin
s_axis_tready_next = 1'b0;
if (frame_ptr_reg < ifg_delay-1) begin
state_next = STATE_IFG;
end else begin
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_WAIT_END;
end
end else begin
state_next = STATE_WAIT_END;
end
end
STATE_IFG: begin
// send IFG
reset_crc = 1'b1;
mii_odd_next = 1'b1;
frame_ptr_next = frame_ptr_reg + 16'd1;
if (frame_ptr_reg < ifg_delay-1) begin
state_next = STATE_IFG;
end else begin
state_next = STATE_IDLE;
end
end
endcase
if (mii_select) begin
mii_msn_next = gmii_txd_next[7:4];
gmii_txd_next[7:4] = 4'd0;
end
end
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 16'd0;
s_axis_tready_reg <= 1'b0;
gmii_tx_en_reg <= 1'b0;
gmii_tx_er_reg <= 1'b0;
crc_state <= 32'hFFFFFFFF;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
s_axis_tready_reg <= s_axis_tready_next;
gmii_tx_en_reg <= gmii_tx_en_next;
gmii_tx_er_reg <= gmii_tx_er_next;
// datapath
if (reset_crc) begin
crc_state <= 32'hFFFFFFFF;
end else if (update_crc) begin
crc_state <= crc_next;
end
end
mii_odd_reg <= mii_odd_next;
mii_msn_reg <= mii_msn_next;
s_tdata_reg <= s_tdata_next;
gmii_txd_reg <= gmii_txd_next;
end
endmodule

0
fpga/src/defines.sv Normal file
View file

67
fpga/src/dualmem_widen.sv Normal file
View file

@ -0,0 +1,67 @@
module dualmem_widen(clka, clkb, dina, dinb, addra, addrb, wea, web, douta, doutb, ena, enb);
input wire clka, clkb;
input [15:0] dina;
input [63:0] dinb;
input [10:0] addra;
input [8:0] addrb;
input [1:0] wea;
input [1:0] web;
input [0:0] ena, enb;
output [15:0] douta;
output [63:0] doutb;
genvar r;
wire [47:0] dout;
`ifdef GENESYSII
generate for (r = 0; r < 2; r=r+1)
RAMB16_S9_S36
RAMB16_S9_S36_inst
(
.CLKA ( clka ), // Port A Clock
.DOA ( douta[r*8 +: 8] ), // Port A 1-bit Data Output
.DOPA ( ),
.ADDRA ( addra ), // Port A 14-bit Address Input
.DIA ( dina[r*8 +: 8] ), // Port A 1-bit Data Input
.DIPA ( 1'b0 ),
.ENA ( ena ), // Port A RAM Enable Input
.SSRA ( 1'b0 ), // Port A Synchronous Set/Reset Input
.WEA ( wea[r] ), // Port A Write Enable Input
.CLKB ( clkb ), // Port B Clock
.DOB ( doutb[r*32 +: 32] ), // Port B 1-bit Data Output
.DOPB ( ),
.ADDRB ( addrb ), // Port B 14-bit Address Input
.DIB ( dinb[r*32 +: 32] ), // Port B 1-bit Data Input
.DIPB ( 4'b0 ),
.ENB ( enb ), // Port B RAM Enable Input
.SSRB ( 1'b0 ), // Port B Synchronous Set/Reset Input
.WEB ( web[r] ) // Port B Write Enable Input
);
endgenerate
`else // !`ifdef FPGA
// This bit is a placeholder
infer_dpram #(.RAM_SIZE(11), .BYTE_WIDTH(8)) ram1 // RAM_SIZE is in words
(
.ram_clk_a(clka),
.ram_en_a(|ena),
.ram_we_a({wea[1],wea[1],wea[1],wea[1],wea[0],wea[0],wea[0],wea[0]}),
.ram_addr_a(addra),
.ram_wrdata_a({dina,dina,dina,dina}),
.ram_rddata_a({dout,douta}),
.ram_clk_b(clkb),
.ram_en_b(|enb),
.ram_we_b({web[1],web[1],web[1],web[1],web[0],web[0],web[0],web[0]}),
.ram_addr_b({2'b0,addrb}),
.ram_wrdata_b(dinb),
.ram_rddata_b(doutb)
);
`endif
endmodule // dualmem

View file

@ -0,0 +1,89 @@
module dualmem_widen8(clka, clkb, dina, dinb, addra, addrb, wea, web, douta, doutb, ena, enb);
input wire clka, clkb;
input [15:0] dina;
input [63:0] dinb;
input [12:0] addra;
input [10:0] addrb;
input [1:0] wea;
input [1:0] web;
input [0:0] ena, enb;
output [15:0] douta;
output [63:0] doutb;
genvar r;
wire [63:0] dout0;
wire [255:0] dout1;
wire [7:0] we0, we1, en0, en1;
wire [63:0] din0;
wire [255:0] din1;
reg [12:0] addra_dly;
reg [10:0] addrb_dly;
`ifdef GENESYSII
assign douta = dout0 >> {addra_dly[12:11],4'b0000};
assign doutb = dout1 >> {addrb_dly[10:9],6'b000000};
assign we0 = wea << {addra[12:11],1'b0};
assign we1 = web << {addrb[10:9],1'b0};
assign en0 = {ena,ena} << {addra[12:11],1'b0};
assign en1 = {enb,enb} << {addrb[10:9],1'b0};
assign din0 = {dina,dina,dina,dina};
assign din1 = {dinb,dinb,dinb,dinb};
always @(posedge clka)
begin
addra_dly <= addra;
addrb_dly <= addrb;
end
generate for (r = 0; r < 8; r=r+1)
RAMB16_S9_S36
RAMB16_S9_S36_inst
(
.CLKA ( clka ), // Port A Clock
.DOA ( dout0[r*8 +: 8] ), // Port A 1-bit Data Output
.DOPA ( ),
.ADDRA ( addra[10:0] ), // Port A 14-bit Address Input
.DIA ( din0[r*8 +: 8] ), // Port A 1-bit Data Input
.DIPA ( 1'b0 ),
.ENA ( en0[r] ), // Port A RAM Enable Input
.SSRA ( 1'b0 ), // Port A Synchronous Set/Reset Input
.WEA ( we0[r] ), // Port A Write Enable Input
.CLKB ( clkb ), // Port B Clock
.DOB ( dout1[r*32 +: 32] ), // Port B 1-bit Data Output
.DOPB ( ),
.ADDRB ( addrb[8:0] ), // Port B 14-bit Address Input
.DIB ( din1[r*32 +: 32] ), // Port B 1-bit Data Input
.DIPB ( 4'b0 ),
.ENB ( en1[r] ), // Port B RAM Enable Input
.SSRB ( 1'b0 ), // Port B Synchronous Set/Reset Input
.WEB ( we1[r] ) // Port B Write Enable Input
);
endgenerate
`else // !`ifdef FPGA
// This bit is a placeholder
infer_dpram #(.RAM_SIZE(11), .BYTE_WIDTH(8)) ram1 // RAM_SIZE is in words
(
.ram_clk_a(clka),
.ram_en_a(|ena),
.ram_we_a({wea[1],wea[1],wea[1],wea[1],wea[0],wea[0],wea[0],wea[0]}),
.ram_addr_a(addra),
.ram_wrdata_a({dina,dina,dina,dina}),
.ram_rddata_a({dout,douta}),
.ram_clk_b(clkb),
.ram_en_b(|enb),
.ram_we_b({web[1],web[1],web[1],web[1],web[0],web[0],web[0],web[0]}),
.ram_addr_b({2'b0,addrb}),
.ram_wrdata_b(dinb),
.ram_rddata_b(doutb)
);
`endif
endmodule // dualmem

316
fpga/src/eth_arb_mux.sv Normal file
View file

@ -0,0 +1,316 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Ethernet arbitrated multiplexer
*/
module eth_arb_mux #
(
parameter S_COUNT = 4,
parameter DATA_WIDTH = 8,
parameter KEEP_ENABLE = (DATA_WIDTH>8),
parameter KEEP_WIDTH = (DATA_WIDTH/8),
parameter ID_ENABLE = 0,
parameter ID_WIDTH = 8,
parameter DEST_ENABLE = 0,
parameter DEST_WIDTH = 8,
parameter USER_ENABLE = 1,
parameter USER_WIDTH = 1,
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
parameter ARB_TYPE = "PRIORITY",
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "HIGH"
)
(
input wire clk,
input wire rst,
/*
* Ethernet frame inputs
*/
input wire [S_COUNT-1:0] s_eth_hdr_valid,
output wire [S_COUNT-1:0] s_eth_hdr_ready,
input wire [S_COUNT*48-1:0] s_eth_dest_mac,
input wire [S_COUNT*48-1:0] s_eth_src_mac,
input wire [S_COUNT*16-1:0] s_eth_type,
input wire [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata,
input wire [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep,
input wire [S_COUNT-1:0] s_eth_payload_axis_tvalid,
output wire [S_COUNT-1:0] s_eth_payload_axis_tready,
input wire [S_COUNT-1:0] s_eth_payload_axis_tlast,
input wire [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid,
input wire [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest,
input wire [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire [ID_WIDTH-1:0] m_eth_payload_axis_tid,
output wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest,
output wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser
);
parameter CL_S_COUNT = $clog2(S_COUNT);
reg frame_reg = 1'b0, frame_next;
reg s_eth_hdr_ready_mask_reg = 1'b0, s_eth_hdr_ready_mask_next;
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next;
reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next;
reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next;
wire [S_COUNT-1:0] request;
wire [S_COUNT-1:0] acknowledge;
wire [S_COUNT-1:0] grant;
wire grant_valid;
wire [CL_S_COUNT-1:0] grant_encoded;
// internal datapath
reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int;
reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int;
reg m_eth_payload_axis_tvalid_int;
reg m_eth_payload_axis_tready_int_reg = 1'b0;
reg m_eth_payload_axis_tlast_int;
reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int;
reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int;
reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int;
wire m_eth_payload_axis_tready_int_early;
assign s_eth_hdr_ready = (!s_eth_hdr_ready_mask_reg && grant_valid) << grant_encoded;
assign s_eth_payload_axis_tready = (m_eth_payload_axis_tready_int_reg && grant_valid) << grant_encoded;
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
// mux for incoming packet
wire [DATA_WIDTH-1:0] current_s_tdata = s_eth_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH];
wire [KEEP_WIDTH-1:0] current_s_tkeep = s_eth_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH];
wire current_s_tvalid = s_eth_payload_axis_tvalid[grant_encoded];
wire current_s_tready = s_eth_payload_axis_tready[grant_encoded];
wire current_s_tlast = s_eth_payload_axis_tlast[grant_encoded];
wire [ID_WIDTH-1:0] current_s_tid = s_eth_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH];
wire [DEST_WIDTH-1:0] current_s_tdest = s_eth_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH];
wire [USER_WIDTH-1:0] current_s_tuser = s_eth_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH];
// arbiter instance
rgmii_arbiter #(
.PORTS(S_COUNT),
.TYPE(ARB_TYPE),
.BLOCK("ACKNOWLEDGE"),
.LSB_PRIORITY(LSB_PRIORITY)
)
arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(acknowledge),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded(grant_encoded)
);
generate
genvar n;
for (n = 0; n < S_COUNT; n = n + 1) begin
assign request[n] = s_eth_hdr_valid[n] && !grant[n];
assign acknowledge[n] = grant[n] && s_eth_payload_axis_tvalid[n] && s_eth_payload_axis_tready[n] && s_eth_payload_axis_tlast[n];
end
endgenerate
always @* begin
frame_next = frame_reg;
s_eth_hdr_ready_mask_next = s_eth_hdr_ready_mask_reg;
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
m_eth_dest_mac_next = m_eth_dest_mac_reg;
m_eth_src_mac_next = m_eth_src_mac_reg;
m_eth_type_next = m_eth_type_reg;
if (s_eth_payload_axis_tvalid[grant_encoded] && s_eth_payload_axis_tready[grant_encoded]) begin
// end of frame detection
if (s_eth_payload_axis_tlast[grant_encoded]) begin
frame_next = 1'b0;
s_eth_hdr_ready_mask_next = 1'b0;
end
end
if (!frame_reg && grant_valid) begin
// start of frame
frame_next = 1'b1;
s_eth_hdr_ready_mask_next = 1'b1;
m_eth_hdr_valid_next = 1'b1;
m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48];
m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48];
m_eth_type_next = s_eth_type[grant_encoded*16 +: 16];
end
// pass through selected packet data
m_eth_payload_axis_tdata_int = current_s_tdata;
m_eth_payload_axis_tkeep_int = current_s_tkeep;
m_eth_payload_axis_tvalid_int = current_s_tvalid && m_eth_payload_axis_tready_int_reg && grant_valid;
m_eth_payload_axis_tlast_int = current_s_tlast;
m_eth_payload_axis_tid_int = current_s_tid;
m_eth_payload_axis_tdest_int = current_s_tdest;
m_eth_payload_axis_tuser_int = current_s_tuser;
end
always @(posedge clk) begin
if (rst) begin
frame_reg <= 1'b0;
s_eth_hdr_ready_mask_reg <= 1'b0;
m_eth_hdr_valid_reg <= 1'b0;
end else begin
frame_reg <= frame_next;
s_eth_hdr_ready_mask_reg <= s_eth_hdr_ready_mask_next;
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
end
m_eth_dest_mac_reg <= m_eth_dest_mac_next;
m_eth_src_mac_reg <= m_eth_src_mac_next;
m_eth_type_reg <= m_eth_type_next;
end
// output datapath logic
reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
reg m_eth_payload_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_eth_payload_axis_temp_to_output;
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
assign m_eth_payload_axis_tid = ID_ENABLE ? m_eth_payload_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_eth_payload_axis_tdest = DEST_ENABLE ? m_eth_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_eth_payload_axis_tuser = USER_ENABLE ? m_eth_payload_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b0;
if (m_eth_payload_axis_tready_int_reg) begin
// input is ready
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_eth_payload_axis_tready) begin
// input is not ready, but output is ready
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_eth_payload_axis_tvalid_reg <= 1'b0;
m_eth_payload_axis_tready_int_reg <= 1'b0;
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int;
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int;
m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int;
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end else if (store_eth_payload_axis_temp_to_output) begin
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg;
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg;
m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg;
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int;
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int;
temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int;
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end
end
endmodule

370
fpga/src/eth_axis_rx.sv Normal file
View file

@ -0,0 +1,370 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream ethernet frame receiver (AXI in, Ethernet frame out)
*/
module eth_axis_rx
(
input wire clk,
input wire rst,
/*
* AXI input
*/
input wire [7:0] s_axis_tdata,
input wire s_axis_tvalid,
output wire s_axis_tready,
input wire s_axis_tlast,
input wire s_axis_tuser,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [7:0] m_eth_payload_axis_tdata,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire m_eth_payload_axis_tuser,
/*
* Status signals
*/
output wire busy,
output wire error_header_early_termination
);
/*
Ethernet frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype 2 octets
This module receives an Ethernet frame on an AXI stream interface, decodes
and strips the headers, then produces the header fields in parallel along
with the payload in a separate AXI stream.
*/
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_READ_HEADER = 2'd1,
STATE_READ_PAYLOAD = 2'd2;
reg [1:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_eth_dest_mac_0;
reg store_eth_dest_mac_1;
reg store_eth_dest_mac_2;
reg store_eth_dest_mac_3;
reg store_eth_dest_mac_4;
reg store_eth_dest_mac_5;
reg store_eth_src_mac_0;
reg store_eth_src_mac_1;
reg store_eth_src_mac_2;
reg store_eth_src_mac_3;
reg store_eth_src_mac_4;
reg store_eth_src_mac_5;
reg store_eth_type_0;
reg store_eth_type_1;
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
reg s_axis_tready_reg = 1'b0, s_axis_tready_next;
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg busy_reg = 1'b0;
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
// internal datapath
reg [7:0] m_eth_payload_axis_tdata_int;
reg m_eth_payload_axis_tvalid_int;
reg m_eth_payload_axis_tready_int_reg = 1'b0;
reg m_eth_payload_axis_tlast_int;
reg m_eth_payload_axis_tuser_int;
wire m_eth_payload_axis_tready_int_early;
assign s_axis_tready = s_axis_tready_reg;
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign busy = busy_reg;
assign error_header_early_termination = error_header_early_termination_reg;
always @* begin
state_next = STATE_IDLE;
s_axis_tready_next = 1'b0;
store_eth_dest_mac_0 = 1'b0;
store_eth_dest_mac_1 = 1'b0;
store_eth_dest_mac_2 = 1'b0;
store_eth_dest_mac_3 = 1'b0;
store_eth_dest_mac_4 = 1'b0;
store_eth_dest_mac_5 = 1'b0;
store_eth_src_mac_0 = 1'b0;
store_eth_src_mac_1 = 1'b0;
store_eth_src_mac_2 = 1'b0;
store_eth_src_mac_3 = 1'b0;
store_eth_src_mac_4 = 1'b0;
store_eth_src_mac_5 = 1'b0;
store_eth_type_0 = 1'b0;
store_eth_type_1 = 1'b0;
frame_ptr_next = frame_ptr_reg;
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
error_header_early_termination_next = 1'b0;
m_eth_payload_axis_tdata_int = 8'd0;
m_eth_payload_axis_tvalid_int = 1'b0;
m_eth_payload_axis_tlast_int = 1'b0;
m_eth_payload_axis_tuser_int = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_ptr_next = 8'd0;
s_axis_tready_next = !m_eth_hdr_valid_reg;
if (s_axis_tready && s_axis_tvalid) begin
// got first word of packet
if (s_axis_tlast) begin
// tlast asserted on first word
error_header_early_termination_next = 1'b1;
state_next = STATE_IDLE;
end else begin
// move to read header state
frame_ptr_next = 1'b1;
store_eth_dest_mac_5 = 1'b1;
state_next = STATE_READ_HEADER;
end
end else begin
state_next = STATE_IDLE;
end
end
STATE_READ_HEADER: begin
// read header
s_axis_tready_next = 1'b1;
if (s_axis_tready && s_axis_tvalid) begin
// word transfer in - store it
frame_ptr_next = frame_ptr_reg + 8'd1;
state_next = STATE_READ_HEADER;
case (frame_ptr_reg)
8'h00: store_eth_dest_mac_5 = 1'b1;
8'h01: store_eth_dest_mac_4 = 1'b1;
8'h02: store_eth_dest_mac_3 = 1'b1;
8'h03: store_eth_dest_mac_2 = 1'b1;
8'h04: store_eth_dest_mac_1 = 1'b1;
8'h05: store_eth_dest_mac_0 = 1'b1;
8'h06: store_eth_src_mac_5 = 1'b1;
8'h07: store_eth_src_mac_4 = 1'b1;
8'h08: store_eth_src_mac_3 = 1'b1;
8'h09: store_eth_src_mac_2 = 1'b1;
8'h0A: store_eth_src_mac_1 = 1'b1;
8'h0B: store_eth_src_mac_0 = 1'b1;
8'h0C: store_eth_type_1 = 1'b1;
8'h0D: begin
store_eth_type_0 = 1'b1;
m_eth_hdr_valid_next = 1'b1;
s_axis_tready_next = m_eth_payload_axis_tready_int_early;
state_next = STATE_READ_PAYLOAD;
end
endcase
if (s_axis_tlast) begin
error_header_early_termination_next = 1'b1;
s_axis_tready_next = !m_eth_hdr_valid_reg;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_READ_HEADER;
end
end
STATE_READ_PAYLOAD: begin
// read payload
s_axis_tready_next = m_eth_payload_axis_tready_int_early;
m_eth_payload_axis_tdata_int = s_axis_tdata;
m_eth_payload_axis_tvalid_int = s_axis_tvalid;
m_eth_payload_axis_tlast_int = s_axis_tlast;
m_eth_payload_axis_tuser_int = s_axis_tuser;
if (s_axis_tready && s_axis_tvalid) begin
// word transfer through
if (s_axis_tlast) begin
s_axis_tready_next = !m_eth_hdr_valid_reg;
state_next = STATE_IDLE;
end else begin
state_next = STATE_READ_PAYLOAD;
end
end else begin
state_next = STATE_READ_PAYLOAD;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 8'd0;
s_axis_tready_reg <= 1'b0;
m_eth_hdr_valid_reg <= 1'b0;
busy_reg <= 1'b0;
error_header_early_termination_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
s_axis_tready_reg <= s_axis_tready_next;
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
error_header_early_termination_reg <= error_header_early_termination_next;
busy_reg <= state_next != STATE_IDLE;
end
// datapath
if (store_eth_dest_mac_0) m_eth_dest_mac_reg[ 7: 0] <= s_axis_tdata;
if (store_eth_dest_mac_1) m_eth_dest_mac_reg[15: 8] <= s_axis_tdata;
if (store_eth_dest_mac_2) m_eth_dest_mac_reg[23:16] <= s_axis_tdata;
if (store_eth_dest_mac_3) m_eth_dest_mac_reg[31:24] <= s_axis_tdata;
if (store_eth_dest_mac_4) m_eth_dest_mac_reg[39:32] <= s_axis_tdata;
if (store_eth_dest_mac_5) m_eth_dest_mac_reg[47:40] <= s_axis_tdata;
if (store_eth_src_mac_0) m_eth_src_mac_reg[ 7: 0] <= s_axis_tdata;
if (store_eth_src_mac_1) m_eth_src_mac_reg[15: 8] <= s_axis_tdata;
if (store_eth_src_mac_2) m_eth_src_mac_reg[23:16] <= s_axis_tdata;
if (store_eth_src_mac_3) m_eth_src_mac_reg[31:24] <= s_axis_tdata;
if (store_eth_src_mac_4) m_eth_src_mac_reg[39:32] <= s_axis_tdata;
if (store_eth_src_mac_5) m_eth_src_mac_reg[47:40] <= s_axis_tdata;
if (store_eth_type_0) m_eth_type_reg[ 7: 0] <= s_axis_tdata;
if (store_eth_type_1) m_eth_type_reg[15: 8] <= s_axis_tdata;
end
// output datapath logic
reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0;
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
reg m_eth_payload_axis_tlast_reg = 1'b0;
reg m_eth_payload_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0;
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
reg temp_m_eth_payload_axis_tuser_reg = 1'b0;
// datapath control
reg store_eth_payload_int_to_output;
reg store_eth_payload_int_to_temp;
reg store_eth_payload_axis_temp_to_output;
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
store_eth_payload_int_to_output = 1'b0;
store_eth_payload_int_to_temp = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b0;
if (m_eth_payload_axis_tready_int_reg) begin
// input is ready
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_eth_payload_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_eth_payload_int_to_temp = 1'b1;
end
end else if (m_eth_payload_axis_tready) begin
// input is not ready, but output is ready
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_eth_payload_axis_tvalid_reg <= 1'b0;
m_eth_payload_axis_tready_int_reg <= 1'b0;
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
end
// datapath
if (store_eth_payload_int_to_output) begin
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end else if (store_eth_payload_axis_temp_to_output) begin
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
end
if (store_eth_payload_int_to_temp) begin
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end
end
endmodule

312
fpga/src/eth_axis_tx.sv Normal file
View file

@ -0,0 +1,312 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* AXI4-Stream ethernet frame transmitter (Ethernet frame in, AXI out)
*/
module eth_axis_tx
(
input wire clk,
input wire rst,
/*
* Ethernet frame input
*/
input wire s_eth_hdr_valid,
output wire s_eth_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [7:0] s_eth_payload_axis_tdata,
input wire s_eth_payload_axis_tvalid,
output wire s_eth_payload_axis_tready,
input wire s_eth_payload_axis_tlast,
input wire s_eth_payload_axis_tuser,
/*
* AXI output
*/
output wire [7:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
output wire m_axis_tuser,
/*
* Status signals
*/
output wire busy
);
/*
Ethernet frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype 2 octets
This module receives an Ethernet frame with header fields in parallel along
with the payload in an AXI stream, combines the header with the payload, and
transmits the complete Ethernet frame on the output AXI stream interface.
*/
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_WRITE_HEADER = 2'd1,
STATE_WRITE_PAYLOAD = 2'd2;
reg [1:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_eth_hdr;
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
reg [47:0] eth_dest_mac_reg = 48'd0;
reg [47:0] eth_src_mac_reg = 48'd0;
reg [15:0] eth_type_reg = 16'd0;
reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next;
reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next;
reg busy_reg = 1'b0;
// internal datapath
reg [7:0] m_axis_tdata_int;
reg m_axis_tvalid_int;
reg m_axis_tready_int_reg = 1'b0;
reg m_axis_tlast_int;
reg m_axis_tuser_int;
wire m_axis_tready_int_early;
assign s_eth_hdr_ready = s_eth_hdr_ready_reg;
assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg;
assign busy = busy_reg;
always @* begin
state_next = STATE_IDLE;
s_eth_hdr_ready_next = 1'b0;
s_eth_payload_axis_tready_next = 1'b0;
store_eth_hdr = 1'b0;
frame_ptr_next = frame_ptr_reg;
m_axis_tdata_int = 8'd0;
m_axis_tvalid_int = 1'b0;
m_axis_tlast_int = 1'b0;
m_axis_tuser_int = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_ptr_next = 8'd0;
s_eth_hdr_ready_next = 1'b1;
if (s_eth_hdr_ready && s_eth_hdr_valid) begin
store_eth_hdr = 1'b1;
s_eth_hdr_ready_next = 1'b0;
if (m_axis_tready_int_reg) begin
m_axis_tvalid_int = 1'b1;
m_axis_tdata_int = s_eth_dest_mac[47:40];
frame_ptr_next = 1'b1;
end
state_next = STATE_WRITE_HEADER;
end else begin
state_next = STATE_IDLE;
end
end
STATE_WRITE_HEADER: begin
// write header
if (m_axis_tready_int_reg) begin
frame_ptr_next = frame_ptr_reg+1;
m_axis_tvalid_int = 1'b1;
state_next = STATE_WRITE_HEADER;
case (frame_ptr_reg)
8'h00: m_axis_tdata_int = eth_dest_mac_reg[47:40];
8'h01: m_axis_tdata_int = eth_dest_mac_reg[39:32];
8'h02: m_axis_tdata_int = eth_dest_mac_reg[31:24];
8'h03: m_axis_tdata_int = eth_dest_mac_reg[23:16];
8'h04: m_axis_tdata_int = eth_dest_mac_reg[15: 8];
8'h05: m_axis_tdata_int = eth_dest_mac_reg[ 7: 0];
8'h06: m_axis_tdata_int = eth_src_mac_reg[47:40];
8'h07: m_axis_tdata_int = eth_src_mac_reg[39:32];
8'h08: m_axis_tdata_int = eth_src_mac_reg[31:24];
8'h09: m_axis_tdata_int = eth_src_mac_reg[23:16];
8'h0A: m_axis_tdata_int = eth_src_mac_reg[15: 8];
8'h0B: m_axis_tdata_int = eth_src_mac_reg[ 7: 0];
8'h0C: m_axis_tdata_int = eth_type_reg[15: 8];
8'h0D: begin
m_axis_tdata_int = eth_type_reg[ 7: 0];
s_eth_payload_axis_tready_next = m_axis_tready_int_early;
state_next = STATE_WRITE_PAYLOAD;
end
endcase
end else begin
state_next = STATE_WRITE_HEADER;
end
end
STATE_WRITE_PAYLOAD: begin
// write payload
s_eth_payload_axis_tready_next = m_axis_tready_int_early;
m_axis_tdata_int = s_eth_payload_axis_tdata;
m_axis_tvalid_int = s_eth_payload_axis_tvalid;
m_axis_tlast_int = s_eth_payload_axis_tlast;
m_axis_tuser_int = s_eth_payload_axis_tuser;
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
// word transfer through
if (s_eth_payload_axis_tlast) begin
s_eth_payload_axis_tready_next = 1'b0;
s_eth_hdr_ready_next = 1'b1;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WRITE_PAYLOAD;
end
end else begin
state_next = STATE_WRITE_PAYLOAD;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 8'd0;
s_eth_hdr_ready_reg <= 1'b0;
s_eth_payload_axis_tready_reg <= 1'b0;
busy_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next;
busy_reg <= state_next != STATE_IDLE;
end
// datapath
if (store_eth_hdr) begin
eth_dest_mac_reg <= s_eth_dest_mac;
eth_src_mac_reg <= s_eth_src_mac;
eth_type_reg <= s_eth_type;
end
end
// output datapath logic
reg [7:0] m_axis_tdata_reg = 8'd0;
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
reg m_axis_tlast_reg = 1'b0;
reg m_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_axis_tdata_reg = 8'd0;
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
reg temp_m_axis_tlast_reg = 1'b0;
reg temp_m_axis_tuser_reg = 1'b0;
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_axis_temp_to_output;
assign m_axis_tdata = m_axis_tdata_reg;
assign m_axis_tvalid = m_axis_tvalid_reg;
assign m_axis_tlast = m_axis_tlast_reg;
assign m_axis_tuser = m_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_axis_tvalid_next = m_axis_tvalid_reg;
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_axis_temp_to_output = 1'b0;
if (m_axis_tready_int_reg) begin
// input is ready
if (m_axis_tready || !m_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_axis_tvalid_next = m_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_axis_tready) begin
// input is not ready, but output is ready
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
temp_m_axis_tvalid_next = 1'b0;
store_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_axis_tvalid_reg <= 1'b0;
m_axis_tready_int_reg <= 1'b0;
temp_m_axis_tvalid_reg <= 1'b0;
end else begin
m_axis_tvalid_reg <= m_axis_tvalid_next;
m_axis_tready_int_reg <= m_axis_tready_int_early;
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_axis_tdata_reg <= m_axis_tdata_int;
m_axis_tlast_reg <= m_axis_tlast_int;
m_axis_tuser_reg <= m_axis_tuser_int;
end else if (store_axis_temp_to_output) begin
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_axis_tdata_reg <= m_axis_tdata_int;
temp_m_axis_tlast_reg <= m_axis_tlast_int;
temp_m_axis_tuser_reg <= m_axis_tuser_int;
end
end
endmodule

127
fpga/src/eth_mac_1g.sv Normal file
View file

@ -0,0 +1,127 @@
/*
Copyright (c) 2015-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* 1G Ethernet MAC
*/
module eth_mac_1g #
(
parameter ENABLE_PADDING = 1,
parameter MIN_FRAME_LENGTH = 64
)
(
input wire rx_clk,
input wire rx_rst,
input wire tx_clk,
input wire tx_rst,
/*
* AXI input
*/
input wire [7:0] tx_axis_tdata,
input wire tx_axis_tvalid,
output wire tx_axis_tready,
input wire tx_axis_tlast,
input wire tx_axis_tuser,
/*
* AXI output
*/
output wire [7:0] rx_axis_tdata,
output wire rx_axis_tvalid,
output wire rx_axis_tlast,
output wire rx_axis_tuser,
/*
* GMII interface
*/
input wire [7:0] gmii_rxd,
input wire gmii_rx_dv,
input wire gmii_rx_er,
output wire [7:0] gmii_txd,
output wire gmii_tx_en,
output wire gmii_tx_er,
/*
* Control
*/
input wire rx_clk_enable,
input wire tx_clk_enable,
input wire rx_mii_select,
input wire tx_mii_select,
/*
* Status
*/
output wire rx_error_bad_frame,
output wire rx_error_bad_fcs,
/*
* Configuration
*/
input wire [7:0] ifg_delay
);
axis_gmii_rx
axis_gmii_rx_inst (
.clk(rx_clk),
.rst(rx_rst),
.gmii_rxd(gmii_rxd),
.gmii_rx_dv(gmii_rx_dv),
.gmii_rx_er(gmii_rx_er),
.m_axis_tdata(rx_axis_tdata),
.m_axis_tvalid(rx_axis_tvalid),
.m_axis_tlast(rx_axis_tlast),
.m_axis_tuser(rx_axis_tuser),
.clk_enable(rx_clk_enable),
.mii_select(rx_mii_select),
.error_bad_frame(rx_error_bad_frame),
.error_bad_fcs(rx_error_bad_fcs)
);
axis_gmii_tx #(
.ENABLE_PADDING(ENABLE_PADDING),
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
)
axis_gmii_tx_inst (
.clk(tx_clk),
.rst(tx_rst),
.s_axis_tdata(tx_axis_tdata),
.s_axis_tvalid(tx_axis_tvalid),
.s_axis_tready(tx_axis_tready),
.s_axis_tlast(tx_axis_tlast),
.s_axis_tuser(tx_axis_tuser),
.gmii_txd(gmii_txd),
.gmii_tx_en(gmii_tx_en),
.gmii_tx_er(gmii_tx_er),
.clk_enable(tx_clk_enable),
.mii_select(tx_mii_select),
.ifg_delay(ifg_delay)
);
endmodule

View file

@ -0,0 +1,253 @@
/*
Copyright (c) 2015-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* 1G Ethernet MAC with RGMII interface
*/
module eth_mac_1g_rgmii #
(
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
parameter TARGET = "GENERIC",
// IODDR style ("IODDR", "IODDR2")
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
// Use IODDR2 for Spartan-6
parameter IODDR_STYLE = "IODDR2",
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
// Use BUFR for Virtex-5, Virtex-6, 7-series
// Use BUFG for Ultrascale
// Use BUFIO2 for Spartan-6
parameter CLOCK_INPUT_STYLE = "BUFIO2",
// Use 90 degree clock for RGMII transmit ("TRUE", "FALSE")
parameter USE_CLK90 = "TRUE",
parameter ENABLE_PADDING = 1,
parameter MIN_FRAME_LENGTH = 64
)
(
input wire gtx_clk,
input wire gtx_clk90,
input wire gtx_rst,
output wire rx_clk,
output wire rx_rst,
output wire tx_clk,
output wire tx_rst,
/*
* AXI input
*/
input wire [7:0] tx_axis_tdata,
input wire tx_axis_tvalid,
output wire tx_axis_tready,
input wire tx_axis_tlast,
input wire tx_axis_tuser,
/*
* AXI output
*/
output wire [7:0] rx_axis_tdata,
output wire rx_axis_tvalid,
output wire rx_axis_tlast,
output wire rx_axis_tuser,
/*
* RGMII interface
*/
input wire rgmii_rx_clk,
input wire [3:0] rgmii_rxd,
input wire rgmii_rx_ctl,
output wire rgmii_tx_clk,
output wire [3:0] rgmii_txd,
output wire rgmii_tx_ctl,
output wire mac_gmii_tx_en,
/*
* Status
*/
output wire rx_error_bad_frame,
output wire rx_error_bad_fcs,
output wire [1:0] speed,
/*
* Configuration
*/
input wire [7:0] ifg_delay
);
wire [7:0] mac_gmii_rxd;
wire mac_gmii_rx_dv;
wire mac_gmii_rx_er;
wire mac_gmii_tx_clk_en;
wire [7:0] mac_gmii_txd;
wire mac_gmii_tx_er;
reg [1:0] speed_reg = 2'b10;
wire mii_select;
reg tx_mii_select_1 = 1'b0;
reg tx_mii_select_2 = 1'b0;
reg tx_mii_select_3 = 1'b0;
always @(posedge tx_clk) begin
tx_mii_select_1 <= mii_select;
tx_mii_select_2 <= tx_mii_select_1;
tx_mii_select_3 <= tx_mii_select_2;
end
reg rx_mii_select_1 = 1'b0;
reg rx_mii_select_2 = 1'b0;
reg rx_mii_select_3 = 1'b0;
always @(posedge rx_clk) begin
rx_mii_select_1 <= mii_select;
rx_mii_select_2 <= rx_mii_select_1;
rx_mii_select_3 <= rx_mii_select_2;
end
// PHY speed detection
reg [2:0] rx_prescale = 3'd0;
always @(posedge rx_clk) begin
rx_prescale <= rx_prescale + 3'd1;
end
reg rx_prescale_sync_1 = 1'b0;
reg rx_prescale_sync_2 = 1'b0;
reg rx_prescale_sync_3 = 1'b0;
always @(posedge gtx_clk) begin
rx_prescale_sync_1 <= rx_prescale[2];
rx_prescale_sync_2 <= rx_prescale_sync_1;
rx_prescale_sync_3 <= rx_prescale_sync_2;
end
reg [6:0] rx_speed_count_1 = 0;
reg [1:0] rx_speed_count_2 = 0;
always @(posedge gtx_clk) begin
if (gtx_rst) begin
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
speed_reg <= 2'b10;
end else begin
rx_speed_count_1 <= rx_speed_count_1 + 1;
if (rx_prescale_sync_2 ^ rx_prescale_sync_3) begin
rx_speed_count_2 <= rx_speed_count_2 + 1;
end
if (&rx_speed_count_1) begin
// reference count overflow - 10M
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
speed_reg <= 2'b00;
end
if (&rx_speed_count_2) begin
// prescaled count overflow - 100M or 1000M
rx_speed_count_1 <= 0;
rx_speed_count_2 <= 0;
if (rx_speed_count_1[6:5]) begin
// large reference count - 100M
speed_reg <= 2'b01;
end else begin
// small reference count - 1000M
speed_reg <= 2'b10;
end
end
end
end
assign speed = speed_reg;
assign mii_select = speed != 2'b10;
rgmii_phy_if #(
.TARGET(TARGET),
.IODDR_STYLE(IODDR_STYLE),
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
.USE_CLK90(USE_CLK90)
)
rgmii_phy_if_inst (
.clk(gtx_clk),
.clk90(gtx_clk90),
.rst(gtx_rst),
.mac_gmii_rx_clk(rx_clk),
.mac_gmii_rx_rst(rx_rst),
.mac_gmii_rxd(mac_gmii_rxd),
.mac_gmii_rx_dv(mac_gmii_rx_dv),
.mac_gmii_rx_er(mac_gmii_rx_er),
.mac_gmii_tx_clk(tx_clk),
.mac_gmii_tx_rst(tx_rst),
.mac_gmii_tx_clk_en(mac_gmii_tx_clk_en),
.mac_gmii_txd(mac_gmii_txd),
.mac_gmii_tx_en(mac_gmii_tx_en),
.mac_gmii_tx_er(mac_gmii_tx_er),
.phy_rgmii_rx_clk(rgmii_rx_clk),
.phy_rgmii_rxd(rgmii_rxd),
.phy_rgmii_rx_ctl(rgmii_rx_ctl),
.phy_rgmii_tx_clk(rgmii_tx_clk),
.phy_rgmii_txd(rgmii_txd),
.phy_rgmii_tx_ctl(rgmii_tx_ctl),
.speed(speed)
);
eth_mac_1g #(
.ENABLE_PADDING(ENABLE_PADDING),
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
)
eth_mac_1g_inst (
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
.tx_axis_tdata(tx_axis_tdata),
.tx_axis_tvalid(tx_axis_tvalid),
.tx_axis_tready(tx_axis_tready),
.tx_axis_tlast(tx_axis_tlast),
.tx_axis_tuser(tx_axis_tuser),
.rx_axis_tdata(rx_axis_tdata),
.rx_axis_tvalid(rx_axis_tvalid),
.rx_axis_tlast(rx_axis_tlast),
.rx_axis_tuser(rx_axis_tuser),
.gmii_rxd(mac_gmii_rxd),
.gmii_rx_dv(mac_gmii_rx_dv),
.gmii_rx_er(mac_gmii_rx_er),
.gmii_txd(mac_gmii_txd),
.gmii_tx_en(mac_gmii_tx_en),
.gmii_tx_er(mac_gmii_tx_er),
.rx_clk_enable(1'b1),
.tx_clk_enable(mac_gmii_tx_clk_en),
.rx_mii_select(rx_mii_select_3),
.tx_mii_select(tx_mii_select_3),
.rx_error_bad_frame(rx_error_bad_frame),
.rx_error_bad_fcs(rx_error_bad_fcs),
.ifg_delay(ifg_delay)
);
endmodule

View file

@ -0,0 +1,199 @@
/*
Copyright (c) 2015-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* 1G Ethernet MAC with RGMII interface and TX and RX FIFOs
*/
module eth_mac_1g_rgmii_fifo #
(
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
parameter TARGET = "GENERIC",
// IODDR style ("IODDR", "IODDR2")
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
// Use IODDR2 for Spartan-6
parameter IODDR_STYLE = "IODDR2",
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
// Use BUFR for Virtex-5, Virtex-6, 7-series
// Use BUFG for Ultrascale
// Use BUFIO2 for Spartan-6
parameter CLOCK_INPUT_STYLE = "BUFIO2",
// Use 90 degree clock for RGMII transmit ("TRUE", "FALSE")
parameter USE_CLK90 = "TRUE",
parameter ENABLE_PADDING = 1,
parameter MIN_FRAME_LENGTH = 64,
parameter TX_FIFO_ADDR_WIDTH = 12,
parameter TX_FRAME_FIFO = 1,
parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO,
parameter TX_DROP_WHEN_FULL = 0,
parameter RX_FIFO_ADDR_WIDTH = 12,
parameter RX_FRAME_FIFO = 1,
parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO,
parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO
)
(
input wire gtx_clk,
input wire gtx_clk90,
input wire gtx_rst,
input wire logic_clk,
input wire logic_rst,
/*
* AXI input
*/
input wire [7:0] tx_axis_tdata,
input wire tx_axis_tvalid,
output wire tx_axis_tready,
input wire tx_axis_tlast,
input wire tx_axis_tuser,
/*
* AXI output
*/
output wire [7:0] rx_axis_tdata,
output wire rx_axis_tvalid,
output wire rx_axis_tlast,
output wire rx_axis_tuser,
/*
* RGMII interface
*/
input wire rgmii_rx_clk,
input wire [3:0] rgmii_rxd,
input wire rgmii_rx_ctl,
output wire rgmii_tx_clk,
output wire [3:0] rgmii_txd,
output wire rgmii_tx_ctl,
output wire mac_gmii_tx_en,
/*
* Status
*/
output wire tx_fifo_overflow,
output wire tx_fifo_bad_frame,
output wire tx_fifo_good_frame,
output wire rx_error_bad_frame,
output wire rx_error_bad_fcs,
output wire rx_fifo_overflow,
output wire rx_fifo_bad_frame,
output wire rx_fifo_good_frame,
output wire [1:0] speed,
/*
* Configuration
*/
input wire [7:0] ifg_delay
);
wire tx_clk;
wire rx_clk;
wire tx_rst;
wire rx_rst;
// synchronize MAC status signals into logic clock domain
wire rx_error_bad_frame_int;
wire rx_error_bad_fcs_int;
reg [1:0] rx_sync_reg_1 = 2'd0;
reg [1:0] rx_sync_reg_2 = 2'd0;
reg [1:0] rx_sync_reg_3 = 2'd0;
reg [1:0] rx_sync_reg_4 = 2'd0;
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
always @(posedge rx_clk or posedge rx_rst) begin
if (rx_rst) begin
rx_sync_reg_1 <= 2'd0;
end else begin
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int};
end
end
always @(posedge logic_clk or posedge logic_rst) begin
if (logic_rst) begin
rx_sync_reg_2 <= 2'd0;
rx_sync_reg_3 <= 2'd0;
rx_sync_reg_4 <= 2'd0;
end else begin
rx_sync_reg_2 <= rx_sync_reg_1;
rx_sync_reg_3 <= rx_sync_reg_2;
rx_sync_reg_4 <= rx_sync_reg_3;
end
end
wire [1:0] speed_int;
reg [1:0] speed_sync_reg_1 = 2'b10;
reg [1:0] speed_sync_reg_2 = 2'b10;
assign speed = speed_sync_reg_2;
always @(posedge logic_clk) begin
speed_sync_reg_1 <= speed_int;
speed_sync_reg_2 <= speed_sync_reg_1;
end
eth_mac_1g_rgmii #(
.TARGET(TARGET),
.IODDR_STYLE(IODDR_STYLE),
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
.USE_CLK90(USE_CLK90),
.ENABLE_PADDING(ENABLE_PADDING),
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
)
eth_mac_1g_rgmii_inst (
.gtx_clk(gtx_clk),
.gtx_clk90(gtx_clk90),
.gtx_rst(gtx_rst),
.tx_clk(tx_clk),
.tx_rst(tx_rst),
.rx_clk(rx_clk),
.rx_rst(rx_rst),
.tx_axis_tdata(tx_axis_tdata),
.tx_axis_tvalid(tx_axis_tvalid),
.tx_axis_tready(tx_axis_tready),
.tx_axis_tlast(tx_axis_tlast),
.tx_axis_tuser(tx_axis_tuser),
.rx_axis_tdata(rx_axis_tdata),
.rx_axis_tvalid(rx_axis_tvalid),
.rx_axis_tlast(rx_axis_tlast),
.rx_axis_tuser(rx_axis_tuser),
.rgmii_rx_clk(rgmii_rx_clk),
.rgmii_rxd(rgmii_rxd),
.rgmii_rx_ctl(rgmii_rx_ctl),
.rgmii_tx_clk(rgmii_tx_clk),
.rgmii_txd(rgmii_txd),
.rgmii_tx_ctl(rgmii_tx_ctl),
.mac_gmii_tx_en(mac_gmii_tx_en),
.rx_error_bad_frame(rx_error_bad_frame_int),
.rx_error_bad_fcs(rx_error_bad_fcs_int),
.speed(speed_int),
.ifg_delay(ifg_delay)
);
endmodule

378
fpga/src/framing_top.sv Normal file
View file

@ -0,0 +1,378 @@
// See LICENSE for license details.
`default_nettype none
module framing_top
(
input wire msoc_clk,
input wire [14:0] core_lsu_addr,
input wire [63:0] core_lsu_wdata,
input wire [7:0] core_lsu_be,
input wire ce_d,
input wire we_d,
input wire framing_sel,
output logic [63:0] framing_rdata,
// Internal 125 MHz clock
input clk_int,
input rst_int,
input clk90_int,
input clk_200_int,
/*
* Ethernet: 1000BASE-T RGMII
*/
input wire phy_rx_clk,
input wire [3:0] phy_rxd,
input wire phy_rx_ctl,
output wire phy_tx_clk,
output wire [3:0] phy_txd,
output wire phy_tx_ctl,
output wire phy_reset_n,
input wire phy_int_n,
input wire phy_pme_n,
input wire phy_mdio_i,
output reg phy_mdio_o,
output reg phy_mdio_oen,
output wire phy_mdc,
output reg eth_irq
);
// obsolete signals to be removedphy_
//
logic [14:0] core_lsu_addr_dly;
logic tx_enable_i, mac_gmii_tx_en;
logic [47:0] mac_address, rx_dest_mac;
logic [10:0] tx_frame_addr, rx_length_axis[0:7], tx_packet_length;
logic [12:0] axis_tx_frame_size;
logic ce_d_dly, avail;
logic [63:0] framing_rdata_pkt, framing_wdata_pkt;
logic [3:0] tx_enable_dly, firstbuf, nextbuf, lastbuf;
logic [2:0] last;
reg byte_sync, sync, irq_en, tx_busy, rx_axis_tvalid_old;
wire [7:0] m_enb = (we_d ? core_lsu_be : 8'hFF);
logic phy_mdclk, cooked, tx_enable_old, loopback, promiscuous;
logic [3:0] spare;
logic [10:0] rx_addr_axis;
/*
* AXI input
*/
reg tx_axis_tvalid;
reg tx_axis_tvalid_dly;
reg tx_axis_tlast;
wire [7:0] tx_axis_tdata;
wire tx_axis_tready;
wire tx_axis_tuser = 1'b0;
/*
* AXI output
*/
wire [7:0] rx_axis_tdata;
wire rx_axis_tvalid;
wire rx_axis_tlast;
wire rx_axis_tuser;
/*
* AXIS Status
*/
wire [31:0] tx_fcs_reg_rev, rx_fcs_reg_rev;
always @(posedge clk_int)
if (rst_int == 1'b1)
begin
byte_sync <= 1'b0;
end
else
begin
if (rx_axis_tvalid && (byte_sync == 0) && (nextbuf != (firstbuf+lastbuf)&15) && ~rx_axis_tvalid_old)
begin
byte_sync <= 1'b1;
end
if (rx_axis_tlast && byte_sync && ~rx_axis_tvalid_old)
begin
last <= 1'b1;
end
else if ((last > 0) && (last < 7))
begin
byte_sync <= 1'b0;
last <= last + 3'b1;
end
else
begin
last <= 3'b0;
end
end
logic [1:0] rx_wr = rx_axis_tvalid << rx_addr_axis[2];
logic [15:0] douta;
assign tx_axis_tdata = douta >> {tx_frame_addr[2],3'b000};
assign phy_mdc = phy_mdclk;
dualmem_widen8 RAMB16_inst_rx (
.clka(clk_int), // Port A Clock
.clkb(msoc_clk), // Port A Clock
.douta(), // Port A 8-bit Data Output
.addra({nextbuf[2:0],rx_addr_axis[10:3],rx_addr_axis[1:0]}), // Port A 11-bit Address Input
.dina({rx_axis_tdata,rx_axis_tdata}), // Port A 8-bit Data Input
.ena(rx_axis_tvalid), // Port A RAM Enable Input
.wea(rx_wr), // Port A Write Enable Input
.doutb(framing_rdata_pkt), // Port B 32-bit Data Output
.addrb(core_lsu_addr[13:3]), // Port B 9-bit Address Input
.dinb(core_lsu_wdata), // Port B 32-bit Data Input
.enb(ce_d & framing_sel & core_lsu_addr[14]),
// Port B RAM Enable Input
.web(we_d ? {(|core_lsu_be[7:4]),(|core_lsu_be[3:0])} : 2'b0) // Port B Write Enable Input
);
dualmem_widen RAMB16_inst_tx (
.clka(~clk_int), // Port A Clock
.clkb(msoc_clk), // Port A Clock
.douta(douta), // Port A 8-bit Data Output
.addra({1'b0,tx_frame_addr[10:3],tx_frame_addr[1:0]}), // Port A 11-bit Address Input
.dina(16'b0), // Port A 8-bit Data Input
.ena(tx_axis_tvalid), // Port A RAM Enable Input
.wea(2'b0), // Port A Write Enable Input
.doutb(framing_wdata_pkt), // Port B 32-bit Data Output
.addrb(core_lsu_addr[11:3]), // Port B 9-bit Address Input
.dinb(core_lsu_wdata), // Port B 32-bit Data Input
.enb(ce_d & framing_sel & (core_lsu_addr[14:12]==3'b001)),
// Port B RAM Enable Input
.web(we_d ? {(|core_lsu_be[7:4]),(|core_lsu_be[3:0])} : 2'b0) // Port B Write Enable Input
);
always @(posedge msoc_clk)
if (rst_int)
begin
core_lsu_addr_dly <= 0;
mac_address <= 48'H230100890702;
tx_packet_length <= 0;
tx_enable_dly <= 0;
cooked <= 1'b0;
loopback <= 1'b0;
spare <= 4'b0;
promiscuous <= 1'b0;
phy_mdio_oen <= 1'b0;
phy_mdio_o <= 1'b0;
phy_mdclk <= 1'b0;
sync <= 1'b0;
firstbuf <= 4'b0;
lastbuf <= 4'b0;
nextbuf <= 4'b0;
eth_irq <= 1'b0;
irq_en <= 1'b0;
ce_d_dly <= 1'b0;
tx_busy <= 1'b0;
avail = 1'b0;
end
else
begin
core_lsu_addr_dly <= core_lsu_addr;
ce_d_dly <= ce_d;
avail = nextbuf != firstbuf;
eth_irq <= avail & irq_en; // make eth_irq go away immediately if irq_en is low
if (framing_sel&we_d&(&core_lsu_be[3:0])&(core_lsu_addr[14:11]==4'b0001))
case(core_lsu_addr[6:3])
0: mac_address[31:0] <= core_lsu_wdata;
1: {irq_en,promiscuous,spare,loopback,cooked,mac_address[47:32]} <= core_lsu_wdata;
2: begin tx_enable_dly <= 10; tx_packet_length <= core_lsu_wdata; end /* tx payload size */
3: begin tx_enable_dly <= 0; tx_packet_length <= 0; end
4: begin {phy_mdio_oen,phy_mdio_o,phy_mdclk} <= core_lsu_wdata; end
5: begin lastbuf <= core_lsu_wdata[3:0]; end
6: begin firstbuf <= core_lsu_wdata[3:0]; end
default:;
endcase
if ((last > 0) && ~sync)
begin
// check broadcast/multicast address
sync <= (rx_dest_mac[47:24]==24'h01005E) | (&rx_dest_mac) | (mac_address == rx_dest_mac) | promiscuous;
end
else if (!last)
begin
if (sync) nextbuf <= nextbuf + 1'b1;
sync <= 1'b0;
end
if (mac_gmii_tx_en && tx_axis_tlast)
begin
tx_enable_dly <= 0;
end
else if (1'b1 == |tx_enable_dly)
begin
tx_busy <= 1'b1;
tx_enable_dly <= tx_enable_dly + 1'b1;
end
else if (~mac_gmii_tx_en)
tx_busy <= 1'b0;
end
always @(posedge clk_int)
if (rst_int)
begin
tx_enable_i <= 1'b0;
end
else
begin
tx_enable_old <= tx_enable_i;
if (mac_gmii_tx_en && tx_axis_tlast)
begin
tx_enable_i <= 1'b0;
end
else if (1'b1 == &tx_enable_dly)
tx_enable_i <= 1'b1;
end
always @* casez({ce_d_dly,core_lsu_addr_dly[14:3]})
13'b10001????0000 : framing_rdata = mac_address[31:0];
13'b10001????0001 : framing_rdata = {irq_en, promiscuous, spare, loopback, cooked, mac_address[47:32]};
13'b1000?????0010 : framing_rdata = {tx_busy, 4'b0, tx_frame_addr, 5'b0, tx_packet_length};
13'b10001????0011 : framing_rdata = tx_fcs_reg_rev;
13'b10001????0100 : framing_rdata = {phy_mdio_i,phy_mdio_oen,phy_mdio_o,phy_mdclk};
13'b10001????0101 : framing_rdata = rx_fcs_reg_rev;
13'b10001????0110 : framing_rdata = {eth_irq, avail, lastbuf, nextbuf, firstbuf};
13'b10001????1??? : framing_rdata = rx_length_axis[core_lsu_addr_dly[5:3]];
13'b10010???????? : framing_rdata = framing_wdata_pkt;
13'b11??????????? : framing_rdata = framing_rdata_pkt;
default: framing_rdata = 'h0;
endcase
parameter dly = 0;
reg [31:0] tx_fcs_reg, rx_fcs_reg;
assign tx_fcs_reg_rev = {tx_fcs_reg[0],tx_fcs_reg[1],tx_fcs_reg[2],tx_fcs_reg[3],
tx_fcs_reg[4],tx_fcs_reg[5],tx_fcs_reg[6],tx_fcs_reg[7],
tx_fcs_reg[8],tx_fcs_reg[9],tx_fcs_reg[10],tx_fcs_reg[11],
tx_fcs_reg[12],tx_fcs_reg[13],tx_fcs_reg[14],tx_fcs_reg[15],
tx_fcs_reg[16],tx_fcs_reg[17],tx_fcs_reg[18],tx_fcs_reg[19],
tx_fcs_reg[20],tx_fcs_reg[21],tx_fcs_reg[22],tx_fcs_reg[23],
tx_fcs_reg[24],tx_fcs_reg[25],tx_fcs_reg[26],tx_fcs_reg[27],
tx_fcs_reg[28],tx_fcs_reg[29],tx_fcs_reg[30],tx_fcs_reg[31]};
assign rx_fcs_reg_rev = {rx_fcs_reg[0],rx_fcs_reg[1],rx_fcs_reg[2],rx_fcs_reg[3],
rx_fcs_reg[4],rx_fcs_reg[5],rx_fcs_reg[6],rx_fcs_reg[7],
rx_fcs_reg[8],rx_fcs_reg[9],rx_fcs_reg[10],rx_fcs_reg[11],
rx_fcs_reg[12],rx_fcs_reg[13],rx_fcs_reg[14],rx_fcs_reg[15],
rx_fcs_reg[16],rx_fcs_reg[17],rx_fcs_reg[18],rx_fcs_reg[19],
rx_fcs_reg[20],rx_fcs_reg[21],rx_fcs_reg[22],rx_fcs_reg[23],
rx_fcs_reg[24],rx_fcs_reg[25],rx_fcs_reg[26],rx_fcs_reg[27],
rx_fcs_reg[28],rx_fcs_reg[29],rx_fcs_reg[30],rx_fcs_reg[31]};
wire axis_tx_byte_sent = &axis_tx_frame_size[1:0];
always @(posedge clk_int)
if (rst_int)
begin
rx_addr_axis <= 'b0;
tx_axis_tvalid <= 'b0;
axis_tx_frame_size <= 0;
tx_axis_tvalid_dly <= 'b0;
tx_frame_addr <= 'b0;
tx_axis_tlast <= 'b0;
rx_dest_mac <= 'b0;
end
else
begin
if (tx_enable_i & (tx_enable_old == 0))
begin
axis_tx_frame_size <= 'b0;
tx_frame_addr <= 'b0;
end
else if (1'b0 == &axis_tx_frame_size)
begin
axis_tx_frame_size <= axis_tx_frame_size + 1;
end
if (tx_axis_tready)
begin
tx_frame_addr <= tx_frame_addr + 1;
tx_axis_tlast <= (tx_frame_addr == tx_packet_length-2) & tx_axis_tvalid_dly;
end
if (axis_tx_byte_sent)
begin
tx_axis_tvalid <= tx_axis_tvalid_dly;
if (tx_enable_old)
tx_axis_tvalid_dly <= 1'b1;
else if (~tx_axis_tlast)
tx_axis_tvalid_dly <= 1'b0;
end
if (rx_axis_tvalid & ~rx_axis_tvalid_old)
begin
rx_addr_axis <= rx_addr_axis + 1;
if (rx_addr_axis < 6)
rx_dest_mac <= {rx_dest_mac[39:0],rx_axis_tdata};
end
if (rx_axis_tlast & ~rx_axis_tvalid_old)
begin
rx_length_axis[nextbuf[2:0]] <= rx_addr_axis + 1;
rx_addr_axis <= 'b0;
end
rx_axis_tvalid_old <= rx_axis_tvalid;
end
rgmii_soc rgmii_soc1
(
.rst_int(rst_int),
.clk_int(clk_int),
.clk90_int(clk90_int),
.clk_200_int(clk_200_int),
/*
* Ethernet: 1000BASE-T RGMII
*/
.phy_rx_clk(phy_rx_clk),
.phy_rxd(phy_rxd),
.phy_rx_ctl(phy_rx_ctl),
.phy_tx_clk(phy_tx_clk),
.phy_txd(phy_txd),
.phy_tx_ctl(phy_tx_ctl),
.phy_reset_n(phy_reset_n),
.phy_int_n(phy_int_n),
.phy_pme_n(phy_pme_n),
.mac_gmii_tx_en(mac_gmii_tx_en),
.tx_axis_tdata(tx_axis_tdata),
.tx_axis_tvalid(tx_axis_tvalid),
.tx_axis_tready(tx_axis_tready),
.tx_axis_tlast(tx_axis_tlast),
.tx_axis_tuser(tx_axis_tuser),
.rx_axis_tdata(rx_axis_tdata),
.rx_axis_tvalid(rx_axis_tvalid),
.rx_axis_tlast(rx_axis_tlast),
.rx_axis_tuser(rx_axis_tuser)
);
`ifdef XILINX_ILA
ila_2 eth_ila_clk_int (
.clk(clk_int), // input wire clk
.probe0(tx_axis_tdata), // input wire [7:0] probe0
.probe1(tx_axis_tvalid), // input wire [0:0] probe1
.probe2(tx_axis_tready), // input wire [0:0] probe2
.probe3(tx_axis_tlast), // input wire [0:0] probe3
.probe4(rx_axis_tdata), // input wire [7:0] probe4
.probe5(rx_axis_tvalid), // input wire [0:0] probe5
.probe6(rx_axis_tlast), // input wire [0:0] probe6
.probe7(rx_axis_tuser), // input wire [0:0] probe7
.probe8(byte_sync),
.probe9(last),
.probe10(rx_addr_axis),
.probe11(rx_dest_mac),
.probe12(tx_enable_i),
.probe13(axis_tx_frame_size),
.probe14(tx_axis_tvalid_dly),
.probe15(tx_frame_addr)
);
ila_3 eth_ila_clk_msoc (
.clk(msoc_clk), // input wire clk
.probe0(sync),
.probe1(avail),
.probe2(nextbuf),
.probe3(tx_enable_dly),
.probe4(tx_busy)
);
`endif
endmodule // framing_top
`default_nettype wire

151
fpga/src/iddr.sv Normal file
View file

@ -0,0 +1,151 @@
/*
Copyright (c) 2016-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Generic IDDR module
*/
module iddr #
(
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
parameter TARGET = "GENERIC",
// IODDR style ("IODDR", "IODDR2")
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
// Use IODDR2 for Spartan-6
parameter IODDR_STYLE = "IODDR2",
// Width of register in bits
parameter WIDTH = 1
)
(
input wire clk,
input wire [WIDTH-1:0] d,
output wire [WIDTH-1:0] q1,
output wire [WIDTH-1:0] q2
);
/*
Provides a consistent input DDR flip flop across multiple FPGA families
_____ _____ _____ _____ ____
clk ____/ \_____/ \_____/ \_____/ \_____/
_ _____ _____ _____ _____ _____ _____ _____ _____ _____ _
d _X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7__X_D8__X_
_______ ___________ ___________ ___________ ___________ _
q1 _______X___________X____D0_____X____D2_____X____D4_____X_
_______ ___________ ___________ ___________ ___________ _
q2 _______X___________X____D1_____X____D3_____X____D5_____X_
*/
genvar n;
generate
if (TARGET == "XILINX") begin
for (n = 0; n < WIDTH; n = n + 1) begin : iddr
if (IODDR_STYLE == "IODDR") begin
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),
.SRTYPE("ASYNC")
)
iddr_inst (
.Q1(q1[n]),
.Q2(q2[n]),
.C(clk),
.CE(1'b1),
.D(d[n]),
.R(1'b0),
.S(1'b0)
);
end else if (IODDR_STYLE == "IODDR2") begin
IDDR2 #(
.DDR_ALIGNMENT("C0")
)
iddr_inst (
.Q0(q1[n]),
.Q1(q2[n]),
.C0(clk),
.C1(~clk),
.CE(1'b1),
.D(d[n]),
.R(1'b0),
.S(1'b0)
);
end
end
end else if (TARGET == "ALTERA") begin
wire [WIDTH-1:0] q1_int;
reg [WIDTH-1:0] q1_delay;
altddio_in #(
.WIDTH(WIDTH),
.POWER_UP_HIGH("OFF")
)
altddio_in_inst (
.aset(1'b0),
.datain(d),
.inclocken(1'b1),
.inclock(clk),
.aclr(1'b0),
.dataout_h(q1_int),
.dataout_l(q2)
);
always @(posedge clk) begin
q1_delay <= q1_int;
end
assign q1 = q1_delay;
end else begin
reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}};
reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}};
reg [WIDTH-1:0] q_reg_1 = {WIDTH{1'b0}};
reg [WIDTH-1:0] q_reg_2 = {WIDTH{1'b0}};
always @(posedge clk) begin
d_reg_1 <= d;
end
always @(negedge clk) begin
d_reg_2 <= d;
end
always @(posedge clk) begin
q_reg_1 <= d_reg_1;
q_reg_2 <= d_reg_2;
end
assign q1 = q_reg_1;
assign q2 = q_reg_2;
end
endgenerate
endmodule

341
fpga/src/ip.sv Normal file
View file

@ -0,0 +1,341 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* IPv4 block, ethernet frame interface
*/
module ip
(
input wire clk,
input wire rst,
/*
* Ethernet frame input
*/
input wire s_eth_hdr_valid,
output wire s_eth_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [7:0] s_eth_payload_axis_tdata,
input wire s_eth_payload_axis_tvalid,
output wire s_eth_payload_axis_tready,
input wire s_eth_payload_axis_tlast,
input wire s_eth_payload_axis_tuser,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [7:0] m_eth_payload_axis_tdata,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire m_eth_payload_axis_tuser,
/*
* ARP requests
*/
output wire arp_request_valid,
input wire arp_request_ready,
output wire [31:0] arp_request_ip,
input wire arp_response_valid,
output wire arp_response_ready,
input wire arp_response_error,
input wire [47:0] arp_response_mac,
/*
* IP input
*/
input wire s_ip_hdr_valid,
output wire s_ip_hdr_ready,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_length,
input wire [7:0] s_ip_ttl,
input wire [7:0] s_ip_protocol,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [7:0] s_ip_payload_axis_tdata,
input wire s_ip_payload_axis_tvalid,
output wire s_ip_payload_axis_tready,
input wire s_ip_payload_axis_tlast,
input wire s_ip_payload_axis_tuser,
/*
* IP output
*/
output wire m_ip_hdr_valid,
input wire m_ip_hdr_ready,
output wire [47:0] m_ip_eth_dest_mac,
output wire [47:0] m_ip_eth_src_mac,
output wire [15:0] m_ip_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [7:0] m_ip_payload_axis_tdata,
output wire m_ip_payload_axis_tvalid,
input wire m_ip_payload_axis_tready,
output wire m_ip_payload_axis_tlast,
output wire m_ip_payload_axis_tuser,
/*
* Status
*/
output wire rx_busy,
output wire tx_busy,
output wire rx_error_header_early_termination,
output wire rx_error_payload_early_termination,
output wire rx_error_invalid_header,
output wire rx_error_invalid_checksum,
output wire tx_error_payload_early_termination,
output wire tx_error_arp_failed,
/*
* Configuration
*/
input wire [47:0] local_mac,
input wire [31:0] local_ip
);
localparam [1:0]
STATE_IDLE = 2'd0,
STATE_ARP_QUERY = 2'd1,
STATE_WAIT_PACKET = 2'd2;
reg [1:0] state_reg = STATE_IDLE, state_next;
reg outgoing_ip_hdr_valid_reg = 1'b0, outgoing_ip_hdr_valid_next;
wire outgoing_ip_hdr_ready;
reg [47:0] outgoing_eth_dest_mac_reg = 48'h000000000000, outgoing_eth_dest_mac_next;
wire outgoing_ip_payload_axis_tready;
/*
* IP frame processing
*/
ip_eth_rx
ip_eth_rx_inst (
.clk(clk),
.rst(rst),
// Ethernet frame input
.s_eth_hdr_valid(s_eth_hdr_valid),
.s_eth_hdr_ready(s_eth_hdr_ready),
.s_eth_dest_mac(s_eth_dest_mac),
.s_eth_src_mac(s_eth_src_mac),
.s_eth_type(s_eth_type),
.s_eth_payload_axis_tdata(s_eth_payload_axis_tdata),
.s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid),
.s_eth_payload_axis_tready(s_eth_payload_axis_tready),
.s_eth_payload_axis_tlast(s_eth_payload_axis_tlast),
.s_eth_payload_axis_tuser(s_eth_payload_axis_tuser),
// IP frame output
.m_ip_hdr_valid(m_ip_hdr_valid),
.m_ip_hdr_ready(m_ip_hdr_ready),
.m_eth_dest_mac(m_ip_eth_dest_mac),
.m_eth_src_mac(m_ip_eth_src_mac),
.m_eth_type(m_ip_eth_type),
.m_ip_version(m_ip_version),
.m_ip_ihl(m_ip_ihl),
.m_ip_dscp(m_ip_dscp),
.m_ip_ecn(m_ip_ecn),
.m_ip_length(m_ip_length),
.m_ip_identification(m_ip_identification),
.m_ip_flags(m_ip_flags),
.m_ip_fragment_offset(m_ip_fragment_offset),
.m_ip_ttl(m_ip_ttl),
.m_ip_protocol(m_ip_protocol),
.m_ip_header_checksum(m_ip_header_checksum),
.m_ip_source_ip(m_ip_source_ip),
.m_ip_dest_ip(m_ip_dest_ip),
.m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
.m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
.m_ip_payload_axis_tready(m_ip_payload_axis_tready),
.m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
.m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
// Status signals
.busy(rx_busy),
.error_header_early_termination(rx_error_header_early_termination),
.error_payload_early_termination(rx_error_payload_early_termination),
.error_invalid_header(rx_error_invalid_header),
.error_invalid_checksum(rx_error_invalid_checksum)
);
ip_eth_tx
ip_eth_tx_inst (
.clk(clk),
.rst(rst),
// IP frame input
.s_ip_hdr_valid(outgoing_ip_hdr_valid_reg),
.s_ip_hdr_ready(outgoing_ip_hdr_ready),
.s_eth_dest_mac(outgoing_eth_dest_mac_reg),
.s_eth_src_mac(local_mac),
.s_eth_type(16'h0800),
.s_ip_dscp(s_ip_dscp),
.s_ip_ecn(s_ip_ecn),
.s_ip_length(s_ip_length),
.s_ip_identification(16'd0),
.s_ip_flags(3'b010),
.s_ip_fragment_offset(13'd0),
.s_ip_ttl(s_ip_ttl),
.s_ip_protocol(s_ip_protocol),
.s_ip_source_ip(s_ip_source_ip),
.s_ip_dest_ip(s_ip_dest_ip),
.s_ip_payload_axis_tdata(s_ip_payload_axis_tdata),
.s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid),
.s_ip_payload_axis_tready(outgoing_ip_payload_axis_tready),
.s_ip_payload_axis_tlast(s_ip_payload_axis_tlast),
.s_ip_payload_axis_tuser(s_ip_payload_axis_tuser),
// Ethernet frame output
.m_eth_hdr_valid(m_eth_hdr_valid),
.m_eth_hdr_ready(m_eth_hdr_ready),
.m_eth_dest_mac(m_eth_dest_mac),
.m_eth_src_mac(m_eth_src_mac),
.m_eth_type(m_eth_type),
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser),
// Status signals
.busy(tx_busy),
.error_payload_early_termination(tx_error_payload_early_termination)
);
reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next;
reg arp_request_valid_reg = 1'b0, arp_request_valid_next;
reg arp_response_ready_reg = 1'b0, arp_response_ready_next;
reg drop_packet_reg = 1'b0, drop_packet_next;
assign s_ip_hdr_ready = s_ip_hdr_ready_reg;
assign s_ip_payload_axis_tready = outgoing_ip_payload_axis_tready || drop_packet_reg;
assign arp_request_valid = arp_request_valid_reg;
assign arp_request_ip = s_ip_dest_ip;
assign arp_response_ready = arp_response_ready_reg;
assign tx_error_arp_failed = arp_response_error;
always @* begin
state_next = STATE_IDLE;
arp_request_valid_next = arp_request_valid_reg && !arp_request_ready;
arp_response_ready_next = 1'b0;
drop_packet_next = 1'b0;
s_ip_hdr_ready_next = 1'b0;
outgoing_ip_hdr_valid_next = outgoing_ip_hdr_valid_reg && !outgoing_ip_hdr_ready;
outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg;
case (state_reg)
STATE_IDLE: begin
// wait for outgoing packet
if (s_ip_hdr_valid) begin
// initiate ARP request
arp_request_valid_next = 1'b1;
arp_response_ready_next = 1'b1;
state_next = STATE_ARP_QUERY;
end else begin
state_next = STATE_IDLE;
end
end
STATE_ARP_QUERY: begin
arp_response_ready_next = 1'b1;
if (arp_response_valid) begin
// wait for ARP reponse
if (arp_response_error) begin
// did not get MAC address; drop packet
s_ip_hdr_ready_next = 1'b1;
drop_packet_next = 1'b1;
state_next = STATE_WAIT_PACKET;
end else begin
// got MAC address; send packet
s_ip_hdr_ready_next = 1'b1;
outgoing_ip_hdr_valid_next = 1'b1;
outgoing_eth_dest_mac_next = arp_response_mac;
state_next = STATE_WAIT_PACKET;
end
end else begin
state_next = STATE_ARP_QUERY;
end
end
STATE_WAIT_PACKET: begin
drop_packet_next = drop_packet_reg;
// wait for packet transfer to complete
if (s_ip_payload_axis_tlast && s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT_PACKET;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
arp_request_valid_reg <= 1'b0;
arp_response_ready_reg <= 1'b0;
drop_packet_reg <= 1'b0;
s_ip_hdr_ready_reg <= 1'b0;
outgoing_ip_hdr_valid_reg <= 1'b0;
end else begin
state_reg <= state_next;
arp_request_valid_reg <= arp_request_valid_next;
arp_response_ready_reg <= arp_response_ready_next;
drop_packet_reg <= drop_packet_next;
s_ip_hdr_ready_reg <= s_ip_hdr_ready_next;
outgoing_ip_hdr_valid_reg <= outgoing_ip_hdr_valid_next;
end
outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next;
end
endmodule

407
fpga/src/ip_arb_mux.sv Normal file
View file

@ -0,0 +1,407 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* IP arbitrated multiplexer
*/
module ip_arb_mux #
(
parameter S_COUNT = 4,
parameter DATA_WIDTH = 8,
parameter KEEP_ENABLE = (DATA_WIDTH>8),
parameter KEEP_WIDTH = (DATA_WIDTH/8),
parameter ID_ENABLE = 0,
parameter ID_WIDTH = 8,
parameter DEST_ENABLE = 0,
parameter DEST_WIDTH = 8,
parameter USER_ENABLE = 1,
parameter USER_WIDTH = 1,
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
parameter ARB_TYPE = "PRIORITY",
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "HIGH"
)
(
input wire clk,
input wire rst,
/*
* IP frame inputs
*/
input wire [S_COUNT-1:0] s_ip_hdr_valid,
output wire [S_COUNT-1:0] s_ip_hdr_ready,
input wire [S_COUNT*48-1:0] s_eth_dest_mac,
input wire [S_COUNT*48-1:0] s_eth_src_mac,
input wire [S_COUNT*16-1:0] s_eth_type,
input wire [S_COUNT*4-1:0] s_ip_version,
input wire [S_COUNT*4-1:0] s_ip_ihl,
input wire [S_COUNT*6-1:0] s_ip_dscp,
input wire [S_COUNT*2-1:0] s_ip_ecn,
input wire [S_COUNT*16-1:0] s_ip_length,
input wire [S_COUNT*16-1:0] s_ip_identification,
input wire [S_COUNT*3-1:0] s_ip_flags,
input wire [S_COUNT*13-1:0] s_ip_fragment_offset,
input wire [S_COUNT*8-1:0] s_ip_ttl,
input wire [S_COUNT*8-1:0] s_ip_protocol,
input wire [S_COUNT*16-1:0] s_ip_header_checksum,
input wire [S_COUNT*32-1:0] s_ip_source_ip,
input wire [S_COUNT*32-1:0] s_ip_dest_ip,
input wire [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata,
input wire [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep,
input wire [S_COUNT-1:0] s_ip_payload_axis_tvalid,
output wire [S_COUNT-1:0] s_ip_payload_axis_tready,
input wire [S_COUNT-1:0] s_ip_payload_axis_tlast,
input wire [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid,
input wire [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest,
input wire [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser,
/*
* IP frame output
*/
output wire m_ip_hdr_valid,
input wire m_ip_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata,
output wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep,
output wire m_ip_payload_axis_tvalid,
input wire m_ip_payload_axis_tready,
output wire m_ip_payload_axis_tlast,
output wire [ID_WIDTH-1:0] m_ip_payload_axis_tid,
output wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest,
output wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser
);
parameter CL_S_COUNT = $clog2(S_COUNT);
reg frame_reg = 1'b0, frame_next;
reg s_ip_hdr_ready_mask_reg = 1'b0, s_ip_hdr_ready_mask_next;
reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next;
reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next;
reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next;
reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next;
reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next;
reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next;
reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next;
reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next;
reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next;
reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next;
reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next;
reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next;
reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next;
reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next;
reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next;
reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next;
wire [S_COUNT-1:0] request;
wire [S_COUNT-1:0] acknowledge;
wire [S_COUNT-1:0] grant;
wire grant_valid;
wire [CL_S_COUNT-1:0] grant_encoded;
// internal datapath
reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int;
reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int;
reg m_ip_payload_axis_tvalid_int;
reg m_ip_payload_axis_tready_int_reg = 1'b0;
reg m_ip_payload_axis_tlast_int;
reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int;
reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int;
reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int;
wire m_ip_payload_axis_tready_int_early;
assign s_ip_hdr_ready = (!s_ip_hdr_ready_mask_reg && grant_valid) << grant_encoded;
assign s_ip_payload_axis_tready = (m_ip_payload_axis_tready_int_reg && grant_valid) << grant_encoded;
assign m_ip_hdr_valid = m_ip_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign m_ip_version = m_ip_version_reg;
assign m_ip_ihl = m_ip_ihl_reg;
assign m_ip_dscp = m_ip_dscp_reg;
assign m_ip_ecn = m_ip_ecn_reg;
assign m_ip_length = m_ip_length_reg;
assign m_ip_identification = m_ip_identification_reg;
assign m_ip_flags = m_ip_flags_reg;
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
assign m_ip_ttl = m_ip_ttl_reg;
assign m_ip_protocol = m_ip_protocol_reg;
assign m_ip_header_checksum = m_ip_header_checksum_reg;
assign m_ip_source_ip = m_ip_source_ip_reg;
assign m_ip_dest_ip = m_ip_dest_ip_reg;
// mux for incoming packet
wire [DATA_WIDTH-1:0] current_s_tdata = s_ip_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH];
wire [KEEP_WIDTH-1:0] current_s_tkeep = s_ip_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH];
wire current_s_tvalid = s_ip_payload_axis_tvalid[grant_encoded];
wire current_s_tready = s_ip_payload_axis_tready[grant_encoded];
wire current_s_tlast = s_ip_payload_axis_tlast[grant_encoded];
wire [ID_WIDTH-1:0] current_s_tid = s_ip_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH];
wire [DEST_WIDTH-1:0] current_s_tdest = s_ip_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH];
wire [USER_WIDTH-1:0] current_s_tuser = s_ip_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH];
// arbiter instance
rgmii_arbiter #(
.PORTS(S_COUNT),
.TYPE(ARB_TYPE),
.BLOCK("ACKNOWLEDGE"),
.LSB_PRIORITY(LSB_PRIORITY)
)
arb_inst (
.clk(clk),
.rst(rst),
.request(request),
.acknowledge(acknowledge),
.grant(grant),
.grant_valid(grant_valid),
.grant_encoded(grant_encoded)
);
generate
genvar n;
for (n = 0; n < S_COUNT; n = n + 1) begin
assign request[n] = s_ip_hdr_valid[n] && !grant[n];
assign acknowledge[n] = grant[n] && s_ip_payload_axis_tvalid[n] && s_ip_payload_axis_tready[n] && s_ip_payload_axis_tlast[n];
end
endgenerate
always @* begin
frame_next = frame_reg;
s_ip_hdr_ready_mask_next = s_ip_hdr_ready_mask_reg;
m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready;
m_eth_dest_mac_next = m_eth_dest_mac_reg;
m_eth_src_mac_next = m_eth_src_mac_reg;
m_eth_type_next = m_eth_type_reg;
m_ip_version_next = m_ip_version_reg;
m_ip_ihl_next = m_ip_ihl_reg;
m_ip_dscp_next = m_ip_dscp_reg;
m_ip_ecn_next = m_ip_ecn_reg;
m_ip_length_next = m_ip_length_reg;
m_ip_identification_next = m_ip_identification_reg;
m_ip_flags_next = m_ip_flags_reg;
m_ip_fragment_offset_next = m_ip_fragment_offset_reg;
m_ip_ttl_next = m_ip_ttl_reg;
m_ip_protocol_next = m_ip_protocol_reg;
m_ip_header_checksum_next = m_ip_header_checksum_reg;
m_ip_source_ip_next = m_ip_source_ip_reg;
m_ip_dest_ip_next = m_ip_dest_ip_reg;
if (s_ip_payload_axis_tvalid[grant_encoded] && s_ip_payload_axis_tready[grant_encoded]) begin
// end of frame detection
if (s_ip_payload_axis_tlast[grant_encoded]) begin
frame_next = 1'b0;
s_ip_hdr_ready_mask_next = 1'b0;
end
end
if (!frame_reg && grant_valid) begin
// start of frame
frame_next = 1'b1;
s_ip_hdr_ready_mask_next = 1'b1;
m_ip_hdr_valid_next = 1'b1;
m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48];
m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48];
m_eth_type_next = s_eth_type[grant_encoded*16 +: 16];
m_ip_version_next = s_ip_version[grant_encoded*4 +: 4];
m_ip_ihl_next = s_ip_ihl[grant_encoded*4 +: 4];
m_ip_dscp_next = s_ip_dscp[grant_encoded*6 +: 6];
m_ip_ecn_next = s_ip_ecn[grant_encoded*2 +: 2];
m_ip_length_next = s_ip_length[grant_encoded*16 +: 16];
m_ip_identification_next = s_ip_identification[grant_encoded*16 +: 16];
m_ip_flags_next = s_ip_flags[grant_encoded*3 +: 3];
m_ip_fragment_offset_next = s_ip_fragment_offset[grant_encoded*13 +: 13];
m_ip_ttl_next = s_ip_ttl[grant_encoded*8 +: 8];
m_ip_protocol_next = s_ip_protocol[grant_encoded*8 +: 8];
m_ip_header_checksum_next = s_ip_header_checksum[grant_encoded*16 +: 16];
m_ip_source_ip_next = s_ip_source_ip[grant_encoded*32 +: 32];
m_ip_dest_ip_next = s_ip_dest_ip[grant_encoded*32 +: 32];
end
// pass through selected packet data
m_ip_payload_axis_tdata_int = current_s_tdata;
m_ip_payload_axis_tkeep_int = current_s_tkeep;
m_ip_payload_axis_tvalid_int = current_s_tvalid && m_ip_payload_axis_tready_int_reg && grant_valid;
m_ip_payload_axis_tlast_int = current_s_tlast;
m_ip_payload_axis_tid_int = current_s_tid;
m_ip_payload_axis_tdest_int = current_s_tdest;
m_ip_payload_axis_tuser_int = current_s_tuser;
end
always @(posedge clk) begin
if (rst) begin
frame_reg <= 1'b0;
s_ip_hdr_ready_mask_reg <= 1'b0;
m_ip_hdr_valid_reg <= 1'b0;
end else begin
frame_reg <= frame_next;
s_ip_hdr_ready_mask_reg <= s_ip_hdr_ready_mask_next;
m_ip_hdr_valid_reg <= m_ip_hdr_valid_next;
end
m_eth_dest_mac_reg <= m_eth_dest_mac_next;
m_eth_src_mac_reg <= m_eth_src_mac_next;
m_eth_type_reg <= m_eth_type_next;
m_ip_version_reg <= m_ip_version_next;
m_ip_ihl_reg <= m_ip_ihl_next;
m_ip_dscp_reg <= m_ip_dscp_next;
m_ip_ecn_reg <= m_ip_ecn_next;
m_ip_length_reg <= m_ip_length_next;
m_ip_identification_reg <= m_ip_identification_next;
m_ip_flags_reg <= m_ip_flags_next;
m_ip_fragment_offset_reg <= m_ip_fragment_offset_next;
m_ip_ttl_reg <= m_ip_ttl_next;
m_ip_protocol_reg <= m_ip_protocol_next;
m_ip_header_checksum_reg <= m_ip_header_checksum_next;
m_ip_source_ip_reg <= m_ip_source_ip_next;
m_ip_dest_ip_reg <= m_ip_dest_ip_next;
end
// output datapath logic
reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next;
reg m_ip_payload_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next;
reg temp_m_ip_payload_axis_tlast_reg = 1'b0;
reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
// datapath control
reg store_axis_int_to_output;
reg store_axis_int_to_temp;
reg store_ip_payload_axis_temp_to_output;
assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg;
assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? m_ip_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg;
assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg;
assign m_ip_payload_axis_tid = ID_ENABLE ? m_ip_payload_axis_tid_reg : {ID_WIDTH{1'b0}};
assign m_ip_payload_axis_tdest = DEST_ENABLE ? m_ip_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}};
assign m_ip_payload_axis_tuser = USER_ENABLE ? m_ip_payload_axis_tuser_reg : {USER_WIDTH{1'b0}};
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg;
temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
store_axis_int_to_output = 1'b0;
store_axis_int_to_temp = 1'b0;
store_ip_payload_axis_temp_to_output = 1'b0;
if (m_ip_payload_axis_tready_int_reg) begin
// input is ready
if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
store_axis_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
store_axis_int_to_temp = 1'b1;
end
end else if (m_ip_payload_axis_tready) begin
// input is not ready, but output is ready
m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
temp_m_ip_payload_axis_tvalid_next = 1'b0;
store_ip_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_ip_payload_axis_tvalid_reg <= 1'b0;
m_ip_payload_axis_tready_int_reg <= 1'b0;
temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next;
m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early;
temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next;
end
// datapath
if (store_axis_int_to_output) begin
m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int;
m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int;
m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int;
m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
end else if (store_ip_payload_axis_temp_to_output) begin
m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg;
m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg;
m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg;
m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg;
m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg;
m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg;
end
if (store_axis_int_to_temp) begin
temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int;
temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int;
temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int;
temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
end
end
endmodule

440
fpga/src/ip_complete.sv Normal file
View file

@ -0,0 +1,440 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* IPv4 and ARP block, ethernet frame interface
*/
module ip_complete #(
parameter ARP_CACHE_ADDR_WIDTH = 9,
parameter ARP_REQUEST_RETRY_COUNT = 4,
parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2,
parameter ARP_REQUEST_TIMEOUT = 125000000*30
)
(
input wire clk,
input wire rst,
/*
* Ethernet frame input
*/
input wire s_eth_hdr_valid,
output wire s_eth_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [7:0] s_eth_payload_axis_tdata,
input wire s_eth_payload_axis_tvalid,
output wire s_eth_payload_axis_tready,
input wire s_eth_payload_axis_tlast,
input wire s_eth_payload_axis_tuser,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [7:0] m_eth_payload_axis_tdata,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire m_eth_payload_axis_tuser,
/*
* IP input
*/
input wire s_ip_hdr_valid,
output wire s_ip_hdr_ready,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_length,
input wire [7:0] s_ip_ttl,
input wire [7:0] s_ip_protocol,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [7:0] s_ip_payload_axis_tdata,
input wire s_ip_payload_axis_tvalid,
output wire s_ip_payload_axis_tready,
input wire s_ip_payload_axis_tlast,
input wire s_ip_payload_axis_tuser,
/*
* IP output
*/
output wire m_ip_hdr_valid,
input wire m_ip_hdr_ready,
output wire [47:0] m_ip_eth_dest_mac,
output wire [47:0] m_ip_eth_src_mac,
output wire [15:0] m_ip_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [7:0] m_ip_payload_axis_tdata,
output wire m_ip_payload_axis_tvalid,
input wire m_ip_payload_axis_tready,
output wire m_ip_payload_axis_tlast,
output wire m_ip_payload_axis_tuser,
/*
* Status
*/
output wire rx_busy,
output wire tx_busy,
output wire rx_error_header_early_termination,
output wire rx_error_payload_early_termination,
output wire rx_error_invalid_header,
output wire rx_error_invalid_checksum,
output wire tx_error_payload_early_termination,
output wire tx_error_arp_failed,
/*
* Configuration
*/
input wire [47:0] local_mac,
input wire [31:0] local_ip,
input wire [31:0] gateway_ip,
input wire [31:0] subnet_mask,
input wire clear_arp_cache
);
/*
This module integrates the IP and ARP modules for a complete IP stack
*/
wire arp_request_valid;
wire arp_request_ready;
wire [31:0] arp_request_ip;
wire arp_response_valid;
wire arp_response_ready;
wire arp_response_error;
wire [47:0] arp_response_mac;
wire ip_rx_eth_hdr_valid;
wire ip_rx_eth_hdr_ready;
wire [47:0] ip_rx_eth_dest_mac;
wire [47:0] ip_rx_eth_src_mac;
wire [15:0] ip_rx_eth_type;
wire [7:0] ip_rx_eth_payload_axis_tdata;
wire ip_rx_eth_payload_axis_tvalid;
wire ip_rx_eth_payload_axis_tready;
wire ip_rx_eth_payload_axis_tlast;
wire ip_rx_eth_payload_axis_tuser;
wire ip_tx_eth_hdr_valid;
wire ip_tx_eth_hdr_ready;
wire [47:0] ip_tx_eth_dest_mac;
wire [47:0] ip_tx_eth_src_mac;
wire [15:0] ip_tx_eth_type;
wire [7:0] ip_tx_eth_payload_axis_tdata;
wire ip_tx_eth_payload_axis_tvalid;
wire ip_tx_eth_payload_axis_tready;
wire ip_tx_eth_payload_axis_tlast;
wire ip_tx_eth_payload_axis_tuser;
wire arp_rx_eth_hdr_valid;
wire arp_rx_eth_hdr_ready;
wire [47:0] arp_rx_eth_dest_mac;
wire [47:0] arp_rx_eth_src_mac;
wire [15:0] arp_rx_eth_type;
wire [7:0] arp_rx_eth_payload_axis_tdata;
wire arp_rx_eth_payload_axis_tvalid;
wire arp_rx_eth_payload_axis_tready;
wire arp_rx_eth_payload_axis_tlast;
wire arp_rx_eth_payload_axis_tuser;
wire arp_tx_eth_hdr_valid;
wire arp_tx_eth_hdr_ready;
wire [47:0] arp_tx_eth_dest_mac;
wire [47:0] arp_tx_eth_src_mac;
wire [15:0] arp_tx_eth_type;
wire [7:0] arp_tx_eth_payload_axis_tdata;
wire arp_tx_eth_payload_axis_tvalid;
wire arp_tx_eth_payload_axis_tready;
wire arp_tx_eth_payload_axis_tlast;
wire arp_tx_eth_payload_axis_tuser;
/*
* Input classifier (eth_type)
*/
wire s_select_ip = (s_eth_type == 16'h0800);
wire s_select_arp = (s_eth_type == 16'h0806);
wire s_select_none = !(s_select_ip || s_select_arp);
reg s_select_ip_reg = 1'b0;
reg s_select_arp_reg = 1'b0;
reg s_select_none_reg = 1'b0;
always @(posedge clk) begin
if (rst) begin
s_select_ip_reg <= 1'b0;
s_select_arp_reg <= 1'b0;
s_select_none_reg <= 1'b0;
end else begin
if (s_eth_payload_axis_tvalid) begin
if ((!s_select_ip_reg && !s_select_arp_reg && !s_select_none_reg) ||
(s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && s_eth_payload_axis_tlast)) begin
s_select_ip_reg <= s_select_ip;
s_select_arp_reg <= s_select_arp;
s_select_none_reg <= s_select_none;
end
end else begin
s_select_ip_reg <= 1'b0;
s_select_arp_reg <= 1'b0;
s_select_none_reg <= 1'b0;
end
end
end
assign ip_rx_eth_hdr_valid = s_select_ip && s_eth_hdr_valid;
assign ip_rx_eth_dest_mac = s_eth_dest_mac;
assign ip_rx_eth_src_mac = s_eth_src_mac;
assign ip_rx_eth_type = 16'h0800;
assign ip_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata;
assign ip_rx_eth_payload_axis_tvalid = s_select_ip_reg && s_eth_payload_axis_tvalid;
assign ip_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast;
assign ip_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser;
assign arp_rx_eth_hdr_valid = s_select_arp && s_eth_hdr_valid;
assign arp_rx_eth_dest_mac = s_eth_dest_mac;
assign arp_rx_eth_src_mac = s_eth_src_mac;
assign arp_rx_eth_type = 16'h0806;
assign arp_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata;
assign arp_rx_eth_payload_axis_tvalid = s_select_arp_reg && s_eth_payload_axis_tvalid;
assign arp_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast;
assign arp_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser;
assign s_eth_hdr_ready = (s_select_ip && ip_rx_eth_hdr_ready) ||
(s_select_arp && arp_rx_eth_hdr_ready) ||
(s_select_none);
assign s_eth_payload_axis_tready = (s_select_ip_reg && ip_rx_eth_payload_axis_tready) ||
(s_select_arp_reg && arp_rx_eth_payload_axis_tready) ||
s_select_none_reg;
/*
* Output arbiter
*/
eth_arb_mux #(
.S_COUNT(2),
.DATA_WIDTH(8),
.KEEP_ENABLE(0),
.ID_ENABLE(0),
.DEST_ENABLE(0),
.USER_ENABLE(1),
.USER_WIDTH(1),
.ARB_TYPE("PRIORITY"),
.LSB_PRIORITY("HIGH")
)
eth_arb_mux_inst (
.clk(clk),
.rst(rst),
// Ethernet frame inputs
.s_eth_hdr_valid({ip_tx_eth_hdr_valid, arp_tx_eth_hdr_valid}),
.s_eth_hdr_ready({ip_tx_eth_hdr_ready, arp_tx_eth_hdr_ready}),
.s_eth_dest_mac({ip_tx_eth_dest_mac, arp_tx_eth_dest_mac}),
.s_eth_src_mac({ip_tx_eth_src_mac, arp_tx_eth_src_mac}),
.s_eth_type({ip_tx_eth_type, arp_tx_eth_type}),
.s_eth_payload_axis_tdata({ip_tx_eth_payload_axis_tdata, arp_tx_eth_payload_axis_tdata}),
.s_eth_payload_axis_tkeep(0),
.s_eth_payload_axis_tvalid({ip_tx_eth_payload_axis_tvalid, arp_tx_eth_payload_axis_tvalid}),
.s_eth_payload_axis_tready({ip_tx_eth_payload_axis_tready, arp_tx_eth_payload_axis_tready}),
.s_eth_payload_axis_tlast({ip_tx_eth_payload_axis_tlast, arp_tx_eth_payload_axis_tlast}),
.s_eth_payload_axis_tid(0),
.s_eth_payload_axis_tdest(0),
.s_eth_payload_axis_tuser({ip_tx_eth_payload_axis_tuser, arp_tx_eth_payload_axis_tuser}),
// Ethernet frame output
.m_eth_hdr_valid(m_eth_hdr_valid),
.m_eth_hdr_ready(m_eth_hdr_ready),
.m_eth_dest_mac(m_eth_dest_mac),
.m_eth_src_mac(m_eth_src_mac),
.m_eth_type(m_eth_type),
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
.m_eth_payload_axis_tkeep(),
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
.m_eth_payload_axis_tid(),
.m_eth_payload_axis_tdest(),
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser)
);
/*
* IP module
*/
ip
ip_inst (
.clk(clk),
.rst(rst),
// Ethernet frame input
.s_eth_hdr_valid(ip_rx_eth_hdr_valid),
.s_eth_hdr_ready(ip_rx_eth_hdr_ready),
.s_eth_dest_mac(ip_rx_eth_dest_mac),
.s_eth_src_mac(ip_rx_eth_src_mac),
.s_eth_type(ip_rx_eth_type),
.s_eth_payload_axis_tdata(ip_rx_eth_payload_axis_tdata),
.s_eth_payload_axis_tvalid(ip_rx_eth_payload_axis_tvalid),
.s_eth_payload_axis_tready(ip_rx_eth_payload_axis_tready),
.s_eth_payload_axis_tlast(ip_rx_eth_payload_axis_tlast),
.s_eth_payload_axis_tuser(ip_rx_eth_payload_axis_tuser),
// Ethernet frame output
.m_eth_hdr_valid(ip_tx_eth_hdr_valid),
.m_eth_hdr_ready(ip_tx_eth_hdr_ready),
.m_eth_dest_mac(ip_tx_eth_dest_mac),
.m_eth_src_mac(ip_tx_eth_src_mac),
.m_eth_type(ip_tx_eth_type),
.m_eth_payload_axis_tdata(ip_tx_eth_payload_axis_tdata),
.m_eth_payload_axis_tvalid(ip_tx_eth_payload_axis_tvalid),
.m_eth_payload_axis_tready(ip_tx_eth_payload_axis_tready),
.m_eth_payload_axis_tlast(ip_tx_eth_payload_axis_tlast),
.m_eth_payload_axis_tuser(ip_tx_eth_payload_axis_tuser),
// IP frame output
.m_ip_hdr_valid(m_ip_hdr_valid),
.m_ip_hdr_ready(m_ip_hdr_ready),
.m_ip_eth_dest_mac(m_ip_eth_dest_mac),
.m_ip_eth_src_mac(m_ip_eth_src_mac),
.m_ip_eth_type(m_ip_eth_type),
.m_ip_version(m_ip_version),
.m_ip_ihl(m_ip_ihl),
.m_ip_dscp(m_ip_dscp),
.m_ip_ecn(m_ip_ecn),
.m_ip_length(m_ip_length),
.m_ip_identification(m_ip_identification),
.m_ip_flags(m_ip_flags),
.m_ip_fragment_offset(m_ip_fragment_offset),
.m_ip_ttl(m_ip_ttl),
.m_ip_protocol(m_ip_protocol),
.m_ip_header_checksum(m_ip_header_checksum),
.m_ip_source_ip(m_ip_source_ip),
.m_ip_dest_ip(m_ip_dest_ip),
.m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
.m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
.m_ip_payload_axis_tready(m_ip_payload_axis_tready),
.m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
.m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
// IP frame input
.s_ip_hdr_valid(s_ip_hdr_valid),
.s_ip_hdr_ready(s_ip_hdr_ready),
.s_ip_dscp(s_ip_dscp),
.s_ip_ecn(s_ip_ecn),
.s_ip_length(s_ip_length),
.s_ip_ttl(s_ip_ttl),
.s_ip_protocol(s_ip_protocol),
.s_ip_source_ip(s_ip_source_ip),
.s_ip_dest_ip(s_ip_dest_ip),
.s_ip_payload_axis_tdata(s_ip_payload_axis_tdata),
.s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid),
.s_ip_payload_axis_tready(s_ip_payload_axis_tready),
.s_ip_payload_axis_tlast(s_ip_payload_axis_tlast),
.s_ip_payload_axis_tuser(s_ip_payload_axis_tuser),
// ARP requests
.arp_request_valid(arp_request_valid),
.arp_request_ready(arp_request_ready),
.arp_request_ip(arp_request_ip),
.arp_response_valid(arp_response_valid),
.arp_response_ready(arp_response_ready),
.arp_response_error(arp_response_error),
.arp_response_mac(arp_response_mac),
// Status
.rx_busy(rx_busy),
.tx_busy(tx_busy),
.rx_error_header_early_termination(rx_error_header_early_termination),
.rx_error_payload_early_termination(rx_error_payload_early_termination),
.rx_error_invalid_header(rx_error_invalid_header),
.rx_error_invalid_checksum(rx_error_invalid_checksum),
.tx_error_payload_early_termination(tx_error_payload_early_termination),
.tx_error_arp_failed(tx_error_arp_failed),
// Configuration
.local_mac(local_mac),
.local_ip(local_ip)
);
/*
* ARP module
*/
arp #(
.CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH),
.REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT),
.REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL),
.REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT)
)
arp_inst (
.clk(clk),
.rst(rst),
// Ethernet frame input
.s_eth_hdr_valid(arp_rx_eth_hdr_valid),
.s_eth_hdr_ready(arp_rx_eth_hdr_ready),
.s_eth_dest_mac(arp_rx_eth_dest_mac),
.s_eth_src_mac(arp_rx_eth_src_mac),
.s_eth_type(arp_rx_eth_type),
.s_eth_payload_axis_tdata(arp_rx_eth_payload_axis_tdata),
.s_eth_payload_axis_tvalid(arp_rx_eth_payload_axis_tvalid),
.s_eth_payload_axis_tready(arp_rx_eth_payload_axis_tready),
.s_eth_payload_axis_tlast(arp_rx_eth_payload_axis_tlast),
.s_eth_payload_axis_tuser(arp_rx_eth_payload_axis_tuser),
// Ethernet frame output
.m_eth_hdr_valid(arp_tx_eth_hdr_valid),
.m_eth_hdr_ready(arp_tx_eth_hdr_ready),
.m_eth_dest_mac(arp_tx_eth_dest_mac),
.m_eth_src_mac(arp_tx_eth_src_mac),
.m_eth_type(arp_tx_eth_type),
.m_eth_payload_axis_tdata(arp_tx_eth_payload_axis_tdata),
.m_eth_payload_axis_tvalid(arp_tx_eth_payload_axis_tvalid),
.m_eth_payload_axis_tready(arp_tx_eth_payload_axis_tready),
.m_eth_payload_axis_tlast(arp_tx_eth_payload_axis_tlast),
.m_eth_payload_axis_tuser(arp_tx_eth_payload_axis_tuser),
// ARP requests
.arp_request_valid(arp_request_valid),
.arp_request_ready(arp_request_ready),
.arp_request_ip(arp_request_ip),
.arp_response_valid(arp_response_valid),
.arp_response_ready(arp_response_ready),
.arp_response_error(arp_response_error),
.arp_response_mac(arp_response_mac),
// Configuration
.local_mac(local_mac),
.local_ip(local_ip),
.gateway_ip(gateway_ip),
.subnet_mask(subnet_mask),
.clear_cache(clear_arp_cache)
);
endmodule

575
fpga/src/ip_eth_rx.sv Normal file
View file

@ -0,0 +1,575 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* IP ethernet frame receiver (Ethernet frame in, IP frame out)
*/
module ip_eth_rx
(
input wire clk,
input wire rst,
/*
* Ethernet frame input
*/
input wire s_eth_hdr_valid,
output wire s_eth_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [7:0] s_eth_payload_axis_tdata,
input wire s_eth_payload_axis_tvalid,
output wire s_eth_payload_axis_tready,
input wire s_eth_payload_axis_tlast,
input wire s_eth_payload_axis_tuser,
/*
* IP frame output
*/
output wire m_ip_hdr_valid,
input wire m_ip_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [7:0] m_ip_payload_axis_tdata,
output wire m_ip_payload_axis_tvalid,
input wire m_ip_payload_axis_tready,
output wire m_ip_payload_axis_tlast,
output wire m_ip_payload_axis_tuser,
/*
* Status signals
*/
output wire busy,
output wire error_header_early_termination,
output wire error_payload_early_termination,
output wire error_invalid_header,
output wire error_invalid_checksum
);
/*
IP Frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype (0x0800) 2 octets
Version (4) 4 bits
IHL (5-15) 4 bits
DSCP (0) 6 bits
ECN (0) 2 bits
length 2 octets
identification (0?) 2 octets
flags (010) 3 bits
fragment offset (0) 13 bits
time to live (64?) 1 octet
protocol 1 octet
header checksum 2 octets
source IP 4 octets
destination IP 4 octets
options (IHL-5)*4 octets
payload length octets
This module receives an Ethernet frame with header fields in parallel and
payload on an AXI stream interface, decodes and strips the IP header fields,
then produces the header fields in parallel along with the IP payload in a
separate AXI stream.
*/
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_READ_HEADER = 3'd1,
STATE_READ_PAYLOAD = 3'd2,
STATE_READ_PAYLOAD_LAST = 3'd3,
STATE_WAIT_LAST = 3'd4;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_eth_hdr;
reg store_ip_version_ihl;
reg store_ip_dscp_ecn;
reg store_ip_length_0;
reg store_ip_length_1;
reg store_ip_identification_0;
reg store_ip_identification_1;
reg store_ip_flags_fragment_offset_0;
reg store_ip_flags_fragment_offset_1;
reg store_ip_ttl;
reg store_ip_protocol;
reg store_ip_header_checksum_0;
reg store_ip_header_checksum_1;
reg store_ip_source_ip_0;
reg store_ip_source_ip_1;
reg store_ip_source_ip_2;
reg store_ip_source_ip_3;
reg store_ip_dest_ip_0;
reg store_ip_dest_ip_1;
reg store_ip_dest_ip_2;
reg store_ip_dest_ip_3;
reg store_last_word;
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next;
reg [7:0] last_word_data_reg = 8'd0;
reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next;
reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next;
reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg [3:0] m_ip_version_reg = 4'd0;
reg [3:0] m_ip_ihl_reg = 4'd0;
reg [5:0] m_ip_dscp_reg = 6'd0;
reg [1:0] m_ip_ecn_reg = 2'd0;
reg [15:0] m_ip_length_reg = 16'd0;
reg [15:0] m_ip_identification_reg = 16'd0;
reg [2:0] m_ip_flags_reg = 3'd0;
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
reg [7:0] m_ip_ttl_reg = 8'd0;
reg [7:0] m_ip_protocol_reg = 8'd0;
reg [15:0] m_ip_header_checksum_reg = 16'd0;
reg [31:0] m_ip_source_ip_reg = 32'd0;
reg [31:0] m_ip_dest_ip_reg = 32'd0;
reg busy_reg = 1'b0;
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
reg error_invalid_header_reg = 1'b0, error_invalid_header_next;
reg error_invalid_checksum_reg = 1'b0, error_invalid_checksum_next;
// internal datapath
reg [7:0] m_ip_payload_axis_tdata_int;
reg m_ip_payload_axis_tvalid_int;
reg m_ip_payload_axis_tready_int_reg = 1'b0;
reg m_ip_payload_axis_tlast_int;
reg m_ip_payload_axis_tuser_int;
wire m_ip_payload_axis_tready_int_early;
assign s_eth_hdr_ready = s_eth_hdr_ready_reg;
assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg;
assign m_ip_hdr_valid = m_ip_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign m_ip_version = m_ip_version_reg;
assign m_ip_ihl = m_ip_ihl_reg;
assign m_ip_dscp = m_ip_dscp_reg;
assign m_ip_ecn = m_ip_ecn_reg;
assign m_ip_length = m_ip_length_reg;
assign m_ip_identification = m_ip_identification_reg;
assign m_ip_flags = m_ip_flags_reg;
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
assign m_ip_ttl = m_ip_ttl_reg;
assign m_ip_protocol = m_ip_protocol_reg;
assign m_ip_header_checksum = m_ip_header_checksum_reg;
assign m_ip_source_ip = m_ip_source_ip_reg;
assign m_ip_dest_ip = m_ip_dest_ip_reg;
assign busy = busy_reg;
assign error_header_early_termination = error_header_early_termination_reg;
assign error_payload_early_termination = error_payload_early_termination_reg;
assign error_invalid_header = error_invalid_header_reg;
assign error_invalid_checksum = error_invalid_checksum_reg;
function [15:0] add1c16b;
input [15:0] a, b;
reg [16:0] t;
begin
t = a+b;
add1c16b = t[15:0] + t[16];
end
endfunction
always @* begin
state_next = STATE_IDLE;
s_eth_hdr_ready_next = 1'b0;
s_eth_payload_axis_tready_next = 1'b0;
store_eth_hdr = 1'b0;
store_ip_version_ihl = 1'b0;
store_ip_dscp_ecn = 1'b0;
store_ip_length_0 = 1'b0;
store_ip_length_1 = 1'b0;
store_ip_identification_0 = 1'b0;
store_ip_identification_1 = 1'b0;
store_ip_flags_fragment_offset_0 = 1'b0;
store_ip_flags_fragment_offset_1 = 1'b0;
store_ip_ttl = 1'b0;
store_ip_protocol = 1'b0;
store_ip_header_checksum_0 = 1'b0;
store_ip_header_checksum_1 = 1'b0;
store_ip_source_ip_0 = 1'b0;
store_ip_source_ip_1 = 1'b0;
store_ip_source_ip_2 = 1'b0;
store_ip_source_ip_3 = 1'b0;
store_ip_dest_ip_0 = 1'b0;
store_ip_dest_ip_1 = 1'b0;
store_ip_dest_ip_2 = 1'b0;
store_ip_dest_ip_3 = 1'b0;
store_last_word = 1'b0;
frame_ptr_next = frame_ptr_reg;
hdr_sum_next = hdr_sum_reg;
m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready;
error_header_early_termination_next = 1'b0;
error_payload_early_termination_next = 1'b0;
error_invalid_header_next = 1'b0;
error_invalid_checksum_next = 1'b0;
m_ip_payload_axis_tdata_int = 8'd0;
m_ip_payload_axis_tvalid_int = 1'b0;
m_ip_payload_axis_tlast_int = 1'b0;
m_ip_payload_axis_tuser_int = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for header
frame_ptr_next = 16'd0;
hdr_sum_next = 16'd0;
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
if (s_eth_hdr_ready && s_eth_hdr_valid) begin
s_eth_hdr_ready_next = 1'b0;
s_eth_payload_axis_tready_next = 1'b1;
store_eth_hdr = 1'b1;
state_next = STATE_READ_HEADER;
end else begin
state_next = STATE_IDLE;
end
end
STATE_READ_HEADER: begin
// read header
s_eth_payload_axis_tready_next = 1'b1;
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
// word transfer in - store it
frame_ptr_next = frame_ptr_reg + 16'd1;
state_next = STATE_READ_HEADER;
if (frame_ptr_reg[0]) begin
hdr_sum_next = add1c16b(hdr_sum_reg, {8'd0, s_eth_payload_axis_tdata});
end else begin
hdr_sum_next = add1c16b(hdr_sum_reg, {s_eth_payload_axis_tdata, 8'd0});
end
case (frame_ptr_reg)
8'h00: store_ip_version_ihl = 1'b1;
8'h01: store_ip_dscp_ecn = 1'b1;
8'h02: store_ip_length_1 = 1'b1;
8'h03: store_ip_length_0 = 1'b1;
8'h04: store_ip_identification_1 = 1'b1;
8'h05: store_ip_identification_0 = 1'b1;
8'h06: store_ip_flags_fragment_offset_1 = 1'b1;
8'h07: store_ip_flags_fragment_offset_0 = 1'b1;
8'h08: store_ip_ttl = 1'b1;
8'h09: store_ip_protocol = 1'b1;
8'h0A: store_ip_header_checksum_1 = 1'b1;
8'h0B: store_ip_header_checksum_0 = 1'b1;
8'h0C: store_ip_source_ip_3 = 1'b1;
8'h0D: store_ip_source_ip_2 = 1'b1;
8'h0E: store_ip_source_ip_1 = 1'b1;
8'h0F: store_ip_source_ip_0 = 1'b1;
8'h10: store_ip_dest_ip_3 = 1'b1;
8'h11: store_ip_dest_ip_2 = 1'b1;
8'h12: store_ip_dest_ip_1 = 1'b1;
8'h13: begin
store_ip_dest_ip_0 = 1'b1;
if (m_ip_version_reg != 4'd4 || m_ip_ihl_reg != 4'd5) begin
error_invalid_header_next = 1'b1;
state_next = STATE_WAIT_LAST;
end else if (hdr_sum_next != 16'hffff) begin
error_invalid_checksum_next = 1'b1;
state_next = STATE_WAIT_LAST;
end else begin
m_ip_hdr_valid_next = 1'b1;
s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
state_next = STATE_READ_PAYLOAD;
end
end
endcase
if (s_eth_payload_axis_tlast) begin
error_header_early_termination_next = 1'b1;
m_ip_hdr_valid_next = 1'b0;
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
s_eth_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_READ_HEADER;
end
end
STATE_READ_PAYLOAD: begin
// read payload
s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
m_ip_payload_axis_tdata_int = s_eth_payload_axis_tdata;
m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid;
m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast;
m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser;
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
// word transfer through
frame_ptr_next = frame_ptr_reg + 16'd1;
if (s_eth_payload_axis_tlast) begin
if (frame_ptr_next != m_ip_length_reg) begin
// end of frame, but length does not match
m_ip_payload_axis_tuser_int = 1'b1;
error_payload_early_termination_next = 1'b1;
end
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
s_eth_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
if (frame_ptr_next == m_ip_length_reg) begin
store_last_word = 1'b1;
m_ip_payload_axis_tvalid_int = 1'b0;
state_next = STATE_READ_PAYLOAD_LAST;
end else begin
state_next = STATE_READ_PAYLOAD;
end
end
end else begin
state_next = STATE_READ_PAYLOAD;
end
end
STATE_READ_PAYLOAD_LAST: begin
// read and discard until end of frame
s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
m_ip_payload_axis_tdata_int = last_word_data_reg;
m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid && s_eth_payload_axis_tlast;
m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast;
m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser;
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
if (s_eth_payload_axis_tlast) begin
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
s_eth_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_READ_PAYLOAD_LAST;
end
end else begin
state_next = STATE_READ_PAYLOAD_LAST;
end
end
STATE_WAIT_LAST: begin
// read and discard until end of frame
s_eth_payload_axis_tready_next = 1'b1;
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
if (s_eth_payload_axis_tlast) begin
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
s_eth_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT_LAST;
end
end else begin
state_next = STATE_WAIT_LAST;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 16'd0;
hdr_sum_reg <= 16'd0;
s_eth_hdr_ready_reg <= 1'b0;
s_eth_payload_axis_tready_reg <= 1'b0;
m_ip_hdr_valid_reg <= 1'b0;
busy_reg <= 1'b0;
error_header_early_termination_reg <= 1'b0;
error_payload_early_termination_reg <= 1'b0;
error_invalid_header_reg <= 1'b0;
error_invalid_checksum_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
hdr_sum_reg <= hdr_sum_next;
s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next;
m_ip_hdr_valid_reg <= m_ip_hdr_valid_next;
error_header_early_termination_reg <= error_header_early_termination_next;
error_payload_early_termination_reg <= error_payload_early_termination_next;
error_invalid_header_reg <= error_invalid_header_next;
error_invalid_checksum_reg <= error_invalid_checksum_next;
busy_reg <= state_next != STATE_IDLE;
end
// datapath
if (store_eth_hdr) begin
m_eth_dest_mac_reg <= s_eth_dest_mac;
m_eth_src_mac_reg <= s_eth_src_mac;
m_eth_type_reg <= s_eth_type;
end
if (store_last_word) begin
last_word_data_reg <= m_ip_payload_axis_tdata_int;
end
if (store_ip_version_ihl) {m_ip_version_reg, m_ip_ihl_reg} <= s_eth_payload_axis_tdata;
if (store_ip_dscp_ecn) {m_ip_dscp_reg, m_ip_ecn_reg} <= s_eth_payload_axis_tdata;
if (store_ip_length_0) m_ip_length_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_ip_length_1) m_ip_length_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_ip_identification_0) m_ip_identification_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_ip_identification_1) m_ip_identification_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_ip_flags_fragment_offset_0) m_ip_fragment_offset_reg[ 7:0] <= s_eth_payload_axis_tdata;
if (store_ip_flags_fragment_offset_1) {m_ip_flags_reg, m_ip_fragment_offset_reg[12:8]} <= s_eth_payload_axis_tdata;
if (store_ip_ttl) m_ip_ttl_reg <= s_eth_payload_axis_tdata;
if (store_ip_protocol) m_ip_protocol_reg <= s_eth_payload_axis_tdata;
if (store_ip_header_checksum_0) m_ip_header_checksum_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_ip_header_checksum_1) m_ip_header_checksum_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_ip_source_ip_0) m_ip_source_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_ip_source_ip_1) m_ip_source_ip_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_ip_source_ip_2) m_ip_source_ip_reg[23:16] <= s_eth_payload_axis_tdata;
if (store_ip_source_ip_3) m_ip_source_ip_reg[31:24] <= s_eth_payload_axis_tdata;
if (store_ip_dest_ip_0) m_ip_dest_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata;
if (store_ip_dest_ip_1) m_ip_dest_ip_reg[15: 8] <= s_eth_payload_axis_tdata;
if (store_ip_dest_ip_2) m_ip_dest_ip_reg[23:16] <= s_eth_payload_axis_tdata;
if (store_ip_dest_ip_3) m_ip_dest_ip_reg[31:24] <= s_eth_payload_axis_tdata;
end
// output datapath logic
reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0;
reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next;
reg m_ip_payload_axis_tlast_reg = 1'b0;
reg m_ip_payload_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0;
reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next;
reg temp_m_ip_payload_axis_tlast_reg = 1'b0;
reg temp_m_ip_payload_axis_tuser_reg = 1'b0;
// datapath control
reg store_ip_payload_int_to_output;
reg store_ip_payload_int_to_temp;
reg store_ip_payload_axis_temp_to_output;
assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg;
assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg;
assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg;
assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg;
temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
store_ip_payload_int_to_output = 1'b0;
store_ip_payload_int_to_temp = 1'b0;
store_ip_payload_axis_temp_to_output = 1'b0;
if (m_ip_payload_axis_tready_int_reg) begin
// input is ready
if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
store_ip_payload_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
store_ip_payload_int_to_temp = 1'b1;
end
end else if (m_ip_payload_axis_tready) begin
// input is not ready, but output is ready
m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
temp_m_ip_payload_axis_tvalid_next = 1'b0;
store_ip_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_ip_payload_axis_tvalid_reg <= 1'b0;
m_ip_payload_axis_tready_int_reg <= 1'b0;
temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next;
m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early;
temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next;
end
// datapath
if (store_ip_payload_int_to_output) begin
m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
end else if (store_ip_payload_axis_temp_to_output) begin
m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg;
m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg;
m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg;
end
if (store_ip_payload_int_to_temp) begin
temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
end
end
endmodule

494
fpga/src/ip_eth_tx.sv Normal file
View file

@ -0,0 +1,494 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* IP ethernet frame transmitter (IP frame in, Ethernet frame out)
*/
module ip_eth_tx
(
input wire clk,
input wire rst,
/*
* IP frame input
*/
input wire s_ip_hdr_valid,
output wire s_ip_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_length,
input wire [15:0] s_ip_identification,
input wire [2:0] s_ip_flags,
input wire [12:0] s_ip_fragment_offset,
input wire [7:0] s_ip_ttl,
input wire [7:0] s_ip_protocol,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [7:0] s_ip_payload_axis_tdata,
input wire s_ip_payload_axis_tvalid,
output wire s_ip_payload_axis_tready,
input wire s_ip_payload_axis_tlast,
input wire s_ip_payload_axis_tuser,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [7:0] m_eth_payload_axis_tdata,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire m_eth_payload_axis_tuser,
/*
* Status signals
*/
output wire busy,
output wire error_payload_early_termination
);
/*
IP Frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype (0x0800) 2 octets
Version (4) 4 bits
IHL (5-15) 4 bits
DSCP (0) 6 bits
ECN (0) 2 bits
length 2 octets
identification (0?) 2 octets
flags (010) 3 bits
fragment offset (0) 13 bits
time to live (64?) 1 octet
protocol 1 octet
header checksum 2 octets
source IP 4 octets
destination IP 4 octets
options (IHL-5)*4 octets
payload length octets
This module receives an IP frame with header fields in parallel along with the
payload in an AXI stream, combines the header with the payload, passes through
the Ethernet headers, and transmits the complete Ethernet payload on an AXI
interface.
*/
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_WRITE_HEADER = 3'd1,
STATE_WRITE_PAYLOAD = 3'd2,
STATE_WRITE_PAYLOAD_LAST = 3'd3,
STATE_WAIT_LAST = 3'd4;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_ip_hdr;
reg store_last_word;
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next;
reg [7:0] last_word_data_reg = 8'd0;
reg [5:0] ip_dscp_reg = 6'd0;
reg [1:0] ip_ecn_reg = 2'd0;
reg [15:0] ip_length_reg = 16'd0;
reg [15:0] ip_identification_reg = 16'd0;
reg [2:0] ip_flags_reg = 3'd0;
reg [12:0] ip_fragment_offset_reg = 13'd0;
reg [7:0] ip_ttl_reg = 8'd0;
reg [7:0] ip_protocol_reg = 8'd0;
reg [31:0] ip_source_ip_reg = 32'd0;
reg [31:0] ip_dest_ip_reg = 32'd0;
reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next;
reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next;
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg busy_reg = 1'b0;
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
// internal datapath
reg [7:0] m_eth_payload_axis_tdata_int;
reg m_eth_payload_axis_tvalid_int;
reg m_eth_payload_axis_tready_int_reg = 1'b0;
reg m_eth_payload_axis_tlast_int;
reg m_eth_payload_axis_tuser_int;
wire m_eth_payload_axis_tready_int_early;
assign s_ip_hdr_ready = s_ip_hdr_ready_reg;
assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg;
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign busy = busy_reg;
assign error_payload_early_termination = error_payload_early_termination_reg;
function [15:0] add1c16b;
input [15:0] a, b;
reg [16:0] t;
begin
t = a+b;
add1c16b = t[15:0] + t[16];
end
endfunction
always @* begin
state_next = STATE_IDLE;
s_ip_hdr_ready_next = 1'b0;
s_ip_payload_axis_tready_next = 1'b0;
store_ip_hdr = 1'b0;
store_last_word = 1'b0;
frame_ptr_next = frame_ptr_reg;
hdr_sum_next = hdr_sum_reg;
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
error_payload_early_termination_next = 1'b0;
m_eth_payload_axis_tdata_int = 8'd0;
m_eth_payload_axis_tvalid_int = 1'b0;
m_eth_payload_axis_tlast_int = 1'b0;
m_eth_payload_axis_tuser_int = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_ptr_next = 16'd0;
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
if (s_ip_hdr_ready && s_ip_hdr_valid) begin
store_ip_hdr = 1'b1;
s_ip_hdr_ready_next = 1'b0;
m_eth_hdr_valid_next = 1'b1;
if (m_eth_payload_axis_tready_int_reg) begin
m_eth_payload_axis_tvalid_int = 1'b1;
m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl
frame_ptr_next = 16'd1;
end
state_next = STATE_WRITE_HEADER;
end else begin
state_next = STATE_IDLE;
end
end
STATE_WRITE_HEADER: begin
// write header
if (m_eth_payload_axis_tready_int_reg) begin
frame_ptr_next = frame_ptr_reg + 16'd1;
m_eth_payload_axis_tvalid_int = 1;
state_next = STATE_WRITE_HEADER;
case (frame_ptr_reg)
8'h00: begin
m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl
end
8'h01: begin
m_eth_payload_axis_tdata_int = {ip_dscp_reg, ip_ecn_reg};
hdr_sum_next = {4'd4, 4'd5, ip_dscp_reg, ip_ecn_reg};
end
8'h02: begin
m_eth_payload_axis_tdata_int = ip_length_reg[15: 8];
hdr_sum_next = add1c16b(hdr_sum_reg, ip_length_reg);
end
8'h03: begin
m_eth_payload_axis_tdata_int = ip_length_reg[ 7: 0];
hdr_sum_next = add1c16b(hdr_sum_reg, ip_identification_reg);
end
8'h04: begin
m_eth_payload_axis_tdata_int = ip_identification_reg[15: 8];
hdr_sum_next = add1c16b(hdr_sum_reg, {ip_flags_reg, ip_fragment_offset_reg});
end
8'h05: begin
m_eth_payload_axis_tdata_int = ip_identification_reg[ 7: 0];
hdr_sum_next = add1c16b(hdr_sum_reg, {ip_ttl_reg, ip_protocol_reg});
end
8'h06: begin
m_eth_payload_axis_tdata_int = {ip_flags_reg, ip_fragment_offset_reg[12:8]};
hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[31:16]);
end
8'h07: begin
m_eth_payload_axis_tdata_int = ip_fragment_offset_reg[ 7: 0];
hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[15:0]);
end
8'h08: begin
m_eth_payload_axis_tdata_int = ip_ttl_reg;
hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[31:16]);
end
8'h09: begin
m_eth_payload_axis_tdata_int = ip_protocol_reg;
hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[15:0]);
end
8'h0A: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[15: 8];
8'h0B: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[ 7: 0];
8'h0C: m_eth_payload_axis_tdata_int = ip_source_ip_reg[31:24];
8'h0D: m_eth_payload_axis_tdata_int = ip_source_ip_reg[23:16];
8'h0E: m_eth_payload_axis_tdata_int = ip_source_ip_reg[15: 8];
8'h0F: m_eth_payload_axis_tdata_int = ip_source_ip_reg[ 7: 0];
8'h10: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[31:24];
8'h11: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[23:16];
8'h12: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[15: 8];
8'h13: begin
m_eth_payload_axis_tdata_int = ip_dest_ip_reg[ 7: 0];
s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early;
state_next = STATE_WRITE_PAYLOAD;
end
endcase
end else begin
state_next = STATE_WRITE_HEADER;
end
end
STATE_WRITE_PAYLOAD: begin
// write payload
s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early;
m_eth_payload_axis_tdata_int = s_ip_payload_axis_tdata;
m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid;
m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast;
m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser;
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
// word transfer through
frame_ptr_next = frame_ptr_reg + 16'd1;
if (s_ip_payload_axis_tlast) begin
if (frame_ptr_next != ip_length_reg) begin
// end of frame, but length does not match
m_eth_payload_axis_tuser_int = 1'b1;
error_payload_early_termination_next = 1'b1;
end
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
s_ip_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
if (frame_ptr_next == ip_length_reg) begin
store_last_word = 1'b1;
m_eth_payload_axis_tvalid_int = 1'b0;
state_next = STATE_WRITE_PAYLOAD_LAST;
end else begin
state_next = STATE_WRITE_PAYLOAD;
end
end
end else begin
state_next = STATE_WRITE_PAYLOAD;
end
end
STATE_WRITE_PAYLOAD_LAST: begin
// read and discard until end of frame
s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early;
m_eth_payload_axis_tdata_int = last_word_data_reg;
m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast;
m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast;
m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser;
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
if (s_ip_payload_axis_tlast) begin
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
s_ip_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WRITE_PAYLOAD_LAST;
end
end else begin
state_next = STATE_WRITE_PAYLOAD_LAST;
end
end
STATE_WAIT_LAST: begin
// read and discard until end of frame
s_ip_payload_axis_tready_next = 1'b1;
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
if (s_ip_payload_axis_tlast) begin
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
s_ip_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT_LAST;
end
end else begin
state_next = STATE_WAIT_LAST;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 16'd0;
hdr_sum_reg <= 16'd0;
s_ip_hdr_ready_reg <= 1'b0;
s_ip_payload_axis_tready_reg <= 1'b0;
m_eth_hdr_valid_reg <= 1'b0;
busy_reg <= 1'b0;
error_payload_early_termination_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
hdr_sum_reg <= hdr_sum_next;
s_ip_hdr_ready_reg <= s_ip_hdr_ready_next;
s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next;
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
busy_reg <= state_next != STATE_IDLE;
error_payload_early_termination_reg <= error_payload_early_termination_next;
end
// datapath
if (store_ip_hdr) begin
m_eth_dest_mac_reg <= s_eth_dest_mac;
m_eth_src_mac_reg <= s_eth_src_mac;
m_eth_type_reg <= s_eth_type;
ip_dscp_reg <= s_ip_dscp;
ip_ecn_reg <= s_ip_ecn;
ip_length_reg <= s_ip_length;
ip_identification_reg <= s_ip_identification;
ip_flags_reg <= s_ip_flags;
ip_fragment_offset_reg <= s_ip_fragment_offset;
ip_ttl_reg <= s_ip_ttl;
ip_protocol_reg <= s_ip_protocol;
ip_source_ip_reg <= s_ip_source_ip;
ip_dest_ip_reg <= s_ip_dest_ip;
end
if (store_last_word) begin
last_word_data_reg <= m_eth_payload_axis_tdata_int;
end
end
// output datapath logic
reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0;
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
reg m_eth_payload_axis_tlast_reg = 1'b0;
reg m_eth_payload_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0;
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
reg temp_m_eth_payload_axis_tuser_reg = 1'b0;
// datapath control
reg store_eth_payload_int_to_output;
reg store_eth_payload_int_to_temp;
reg store_eth_payload_axis_temp_to_output;
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
store_eth_payload_int_to_output = 1'b0;
store_eth_payload_int_to_temp = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b0;
if (m_eth_payload_axis_tready_int_reg) begin
// input is ready
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_eth_payload_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
store_eth_payload_int_to_temp = 1'b1;
end
end else if (m_eth_payload_axis_tready) begin
// input is not ready, but output is ready
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
temp_m_eth_payload_axis_tvalid_next = 1'b0;
store_eth_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_eth_payload_axis_tvalid_reg <= 1'b0;
m_eth_payload_axis_tready_int_reg <= 1'b0;
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
end
// datapath
if (store_eth_payload_int_to_output) begin
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end else if (store_eth_payload_axis_temp_to_output) begin
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
end
if (store_eth_payload_int_to_temp) begin
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
end
end
endmodule

142
fpga/src/oddr.sv Normal file
View file

@ -0,0 +1,142 @@
/*
Copyright (c) 2016-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Generic ODDR module
*/
module oddr #
(
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
parameter TARGET = "GENERIC",
// IODDR style ("IODDR", "IODDR2")
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
// Use IODDR2 for Spartan-6
parameter IODDR_STYLE = "IODDR2",
// Width of register in bits
parameter WIDTH = 1
)
(
input wire clk,
input wire [WIDTH-1:0] d1,
input wire [WIDTH-1:0] d2,
output wire [WIDTH-1:0] q
);
/*
Provides a consistent output DDR flip flop across multiple FPGA families
_____ _____ _____ _____
clk ____/ \_____/ \_____/ \_____/ \_____
_ ___________ ___________ ___________ ___________ __
d1 _X____D0_____X____D2_____X____D4_____X____D6_____X__
_ ___________ ___________ ___________ ___________ __
d2 _X____D1_____X____D3_____X____D5_____X____D7_____X__
_____ _____ _____ _____ _____ _____ _____ _____ ____
d _____X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7_
*/
genvar n;
generate
if (TARGET == "XILINX") begin
for (n = 0; n < WIDTH; n = n + 1) begin : oddr
if (IODDR_STYLE == "IODDR") begin
ODDR #(
.DDR_CLK_EDGE("SAME_EDGE"),
.SRTYPE("ASYNC")
)
oddr_inst (
.Q(q[n]),
.C(clk),
.CE(1'b1),
.D1(d1[n]),
.D2(d2[n]),
.R(1'b0),
.S(1'b0)
);
end else if (IODDR_STYLE == "IODDR2") begin
ODDR2 #(
.DDR_ALIGNMENT("C0"),
.SRTYPE("ASYNC")
)
oddr_inst (
.Q(q[n]),
.C0(clk),
.C1(~clk),
.CE(1'b1),
.D0(d1[n]),
.D1(d2[n]),
.R(1'b0),
.S(1'b0)
);
end
end
end else if (TARGET == "ALTERA") begin
altddio_out #(
.WIDTH(WIDTH),
.POWER_UP_HIGH("OFF"),
.OE_REG("UNUSED")
)
altddio_out_inst (
.aset(1'b0),
.datain_h(d1),
.datain_l(d2),
.outclocken(1'b1),
.outclock(clk),
.aclr(1'b0),
.dataout(q)
);
end else begin
reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}};
reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}};
reg [WIDTH-1:0] q_reg = {WIDTH{1'b0}};
always @(posedge clk) begin
d_reg_1 <= d1;
d_reg_2 <= d2;
end
always @(posedge clk) begin
q_reg <= d1;
end
always @(negedge clk) begin
q_reg <= d_reg_2;
end
assign q = q_reg;
end
endgenerate
endmodule

View file

@ -0,0 +1,94 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Priority encoder module
*/
module priority_encoder #
(
parameter WIDTH = 4,
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "LOW"
)
(
input wire [WIDTH-1:0] input_unencoded,
output wire output_valid,
output wire [$clog2(WIDTH)-1:0] output_encoded,
output wire [WIDTH-1:0] output_unencoded
);
// power-of-two width
parameter W1 = 2**$clog2(WIDTH);
parameter W2 = W1/2;
generate
if (WIDTH == 2) begin
// two inputs - just an OR gate
assign output_valid = |input_unencoded;
if (LSB_PRIORITY == "LOW") begin
assign output_encoded = input_unencoded[1];
end else begin
assign output_encoded = ~input_unencoded[0];
end
end else begin
// more than two inputs - split into two parts and recurse
// also pad input to correct power-of-two width
wire [$clog2(W2)-1:0] out1, out2;
wire valid1, valid2;
priority_encoder #(
.WIDTH(W2),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_inst1 (
.input_unencoded(input_unencoded[W2-1:0]),
.output_valid(valid1),
.output_encoded(out1)
);
priority_encoder #(
.WIDTH(W2),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_inst2 (
.input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}),
.output_valid(valid2),
.output_encoded(out2)
);
// multiplexer to select part
assign output_valid = valid1 | valid2;
if (LSB_PRIORITY == "LOW") begin
assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1};
end else begin
assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2};
end
end
endgenerate
// unencoded output
assign output_unencoded = 1 << output_encoded;
endmodule

153
fpga/src/rgmii_arbiter.sv Normal file
View file

@ -0,0 +1,153 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Arbiter module
*/
module rgmii_arbiter #
(
parameter PORTS = 4,
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
parameter TYPE = "PRIORITY",
// block type: "NONE", "REQUEST", "ACKNOWLEDGE"
parameter BLOCK = "NONE",
// LSB priority: "LOW", "HIGH"
parameter LSB_PRIORITY = "LOW"
)
(
input wire clk,
input wire rst,
input wire [PORTS-1:0] request,
input wire [PORTS-1:0] acknowledge,
output wire [PORTS-1:0] grant,
output wire grant_valid,
output wire [$clog2(PORTS)-1:0] grant_encoded
);
reg [PORTS-1:0] grant_reg = 0, grant_next;
reg grant_valid_reg = 0, grant_valid_next;
reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next;
assign grant_valid = grant_valid_reg;
assign grant = grant_reg;
assign grant_encoded = grant_encoded_reg;
wire request_valid;
wire [$clog2(PORTS)-1:0] request_index;
wire [PORTS-1:0] request_mask;
priority_encoder #(
.WIDTH(PORTS),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_inst (
.input_unencoded(request),
.output_valid(request_valid),
.output_encoded(request_index),
.output_unencoded(request_mask)
);
reg [PORTS-1:0] mask_reg = 0, mask_next;
wire masked_request_valid;
wire [$clog2(PORTS)-1:0] masked_request_index;
wire [PORTS-1:0] masked_request_mask;
priority_encoder #(
.WIDTH(PORTS),
.LSB_PRIORITY(LSB_PRIORITY)
)
priority_encoder_masked (
.input_unencoded(request & mask_reg),
.output_valid(masked_request_valid),
.output_encoded(masked_request_index),
.output_unencoded(masked_request_mask)
);
always @* begin
grant_next = 0;
grant_valid_next = 0;
grant_encoded_next = 0;
mask_next = mask_reg;
if (BLOCK == "REQUEST" && grant_reg & request) begin
// granted request still asserted; hold it
grant_valid_next = grant_valid_reg;
grant_next = grant_reg;
grant_encoded_next = grant_encoded_reg;
end else if (BLOCK == "ACKNOWLEDGE" && grant_valid && !(grant_reg & acknowledge)) begin
// granted request not yet acknowledged; hold it
grant_valid_next = grant_valid_reg;
grant_next = grant_reg;
grant_encoded_next = grant_encoded_reg;
end else if (request_valid) begin
if (TYPE == "PRIORITY") begin
grant_valid_next = 1;
grant_next = request_mask;
grant_encoded_next = request_index;
end else if (TYPE == "ROUND_ROBIN") begin
if (masked_request_valid) begin
grant_valid_next = 1;
grant_next = masked_request_mask;
grant_encoded_next = masked_request_index;
if (LSB_PRIORITY == "LOW") begin
mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index);
end else begin
mask_next = {PORTS{1'b1}} << (masked_request_index + 1);
end
end else begin
grant_valid_next = 1;
grant_next = request_mask;
grant_encoded_next = request_index;
if (LSB_PRIORITY == "LOW") begin
mask_next = {PORTS{1'b1}} >> (PORTS - request_index);
end else begin
mask_next = {PORTS{1'b1}} << (request_index + 1);
end
end
end
end
end
always @(posedge clk) begin
if (rst) begin
grant_reg <= 0;
grant_valid_reg <= 0;
grant_encoded_reg <= 0;
mask_reg <= 0;
end else begin
grant_reg <= grant_next;
grant_valid_reg <= grant_valid_next;
grant_encoded_reg <= grant_encoded_next;
mask_reg <= mask_next;
end
end
endmodule

132
fpga/src/rgmii_core.sv Normal file
View file

@ -0,0 +1,132 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* FPGA core logic
*/
module rgmii_core #
(
parameter TARGET = "XILINX"
)
(
/*
* Clock: 125MHz
* Synchronous reset
*/
input wire clk,
input wire clk90,
input wire rst,
/*
* Ethernet: 1000BASE-T RGMII
*/
input wire phy_rx_clk,
input wire [3:0] phy_rxd,
input wire phy_rx_ctl,
output wire phy_tx_clk,
output wire [3:0] phy_txd,
output wire phy_tx_ctl,
output wire phy_reset_n,
input wire phy_int_n,
input wire phy_pme_n,
output wire mac_gmii_tx_en,
/*
* AXI input
*/
input tx_axis_tvalid,
input tx_axis_tlast,
input [7:0] tx_axis_tdata,
output tx_axis_tready,
input tx_axis_tuser,
/*
* AXI output
*/
output wire [7:0] rx_axis_tdata,
output wire rx_axis_tvalid,
output wire rx_axis_tlast,
output wire rx_axis_tuser
);
assign phy_reset_n = !rst;
eth_mac_1g_rgmii_fifo #(
.TARGET(TARGET),
.IODDR_STYLE("IODDR"),
.CLOCK_INPUT_STYLE("BUFR"),
.USE_CLK90("TRUE"),
.ENABLE_PADDING(1),
.MIN_FRAME_LENGTH(64),
.TX_FIFO_ADDR_WIDTH(12),
.TX_FRAME_FIFO(1),
.RX_FIFO_ADDR_WIDTH(12),
.RX_FRAME_FIFO(1)
)
eth_mac_inst (
.gtx_clk(clk),
.gtx_clk90(clk90),
.gtx_rst(rst),
.logic_clk(clk),
.logic_rst(rst),
.tx_axis_tdata(tx_axis_tdata),
.tx_axis_tvalid(tx_axis_tvalid),
.tx_axis_tready(tx_axis_tready),
.tx_axis_tlast(tx_axis_tlast),
.tx_axis_tuser(tx_axis_tuser),
.rx_axis_tdata(rx_axis_tdata),
.rx_axis_tvalid(rx_axis_tvalid),
.rx_axis_tlast(rx_axis_tlast),
.rx_axis_tuser(rx_axis_tuser),
.rgmii_rx_clk(phy_rx_clk),
.rgmii_rxd(phy_rxd),
.rgmii_rx_ctl(phy_rx_ctl),
.rgmii_tx_clk(phy_tx_clk),
.rgmii_txd(phy_txd),
.rgmii_tx_ctl(phy_tx_ctl),
.mac_gmii_tx_en(mac_gmii_tx_en),
.tx_fifo_overflow(),
.tx_fifo_bad_frame(),
.tx_fifo_good_frame(),
.rx_error_bad_frame(),
.rx_error_bad_fcs(),
.rx_fifo_overflow(),
.rx_fifo_bad_frame(),
.rx_fifo_good_frame(),
.speed(),
.ifg_delay(12)
);
endmodule

442
fpga/src/rgmii_lfsr.sv Normal file
View file

@ -0,0 +1,442 @@
/*
Copyright (c) 2016-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Parametrizable combinatorial parallel LFSR/CRC
*/
module rgmii_lfsr #
(
// width of LFSR
parameter LFSR_WIDTH = 31,
// LFSR polynomial
parameter LFSR_POLY = 31'h10000001,
// LFSR configuration: "GALOIS", "FIBONACCI"
parameter LFSR_CONFIG = "FIBONACCI",
// LFSR feed forward enable
parameter LFSR_FEED_FORWARD = 0,
// bit-reverse input and output
parameter REVERSE = 0,
// width of data input
parameter DATA_WIDTH = 8,
// implementation style: "AUTO", "LOOP", "REDUCTION"
parameter STYLE = "AUTO"
)
(
input wire [DATA_WIDTH-1:0] data_in,
input wire [LFSR_WIDTH-1:0] state_in,
output wire [DATA_WIDTH-1:0] data_out,
output wire [LFSR_WIDTH-1:0] state_out
);
/*
Fully parametrizable combinatorial parallel LFSR/CRC module. Implements an unrolled LFSR
next state computation, shifting DATA_WIDTH bits per pass through the module. Input data
is XORed with LFSR feedback path, tie data_in to zero if this is not required.
Works in two parts: statically computes a set of bit masks, then uses these bit masks to
select bits for XORing to compute the next state.
Ports:
data_in
Data bits to be shifted through the LFSR (DATA_WIDTH bits)
state_in
LFSR/CRC current state input (LFSR_WIDTH bits)
data_out
Data bits shifted out of LFSR (DATA_WIDTH bits)
state_out
LFSR/CRC next state output (LFSR_WIDTH bits)
Parameters:
LFSR_WIDTH
Specify width of LFSR/CRC register
LFSR_POLY
Specify the LFSR/CRC polynomial in hex format. For example, the polynomial
x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
would be represented as
32'h04c11db7
Note that the largest term (x^32) is suppressed. This term is generated automatically based
on LFSR_WIDTH.
LFSR_CONFIG
Specify the LFSR configuration, either Fibonacci or Galois. Fibonacci is generally used
for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators,
scramblers, and descrambers, while Galois is generally used for cyclic redundancy check
generators and checkers.
Fibonacci style (example for 64b66b scrambler, 0x8000000001)
DIN (LSB first)
|
V
(+)<---------------------------(+)<-----------------------------.
| ^ |
| .----. .----. .----. | .----. .----. .----. |
+->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--'
| '----' '----' '----' '----' '----' '----'
V
DOUT
Galois style (example for CRC16, 0x8005)
,-------------------+-------------------------+----------(+)<-- DIN (MSB first)
| | | ^
| .----. .----. V .----. .----. V .----. |
`->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |--+---> DOUT
'----' '----' '----' '----' '----'
LFSR_FEED_FORWARD
Generate feed forward instead of feed back LFSR. Enable this for PRBS checking and self-
synchronous descrambling.
Fibonacci feed-forward style (example for 64b66b descrambler, 0x8000000001)
DIN (LSB first)
|
| .----. .----. .----. .----. .----. .----.
+->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--.
| '----' '----' '----' | '----' '----' '----' |
| V |
(+)<---------------------------(+)------------------------------'
|
V
DOUT
Galois feed-forward style
,-------------------+-------------------------+------------+--- DIN (MSB first)
| | | |
| .----. .----. V .----. .----. V .----. V
`->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |->(+)-> DOUT
'----' '----' '----' '----' '----'
REVERSE
Bit-reverse LFSR input and output. Shifts MSB first by default, set REVERSE for LSB first.
DATA_WIDTH
Specify width of input and output data bus. The module will perform one shift per input
data bit, so if the input data bus is not required tie data_in to zero and set DATA_WIDTH
to the required number of shifts per clock cycle.
STYLE
Specify implementation style. Can be "AUTO", "LOOP", or "REDUCTION". When "AUTO"
is selected, implemenation will be "LOOP" or "REDUCTION" based on synthesis translate
directives. "REDUCTION" and "LOOP" are functionally identical, however they simulate
and synthesize differently. "REDUCTION" is implemented with a loop over a Verilog
reduction operator. "LOOP" is implemented as a doubly-nested loop with no reduction
operator. "REDUCTION" is very fast for simulation in iverilog and synthesizes well in
Quartus but synthesizes poorly in ISE, likely due to large inferred XOR gates causing
problems with the optimizer. "LOOP" synthesizes will in both ISE and Quartus. "AUTO"
will default to "REDUCTION" when simulating and "LOOP" for synthesizers that obey
synthesis translate directives.
Settings for common LFSR/CRC implementations:
Name Configuration Length Polynomial Initial value Notes
CRC16-IBM Galois, bit-reverse 16 16'h8005 16'hffff
CRC16-CCITT Galois 16 16'h1021 16'h1d0f
CRC32 Galois, bit-reverse 32 32'h04c11db7 32'hffffffff Ethernet FCS; invert final output
PRBS6 Fibonacci 6 6'h21 any
PRBS7 Fibonacci 7 7'h41 any
PRBS9 Fibonacci 9 9'h021 any ITU V.52
PRBS10 Fibonacci 10 10'h081 any ITU
PRBS11 Fibonacci 11 11'h201 any ITU O.152
PRBS15 Fibonacci, inverted 15 15'h4001 any ITU O.152
PRBS17 Fibonacci 17 17'h04001 any
PRBS20 Fibonacci 20 20'h00009 any ITU V.57
PRBS23 Fibonacci, inverted 23 23'h040001 any ITU O.151
PRBS29 Fibonacci, inverted 29 29'h08000001 any
PRBS31 Fibonacci, inverted 31 31'h10000001 any
64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet
128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3
*/
reg [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0];
reg [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0];
reg [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0];
reg [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0];
reg [LFSR_WIDTH-1:0] state_val = 0;
reg [DATA_WIDTH-1:0] data_val = 0;
integer i, j, k;
initial begin
// init bit masks
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
lfsr_mask_state[i] = {LFSR_WIDTH{1'b0}};
lfsr_mask_state[i][i] = 1'b1;
lfsr_mask_data[i] = {DATA_WIDTH{1'b0}};
end
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
output_mask_state[i] = {LFSR_WIDTH{1'b0}};
if (i < LFSR_WIDTH) begin
output_mask_state[i][i] = 1'b1;
end
output_mask_data[i] = {DATA_WIDTH{1'b0}};
end
// simulate shift register
if (LFSR_CONFIG == "FIBONACCI") begin
// Fibonacci configuration
for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin
// determine shift in value
// current value in last FF, XOR with input data bit (MSB first)
state_val = lfsr_mask_state[LFSR_WIDTH-1];
data_val = lfsr_mask_data[LFSR_WIDTH-1];
data_val = data_val ^ (1 << i);
// add XOR inputs from correct indicies
for (j = 1; j < LFSR_WIDTH; j = j + 1) begin
if (LFSR_POLY & (1 << j)) begin
state_val = lfsr_mask_state[j-1] ^ state_val;
data_val = lfsr_mask_data[j-1] ^ data_val;
end
end
// shift
for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin
lfsr_mask_state[j] = lfsr_mask_state[j-1];
lfsr_mask_data[j] = lfsr_mask_data[j-1];
end
for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin
output_mask_state[j] = output_mask_state[j-1];
output_mask_data[j] = output_mask_data[j-1];
end
output_mask_state[0] = state_val;
output_mask_data[0] = data_val;
if (LFSR_FEED_FORWARD) begin
// only shift in new input data
state_val = {LFSR_WIDTH{1'b0}};
data_val = 1 << i;
end
lfsr_mask_state[0] = state_val;
lfsr_mask_data[0] = data_val;
end
end else if (LFSR_CONFIG == "GALOIS") begin
// Galois configuration
for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin
// determine shift in value
// current value in last FF, XOR with input data bit (MSB first)
state_val = lfsr_mask_state[LFSR_WIDTH-1];
data_val = lfsr_mask_data[LFSR_WIDTH-1];
data_val = data_val ^ (1 << i);
// shift
for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin
lfsr_mask_state[j] = lfsr_mask_state[j-1];
lfsr_mask_data[j] = lfsr_mask_data[j-1];
end
for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin
output_mask_state[j] = output_mask_state[j-1];
output_mask_data[j] = output_mask_data[j-1];
end
output_mask_state[0] = state_val;
output_mask_data[0] = data_val;
if (LFSR_FEED_FORWARD) begin
// only shift in new input data
state_val = {LFSR_WIDTH{1'b0}};
data_val = 1 << i;
end
lfsr_mask_state[0] = state_val;
lfsr_mask_data[0] = data_val;
// add XOR inputs at correct indicies
for (j = 1; j < LFSR_WIDTH; j = j + 1) begin
if (LFSR_POLY & (1 << j)) begin
lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val;
lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val;
end
end
end
end else begin
$error("Error: unknown configuration setting!");
$finish;
end
// reverse bits if selected
if (REVERSE) begin
// reverse order
for (i = 0; i < LFSR_WIDTH/2; i = i + 1) begin
state_val = lfsr_mask_state[i];
data_val = lfsr_mask_data[i];
lfsr_mask_state[i] = lfsr_mask_state[LFSR_WIDTH-i-1];
lfsr_mask_data[i] = lfsr_mask_data[LFSR_WIDTH-i-1];
lfsr_mask_state[LFSR_WIDTH-i-1] = state_val;
lfsr_mask_data[LFSR_WIDTH-i-1] = data_val;
end
for (i = 0; i < DATA_WIDTH/2; i = i + 1) begin
state_val = output_mask_state[i];
data_val = output_mask_data[i];
output_mask_state[i] = output_mask_state[DATA_WIDTH-i-1];
output_mask_data[i] = output_mask_data[DATA_WIDTH-i-1];
output_mask_state[DATA_WIDTH-i-1] = state_val;
output_mask_data[DATA_WIDTH-i-1] = data_val;
end
// reverse bits
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
state_val = 0;
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
state_val[j] = lfsr_mask_state[i][LFSR_WIDTH-j-1];
end
lfsr_mask_state[i] = state_val;
data_val = 0;
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
data_val[j] = lfsr_mask_data[i][DATA_WIDTH-j-1];
end
lfsr_mask_data[i] = data_val;
end
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
state_val = 0;
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
state_val[j] = output_mask_state[i][LFSR_WIDTH-j-1];
end
output_mask_state[i] = state_val;
data_val = 0;
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
data_val[j] = output_mask_data[i][DATA_WIDTH-j-1];
end
output_mask_data[i] = data_val;
end
end
// for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
// $display("%b %b", lfsr_mask_state[i], lfsr_mask_data[i]);
// end
end
// synthesis translate_off
`define SIMULATION
// synthesis translate_on
`ifdef SIMULATION
// "AUTO" style is "REDUCTION" for faster simulation
parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE;
`else
// "AUTO" style is "LOOP" for better synthesis result
parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE;
`endif
genvar n;
generate
if (STYLE_INT == "REDUCTION") begin
// use Verilog reduction operator
// fast in iverilog
// significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer)
// slightly smaller than generated code with Quartus
// --> better for simulation
for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : loop1
assign state_out[n] = ^{(state_in & lfsr_mask_state[n]), (data_in & lfsr_mask_data[n])};
end
for (n = 0; n < DATA_WIDTH; n = n + 1) begin : loop2
assign data_out[n] = ^{(state_in & output_mask_state[n]), (data_in & output_mask_data[n])};
end
end else if (STYLE_INT == "LOOP") begin
// use nested loops
// very slow in iverilog
// slightly smaller than generated code with ISE
// same size as generated code with Quartus
// --> better for synthesis
reg [LFSR_WIDTH-1:0] state_out_reg = 0;
reg [DATA_WIDTH-1:0] data_out_reg = 0;
assign state_out = state_out_reg;
assign data_out = data_out_reg;
always @* begin
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
state_out_reg[i] = 0;
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
if (lfsr_mask_state[i][j]) begin
state_out_reg[i] = state_out_reg[i] ^ state_in[j];
end
end
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
if (lfsr_mask_data[i][j]) begin
state_out_reg[i] = state_out_reg[i] ^ data_in[j];
end
end
end
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
data_out_reg[i] = 0;
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
if (output_mask_state[i][j]) begin
data_out_reg[i] = data_out_reg[i] ^ state_in[j];
end
end
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
if (output_mask_data[i][j]) begin
data_out_reg[i] = data_out_reg[i] ^ data_in[j];
end
end
end
end
end else begin
initial begin
$error("Error: unknown style setting!");
$finish;
end
end
endgenerate
endmodule

262
fpga/src/rgmii_phy_if.sv Normal file
View file

@ -0,0 +1,262 @@
/*
Copyright (c) 2015-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* RGMII PHY interface
*/
module rgmii_phy_if #
(
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
parameter TARGET = "GENERIC",
// IODDR style ("IODDR", "IODDR2")
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
// Use IODDR2 for Spartan-6
parameter IODDR_STYLE = "IODDR2",
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
// Use BUFR for Virtex-5, Virtex-6, 7-series
// Use BUFG for Ultrascale
// Use BUFIO2 for Spartan-6
parameter CLOCK_INPUT_STYLE = "BUFIO2",
// Use 90 degree clock for RGMII transmit ("TRUE", "FALSE")
parameter USE_CLK90 = "TRUE"
)
(
input wire clk,
input wire clk90,
input wire rst,
/*
* GMII interface to MAC
*/
output wire mac_gmii_rx_clk,
output wire mac_gmii_rx_rst,
output wire [7:0] mac_gmii_rxd,
output wire mac_gmii_rx_dv,
output wire mac_gmii_rx_er,
output wire mac_gmii_tx_clk,
output wire mac_gmii_tx_rst,
output wire mac_gmii_tx_clk_en,
input wire [7:0] mac_gmii_txd,
input wire mac_gmii_tx_en,
input wire mac_gmii_tx_er,
/*
* RGMII interface to PHY
*/
input wire phy_rgmii_rx_clk,
input wire [3:0] phy_rgmii_rxd,
input wire phy_rgmii_rx_ctl,
output wire phy_rgmii_tx_clk,
output wire [3:0] phy_rgmii_txd,
output wire phy_rgmii_tx_ctl,
/*
* Control
*/
input wire [1:0] speed
);
// receive
wire rgmii_rx_ctl_1;
wire rgmii_rx_ctl_2;
ssio_ddr_in #
(
.TARGET(TARGET),
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
.IODDR_STYLE(IODDR_STYLE),
.WIDTH(5)
)
rx_ssio_ddr_inst (
.input_clk(phy_rgmii_rx_clk),
.input_d({phy_rgmii_rxd, phy_rgmii_rx_ctl}),
.output_clk(mac_gmii_rx_clk),
.output_q1({mac_gmii_rxd[3:0], rgmii_rx_ctl_1}),
.output_q2({mac_gmii_rxd[7:4], rgmii_rx_ctl_2})
);
assign mac_gmii_rx_dv = rgmii_rx_ctl_1;
assign mac_gmii_rx_er = rgmii_rx_ctl_1 ^ rgmii_rx_ctl_2;
// transmit
reg rgmii_tx_clk_1 = 1'b1;
reg rgmii_tx_clk_2 = 1'b0;
reg rgmii_tx_clk_rise = 1'b1;
reg rgmii_tx_clk_fall = 1'b1;
reg [5:0] count_reg = 6'd0, count_next;
always @(posedge clk) begin
if (rst) begin
rgmii_tx_clk_1 <= 1'b1;
rgmii_tx_clk_2 <= 1'b0;
rgmii_tx_clk_rise <= 1'b1;
rgmii_tx_clk_fall <= 1'b1;
count_reg <= 0;
end else begin
rgmii_tx_clk_1 <= rgmii_tx_clk_2;
if (speed == 2'b00) begin
// 10M
count_reg <= count_reg + 1;
rgmii_tx_clk_rise <= 1'b0;
rgmii_tx_clk_fall <= 1'b0;
if (count_reg == 24) begin
rgmii_tx_clk_1 <= 1'b1;
rgmii_tx_clk_2 <= 1'b1;
rgmii_tx_clk_rise <= 1'b1;
end else if (count_reg >= 49) begin
rgmii_tx_clk_1 <= 1'b0;
rgmii_tx_clk_2 <= 1'b0;
rgmii_tx_clk_fall <= 1'b1;
count_reg <= 0;
end
end else if (speed == 2'b01) begin
// 100M
count_reg <= count_reg + 1;
rgmii_tx_clk_rise <= 1'b0;
rgmii_tx_clk_fall <= 1'b0;
if (count_reg == 2) begin
rgmii_tx_clk_1 <= 1'b1;
rgmii_tx_clk_2 <= 1'b1;
rgmii_tx_clk_rise <= 1'b1;
end else if (count_reg >= 4) begin
rgmii_tx_clk_2 <= 1'b0;
rgmii_tx_clk_fall <= 1'b1;
count_reg <= 0;
end
end else begin
// 1000M
rgmii_tx_clk_1 <= 1'b1;
rgmii_tx_clk_2 <= 1'b0;
rgmii_tx_clk_rise <= 1'b1;
rgmii_tx_clk_fall <= 1'b1;
end
end
end
reg [3:0] rgmii_txd_1;
reg [3:0] rgmii_txd_2;
reg rgmii_tx_ctl_1;
reg rgmii_tx_ctl_2;
reg gmii_clk_en;
always @* begin
if (speed == 2'b00) begin
// 10M
rgmii_txd_1 = mac_gmii_txd[3:0];
rgmii_txd_2 = mac_gmii_txd[3:0];
if (rgmii_tx_clk_2) begin
rgmii_tx_ctl_1 = mac_gmii_tx_en;
rgmii_tx_ctl_2 = mac_gmii_tx_en;
end else begin
rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er;
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
end
gmii_clk_en = rgmii_tx_clk_fall;
end else if (speed == 2'b01) begin
// 100M
rgmii_txd_1 = mac_gmii_txd[3:0];
rgmii_txd_2 = mac_gmii_txd[3:0];
if (rgmii_tx_clk_2) begin
rgmii_tx_ctl_1 = mac_gmii_tx_en;
rgmii_tx_ctl_2 = mac_gmii_tx_en;
end else begin
rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er;
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
end
gmii_clk_en = rgmii_tx_clk_fall;
end else begin
// 1000M
rgmii_txd_1 = mac_gmii_txd[3:0];
rgmii_txd_2 = mac_gmii_txd[7:4];
rgmii_tx_ctl_1 = mac_gmii_tx_en;
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
gmii_clk_en = 1;
end
end
wire phy_rgmii_tx_clk_new;
wire [3:0] phy_rgmii_txd_new;
wire phy_rgmii_tx_ctl_new;
oddr #(
.TARGET(TARGET),
.IODDR_STYLE(IODDR_STYLE),
.WIDTH(1)
)
clk_oddr_inst (
.clk(USE_CLK90 == "TRUE" ? clk90 : clk),
.d1(rgmii_tx_clk_1),
.d2(rgmii_tx_clk_2),
.q(phy_rgmii_tx_clk)
);
oddr #(
.TARGET(TARGET),
.IODDR_STYLE(IODDR_STYLE),
.WIDTH(5)
)
data_oddr_inst (
.clk(clk),
.d1({rgmii_txd_1, rgmii_tx_ctl_1}),
.d2({rgmii_txd_2, rgmii_tx_ctl_2}),
.q({phy_rgmii_txd, phy_rgmii_tx_ctl})
);
assign mac_gmii_tx_clk = clk;
assign mac_gmii_tx_clk_en = gmii_clk_en;
// reset sync
reg [3:0] tx_rst_reg = 4'hf;
assign mac_gmii_tx_rst = tx_rst_reg[0];
always @(posedge mac_gmii_tx_clk or posedge rst) begin
if (rst) begin
tx_rst_reg <= 4'hf;
end else begin
tx_rst_reg <= {1'b0, tx_rst_reg[3:1]};
end
end
reg [3:0] rx_rst_reg = 4'hf;
assign mac_gmii_rx_rst = rx_rst_reg[0];
always @(posedge mac_gmii_rx_clk or posedge rst) begin
if (rst) begin
rx_rst_reg <= 4'hf;
end else begin
rx_rst_reg <= {1'b0, rx_rst_reg[3:1]};
end
end
endmodule

216
fpga/src/rgmii_soc.sv Normal file
View file

@ -0,0 +1,216 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
based on fpga.v
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* RGMII top-level module
*/
module rgmii_soc (
// Internal 125 MHz clock
input clk_int,
input rst_int,
input clk90_int,
input clk_200_int,
/*
* Ethernet: 1000BASE-T RGMII
*/
input wire phy_rx_clk,
input wire [3:0] phy_rxd,
input wire phy_rx_ctl,
output wire phy_tx_clk,
output wire [3:0] phy_txd,
output wire phy_tx_ctl,
output wire phy_reset_n,
input wire phy_int_n,
input wire phy_pme_n,
output wire mac_gmii_tx_en,
/*
* AXI input
*/
input tx_axis_tvalid,
input tx_axis_tlast,
input [7:0] tx_axis_tdata,
output tx_axis_tready,
input tx_axis_tuser,
/*
* AXI output
*/
output wire [7:0] rx_axis_tdata,
output wire rx_axis_tvalid,
output wire rx_axis_tlast,
output rx_axis_tuser
);
// IODELAY elements for RGMII interface to PHY
wire [3:0] phy_rxd_delay;
wire phy_rx_ctl_delay;
IDELAYCTRL
idelayctrl_inst
(
.REFCLK(clk_200_int),
.RST(rst_int),
.RDY()
);
IDELAYE2 #(
.IDELAY_TYPE("FIXED")
)
phy_rxd_idelay_0
(
.IDATAIN(phy_rxd[0]),
.DATAOUT(phy_rxd_delay[0]),
.DATAIN(1'b0),
.C(1'b0),
.CE(1'b0),
.INC(1'b0),
.CINVCTRL(1'b0),
.CNTVALUEIN(5'd0),
.CNTVALUEOUT(),
.LD(1'b0),
.LDPIPEEN(1'b0),
.REGRST(1'b0)
);
IDELAYE2 #(
.IDELAY_TYPE("FIXED")
)
phy_rxd_idelay_1
(
.IDATAIN(phy_rxd[1]),
.DATAOUT(phy_rxd_delay[1]),
.DATAIN(1'b0),
.C(1'b0),
.CE(1'b0),
.INC(1'b0),
.CINVCTRL(1'b0),
.CNTVALUEIN(5'd0),
.CNTVALUEOUT(),
.LD(1'b0),
.LDPIPEEN(1'b0),
.REGRST(1'b0)
);
IDELAYE2 #(
.IDELAY_TYPE("FIXED")
)
phy_rxd_idelay_2
(
.IDATAIN(phy_rxd[2]),
.DATAOUT(phy_rxd_delay[2]),
.DATAIN(1'b0),
.C(1'b0),
.CE(1'b0),
.INC(1'b0),
.CINVCTRL(1'b0),
.CNTVALUEIN(5'd0),
.CNTVALUEOUT(),
.LD(1'b0),
.LDPIPEEN(1'b0),
.REGRST(1'b0)
);
IDELAYE2 #(
.IDELAY_TYPE("FIXED")
)
phy_rxd_idelay_3
(
.IDATAIN(phy_rxd[3]),
.DATAOUT(phy_rxd_delay[3]),
.DATAIN(1'b0),
.C(1'b0),
.CE(1'b0),
.INC(1'b0),
.CINVCTRL(1'b0),
.CNTVALUEIN(5'd0),
.CNTVALUEOUT(),
.LD(1'b0),
.LDPIPEEN(1'b0),
.REGRST(1'b0)
);
IDELAYE2 #(
.IDELAY_VALUE(0),
.IDELAY_TYPE("FIXED")
)
phy_rx_ctl_idelay
(
.IDATAIN(phy_rx_ctl),
.DATAOUT(phy_rx_ctl_delay),
.DATAIN(1'b0),
.C(1'b0),
.CE(1'b0),
.INC(1'b0),
.CINVCTRL(1'b0),
.CNTVALUEIN(5'd0),
.CNTVALUEOUT(),
.LD(1'b0),
.LDPIPEEN(1'b0),
.REGRST(1'b0)
);
rgmii_core
core_inst (
/*
* Clock: 125MHz
* Synchronous reset
*/
.clk(clk_int),
.clk90(clk90_int),
.rst(rst_int),
/*
* Ethernet: 1000BASE-T RGMII
*/
.phy_rx_clk(phy_rx_clk),
.phy_rxd(phy_rxd_delay),
.phy_rx_ctl(phy_rx_ctl_delay),
.phy_tx_clk(phy_tx_clk),
.phy_txd(phy_txd),
.phy_tx_ctl(phy_tx_ctl),
.phy_reset_n(phy_reset_n),
.phy_int_n(phy_int_n),
.phy_pme_n(phy_pme_n),
.mac_gmii_tx_en(mac_gmii_tx_en),
.tx_axis_tdata(tx_axis_tdata),
.tx_axis_tvalid(tx_axis_tvalid),
.tx_axis_tready(tx_axis_tready),
.tx_axis_tlast(tx_axis_tlast),
.tx_axis_tuser(tx_axis_tuser),
.rx_axis_tdata(rx_axis_tdata),
.rx_axis_tvalid(rx_axis_tvalid),
.rx_axis_tlast(rx_axis_tlast),
.rx_axis_tuser(rx_axis_tuser)
);
endmodule

171
fpga/src/ssio_ddr_in.sv Normal file
View file

@ -0,0 +1,171 @@
/*
Copyright (c) 2016-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* Generic source synchronous DDR input
*/
module ssio_ddr_in #
(
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
parameter TARGET = "GENERIC",
// IODDR style ("IODDR", "IODDR2")
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
// Use IODDR2 for Spartan-6
parameter IODDR_STYLE = "IODDR2",
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
// Use BUFR for Virtex-5, Virtex-6, 7-series
// Use BUFG for Ultrascale
// Use BUFIO2 for Spartan-6
parameter CLOCK_INPUT_STYLE = "BUFIO2",
// Width of register in bits
parameter WIDTH = 1
)
(
input wire input_clk,
input wire [WIDTH-1:0] input_d,
output wire output_clk,
output wire [WIDTH-1:0] output_q1,
output wire [WIDTH-1:0] output_q2
);
wire clk_int;
wire clk_io;
generate
if (TARGET == "XILINX") begin
// use Xilinx clocking primitives
if (CLOCK_INPUT_STYLE == "BUFG") begin
// buffer RX clock
BUFG
clk_bufg (
.I(input_clk),
.O(clk_int)
);
// pass through RX clock to logic and input buffers
assign clk_io = clk_int;
assign output_clk = clk_int;
end else if (CLOCK_INPUT_STYLE == "BUFR") begin
assign clk_int = input_clk;
// pass through RX clock to input buffers
BUFIO
clk_bufio (
.I(clk_int),
.O(clk_io)
);
// pass through RX clock to logic
BUFR #(
.BUFR_DIVIDE("BYPASS")
)
clk_bufr (
.I(clk_int),
.O(output_clk),
.CE(1'b1),
.CLR(1'b0)
);
end else if (CLOCK_INPUT_STYLE == "BUFIO") begin
assign clk_int = input_clk;
// pass through RX clock to input buffers
BUFIO
clk_bufio (
.I(clk_int),
.O(clk_io)
);
// pass through RX clock to MAC
BUFG
clk_bufg (
.I(clk_int),
.O(output_clk)
);
end else if (CLOCK_INPUT_STYLE == "BUFIO2") begin
// pass through RX clock to input buffers
BUFIO2 #(
.DIVIDE(1),
.DIVIDE_BYPASS("TRUE"),
.I_INVERT("FALSE"),
.USE_DOUBLER("FALSE")
)
clk_bufio (
.I(input_clk),
.DIVCLK(clk_int),
.IOCLK(clk_io),
.SERDESSTROBE()
);
// pass through RX clock to MAC
BUFG
clk_bufg (
.I(clk_int),
.O(output_clk)
);
end
end else begin
// pass through RX clock to input buffers
assign clk_io = input_clk;
// pass through RX clock to logic
assign clk_int = input_clk;
assign output_clk = clk_int;
end
endgenerate
iddr #(
.TARGET(TARGET),
.IODDR_STYLE(IODDR_STYLE),
.WIDTH(WIDTH)
)
data_iddr_inst (
.clk(clk_io),
.d(input_d),
.q1(output_q1),
.q2(output_q2)
);
endmodule

52
fpga/src/sync_reset.sv Normal file
View file

@ -0,0 +1,52 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog-2001
`timescale 1 ns / 1 ps
/*
* Synchronizes an active-high asynchronous reset signal to a given clock by
* using a pipeline of N registers.
*/
module sync_reset #(
parameter N=2 // depth of synchronizer
)(
input wire clk,
input wire rst,
output wire sync_reset_out
);
reg [N-1:0] sync_reg = {N{1'b1}};
assign sync_reset_out = sync_reg[N-1];
always @(posedge clk or posedge rst) begin
if (rst)
sync_reg <= {N{1'b1}};
else
sync_reg <= {sync_reg[N-2:0], 1'b0};
end
endmodule

413
fpga/src/udp.sv Normal file
View file

@ -0,0 +1,413 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* UDP block, IP interface
*/
module udp #
(
parameter CHECKSUM_GEN_ENABLE = 1,
parameter CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11,
parameter CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3
)
(
input wire clk,
input wire rst,
/*
* IP frame input
*/
input wire s_ip_hdr_valid,
output wire s_ip_hdr_ready,
input wire [47:0] s_ip_eth_dest_mac,
input wire [47:0] s_ip_eth_src_mac,
input wire [15:0] s_ip_eth_type,
input wire [3:0] s_ip_version,
input wire [3:0] s_ip_ihl,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_length,
input wire [15:0] s_ip_identification,
input wire [2:0] s_ip_flags,
input wire [12:0] s_ip_fragment_offset,
input wire [7:0] s_ip_ttl,
input wire [7:0] s_ip_protocol,
input wire [15:0] s_ip_header_checksum,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [7:0] s_ip_payload_axis_tdata,
input wire s_ip_payload_axis_tvalid,
output wire s_ip_payload_axis_tready,
input wire s_ip_payload_axis_tlast,
input wire s_ip_payload_axis_tuser,
/*
* IP frame output
*/
output wire m_ip_hdr_valid,
input wire m_ip_hdr_ready,
output wire [47:0] m_ip_eth_dest_mac,
output wire [47:0] m_ip_eth_src_mac,
output wire [15:0] m_ip_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [7:0] m_ip_payload_axis_tdata,
output wire m_ip_payload_axis_tvalid,
input wire m_ip_payload_axis_tready,
output wire m_ip_payload_axis_tlast,
output wire m_ip_payload_axis_tuser,
/*
* UDP frame input
*/
input wire s_udp_hdr_valid,
output wire s_udp_hdr_ready,
input wire [47:0] s_udp_eth_dest_mac,
input wire [47:0] s_udp_eth_src_mac,
input wire [15:0] s_udp_eth_type,
input wire [3:0] s_udp_ip_version,
input wire [3:0] s_udp_ip_ihl,
input wire [5:0] s_udp_ip_dscp,
input wire [1:0] s_udp_ip_ecn,
input wire [15:0] s_udp_ip_identification,
input wire [2:0] s_udp_ip_flags,
input wire [12:0] s_udp_ip_fragment_offset,
input wire [7:0] s_udp_ip_ttl,
input wire [15:0] s_udp_ip_header_checksum,
input wire [31:0] s_udp_ip_source_ip,
input wire [31:0] s_udp_ip_dest_ip,
input wire [15:0] s_udp_source_port,
input wire [15:0] s_udp_dest_port,
input wire [15:0] s_udp_length,
input wire [15:0] s_udp_checksum,
input wire [7:0] s_udp_payload_axis_tdata,
input wire s_udp_payload_axis_tvalid,
output wire s_udp_payload_axis_tready,
input wire s_udp_payload_axis_tlast,
input wire s_udp_payload_axis_tuser,
/*
* UDP frame output
*/
output wire m_udp_hdr_valid,
input wire m_udp_hdr_ready,
output wire [47:0] m_udp_eth_dest_mac,
output wire [47:0] m_udp_eth_src_mac,
output wire [15:0] m_udp_eth_type,
output wire [3:0] m_udp_ip_version,
output wire [3:0] m_udp_ip_ihl,
output wire [5:0] m_udp_ip_dscp,
output wire [1:0] m_udp_ip_ecn,
output wire [15:0] m_udp_ip_length,
output wire [15:0] m_udp_ip_identification,
output wire [2:0] m_udp_ip_flags,
output wire [12:0] m_udp_ip_fragment_offset,
output wire [7:0] m_udp_ip_ttl,
output wire [7:0] m_udp_ip_protocol,
output wire [15:0] m_udp_ip_header_checksum,
output wire [31:0] m_udp_ip_source_ip,
output wire [31:0] m_udp_ip_dest_ip,
output wire [15:0] m_udp_source_port,
output wire [15:0] m_udp_dest_port,
output wire [15:0] m_udp_length,
output wire [15:0] m_udp_checksum,
output wire [7:0] m_udp_payload_axis_tdata,
output wire m_udp_payload_axis_tvalid,
input wire m_udp_payload_axis_tready,
output wire m_udp_payload_axis_tlast,
output wire m_udp_payload_axis_tuser,
/*
* Status signals
*/
output wire rx_busy,
output wire tx_busy,
output wire rx_error_header_early_termination,
output wire rx_error_payload_early_termination,
output wire tx_error_payload_early_termination
);
wire tx_udp_hdr_valid;
wire tx_udp_hdr_ready;
wire [47:0] tx_udp_eth_dest_mac;
wire [47:0] tx_udp_eth_src_mac;
wire [15:0] tx_udp_eth_type;
wire [3:0] tx_udp_ip_version;
wire [3:0] tx_udp_ip_ihl;
wire [5:0] tx_udp_ip_dscp;
wire [1:0] tx_udp_ip_ecn;
wire [15:0] tx_udp_ip_identification;
wire [2:0] tx_udp_ip_flags;
wire [12:0] tx_udp_ip_fragment_offset;
wire [7:0] tx_udp_ip_ttl;
wire [15:0] tx_udp_ip_header_checksum;
wire [31:0] tx_udp_ip_source_ip;
wire [31:0] tx_udp_ip_dest_ip;
wire [15:0] tx_udp_source_port;
wire [15:0] tx_udp_dest_port;
wire [15:0] tx_udp_length;
wire [15:0] tx_udp_checksum;
wire [7:0] tx_udp_payload_axis_tdata;
wire tx_udp_payload_axis_tvalid;
wire tx_udp_payload_axis_tready;
wire tx_udp_payload_axis_tlast;
wire tx_udp_payload_axis_tuser;
udp_ip_rx
udp_ip_rx_inst (
.clk(clk),
.rst(rst),
// IP frame input
.s_ip_hdr_valid(s_ip_hdr_valid),
.s_ip_hdr_ready(s_ip_hdr_ready),
.s_eth_dest_mac(s_ip_eth_dest_mac),
.s_eth_src_mac(s_ip_eth_src_mac),
.s_eth_type(s_ip_eth_type),
.s_ip_version(s_ip_version),
.s_ip_ihl(s_ip_ihl),
.s_ip_dscp(s_ip_dscp),
.s_ip_ecn(s_ip_ecn),
.s_ip_length(s_ip_length),
.s_ip_identification(s_ip_identification),
.s_ip_flags(s_ip_flags),
.s_ip_fragment_offset(s_ip_fragment_offset),
.s_ip_ttl(s_ip_ttl),
.s_ip_protocol(s_ip_protocol),
.s_ip_header_checksum(s_ip_header_checksum),
.s_ip_source_ip(s_ip_source_ip),
.s_ip_dest_ip(s_ip_dest_ip),
.s_ip_payload_axis_tdata(s_ip_payload_axis_tdata),
.s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid),
.s_ip_payload_axis_tready(s_ip_payload_axis_tready),
.s_ip_payload_axis_tlast(s_ip_payload_axis_tlast),
.s_ip_payload_axis_tuser(s_ip_payload_axis_tuser),
// UDP frame output
.m_udp_hdr_valid(m_udp_hdr_valid),
.m_udp_hdr_ready(m_udp_hdr_ready),
.m_eth_dest_mac(m_udp_eth_dest_mac),
.m_eth_src_mac(m_udp_eth_src_mac),
.m_eth_type(m_udp_eth_type),
.m_ip_version(m_udp_ip_version),
.m_ip_ihl(m_udp_ip_ihl),
.m_ip_dscp(m_udp_ip_dscp),
.m_ip_ecn(m_udp_ip_ecn),
.m_ip_length(m_udp_ip_length),
.m_ip_identification(m_udp_ip_identification),
.m_ip_flags(m_udp_ip_flags),
.m_ip_fragment_offset(m_udp_ip_fragment_offset),
.m_ip_ttl(m_udp_ip_ttl),
.m_ip_protocol(m_udp_ip_protocol),
.m_ip_header_checksum(m_udp_ip_header_checksum),
.m_ip_source_ip(m_udp_ip_source_ip),
.m_ip_dest_ip(m_udp_ip_dest_ip),
.m_udp_source_port(m_udp_source_port),
.m_udp_dest_port(m_udp_dest_port),
.m_udp_length(m_udp_length),
.m_udp_checksum(m_udp_checksum),
.m_udp_payload_axis_tdata(m_udp_payload_axis_tdata),
.m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid),
.m_udp_payload_axis_tready(m_udp_payload_axis_tready),
.m_udp_payload_axis_tlast(m_udp_payload_axis_tlast),
.m_udp_payload_axis_tuser(m_udp_payload_axis_tuser),
// Status signals
.busy(rx_busy),
.error_header_early_termination(rx_error_header_early_termination),
.error_payload_early_termination(rx_error_payload_early_termination)
);
generate
if (CHECKSUM_GEN_ENABLE) begin
udp_checksum_gen #(
.PAYLOAD_FIFO_ADDR_WIDTH(CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH),
.HEADER_FIFO_ADDR_WIDTH(CHECKSUM_HEADER_FIFO_ADDR_WIDTH)
)
udp_checksum_gen_inst (
.clk(clk),
.rst(rst),
// UDP frame input
.s_udp_hdr_valid(s_udp_hdr_valid),
.s_udp_hdr_ready(s_udp_hdr_ready),
.s_eth_dest_mac(s_udp_eth_dest_mac),
.s_eth_src_mac(s_udp_eth_src_mac),
.s_eth_type(s_udp_eth_type),
.s_ip_version(s_udp_ip_version),
.s_ip_ihl(s_udp_ip_ihl),
.s_ip_dscp(s_udp_ip_dscp),
.s_ip_ecn(s_udp_ip_ecn),
.s_ip_identification(s_udp_ip_identification),
.s_ip_flags(s_udp_ip_flags),
.s_ip_fragment_offset(s_udp_ip_fragment_offset),
.s_ip_ttl(s_udp_ip_ttl),
.s_ip_header_checksum(s_udp_ip_header_checksum),
.s_ip_source_ip(s_udp_ip_source_ip),
.s_ip_dest_ip(s_udp_ip_dest_ip),
.s_udp_source_port(s_udp_source_port),
.s_udp_dest_port(s_udp_dest_port),
.s_udp_payload_axis_tdata(s_udp_payload_axis_tdata),
.s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid),
.s_udp_payload_axis_tready(s_udp_payload_axis_tready),
.s_udp_payload_axis_tlast(s_udp_payload_axis_tlast),
.s_udp_payload_axis_tuser(s_udp_payload_axis_tuser),
// UDP frame output
.m_udp_hdr_valid(tx_udp_hdr_valid),
.m_udp_hdr_ready(tx_udp_hdr_ready),
.m_eth_dest_mac(tx_udp_eth_dest_mac),
.m_eth_src_mac(tx_udp_eth_src_mac),
.m_eth_type(tx_udp_eth_type),
.m_ip_version(tx_udp_ip_version),
.m_ip_ihl(tx_udp_ip_ihl),
.m_ip_dscp(tx_udp_ip_dscp),
.m_ip_ecn(tx_udp_ip_ecn),
.m_ip_length(),
.m_ip_identification(tx_udp_ip_identification),
.m_ip_flags(tx_udp_ip_flags),
.m_ip_fragment_offset(tx_udp_ip_fragment_offset),
.m_ip_ttl(tx_udp_ip_ttl),
.m_ip_protocol(),
.m_ip_header_checksum(tx_udp_ip_header_checksum),
.m_ip_source_ip(tx_udp_ip_source_ip),
.m_ip_dest_ip(tx_udp_ip_dest_ip),
.m_udp_source_port(tx_udp_source_port),
.m_udp_dest_port(tx_udp_dest_port),
.m_udp_length(tx_udp_length),
.m_udp_checksum(tx_udp_checksum),
.m_udp_payload_axis_tdata(tx_udp_payload_axis_tdata),
.m_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid),
.m_udp_payload_axis_tready(tx_udp_payload_axis_tready),
.m_udp_payload_axis_tlast(tx_udp_payload_axis_tlast),
.m_udp_payload_axis_tuser(tx_udp_payload_axis_tuser),
// Status signals
.busy()
);
end else begin
assign tx_udp_hdr_valid = s_udp_hdr_valid;
assign s_udp_hdr_ready = tx_udp_hdr_ready;
assign tx_udp_eth_dest_mac = s_udp_eth_dest_mac;
assign tx_udp_eth_src_mac = s_udp_eth_src_mac;
assign tx_udp_eth_type = s_udp_eth_type;
assign tx_udp_ip_version = s_udp_ip_version;
assign tx_udp_ip_ihl = s_udp_ip_ihl;
assign tx_udp_ip_dscp = s_udp_ip_dscp;
assign tx_udp_ip_ecn = s_udp_ip_ecn;
assign tx_udp_ip_identification = s_udp_ip_identification;
assign tx_udp_ip_flags = s_udp_ip_flags;
assign tx_udp_ip_fragment_offset = s_udp_ip_fragment_offset;
assign tx_udp_ip_ttl = s_udp_ip_ttl;
assign tx_udp_ip_header_checksum = s_udp_ip_header_checksum;
assign tx_udp_ip_source_ip = s_udp_ip_source_ip;
assign tx_udp_ip_dest_ip = s_udp_ip_dest_ip;
assign tx_udp_source_port = s_udp_source_port;
assign tx_udp_dest_port = s_udp_dest_port;
assign tx_udp_length = s_udp_length;
assign tx_udp_checksum = s_udp_checksum;
assign tx_udp_payload_axis_tdata = s_udp_payload_axis_tdata;
assign tx_udp_payload_axis_tvalid = s_udp_payload_axis_tvalid;
assign s_udp_payload_axis_tready = tx_udp_payload_axis_tready;
assign tx_udp_payload_axis_tlast = s_udp_payload_axis_tlast;
assign tx_udp_payload_axis_tuser = s_udp_payload_axis_tuser;
end
endgenerate
udp_ip_tx
udp_ip_tx_inst (
.clk(clk),
.rst(rst),
// UDP frame input
.s_udp_hdr_valid(tx_udp_hdr_valid),
.s_udp_hdr_ready(tx_udp_hdr_ready),
.s_eth_dest_mac(tx_udp_eth_dest_mac),
.s_eth_src_mac(tx_udp_eth_src_mac),
.s_eth_type(tx_udp_eth_type),
.s_ip_version(tx_udp_ip_version),
.s_ip_ihl(tx_udp_ip_ihl),
.s_ip_dscp(tx_udp_ip_dscp),
.s_ip_ecn(tx_udp_ip_ecn),
.s_ip_identification(tx_udp_ip_identification),
.s_ip_flags(tx_udp_ip_flags),
.s_ip_fragment_offset(tx_udp_ip_fragment_offset),
.s_ip_ttl(tx_udp_ip_ttl),
.s_ip_protocol(8'h11),
.s_ip_header_checksum(tx_udp_ip_header_checksum),
.s_ip_source_ip(tx_udp_ip_source_ip),
.s_ip_dest_ip(tx_udp_ip_dest_ip),
.s_udp_source_port(tx_udp_source_port),
.s_udp_dest_port(tx_udp_dest_port),
.s_udp_length(tx_udp_length),
.s_udp_checksum(tx_udp_checksum),
.s_udp_payload_axis_tdata(tx_udp_payload_axis_tdata),
.s_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid),
.s_udp_payload_axis_tready(tx_udp_payload_axis_tready),
.s_udp_payload_axis_tlast(tx_udp_payload_axis_tlast),
.s_udp_payload_axis_tuser(tx_udp_payload_axis_tuser),
// IP frame output
.m_ip_hdr_valid(m_ip_hdr_valid),
.m_ip_hdr_ready(m_ip_hdr_ready),
.m_eth_dest_mac(m_ip_eth_dest_mac),
.m_eth_src_mac(m_ip_eth_src_mac),
.m_eth_type(m_ip_eth_type),
.m_ip_version(m_ip_version),
.m_ip_ihl(m_ip_ihl),
.m_ip_dscp(m_ip_dscp),
.m_ip_ecn(m_ip_ecn),
.m_ip_length(m_ip_length),
.m_ip_identification(m_ip_identification),
.m_ip_flags(m_ip_flags),
.m_ip_fragment_offset(m_ip_fragment_offset),
.m_ip_ttl(m_ip_ttl),
.m_ip_protocol(m_ip_protocol),
.m_ip_header_checksum(m_ip_header_checksum),
.m_ip_source_ip(m_ip_source_ip),
.m_ip_dest_ip(m_ip_dest_ip),
.m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
.m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
.m_ip_payload_axis_tready(m_ip_payload_axis_tready),
.m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
.m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
// Status signals
.busy(tx_busy),
.error_payload_early_termination(tx_error_payload_early_termination)
);
endmodule

View file

@ -0,0 +1,555 @@
/*
Copyright (c) 2016-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* UDP checksum calculation module
*/
module udp_checksum_gen #
(
parameter PAYLOAD_FIFO_ADDR_WIDTH = 11,
parameter HEADER_FIFO_ADDR_WIDTH = 3
)
(
input wire clk,
input wire rst,
/*
* UDP frame input
*/
input wire s_udp_hdr_valid,
output wire s_udp_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [3:0] s_ip_version,
input wire [3:0] s_ip_ihl,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_identification,
input wire [2:0] s_ip_flags,
input wire [12:0] s_ip_fragment_offset,
input wire [7:0] s_ip_ttl,
input wire [15:0] s_ip_header_checksum,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [15:0] s_udp_source_port,
input wire [15:0] s_udp_dest_port,
input wire [7:0] s_udp_payload_axis_tdata,
input wire s_udp_payload_axis_tvalid,
output wire s_udp_payload_axis_tready,
input wire s_udp_payload_axis_tlast,
input wire s_udp_payload_axis_tuser,
/*
* UDP frame output
*/
output wire m_udp_hdr_valid,
input wire m_udp_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [15:0] m_udp_source_port,
output wire [15:0] m_udp_dest_port,
output wire [15:0] m_udp_length,
output wire [15:0] m_udp_checksum,
output wire [7:0] m_udp_payload_axis_tdata,
output wire m_udp_payload_axis_tvalid,
input wire m_udp_payload_axis_tready,
output wire m_udp_payload_axis_tlast,
output wire m_udp_payload_axis_tuser,
/*
* Status signals
*/
output wire busy
);
/*
UDP Frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype (0x0800) 2 octets
Version (4) 4 bits
IHL (5-15) 4 bits
DSCP (0) 6 bits
ECN (0) 2 bits
length 2 octets
identification (0?) 2 octets
flags (010) 3 bits
fragment offset (0) 13 bits
time to live (64?) 1 octet
protocol 1 octet
header checksum 2 octets
source IP 4 octets
destination IP 4 octets
options (IHL-5)*4 octets
source port 2 octets
desination port 2 octets
length 2 octets
checksum 2 octets
payload length octets
This module receives a UDP frame with header fields in parallel and payload on
an AXI stream interface, calculates the length and checksum, then produces the
header fields in parallel along with the UDP payload in a separate AXI stream.
*/
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_SUM_HEADER_1 = 3'd1,
STATE_SUM_HEADER_2 = 3'd2,
STATE_SUM_HEADER_3 = 3'd3,
STATE_SUM_PAYLOAD = 3'd4,
STATE_FINISH_SUM = 3'd5;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_udp_hdr;
reg shift_payload_in;
reg [31:0] checksum_part;
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
reg [31:0] checksum_reg = 32'd0, checksum_next;
reg [47:0] eth_dest_mac_reg = 48'd0;
reg [47:0] eth_src_mac_reg = 48'd0;
reg [15:0] eth_type_reg = 16'd0;
reg [3:0] ip_version_reg = 4'd0;
reg [3:0] ip_ihl_reg = 4'd0;
reg [5:0] ip_dscp_reg = 6'd0;
reg [1:0] ip_ecn_reg = 2'd0;
reg [15:0] ip_identification_reg = 16'd0;
reg [2:0] ip_flags_reg = 3'd0;
reg [12:0] ip_fragment_offset_reg = 13'd0;
reg [7:0] ip_ttl_reg = 8'd0;
reg [15:0] ip_header_checksum_reg = 16'd0;
reg [31:0] ip_source_ip_reg = 32'd0;
reg [31:0] ip_dest_ip_reg = 32'd0;
reg [15:0] udp_source_port_reg = 16'd0;
reg [15:0] udp_dest_port_reg = 16'd0;
reg hdr_valid_reg = 0, hdr_valid_next;
reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next;
reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next;
reg busy_reg = 1'b0;
/*
* UDP Payload FIFO
*/
wire [7:0] s_udp_payload_fifo_tdata;
wire s_udp_payload_fifo_tvalid;
wire s_udp_payload_fifo_tready;
wire s_udp_payload_fifo_tlast;
wire s_udp_payload_fifo_tuser;
wire [7:0] m_udp_payload_fifo_tdata;
wire m_udp_payload_fifo_tvalid;
wire m_udp_payload_fifo_tready;
wire m_udp_payload_fifo_tlast;
wire m_udp_payload_fifo_tuser;
axis_fifo #(
.ADDR_WIDTH(PAYLOAD_FIFO_ADDR_WIDTH),
.DATA_WIDTH(8),
.KEEP_ENABLE(0),
.LAST_ENABLE(1),
.ID_ENABLE(0),
.DEST_ENABLE(0),
.USER_ENABLE(1),
.USER_WIDTH(1),
.FRAME_FIFO(0)
)
payload_fifo (
.clk(clk),
.rst(rst),
// AXI input
.s_axis_tdata(s_udp_payload_fifo_tdata),
.s_axis_tkeep(0),
.s_axis_tvalid(s_udp_payload_fifo_tvalid),
.s_axis_tready(s_udp_payload_fifo_tready),
.s_axis_tlast(s_udp_payload_fifo_tlast),
.s_axis_tid(0),
.s_axis_tdest(0),
.s_axis_tuser(s_udp_payload_fifo_tuser),
// AXI output
.m_axis_tdata(m_udp_payload_fifo_tdata),
.m_axis_tkeep(),
.m_axis_tvalid(m_udp_payload_fifo_tvalid),
.m_axis_tready(m_udp_payload_fifo_tready),
.m_axis_tlast(m_udp_payload_fifo_tlast),
.m_axis_tid(),
.m_axis_tdest(),
.m_axis_tuser(m_udp_payload_fifo_tuser),
// Status
.status_overflow(),
.status_bad_frame(),
.status_good_frame()
);
assign s_udp_payload_fifo_tdata = s_udp_payload_axis_tdata;
assign s_udp_payload_fifo_tvalid = s_udp_payload_axis_tvalid && shift_payload_in;
assign s_udp_payload_axis_tready = s_udp_payload_fifo_tready && shift_payload_in;
assign s_udp_payload_fifo_tlast = s_udp_payload_axis_tlast;
assign s_udp_payload_fifo_tuser = s_udp_payload_axis_tuser;
assign m_udp_payload_axis_tdata = m_udp_payload_fifo_tdata;
assign m_udp_payload_axis_tvalid = m_udp_payload_fifo_tvalid;
assign m_udp_payload_fifo_tready = m_udp_payload_axis_tready;
assign m_udp_payload_axis_tlast = m_udp_payload_fifo_tlast;
assign m_udp_payload_axis_tuser = m_udp_payload_fifo_tuser;
/*
* UDP Header FIFO
*/
reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_wr_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_wr_ptr_next;
reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_rd_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_rd_ptr_next;
reg [47:0] eth_dest_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [47:0] eth_src_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [15:0] eth_type_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [3:0] ip_version_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [3:0] ip_ihl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [5:0] ip_dscp_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [1:0] ip_ecn_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [15:0] ip_identification_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [2:0] ip_flags_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [12:0] ip_fragment_offset_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [7:0] ip_ttl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [15:0] ip_header_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [31:0] ip_source_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [31:0] ip_dest_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [15:0] udp_source_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [15:0] udp_dest_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [15:0] udp_length_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [15:0] udp_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg [3:0] m_ip_version_reg = 4'd0;
reg [3:0] m_ip_ihl_reg = 4'd0;
reg [5:0] m_ip_dscp_reg = 6'd0;
reg [1:0] m_ip_ecn_reg = 2'd0;
reg [15:0] m_ip_identification_reg = 16'd0;
reg [2:0] m_ip_flags_reg = 3'd0;
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
reg [7:0] m_ip_ttl_reg = 8'd0;
reg [15:0] m_ip_header_checksum_reg = 16'd0;
reg [31:0] m_ip_source_ip_reg = 32'd0;
reg [31:0] m_ip_dest_ip_reg = 32'd0;
reg [15:0] m_udp_source_port_reg = 16'd0;
reg [15:0] m_udp_dest_port_reg = 16'd0;
reg [15:0] m_udp_length_reg = 16'd0;
reg [15:0] m_udp_checksum_reg = 16'd0;
reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next;
// full when first MSB different but rest same
wire header_fifo_full = ((header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH] != header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH]) &&
(header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0] == header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]));
// empty when pointers match exactly
wire header_fifo_empty = header_fifo_wr_ptr_reg == header_fifo_rd_ptr_reg;
// control signals
reg header_fifo_write;
reg header_fifo_read;
wire header_fifo_ready = !header_fifo_full;
assign m_udp_hdr_valid = m_udp_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign m_ip_version = m_ip_version_reg;
assign m_ip_ihl = m_ip_ihl_reg;
assign m_ip_dscp = m_ip_dscp_reg;
assign m_ip_ecn = m_ip_ecn_reg;
assign m_ip_length = m_udp_length_reg + 16'd20;
assign m_ip_identification = m_ip_identification_reg;
assign m_ip_flags = m_ip_flags_reg;
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
assign m_ip_ttl = m_ip_ttl_reg;
assign m_ip_protocol = 8'h11;
assign m_ip_header_checksum = m_ip_header_checksum_reg;
assign m_ip_source_ip = m_ip_source_ip_reg;
assign m_ip_dest_ip = m_ip_dest_ip_reg;
assign m_udp_source_port = m_udp_source_port_reg;
assign m_udp_dest_port = m_udp_dest_port_reg;
assign m_udp_length = m_udp_length_reg;
assign m_udp_checksum = m_udp_checksum_reg;
// Write logic
always @* begin
header_fifo_write = 1'b0;
header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg;
if (hdr_valid_reg) begin
// input data valid
if (~header_fifo_full) begin
// not full, perform write
header_fifo_write = 1'b1;
header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg + 1;
end
end
end
always @(posedge clk) begin
if (rst) begin
header_fifo_wr_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}};
end else begin
header_fifo_wr_ptr_reg <= header_fifo_wr_ptr_next;
end
if (header_fifo_write) begin
eth_dest_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_dest_mac_reg;
eth_src_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_src_mac_reg;
eth_type_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_type_reg;
ip_version_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_version_reg;
ip_ihl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ihl_reg;
ip_dscp_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dscp_reg;
ip_ecn_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ecn_reg;
ip_identification_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_identification_reg;
ip_flags_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_flags_reg;
ip_fragment_offset_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_fragment_offset_reg;
ip_ttl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ttl_reg;
ip_header_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_header_checksum_reg;
ip_source_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_source_ip_reg;
ip_dest_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dest_ip_reg;
udp_source_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_source_port_reg;
udp_dest_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_dest_port_reg;
udp_length_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= frame_ptr_reg;
udp_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= checksum_reg[15:0];
end
end
// Read logic
always @* begin
header_fifo_read = 1'b0;
header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg;
m_udp_hdr_valid_next = m_udp_hdr_valid_reg;
if (m_udp_hdr_ready || !m_udp_hdr_valid) begin
// output data not valid OR currently being transferred
if (!header_fifo_empty) begin
// not empty, perform read
header_fifo_read = 1'b1;
m_udp_hdr_valid_next = 1'b1;
header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg + 1;
end else begin
// empty, invalidate
m_udp_hdr_valid_next = 1'b0;
end
end
end
always @(posedge clk) begin
if (rst) begin
header_fifo_rd_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}};
m_udp_hdr_valid_reg <= 1'b0;
end else begin
header_fifo_rd_ptr_reg <= header_fifo_rd_ptr_next;
m_udp_hdr_valid_reg <= m_udp_hdr_valid_next;
end
if (header_fifo_read) begin
m_eth_dest_mac_reg <= eth_dest_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_eth_src_mac_reg <= eth_src_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_eth_type_reg <= eth_type_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_version_reg <= ip_version_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_ihl_reg <= ip_ihl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_dscp_reg <= ip_dscp_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_ecn_reg <= ip_ecn_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_identification_reg <= ip_identification_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_flags_reg <= ip_flags_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_fragment_offset_reg <= ip_fragment_offset_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_ttl_reg <= ip_ttl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_header_checksum_reg <= ip_header_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_source_ip_reg <= ip_source_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_ip_dest_ip_reg <= ip_dest_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_udp_source_port_reg <= udp_source_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_udp_dest_port_reg <= udp_dest_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_udp_length_reg <= udp_length_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
m_udp_checksum_reg <= udp_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
end
end
assign s_udp_hdr_ready = s_udp_hdr_ready_reg;
assign busy = busy_reg;
always @* begin
state_next = STATE_IDLE;
s_udp_hdr_ready_next = 1'b0;
s_udp_payload_axis_tready_next = 1'b0;
store_udp_hdr = 1'b0;
shift_payload_in = 1'b0;
frame_ptr_next = frame_ptr_reg;
checksum_next = checksum_reg;
hdr_valid_next = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state
s_udp_hdr_ready_next = header_fifo_ready;
if (s_udp_hdr_ready && s_udp_hdr_valid) begin
store_udp_hdr = 1'b1;
frame_ptr_next = 0;
// 16'h0011 = zero padded type field
// 16'h0010 = header length times two
checksum_next = 16'h0011 + 16'h0010;
s_udp_hdr_ready_next = 1'b0;
state_next = STATE_SUM_HEADER_1;
end else begin
state_next = STATE_IDLE;
end
end
STATE_SUM_HEADER_1: begin
// sum pseudo header and header
checksum_next = checksum_reg + ip_source_ip_reg[31:16] + ip_source_ip_reg[15:0];
state_next = STATE_SUM_HEADER_2;
end
STATE_SUM_HEADER_2: begin
// sum pseudo header and header
checksum_next = checksum_reg + ip_dest_ip_reg[31:16] + ip_dest_ip_reg[15:0];
state_next = STATE_SUM_HEADER_3;
end
STATE_SUM_HEADER_3: begin
// sum pseudo header and header
checksum_next = checksum_reg + udp_source_port_reg + udp_dest_port_reg;
frame_ptr_next = 8;
state_next = STATE_SUM_PAYLOAD;
end
STATE_SUM_PAYLOAD: begin
// sum payload
shift_payload_in = 1'b1;
if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin
// checksum computation for payload - alternately store and accumulate
// add 2 for length calculation (two length fields in pseudo header)
if (frame_ptr_reg[0]) begin
checksum_next = checksum_reg + {8'h00, s_udp_payload_axis_tdata} + 2;
end else begin
checksum_next = checksum_reg + {s_udp_payload_axis_tdata, 8'h00} + 2;
end
frame_ptr_next = frame_ptr_reg + 1;
if (s_udp_payload_axis_tlast) begin
state_next = STATE_FINISH_SUM;
end else begin
state_next = STATE_SUM_PAYLOAD;
end
end else begin
state_next = STATE_SUM_PAYLOAD;
end
end
STATE_FINISH_SUM: begin
// add MSW (twice!) for proper ones complement sum
checksum_part = checksum_reg[15:0] + checksum_reg[31:16];
checksum_next = ~(checksum_part[15:0] + checksum_part[16]);
hdr_valid_next = 1;
state_next = STATE_IDLE;
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
s_udp_hdr_ready_reg <= 1'b0;
s_udp_payload_axis_tready_reg <= 1'b0;
hdr_valid_reg <= 1'b0;
busy_reg <= 1'b0;
end else begin
state_reg <= state_next;
s_udp_hdr_ready_reg <= s_udp_hdr_ready_next;
s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next;
hdr_valid_reg <= hdr_valid_next;
busy_reg <= state_next != STATE_IDLE;
end
frame_ptr_reg <= frame_ptr_next;
checksum_reg <= checksum_next;
// datapath
if (store_udp_hdr) begin
eth_dest_mac_reg <= s_eth_dest_mac;
eth_src_mac_reg <= s_eth_src_mac;
eth_type_reg <= s_eth_type;
ip_version_reg <= s_ip_version;
ip_ihl_reg <= s_ip_ihl;
ip_dscp_reg <= s_ip_dscp;
ip_ecn_reg <= s_ip_ecn;
ip_identification_reg <= s_ip_identification;
ip_flags_reg <= s_ip_flags;
ip_fragment_offset_reg <= s_ip_fragment_offset;
ip_ttl_reg <= s_ip_ttl;
ip_header_checksum_reg <= s_ip_header_checksum;
ip_source_ip_reg <= s_ip_source_ip;
ip_dest_ip_reg <= s_ip_dest_ip;
udp_source_port_reg <= s_udp_source_port;
udp_dest_port_reg <= s_udp_dest_port;
end
end
endmodule

637
fpga/src/udp_complete.sv Normal file
View file

@ -0,0 +1,637 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* IPv4 and ARP block with UDP support, ethernet frame interface
*/
module udp_complete #(
parameter ARP_CACHE_ADDR_WIDTH = 9,
parameter ARP_REQUEST_RETRY_COUNT = 4,
parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2,
parameter ARP_REQUEST_TIMEOUT = 125000000*30,
parameter UDP_CHECKSUM_GEN_ENABLE = 1,
parameter UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11,
parameter UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3
)
(
input wire clk,
input wire rst,
/*
* Ethernet frame input
*/
input wire s_eth_hdr_valid,
output wire s_eth_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [7:0] s_eth_payload_axis_tdata,
input wire s_eth_payload_axis_tvalid,
output wire s_eth_payload_axis_tready,
input wire s_eth_payload_axis_tlast,
input wire s_eth_payload_axis_tuser,
/*
* Ethernet frame output
*/
output wire m_eth_hdr_valid,
input wire m_eth_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [7:0] m_eth_payload_axis_tdata,
output wire m_eth_payload_axis_tvalid,
input wire m_eth_payload_axis_tready,
output wire m_eth_payload_axis_tlast,
output wire m_eth_payload_axis_tuser,
/*
* IP input
*/
input wire s_ip_hdr_valid,
output wire s_ip_hdr_ready,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_length,
input wire [7:0] s_ip_ttl,
input wire [7:0] s_ip_protocol,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [7:0] s_ip_payload_axis_tdata,
input wire s_ip_payload_axis_tvalid,
output wire s_ip_payload_axis_tready,
input wire s_ip_payload_axis_tlast,
input wire s_ip_payload_axis_tuser,
/*
* IP output
*/
output wire m_ip_hdr_valid,
input wire m_ip_hdr_ready,
output wire [47:0] m_ip_eth_dest_mac,
output wire [47:0] m_ip_eth_src_mac,
output wire [15:0] m_ip_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [7:0] m_ip_payload_axis_tdata,
output wire m_ip_payload_axis_tvalid,
input wire m_ip_payload_axis_tready,
output wire m_ip_payload_axis_tlast,
output wire m_ip_payload_axis_tuser,
/*
* UDP input
*/
input wire s_udp_hdr_valid,
output wire s_udp_hdr_ready,
input wire [5:0] s_udp_ip_dscp,
input wire [1:0] s_udp_ip_ecn,
input wire [7:0] s_udp_ip_ttl,
input wire [31:0] s_udp_ip_source_ip,
input wire [31:0] s_udp_ip_dest_ip,
input wire [15:0] s_udp_source_port,
input wire [15:0] s_udp_dest_port,
input wire [15:0] s_udp_length,
input wire [15:0] s_udp_checksum,
input wire [7:0] s_udp_payload_axis_tdata,
input wire s_udp_payload_axis_tvalid,
output wire s_udp_payload_axis_tready,
input wire s_udp_payload_axis_tlast,
input wire s_udp_payload_axis_tuser,
/*
* UDP output
*/
output wire m_udp_hdr_valid,
input wire m_udp_hdr_ready,
output wire [47:0] m_udp_eth_dest_mac,
output wire [47:0] m_udp_eth_src_mac,
output wire [15:0] m_udp_eth_type,
output wire [3:0] m_udp_ip_version,
output wire [3:0] m_udp_ip_ihl,
output wire [5:0] m_udp_ip_dscp,
output wire [1:0] m_udp_ip_ecn,
output wire [15:0] m_udp_ip_length,
output wire [15:0] m_udp_ip_identification,
output wire [2:0] m_udp_ip_flags,
output wire [12:0] m_udp_ip_fragment_offset,
output wire [7:0] m_udp_ip_ttl,
output wire [7:0] m_udp_ip_protocol,
output wire [15:0] m_udp_ip_header_checksum,
output wire [31:0] m_udp_ip_source_ip,
output wire [31:0] m_udp_ip_dest_ip,
output wire [15:0] m_udp_source_port,
output wire [15:0] m_udp_dest_port,
output wire [15:0] m_udp_length,
output wire [15:0] m_udp_checksum,
output wire [7:0] m_udp_payload_axis_tdata,
output wire m_udp_payload_axis_tvalid,
input wire m_udp_payload_axis_tready,
output wire m_udp_payload_axis_tlast,
output wire m_udp_payload_axis_tuser,
/*
* Status
*/
output wire ip_rx_busy,
output wire ip_tx_busy,
output wire udp_rx_busy,
output wire udp_tx_busy,
output wire ip_rx_error_header_early_termination,
output wire ip_rx_error_payload_early_termination,
output wire ip_rx_error_invalid_header,
output wire ip_rx_error_invalid_checksum,
output wire ip_tx_error_payload_early_termination,
output wire ip_tx_error_arp_failed,
output wire udp_rx_error_header_early_termination,
output wire udp_rx_error_payload_early_termination,
output wire udp_tx_error_payload_early_termination,
/*
* Configuration
*/
input wire [47:0] local_mac,
input wire [31:0] local_ip,
input wire [31:0] gateway_ip,
input wire [31:0] subnet_mask,
input wire clear_arp_cache
);
wire ip_rx_ip_hdr_valid;
wire ip_rx_ip_hdr_ready;
wire [47:0] ip_rx_ip_eth_dest_mac;
wire [47:0] ip_rx_ip_eth_src_mac;
wire [15:0] ip_rx_ip_eth_type;
wire [3:0] ip_rx_ip_version;
wire [3:0] ip_rx_ip_ihl;
wire [5:0] ip_rx_ip_dscp;
wire [1:0] ip_rx_ip_ecn;
wire [15:0] ip_rx_ip_length;
wire [15:0] ip_rx_ip_identification;
wire [2:0] ip_rx_ip_flags;
wire [12:0] ip_rx_ip_fragment_offset;
wire [7:0] ip_rx_ip_ttl;
wire [7:0] ip_rx_ip_protocol;
wire [15:0] ip_rx_ip_header_checksum;
wire [31:0] ip_rx_ip_source_ip;
wire [31:0] ip_rx_ip_dest_ip;
wire [7:0] ip_rx_ip_payload_axis_tdata;
wire ip_rx_ip_payload_axis_tvalid;
wire ip_rx_ip_payload_axis_tlast;
wire ip_rx_ip_payload_axis_tuser;
wire ip_rx_ip_payload_axis_tready;
wire ip_tx_ip_hdr_valid;
wire ip_tx_ip_hdr_ready;
wire [5:0] ip_tx_ip_dscp;
wire [1:0] ip_tx_ip_ecn;
wire [15:0] ip_tx_ip_length;
wire [7:0] ip_tx_ip_ttl;
wire [7:0] ip_tx_ip_protocol;
wire [31:0] ip_tx_ip_source_ip;
wire [31:0] ip_tx_ip_dest_ip;
wire [7:0] ip_tx_ip_payload_axis_tdata;
wire ip_tx_ip_payload_axis_tvalid;
wire ip_tx_ip_payload_axis_tlast;
wire ip_tx_ip_payload_axis_tuser;
wire ip_tx_ip_payload_axis_tready;
wire udp_rx_ip_hdr_valid;
wire udp_rx_ip_hdr_ready;
wire [47:0] udp_rx_ip_eth_dest_mac;
wire [47:0] udp_rx_ip_eth_src_mac;
wire [15:0] udp_rx_ip_eth_type;
wire [3:0] udp_rx_ip_version;
wire [3:0] udp_rx_ip_ihl;
wire [5:0] udp_rx_ip_dscp;
wire [1:0] udp_rx_ip_ecn;
wire [15:0] udp_rx_ip_length;
wire [15:0] udp_rx_ip_identification;
wire [2:0] udp_rx_ip_flags;
wire [12:0] udp_rx_ip_fragment_offset;
wire [7:0] udp_rx_ip_ttl;
wire [7:0] udp_rx_ip_protocol;
wire [15:0] udp_rx_ip_header_checksum;
wire [31:0] udp_rx_ip_source_ip;
wire [31:0] udp_rx_ip_dest_ip;
wire [7:0] udp_rx_ip_payload_axis_tdata;
wire udp_rx_ip_payload_axis_tvalid;
wire udp_rx_ip_payload_axis_tlast;
wire udp_rx_ip_payload_axis_tuser;
wire udp_rx_ip_payload_axis_tready;
wire udp_tx_ip_hdr_valid;
wire udp_tx_ip_hdr_ready;
wire [5:0] udp_tx_ip_dscp;
wire [1:0] udp_tx_ip_ecn;
wire [15:0] udp_tx_ip_length;
wire [7:0] udp_tx_ip_ttl;
wire [7:0] udp_tx_ip_protocol;
wire [31:0] udp_tx_ip_source_ip;
wire [31:0] udp_tx_ip_dest_ip;
wire [7:0] udp_tx_ip_payload_axis_tdata;
wire udp_tx_ip_payload_axis_tvalid;
wire udp_tx_ip_payload_axis_tlast;
wire udp_tx_ip_payload_axis_tuser;
wire udp_tx_ip_payload_axis_tready;
/*
* Input classifier (ip_protocol)
*/
wire s_select_udp = (ip_rx_ip_protocol == 8'h11);
wire s_select_ip = !s_select_udp;
reg s_select_udp_reg = 1'b0;
reg s_select_ip_reg = 1'b0;
always @(posedge clk) begin
if (rst) begin
s_select_udp_reg <= 1'b0;
s_select_ip_reg <= 1'b0;
end else begin
if (ip_rx_ip_payload_axis_tvalid) begin
if ((!s_select_udp_reg && !s_select_ip_reg) ||
(ip_rx_ip_payload_axis_tvalid && ip_rx_ip_payload_axis_tready && ip_rx_ip_payload_axis_tlast)) begin
s_select_udp_reg <= s_select_udp;
s_select_ip_reg <= s_select_ip;
end
end else begin
s_select_udp_reg <= 1'b0;
s_select_ip_reg <= 1'b0;
end
end
end
// IP frame to UDP module
assign udp_rx_ip_hdr_valid = s_select_udp && ip_rx_ip_hdr_valid;
assign udp_rx_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac;
assign udp_rx_ip_eth_src_mac = ip_rx_ip_eth_src_mac;
assign udp_rx_ip_eth_type = ip_rx_ip_eth_type;
assign udp_rx_ip_version = ip_rx_ip_version;
assign udp_rx_ip_ihl = ip_rx_ip_ihl;
assign udp_rx_ip_dscp = ip_rx_ip_dscp;
assign udp_rx_ip_ecn = ip_rx_ip_ecn;
assign udp_rx_ip_length = ip_rx_ip_length;
assign udp_rx_ip_identification = ip_rx_ip_identification;
assign udp_rx_ip_flags = ip_rx_ip_flags;
assign udp_rx_ip_fragment_offset = ip_rx_ip_fragment_offset;
assign udp_rx_ip_ttl = ip_rx_ip_ttl;
assign udp_rx_ip_protocol = 8'h11;
assign udp_rx_ip_header_checksum = ip_rx_ip_header_checksum;
assign udp_rx_ip_source_ip = ip_rx_ip_source_ip;
assign udp_rx_ip_dest_ip = ip_rx_ip_dest_ip;
assign udp_rx_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata;
assign udp_rx_ip_payload_axis_tvalid = s_select_udp_reg && ip_rx_ip_payload_axis_tvalid;
assign udp_rx_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast;
assign udp_rx_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser;
// External IP frame output
assign m_ip_hdr_valid = s_select_ip && ip_rx_ip_hdr_valid;
assign m_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac;
assign m_ip_eth_src_mac = ip_rx_ip_eth_src_mac;
assign m_ip_eth_type = ip_rx_ip_eth_type;
assign m_ip_version = ip_rx_ip_version;
assign m_ip_ihl = ip_rx_ip_ihl;
assign m_ip_dscp = ip_rx_ip_dscp;
assign m_ip_ecn = ip_rx_ip_ecn;
assign m_ip_length = ip_rx_ip_length;
assign m_ip_identification = ip_rx_ip_identification;
assign m_ip_flags = ip_rx_ip_flags;
assign m_ip_fragment_offset = ip_rx_ip_fragment_offset;
assign m_ip_ttl = ip_rx_ip_ttl;
assign m_ip_protocol = ip_rx_ip_protocol;
assign m_ip_header_checksum = ip_rx_ip_header_checksum;
assign m_ip_source_ip = ip_rx_ip_source_ip;
assign m_ip_dest_ip = ip_rx_ip_dest_ip;
assign m_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata;
assign m_ip_payload_axis_tvalid = s_select_ip_reg && ip_rx_ip_payload_axis_tvalid;
assign m_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast;
assign m_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser;
assign ip_rx_ip_hdr_ready = (s_select_udp && udp_rx_ip_hdr_ready) ||
(s_select_ip && m_ip_hdr_ready);
assign ip_rx_ip_payload_axis_tready = (s_select_udp_reg && udp_rx_ip_payload_axis_tready) ||
(s_select_ip_reg && m_ip_payload_axis_tready);
/*
* Output arbiter
*/
ip_arb_mux #(
.S_COUNT(2),
.DATA_WIDTH(8),
.KEEP_ENABLE(0),
.ID_ENABLE(0),
.DEST_ENABLE(0),
.USER_ENABLE(1),
.USER_WIDTH(1),
.ARB_TYPE("PRIORITY"),
.LSB_PRIORITY("HIGH")
)
ip_arb_mux_inst (
.clk(clk),
.rst(rst),
// IP frame inputs
.s_ip_hdr_valid({s_ip_hdr_valid, udp_tx_ip_hdr_valid}),
.s_ip_hdr_ready({s_ip_hdr_ready, udp_tx_ip_hdr_ready}),
.s_eth_dest_mac(0),
.s_eth_src_mac(0),
.s_eth_type(0),
.s_ip_version(0),
.s_ip_ihl(0),
.s_ip_dscp({s_ip_dscp, udp_tx_ip_dscp}),
.s_ip_ecn({s_ip_ecn, udp_tx_ip_ecn}),
.s_ip_length({s_ip_length, udp_tx_ip_length}),
.s_ip_identification(0),
.s_ip_flags(0),
.s_ip_fragment_offset(0),
.s_ip_ttl({s_ip_ttl, udp_tx_ip_ttl}),
.s_ip_protocol({s_ip_protocol, udp_tx_ip_protocol}),
.s_ip_header_checksum(0),
.s_ip_source_ip({s_ip_source_ip, udp_tx_ip_source_ip}),
.s_ip_dest_ip({s_ip_dest_ip, udp_tx_ip_dest_ip}),
.s_ip_payload_axis_tdata({s_ip_payload_axis_tdata, udp_tx_ip_payload_axis_tdata}),
.s_ip_payload_axis_tkeep(0),
.s_ip_payload_axis_tvalid({s_ip_payload_axis_tvalid, udp_tx_ip_payload_axis_tvalid}),
.s_ip_payload_axis_tready({s_ip_payload_axis_tready, udp_tx_ip_payload_axis_tready}),
.s_ip_payload_axis_tlast({s_ip_payload_axis_tlast, udp_tx_ip_payload_axis_tlast}),
.s_ip_payload_axis_tid(0),
.s_ip_payload_axis_tdest(0),
.s_ip_payload_axis_tuser({s_ip_payload_axis_tuser, udp_tx_ip_payload_axis_tuser}),
// IP frame output
.m_ip_hdr_valid(ip_tx_ip_hdr_valid),
.m_ip_hdr_ready(ip_tx_ip_hdr_ready),
.m_eth_dest_mac(),
.m_eth_src_mac(),
.m_eth_type(),
.m_ip_version(),
.m_ip_ihl(),
.m_ip_dscp(ip_tx_ip_dscp),
.m_ip_ecn(ip_tx_ip_ecn),
.m_ip_length(ip_tx_ip_length),
.m_ip_identification(),
.m_ip_flags(),
.m_ip_fragment_offset(),
.m_ip_ttl(ip_tx_ip_ttl),
.m_ip_protocol(ip_tx_ip_protocol),
.m_ip_header_checksum(),
.m_ip_source_ip(ip_tx_ip_source_ip),
.m_ip_dest_ip(ip_tx_ip_dest_ip),
.m_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata),
.m_ip_payload_axis_tkeep(),
.m_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid),
.m_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready),
.m_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast),
.m_ip_payload_axis_tid(),
.m_ip_payload_axis_tdest(),
.m_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser)
);
/*
* IP stack
*/
ip_complete #(
.ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH),
.ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT),
.ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL),
.ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT)
)
ip_complete_inst (
.clk(clk),
.rst(rst),
// Ethernet frame input
.s_eth_hdr_valid(s_eth_hdr_valid),
.s_eth_hdr_ready(s_eth_hdr_ready),
.s_eth_dest_mac(s_eth_dest_mac),
.s_eth_src_mac(s_eth_src_mac),
.s_eth_type(s_eth_type),
.s_eth_payload_axis_tdata(s_eth_payload_axis_tdata),
.s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid),
.s_eth_payload_axis_tready(s_eth_payload_axis_tready),
.s_eth_payload_axis_tlast(s_eth_payload_axis_tlast),
.s_eth_payload_axis_tuser(s_eth_payload_axis_tuser),
// Ethernet frame output
.m_eth_hdr_valid(m_eth_hdr_valid),
.m_eth_hdr_ready(m_eth_hdr_ready),
.m_eth_dest_mac(m_eth_dest_mac),
.m_eth_src_mac(m_eth_src_mac),
.m_eth_type(m_eth_type),
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser),
// IP frame input
.s_ip_hdr_valid(ip_tx_ip_hdr_valid),
.s_ip_hdr_ready(ip_tx_ip_hdr_ready),
.s_ip_dscp(ip_tx_ip_dscp),
.s_ip_ecn(ip_tx_ip_ecn),
.s_ip_length(ip_tx_ip_length),
.s_ip_ttl(ip_tx_ip_ttl),
.s_ip_protocol(ip_tx_ip_protocol),
.s_ip_source_ip(ip_tx_ip_source_ip),
.s_ip_dest_ip(ip_tx_ip_dest_ip),
.s_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata),
.s_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid),
.s_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready),
.s_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast),
.s_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser),
// IP frame output
.m_ip_hdr_valid(ip_rx_ip_hdr_valid),
.m_ip_hdr_ready(ip_rx_ip_hdr_ready),
.m_ip_eth_dest_mac(ip_rx_ip_eth_dest_mac),
.m_ip_eth_src_mac(ip_rx_ip_eth_src_mac),
.m_ip_eth_type(ip_rx_ip_eth_type),
.m_ip_version(ip_rx_ip_version),
.m_ip_ihl(ip_rx_ip_ihl),
.m_ip_dscp(ip_rx_ip_dscp),
.m_ip_ecn(ip_rx_ip_ecn),
.m_ip_length(ip_rx_ip_length),
.m_ip_identification(ip_rx_ip_identification),
.m_ip_flags(ip_rx_ip_flags),
.m_ip_fragment_offset(ip_rx_ip_fragment_offset),
.m_ip_ttl(ip_rx_ip_ttl),
.m_ip_protocol(ip_rx_ip_protocol),
.m_ip_header_checksum(ip_rx_ip_header_checksum),
.m_ip_source_ip(ip_rx_ip_source_ip),
.m_ip_dest_ip(ip_rx_ip_dest_ip),
.m_ip_payload_axis_tdata(ip_rx_ip_payload_axis_tdata),
.m_ip_payload_axis_tvalid(ip_rx_ip_payload_axis_tvalid),
.m_ip_payload_axis_tready(ip_rx_ip_payload_axis_tready),
.m_ip_payload_axis_tlast(ip_rx_ip_payload_axis_tlast),
.m_ip_payload_axis_tuser(ip_rx_ip_payload_axis_tuser),
// Status
.rx_busy(ip_rx_busy),
.tx_busy(ip_tx_busy),
.rx_error_header_early_termination(ip_rx_error_header_early_termination),
.rx_error_payload_early_termination(ip_rx_error_payload_early_termination),
.rx_error_invalid_header(ip_rx_error_invalid_header),
.rx_error_invalid_checksum(ip_rx_error_invalid_checksum),
.tx_error_payload_early_termination(ip_tx_error_payload_early_termination),
.tx_error_arp_failed(ip_tx_error_arp_failed),
// Configuration
.local_mac(local_mac),
.local_ip(local_ip),
.gateway_ip(gateway_ip),
.subnet_mask(subnet_mask),
.clear_arp_cache(clear_arp_cache)
);
/*
* UDP interface
*/
udp #(
.CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE),
.CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH),
.CHECKSUM_HEADER_FIFO_ADDR_WIDTH(UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH)
)
udp_inst (
.clk(clk),
.rst(rst),
// IP frame input
.s_ip_hdr_valid(udp_rx_ip_hdr_valid),
.s_ip_hdr_ready(udp_rx_ip_hdr_ready),
.s_ip_eth_dest_mac(udp_rx_ip_eth_dest_mac),
.s_ip_eth_src_mac(udp_rx_ip_eth_src_mac),
.s_ip_eth_type(udp_rx_ip_eth_type),
.s_ip_version(udp_rx_ip_version),
.s_ip_ihl(udp_rx_ip_ihl),
.s_ip_dscp(udp_rx_ip_dscp),
.s_ip_ecn(udp_rx_ip_ecn),
.s_ip_length(udp_rx_ip_length),
.s_ip_identification(udp_rx_ip_identification),
.s_ip_flags(udp_rx_ip_flags),
.s_ip_fragment_offset(udp_rx_ip_fragment_offset),
.s_ip_ttl(udp_rx_ip_ttl),
.s_ip_protocol(udp_rx_ip_protocol),
.s_ip_header_checksum(udp_rx_ip_header_checksum),
.s_ip_source_ip(udp_rx_ip_source_ip),
.s_ip_dest_ip(udp_rx_ip_dest_ip),
.s_ip_payload_axis_tdata(udp_rx_ip_payload_axis_tdata),
.s_ip_payload_axis_tvalid(udp_rx_ip_payload_axis_tvalid),
.s_ip_payload_axis_tready(udp_rx_ip_payload_axis_tready),
.s_ip_payload_axis_tlast(udp_rx_ip_payload_axis_tlast),
.s_ip_payload_axis_tuser(udp_rx_ip_payload_axis_tuser),
// IP frame output
.m_ip_hdr_valid(udp_tx_ip_hdr_valid),
.m_ip_hdr_ready(udp_tx_ip_hdr_ready),
.m_ip_eth_dest_mac(),
.m_ip_eth_src_mac(),
.m_ip_eth_type(),
.m_ip_version(),
.m_ip_ihl(),
.m_ip_dscp(udp_tx_ip_dscp),
.m_ip_ecn(udp_tx_ip_ecn),
.m_ip_length(udp_tx_ip_length),
.m_ip_identification(),
.m_ip_flags(),
.m_ip_fragment_offset(),
.m_ip_ttl(udp_tx_ip_ttl),
.m_ip_protocol(udp_tx_ip_protocol),
.m_ip_header_checksum(),
.m_ip_source_ip(udp_tx_ip_source_ip),
.m_ip_dest_ip(udp_tx_ip_dest_ip),
.m_ip_payload_axis_tdata(udp_tx_ip_payload_axis_tdata),
.m_ip_payload_axis_tvalid(udp_tx_ip_payload_axis_tvalid),
.m_ip_payload_axis_tready(udp_tx_ip_payload_axis_tready),
.m_ip_payload_axis_tlast(udp_tx_ip_payload_axis_tlast),
.m_ip_payload_axis_tuser(udp_tx_ip_payload_axis_tuser),
// UDP frame input
.s_udp_hdr_valid(s_udp_hdr_valid),
.s_udp_hdr_ready(s_udp_hdr_ready),
.s_udp_eth_dest_mac(48'd0),
.s_udp_eth_src_mac(48'd0),
.s_udp_eth_type(16'd0),
.s_udp_ip_version(4'd0),
.s_udp_ip_ihl(4'd0),
.s_udp_ip_dscp(s_udp_ip_dscp),
.s_udp_ip_ecn(s_udp_ip_ecn),
.s_udp_ip_identification(16'd0),
.s_udp_ip_flags(3'd0),
.s_udp_ip_fragment_offset(13'd0),
.s_udp_ip_ttl(s_udp_ip_ttl),
.s_udp_ip_header_checksum(16'd0),
.s_udp_ip_source_ip(s_udp_ip_source_ip),
.s_udp_ip_dest_ip(s_udp_ip_dest_ip),
.s_udp_source_port(s_udp_source_port),
.s_udp_dest_port(s_udp_dest_port),
.s_udp_length(s_udp_length),
.s_udp_checksum(s_udp_checksum),
.s_udp_payload_axis_tdata(s_udp_payload_axis_tdata),
.s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid),
.s_udp_payload_axis_tready(s_udp_payload_axis_tready),
.s_udp_payload_axis_tlast(s_udp_payload_axis_tlast),
.s_udp_payload_axis_tuser(s_udp_payload_axis_tuser),
// UDP frame output
.m_udp_hdr_valid(m_udp_hdr_valid),
.m_udp_hdr_ready(m_udp_hdr_ready),
.m_udp_eth_dest_mac(m_udp_eth_dest_mac),
.m_udp_eth_src_mac(m_udp_eth_src_mac),
.m_udp_eth_type(m_udp_eth_type),
.m_udp_ip_version(m_udp_ip_version),
.m_udp_ip_ihl(m_udp_ip_ihl),
.m_udp_ip_dscp(m_udp_ip_dscp),
.m_udp_ip_ecn(m_udp_ip_ecn),
.m_udp_ip_length(m_udp_ip_length),
.m_udp_ip_identification(m_udp_ip_identification),
.m_udp_ip_flags(m_udp_ip_flags),
.m_udp_ip_fragment_offset(m_udp_ip_fragment_offset),
.m_udp_ip_ttl(m_udp_ip_ttl),
.m_udp_ip_protocol(m_udp_ip_protocol),
.m_udp_ip_header_checksum(m_udp_ip_header_checksum),
.m_udp_ip_source_ip(m_udp_ip_source_ip),
.m_udp_ip_dest_ip(m_udp_ip_dest_ip),
.m_udp_source_port(m_udp_source_port),
.m_udp_dest_port(m_udp_dest_port),
.m_udp_length(m_udp_length),
.m_udp_checksum(m_udp_checksum),
.m_udp_payload_axis_tdata(m_udp_payload_axis_tdata),
.m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid),
.m_udp_payload_axis_tready(m_udp_payload_axis_tready),
.m_udp_payload_axis_tlast(m_udp_payload_axis_tlast),
.m_udp_payload_axis_tuser(m_udp_payload_axis_tuser),
// Status
.rx_busy(udp_rx_busy),
.tx_busy(udp_tx_busy),
.rx_error_header_early_termination(udp_rx_error_header_early_termination),
.rx_error_payload_early_termination(udp_rx_error_payload_early_termination),
.tx_error_payload_early_termination(udp_tx_error_payload_early_termination)
);
endmodule

528
fpga/src/udp_ip_rx.sv Normal file
View file

@ -0,0 +1,528 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* UDP ethernet frame receiver (IP frame in, UDP frame out)
*/
module udp_ip_rx
(
input wire clk,
input wire rst,
/*
* IP frame input
*/
input wire s_ip_hdr_valid,
output wire s_ip_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [3:0] s_ip_version,
input wire [3:0] s_ip_ihl,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_length,
input wire [15:0] s_ip_identification,
input wire [2:0] s_ip_flags,
input wire [12:0] s_ip_fragment_offset,
input wire [7:0] s_ip_ttl,
input wire [7:0] s_ip_protocol,
input wire [15:0] s_ip_header_checksum,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [7:0] s_ip_payload_axis_tdata,
input wire s_ip_payload_axis_tvalid,
output wire s_ip_payload_axis_tready,
input wire s_ip_payload_axis_tlast,
input wire s_ip_payload_axis_tuser,
/*
* UDP frame output
*/
output wire m_udp_hdr_valid,
input wire m_udp_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [15:0] m_udp_source_port,
output wire [15:0] m_udp_dest_port,
output wire [15:0] m_udp_length,
output wire [15:0] m_udp_checksum,
output wire [7:0] m_udp_payload_axis_tdata,
output wire m_udp_payload_axis_tvalid,
input wire m_udp_payload_axis_tready,
output wire m_udp_payload_axis_tlast,
output wire m_udp_payload_axis_tuser,
/*
* Status signals
*/
output wire busy,
output wire error_header_early_termination,
output wire error_payload_early_termination
);
/*
UDP Frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype (0x0800) 2 octets
Version (4) 4 bits
IHL (5-15) 4 bits
DSCP (0) 6 bits
ECN (0) 2 bits
length 2 octets
identification (0?) 2 octets
flags (010) 3 bits
fragment offset (0) 13 bits
time to live (64?) 1 octet
protocol 1 octet
header checksum 2 octets
source IP 4 octets
destination IP 4 octets
options (IHL-5)*4 octets
source port 2 octets
desination port 2 octets
length 2 octets
checksum 2 octets
payload length octets
This module receives an IP frame with header fields in parallel and payload on
an AXI stream interface, decodes and strips the UDP header fields, then
produces the header fields in parallel along with the UDP payload in a
separate AXI stream.
*/
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_READ_HEADER = 3'd1,
STATE_READ_PAYLOAD = 3'd2,
STATE_READ_PAYLOAD_LAST = 3'd3,
STATE_WAIT_LAST = 3'd4;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_ip_hdr;
reg store_udp_source_port_0;
reg store_udp_source_port_1;
reg store_udp_dest_port_0;
reg store_udp_dest_port_1;
reg store_udp_length_0;
reg store_udp_length_1;
reg store_udp_checksum_0;
reg store_udp_checksum_1;
reg store_last_word;
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
reg [7:0] last_word_data_reg = 8'd0;
reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg [3:0] m_ip_version_reg = 4'd0;
reg [3:0] m_ip_ihl_reg = 4'd0;
reg [5:0] m_ip_dscp_reg = 6'd0;
reg [1:0] m_ip_ecn_reg = 2'd0;
reg [15:0] m_ip_length_reg = 16'd0;
reg [15:0] m_ip_identification_reg = 16'd0;
reg [2:0] m_ip_flags_reg = 3'd0;
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
reg [7:0] m_ip_ttl_reg = 8'd0;
reg [7:0] m_ip_protocol_reg = 8'd0;
reg [15:0] m_ip_header_checksum_reg = 16'd0;
reg [31:0] m_ip_source_ip_reg = 32'd0;
reg [31:0] m_ip_dest_ip_reg = 32'd0;
reg [15:0] m_udp_source_port_reg = 16'd0;
reg [15:0] m_udp_dest_port_reg = 16'd0;
reg [15:0] m_udp_length_reg = 16'd0;
reg [15:0] m_udp_checksum_reg = 16'd0;
reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next;
reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next;
reg busy_reg = 1'b0;
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
// internal datapath
reg [7:0] m_udp_payload_axis_tdata_int;
reg m_udp_payload_axis_tvalid_int;
reg m_udp_payload_axis_tready_int_reg = 1'b0;
reg m_udp_payload_axis_tlast_int;
reg m_udp_payload_axis_tuser_int;
wire m_udp_payload_axis_tready_int_early;
assign s_ip_hdr_ready = s_ip_hdr_ready_reg;
assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg;
assign m_udp_hdr_valid = m_udp_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign m_ip_version = m_ip_version_reg;
assign m_ip_ihl = m_ip_ihl_reg;
assign m_ip_dscp = m_ip_dscp_reg;
assign m_ip_ecn = m_ip_ecn_reg;
assign m_ip_length = m_ip_length_reg;
assign m_ip_identification = m_ip_identification_reg;
assign m_ip_flags = m_ip_flags_reg;
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
assign m_ip_ttl = m_ip_ttl_reg;
assign m_ip_protocol = m_ip_protocol_reg;
assign m_ip_header_checksum = m_ip_header_checksum_reg;
assign m_ip_source_ip = m_ip_source_ip_reg;
assign m_ip_dest_ip = m_ip_dest_ip_reg;
assign m_udp_source_port = m_udp_source_port_reg;
assign m_udp_dest_port = m_udp_dest_port_reg;
assign m_udp_length = m_udp_length_reg;
assign m_udp_checksum = m_udp_checksum_reg;
assign busy = busy_reg;
assign error_header_early_termination = error_header_early_termination_reg;
assign error_payload_early_termination = error_payload_early_termination_reg;
always @* begin
state_next = STATE_IDLE;
s_ip_hdr_ready_next = 1'b0;
s_ip_payload_axis_tready_next = 1'b0;
store_ip_hdr = 1'b0;
store_udp_source_port_0 = 1'b0;
store_udp_source_port_1 = 1'b0;
store_udp_dest_port_0 = 1'b0;
store_udp_dest_port_1 = 1'b0;
store_udp_length_0 = 1'b0;
store_udp_length_1 = 1'b0;
store_udp_checksum_0 = 1'b0;
store_udp_checksum_1 = 1'b0;
store_last_word = 1'b0;
frame_ptr_next = frame_ptr_reg;
m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready;
error_header_early_termination_next = 1'b0;
error_payload_early_termination_next = 1'b0;
m_udp_payload_axis_tdata_int = 8'd0;
m_udp_payload_axis_tvalid_int = 1'b0;
m_udp_payload_axis_tlast_int = 1'b0;
m_udp_payload_axis_tuser_int = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for header
frame_ptr_next = 16'd0;
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
if (s_ip_hdr_ready && s_ip_hdr_valid) begin
s_ip_hdr_ready_next = 1'b0;
s_ip_payload_axis_tready_next = 1'b1;
store_ip_hdr = 1'b1;
state_next = STATE_READ_HEADER;
end else begin
state_next = STATE_IDLE;
end
end
STATE_READ_HEADER: begin
// read header state
s_ip_payload_axis_tready_next = 1'b1;
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
// word transfer in - store it
frame_ptr_next = frame_ptr_reg + 16'd1;
state_next = STATE_READ_HEADER;
case (frame_ptr_reg)
8'h00: store_udp_source_port_1 = 1'b1;
8'h01: store_udp_source_port_0 = 1'b1;
8'h02: store_udp_dest_port_1 = 1'b1;
8'h03: store_udp_dest_port_0 = 1'b1;
8'h04: store_udp_length_1 = 1'b1;
8'h05: store_udp_length_0 = 1'b1;
8'h06: store_udp_checksum_1 = 1'b1;
8'h07: begin
store_udp_checksum_0 = 1'b1;
m_udp_hdr_valid_next = 1'b1;
s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early;
state_next = STATE_READ_PAYLOAD;
end
endcase
if (s_ip_payload_axis_tlast) begin
error_header_early_termination_next = 1'b1;
m_udp_hdr_valid_next = 1'b0;
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
s_ip_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end
end else begin
state_next = STATE_READ_HEADER;
end
end
STATE_READ_PAYLOAD: begin
// read payload
s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early;
m_udp_payload_axis_tdata_int = s_ip_payload_axis_tdata;
m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid;
m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast;
m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser;
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
// word transfer through
frame_ptr_next = frame_ptr_reg + 16'd1;
if (s_ip_payload_axis_tlast) begin
if (frame_ptr_next != m_udp_length_reg) begin
// end of frame, but length does not match
m_udp_payload_axis_tuser_int = 1'b1;
error_payload_early_termination_next = 1'b1;
end
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
s_ip_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
if (frame_ptr_next == m_udp_length_reg) begin
store_last_word = 1'b1;
m_udp_payload_axis_tvalid_int = 1'b0;
state_next = STATE_READ_PAYLOAD_LAST;
end else begin
state_next = STATE_READ_PAYLOAD;
end
end
end else begin
state_next = STATE_READ_PAYLOAD;
end
end
STATE_READ_PAYLOAD_LAST: begin
// read and discard until end of frame
s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early;
m_udp_payload_axis_tdata_int = last_word_data_reg;
m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast;
m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast;
m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser;
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
if (s_ip_payload_axis_tlast) begin
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
s_ip_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_READ_PAYLOAD_LAST;
end
end else begin
state_next = STATE_READ_PAYLOAD_LAST;
end
end
STATE_WAIT_LAST: begin
// wait for end of frame; read and discard
s_ip_payload_axis_tready_next = 1'b1;
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
if (s_ip_payload_axis_tlast) begin
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
s_ip_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT_LAST;
end
end else begin
state_next = STATE_WAIT_LAST;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 16'd0;
s_ip_hdr_ready_reg <= 1'b0;
s_ip_payload_axis_tready_reg <= 1'b0;
m_udp_hdr_valid_reg <= 1'b0;
busy_reg <= 1'b0;
error_header_early_termination_reg <= 1'b0;
error_payload_early_termination_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
s_ip_hdr_ready_reg <= s_ip_hdr_ready_next;
s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next;
m_udp_hdr_valid_reg <= m_udp_hdr_valid_next;
error_header_early_termination_reg <= error_header_early_termination_next;
error_payload_early_termination_reg <= error_payload_early_termination_next;
busy_reg <= state_next != STATE_IDLE;
end
// datapath
if (store_ip_hdr) begin
m_eth_dest_mac_reg <= s_eth_dest_mac;
m_eth_src_mac_reg <= s_eth_src_mac;
m_eth_type_reg <= s_eth_type;
m_ip_version_reg <= s_ip_version;
m_ip_ihl_reg <= s_ip_ihl;
m_ip_dscp_reg <= s_ip_dscp;
m_ip_ecn_reg <= s_ip_ecn;
m_ip_length_reg <= s_ip_length;
m_ip_identification_reg <= s_ip_identification;
m_ip_flags_reg <= s_ip_flags;
m_ip_fragment_offset_reg <= s_ip_fragment_offset;
m_ip_ttl_reg <= s_ip_ttl;
m_ip_protocol_reg <= s_ip_protocol;
m_ip_header_checksum_reg <= s_ip_header_checksum;
m_ip_source_ip_reg <= s_ip_source_ip;
m_ip_dest_ip_reg <= s_ip_dest_ip;
end
if (store_last_word) begin
last_word_data_reg <= m_udp_payload_axis_tdata_int;
end
if (store_udp_source_port_0) m_udp_source_port_reg[ 7: 0] <= s_ip_payload_axis_tdata;
if (store_udp_source_port_1) m_udp_source_port_reg[15: 8] <= s_ip_payload_axis_tdata;
if (store_udp_dest_port_0) m_udp_dest_port_reg[ 7: 0] <= s_ip_payload_axis_tdata;
if (store_udp_dest_port_1) m_udp_dest_port_reg[15: 8] <= s_ip_payload_axis_tdata;
if (store_udp_length_0) m_udp_length_reg[ 7: 0] <= s_ip_payload_axis_tdata;
if (store_udp_length_1) m_udp_length_reg[15: 8] <= s_ip_payload_axis_tdata;
if (store_udp_checksum_0) m_udp_checksum_reg[ 7: 0] <= s_ip_payload_axis_tdata;
if (store_udp_checksum_1) m_udp_checksum_reg[15: 8] <= s_ip_payload_axis_tdata;
end
// output datapath logic
reg [7:0] m_udp_payload_axis_tdata_reg = 8'd0;
reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next;
reg m_udp_payload_axis_tlast_reg = 1'b0;
reg m_udp_payload_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_udp_payload_axis_tdata_reg = 8'd0;
reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next;
reg temp_m_udp_payload_axis_tlast_reg = 1'b0;
reg temp_m_udp_payload_axis_tuser_reg = 1'b0;
// datapath control
reg store_udp_payload_int_to_output;
reg store_udp_payload_int_to_temp;
reg store_udp_payload_axis_temp_to_output;
assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg;
assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg;
assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg;
assign m_udp_payload_axis_tuser = m_udp_payload_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg;
temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg;
store_udp_payload_int_to_output = 1'b0;
store_udp_payload_int_to_temp = 1'b0;
store_udp_payload_axis_temp_to_output = 1'b0;
if (m_udp_payload_axis_tready_int_reg) begin
// input is ready
if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int;
store_udp_payload_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int;
store_udp_payload_int_to_temp = 1'b1;
end
end else if (m_udp_payload_axis_tready) begin
// input is not ready, but output is ready
m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg;
temp_m_udp_payload_axis_tvalid_next = 1'b0;
store_udp_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_udp_payload_axis_tvalid_reg <= 1'b0;
m_udp_payload_axis_tready_int_reg <= 1'b0;
temp_m_udp_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next;
m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early;
temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next;
end
// datapath
if (store_udp_payload_int_to_output) begin
m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int;
m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int;
m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int;
end else if (store_udp_payload_axis_temp_to_output) begin
m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg;
m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg;
m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg;
end
if (store_udp_payload_int_to_temp) begin
temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int;
temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int;
temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int;
end
end
endmodule

489
fpga/src/udp_ip_tx.sv Normal file
View file

@ -0,0 +1,489 @@
/*
Copyright (c) 2014-2018 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Language: Verilog 2001
`timescale 1ns / 1ps
/*
* UDP ethernet frame transmitter (UDP frame in, IP frame out)
*/
module udp_ip_tx
(
input wire clk,
input wire rst,
/*
* UDP frame input
*/
input wire s_udp_hdr_valid,
output wire s_udp_hdr_ready,
input wire [47:0] s_eth_dest_mac,
input wire [47:0] s_eth_src_mac,
input wire [15:0] s_eth_type,
input wire [3:0] s_ip_version,
input wire [3:0] s_ip_ihl,
input wire [5:0] s_ip_dscp,
input wire [1:0] s_ip_ecn,
input wire [15:0] s_ip_identification,
input wire [2:0] s_ip_flags,
input wire [12:0] s_ip_fragment_offset,
input wire [7:0] s_ip_ttl,
input wire [7:0] s_ip_protocol,
input wire [15:0] s_ip_header_checksum,
input wire [31:0] s_ip_source_ip,
input wire [31:0] s_ip_dest_ip,
input wire [15:0] s_udp_source_port,
input wire [15:0] s_udp_dest_port,
input wire [15:0] s_udp_length,
input wire [15:0] s_udp_checksum,
input wire [7:0] s_udp_payload_axis_tdata,
input wire s_udp_payload_axis_tvalid,
output wire s_udp_payload_axis_tready,
input wire s_udp_payload_axis_tlast,
input wire s_udp_payload_axis_tuser,
/*
* IP frame output
*/
output wire m_ip_hdr_valid,
input wire m_ip_hdr_ready,
output wire [47:0] m_eth_dest_mac,
output wire [47:0] m_eth_src_mac,
output wire [15:0] m_eth_type,
output wire [3:0] m_ip_version,
output wire [3:0] m_ip_ihl,
output wire [5:0] m_ip_dscp,
output wire [1:0] m_ip_ecn,
output wire [15:0] m_ip_length,
output wire [15:0] m_ip_identification,
output wire [2:0] m_ip_flags,
output wire [12:0] m_ip_fragment_offset,
output wire [7:0] m_ip_ttl,
output wire [7:0] m_ip_protocol,
output wire [15:0] m_ip_header_checksum,
output wire [31:0] m_ip_source_ip,
output wire [31:0] m_ip_dest_ip,
output wire [7:0] m_ip_payload_axis_tdata,
output wire m_ip_payload_axis_tvalid,
input wire m_ip_payload_axis_tready,
output wire m_ip_payload_axis_tlast,
output wire m_ip_payload_axis_tuser,
/*
* Status signals
*/
output wire busy,
output wire error_payload_early_termination
);
/*
UDP Frame
Field Length
Destination MAC address 6 octets
Source MAC address 6 octets
Ethertype (0x0800) 2 octets
Version (4) 4 bits
IHL (5-15) 4 bits
DSCP (0) 6 bits
ECN (0) 2 bits
length 2 octets
identification (0?) 2 octets
flags (010) 3 bits
fragment offset (0) 13 bits
time to live (64?) 1 octet
protocol 1 octet
header checksum 2 octets
source IP 4 octets
destination IP 4 octets
options (IHL-5)*4 octets
source port 2 octets
desination port 2 octets
length 2 octets
checksum 2 octets
payload length octets
This module receives a UDP frame with header fields in parallel along with the
payload in an AXI stream, combines the header with the payload, passes through
the IP headers, and transmits the complete IP payload on an AXI interface.
*/
localparam [2:0]
STATE_IDLE = 3'd0,
STATE_WRITE_HEADER = 3'd1,
STATE_WRITE_PAYLOAD = 3'd2,
STATE_WRITE_PAYLOAD_LAST = 3'd3,
STATE_WAIT_LAST = 3'd4;
reg [2:0] state_reg = STATE_IDLE, state_next;
// datapath control signals
reg store_udp_hdr;
reg store_last_word;
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
reg [7:0] last_word_data_reg = 8'd0;
reg [15:0] udp_source_port_reg = 16'd0;
reg [15:0] udp_dest_port_reg = 16'd0;
reg [15:0] udp_length_reg = 16'd0;
reg [15:0] udp_checksum_reg = 16'd0;
reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next;
reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next;
reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next;
reg [47:0] m_eth_dest_mac_reg = 48'd0;
reg [47:0] m_eth_src_mac_reg = 48'd0;
reg [15:0] m_eth_type_reg = 16'd0;
reg [3:0] m_ip_version_reg = 4'd0;
reg [3:0] m_ip_ihl_reg = 4'd0;
reg [5:0] m_ip_dscp_reg = 6'd0;
reg [1:0] m_ip_ecn_reg = 2'd0;
reg [15:0] m_ip_length_reg = 16'd0;
reg [15:0] m_ip_identification_reg = 16'd0;
reg [2:0] m_ip_flags_reg = 3'd0;
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
reg [7:0] m_ip_ttl_reg = 8'd0;
reg [7:0] m_ip_protocol_reg = 8'd0;
reg [15:0] m_ip_header_checksum_reg = 16'd0;
reg [31:0] m_ip_source_ip_reg = 32'd0;
reg [31:0] m_ip_dest_ip_reg = 32'd0;
reg busy_reg = 1'b0;
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
// internal datapath
reg [7:0] m_ip_payload_axis_tdata_int;
reg m_ip_payload_axis_tvalid_int;
reg m_ip_payload_axis_tready_int_reg = 1'b0;
reg m_ip_payload_axis_tlast_int;
reg m_ip_payload_axis_tuser_int;
wire m_ip_payload_axis_tready_int_early;
assign s_udp_hdr_ready = s_udp_hdr_ready_reg;
assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg;
assign m_ip_hdr_valid = m_ip_hdr_valid_reg;
assign m_eth_dest_mac = m_eth_dest_mac_reg;
assign m_eth_src_mac = m_eth_src_mac_reg;
assign m_eth_type = m_eth_type_reg;
assign m_ip_version = m_ip_version_reg;
assign m_ip_ihl = m_ip_ihl_reg;
assign m_ip_dscp = m_ip_dscp_reg;
assign m_ip_ecn = m_ip_ecn_reg;
assign m_ip_length = m_ip_length_reg;
assign m_ip_identification = m_ip_identification_reg;
assign m_ip_flags = m_ip_flags_reg;
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
assign m_ip_ttl = m_ip_ttl_reg;
assign m_ip_protocol = m_ip_protocol_reg;
assign m_ip_header_checksum = m_ip_header_checksum_reg;
assign m_ip_source_ip = m_ip_source_ip_reg;
assign m_ip_dest_ip = m_ip_dest_ip_reg;
assign busy = busy_reg;
assign error_payload_early_termination = error_payload_early_termination_reg;
always @* begin
state_next = STATE_IDLE;
s_udp_hdr_ready_next = 1'b0;
s_udp_payload_axis_tready_next = 1'b0;
store_udp_hdr = 1'b0;
store_last_word = 1'b0;
frame_ptr_next = frame_ptr_reg;
m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready;
error_payload_early_termination_next = 1'b0;
m_ip_payload_axis_tdata_int = 8'd0;
m_ip_payload_axis_tvalid_int = 1'b0;
m_ip_payload_axis_tlast_int = 1'b0;
m_ip_payload_axis_tuser_int = 1'b0;
case (state_reg)
STATE_IDLE: begin
// idle state - wait for data
frame_ptr_next = 16'd0;
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
if (s_udp_hdr_ready && s_udp_hdr_valid) begin
store_udp_hdr = 1'b1;
s_udp_hdr_ready_next = 1'b0;
m_ip_hdr_valid_next = 1'b1;
if (m_ip_payload_axis_tready_int_reg) begin
m_ip_payload_axis_tvalid_int = 1'b1;
m_ip_payload_axis_tdata_int = s_udp_source_port[15: 8];
frame_ptr_next = 1'b1;
end
state_next = STATE_WRITE_HEADER;
end else begin
state_next = STATE_IDLE;
end
end
STATE_WRITE_HEADER: begin
// write header state
if (m_ip_payload_axis_tready_int_reg) begin
// word transfer out
frame_ptr_next = frame_ptr_reg + 16'd1;
m_ip_payload_axis_tvalid_int = 1'b1;
state_next = STATE_WRITE_HEADER;
case (frame_ptr_reg)
8'h00: m_ip_payload_axis_tdata_int = udp_source_port_reg[15: 8];
8'h01: m_ip_payload_axis_tdata_int = udp_source_port_reg[ 7: 0];
8'h02: m_ip_payload_axis_tdata_int = udp_dest_port_reg[15: 8];
8'h03: m_ip_payload_axis_tdata_int = udp_dest_port_reg[ 7: 0];
8'h04: m_ip_payload_axis_tdata_int = udp_length_reg[15: 8];
8'h05: m_ip_payload_axis_tdata_int = udp_length_reg[ 7: 0];
8'h06: m_ip_payload_axis_tdata_int = udp_checksum_reg[15: 8];
8'h07: begin
m_ip_payload_axis_tdata_int = udp_checksum_reg[ 7: 0];
s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
state_next = STATE_WRITE_PAYLOAD;
end
endcase
end else begin
state_next = STATE_WRITE_HEADER;
end
end
STATE_WRITE_PAYLOAD: begin
// write payload
s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
m_ip_payload_axis_tdata_int = s_udp_payload_axis_tdata;
m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid;
m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast;
m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser;
if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin
// word transfer through
frame_ptr_next = frame_ptr_reg + 16'd1;
if (s_udp_payload_axis_tlast) begin
if (frame_ptr_next != udp_length_reg) begin
// end of frame, but length does not match
m_ip_payload_axis_tuser_int = 1'b1;
error_payload_early_termination_next = 1'b1;
end
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
s_udp_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
if (frame_ptr_next == udp_length_reg) begin
store_last_word = 1'b1;
m_ip_payload_axis_tvalid_int = 1'b0;
state_next = STATE_WRITE_PAYLOAD_LAST;
end else begin
state_next = STATE_WRITE_PAYLOAD;
end
end
end else begin
state_next = STATE_WRITE_PAYLOAD;
end
end
STATE_WRITE_PAYLOAD_LAST: begin
// read and discard until end of frame
s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
m_ip_payload_axis_tdata_int = last_word_data_reg;
m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid && s_udp_payload_axis_tlast;
m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast;
m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser;
if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin
if (s_udp_payload_axis_tlast) begin
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
s_udp_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WRITE_PAYLOAD_LAST;
end
end else begin
state_next = STATE_WRITE_PAYLOAD_LAST;
end
end
STATE_WAIT_LAST: begin
// wait for end of frame; read and discard
s_udp_payload_axis_tready_next = 1'b1;
if (s_udp_payload_axis_tvalid) begin
if (s_udp_payload_axis_tlast) begin
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
s_udp_payload_axis_tready_next = 1'b0;
state_next = STATE_IDLE;
end else begin
state_next = STATE_WAIT_LAST;
end
end else begin
state_next = STATE_WAIT_LAST;
end
end
endcase
end
always @(posedge clk) begin
if (rst) begin
state_reg <= STATE_IDLE;
frame_ptr_reg <= 16'd0;
s_udp_hdr_ready_reg <= 1'b0;
s_udp_payload_axis_tready_reg <= 1'b0;
m_ip_hdr_valid_reg <= 1'b0;
busy_reg <= 1'b0;
error_payload_early_termination_reg <= 1'b0;
end else begin
state_reg <= state_next;
frame_ptr_reg <= frame_ptr_next;
s_udp_hdr_ready_reg <= s_udp_hdr_ready_next;
s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next;
m_ip_hdr_valid_reg <= m_ip_hdr_valid_next;
busy_reg <= state_next != STATE_IDLE;
error_payload_early_termination_reg <= error_payload_early_termination_next;
end
// datapath
if (store_udp_hdr) begin
m_eth_dest_mac_reg <= s_eth_dest_mac;
m_eth_src_mac_reg <= s_eth_src_mac;
m_eth_type_reg <= s_eth_type;
m_ip_version_reg <= s_ip_version;
m_ip_ihl_reg <= s_ip_ihl;
m_ip_dscp_reg <= s_ip_dscp;
m_ip_ecn_reg <= s_ip_ecn;
m_ip_length_reg <= s_udp_length + 20;
m_ip_identification_reg <= s_ip_identification;
m_ip_flags_reg <= s_ip_flags;
m_ip_fragment_offset_reg <= s_ip_fragment_offset;
m_ip_ttl_reg <= s_ip_ttl;
m_ip_protocol_reg <= s_ip_protocol;
m_ip_header_checksum_reg <= s_ip_header_checksum;
m_ip_source_ip_reg <= s_ip_source_ip;
m_ip_dest_ip_reg <= s_ip_dest_ip;
udp_source_port_reg <= s_udp_source_port;
udp_dest_port_reg <= s_udp_dest_port;
udp_length_reg <= s_udp_length;
udp_checksum_reg <= s_udp_checksum;
end
if (store_last_word) begin
last_word_data_reg <= m_ip_payload_axis_tdata_int;
end
end
// output datapath logic
reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0;
reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next;
reg m_ip_payload_axis_tlast_reg = 1'b0;
reg m_ip_payload_axis_tuser_reg = 1'b0;
reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0;
reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next;
reg temp_m_ip_payload_axis_tlast_reg = 1'b0;
reg temp_m_ip_payload_axis_tuser_reg = 1'b0;
// datapath control
reg store_ip_payload_int_to_output;
reg store_ip_payload_int_to_temp;
reg store_ip_payload_axis_temp_to_output;
assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg;
assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg;
assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg;
assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg;
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int));
always @* begin
// transfer sink ready state to source
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg;
temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
store_ip_payload_int_to_output = 1'b0;
store_ip_payload_int_to_temp = 1'b0;
store_ip_payload_axis_temp_to_output = 1'b0;
if (m_ip_payload_axis_tready_int_reg) begin
// input is ready
if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin
// output is ready or currently not valid, transfer data to output
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
store_ip_payload_int_to_output = 1'b1;
end else begin
// output is not ready, store input in temp
temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
store_ip_payload_int_to_temp = 1'b1;
end
end else if (m_ip_payload_axis_tready) begin
// input is not ready, but output is ready
m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
temp_m_ip_payload_axis_tvalid_next = 1'b0;
store_ip_payload_axis_temp_to_output = 1'b1;
end
end
always @(posedge clk) begin
if (rst) begin
m_ip_payload_axis_tvalid_reg <= 1'b0;
m_ip_payload_axis_tready_int_reg <= 1'b0;
temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
end else begin
m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next;
m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early;
temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next;
end
// datapath
if (store_ip_payload_int_to_output) begin
m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
end else if (store_ip_payload_axis_temp_to_output) begin
m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg;
m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg;
m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg;
end
if (store_ip_payload_int_to_temp) begin
temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
end
end
endmodule

View file

@ -8,10 +8,20 @@ set_property board_part $boardName [current_project]
create_ip -name clk_wiz -vendor xilinx.com -library ip -module_name $ipName
set_property -dict [list CONFIG.PRIM_IN_FREQ {200.000} CONFIG.CLKOUT2_USED {true} CONFIG.CLKOUT3_USED {true} CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {50} CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {25} CONFIG.CLKIN1_JITTER_PS {50.0} CONFIG.MMCM_DIVCLK_DIVIDE {1} CONFIG.MMCM_CLKFBOUT_MULT_F {5.000} CONFIG.MMCM_CLKIN1_PERIOD {5.000} CONFIG.MMCM_CLKIN2_PERIOD {10.0} CONFIG.MMCM_CLKOUT0_DIVIDE_F {20.000} CONFIG.MMCM_CLKOUT1_DIVIDE {40} CONFIG.MMCM_CLKOUT2_DIVIDE {10} CONFIG.NUM_OUT_CLKS {3} CONFIG.CLKOUT1_JITTER {129.198} CONFIG.CLKOUT1_PHASE_ERROR {89.971} CONFIG.CLKOUT2_JITTER {148.629} CONFIG.CLKOUT2_PHASE_ERROR {89.971} CONFIG.CLKOUT3_JITTER {112.316} CONFIG.CLKOUT3_PHASE_ERROR {89.971}] [get_ips $ipName]
set_property -dict [list CONFIG.PRIM_IN_FREQ {200.000} \
CONFIG.NUM_OUT_CLKS {4} \
CONFIG.CLKOUT2_USED {true} \
CONFIG.CLKOUT3_USED {true} \
CONFIG.CLKOUT4_USED {true} \
CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {50} \
CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {125} \
CONFIG.CLKOUT3_REQUESTED_OUT_FREQ {125} \
CONFIG.CLKOUT3_REQUESTED_PHASE {90.000} \
CONFIG.CLKIN1_JITTER_PS {50.0} \
] [get_ips $ipName]
generate_target {instantiation_template} [get_files ./$ipName.srcs/sources_1/ip/$ipName/$ipName.xci]
generate_target all [get_files ./$ipName.srcs/sources_1/ip/$ipName/$ipName.xci]
create_ip_run [get_files -of_objects [get_fileset sources_1] ./$ipName.srcs/sources_1/ip/$ipName/$ipName.xci]
launch_run -jobs 8 ${ipName}_synth_1
wait_on_run ${ipName}_synth_1
wait_on_run ${ipName}_synth_1