diff --git a/fpga/Makefile b/fpga/Makefile index c2813b760..d322545f8 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -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)) diff --git a/fpga/src/ariane_peripherals_xilinx.sv b/fpga/src/ariane_peripherals_xilinx.sv index 6090ac1fa..849b9dc30 100644 --- a/fpga/src/ariane_peripherals_xilinx.sv +++ b/fpga/src/ariane_peripherals_xilinx.sv @@ -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 diff --git a/fpga/src/ariane_xilinx.sv b/fpga/src/ariane_xilinx.sv index 0f8563ea0..dd4988e7d 100644 --- a/fpga/src/ariane_xilinx.sv +++ b/fpga/src/ariane_xilinx.sv @@ -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 ) diff --git a/fpga/src/arp.sv b/fpga/src/arp.sv new file mode 100644 index 000000000..7ebe5c5cb --- /dev/null +++ b/fpga/src/arp.sv @@ -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 diff --git a/fpga/src/arp_cache.sv b/fpga/src/arp_cache.sv new file mode 100644 index 000000000..e78fba634 --- /dev/null +++ b/fpga/src/arp_cache.sv @@ -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 diff --git a/fpga/src/arp_eth_rx.sv b/fpga/src/arp_eth_rx.sv new file mode 100644 index 000000000..381098e87 --- /dev/null +++ b/fpga/src/arp_eth_rx.sv @@ -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 diff --git a/fpga/src/arp_eth_tx.sv b/fpga/src/arp_eth_tx.sv new file mode 100644 index 000000000..888f3cf62 --- /dev/null +++ b/fpga/src/arp_eth_tx.sv @@ -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 diff --git a/fpga/src/axis_async_fifo.sv b/fpga/src/axis_async_fifo.sv new file mode 100644 index 000000000..697223aa8 --- /dev/null +++ b/fpga/src/axis_async_fifo.sv @@ -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 diff --git a/fpga/src/axis_fifo.sv b/fpga/src/axis_fifo.sv new file mode 100644 index 000000000..83d89900d --- /dev/null +++ b/fpga/src/axis_fifo.sv @@ -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 diff --git a/fpga/src/axis_gmii_rx.sv b/fpga/src/axis_gmii_rx.sv new file mode 100644 index 000000000..4995b0ad0 --- /dev/null +++ b/fpga/src/axis_gmii_rx.sv @@ -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 diff --git a/fpga/src/axis_gmii_tx.sv b/fpga/src/axis_gmii_tx.sv new file mode 100644 index 000000000..7fae55ec0 --- /dev/null +++ b/fpga/src/axis_gmii_tx.sv @@ -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 diff --git a/fpga/src/defines.sv b/fpga/src/defines.sv new file mode 100644 index 000000000..e69de29bb diff --git a/fpga/src/dualmem_widen.sv b/fpga/src/dualmem_widen.sv new file mode 100644 index 000000000..651447461 --- /dev/null +++ b/fpga/src/dualmem_widen.sv @@ -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 diff --git a/fpga/src/dualmem_widen8.sv b/fpga/src/dualmem_widen8.sv new file mode 100644 index 000000000..4f6c290b7 --- /dev/null +++ b/fpga/src/dualmem_widen8.sv @@ -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 diff --git a/fpga/src/eth_arb_mux.sv b/fpga/src/eth_arb_mux.sv new file mode 100644 index 000000000..1aecf6b25 --- /dev/null +++ b/fpga/src/eth_arb_mux.sv @@ -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 diff --git a/fpga/src/eth_axis_rx.sv b/fpga/src/eth_axis_rx.sv new file mode 100644 index 000000000..f2e399aaf --- /dev/null +++ b/fpga/src/eth_axis_rx.sv @@ -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 diff --git a/fpga/src/eth_axis_tx.sv b/fpga/src/eth_axis_tx.sv new file mode 100644 index 000000000..55388f278 --- /dev/null +++ b/fpga/src/eth_axis_tx.sv @@ -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 diff --git a/fpga/src/eth_mac_1g.sv b/fpga/src/eth_mac_1g.sv new file mode 100644 index 000000000..ceb134b66 --- /dev/null +++ b/fpga/src/eth_mac_1g.sv @@ -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 diff --git a/fpga/src/eth_mac_1g_rgmii.sv b/fpga/src/eth_mac_1g_rgmii.sv new file mode 100644 index 000000000..51d26d893 --- /dev/null +++ b/fpga/src/eth_mac_1g_rgmii.sv @@ -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 diff --git a/fpga/src/eth_mac_1g_rgmii_fifo.sv b/fpga/src/eth_mac_1g_rgmii_fifo.sv new file mode 100644 index 000000000..3c47bd94d --- /dev/null +++ b/fpga/src/eth_mac_1g_rgmii_fifo.sv @@ -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 diff --git a/fpga/src/framing_top.sv b/fpga/src/framing_top.sv new file mode 100644 index 000000000..396134987 --- /dev/null +++ b/fpga/src/framing_top.sv @@ -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 diff --git a/fpga/src/iddr.sv b/fpga/src/iddr.sv new file mode 100644 index 000000000..473a06d35 --- /dev/null +++ b/fpga/src/iddr.sv @@ -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 diff --git a/fpga/src/ip.sv b/fpga/src/ip.sv new file mode 100644 index 000000000..6c884ce35 --- /dev/null +++ b/fpga/src/ip.sv @@ -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 diff --git a/fpga/src/ip_arb_mux.sv b/fpga/src/ip_arb_mux.sv new file mode 100644 index 000000000..ab000454c --- /dev/null +++ b/fpga/src/ip_arb_mux.sv @@ -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 diff --git a/fpga/src/ip_complete.sv b/fpga/src/ip_complete.sv new file mode 100644 index 000000000..f16d697b3 --- /dev/null +++ b/fpga/src/ip_complete.sv @@ -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 diff --git a/fpga/src/ip_eth_rx.sv b/fpga/src/ip_eth_rx.sv new file mode 100644 index 000000000..93fce01a2 --- /dev/null +++ b/fpga/src/ip_eth_rx.sv @@ -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 diff --git a/fpga/src/ip_eth_tx.sv b/fpga/src/ip_eth_tx.sv new file mode 100644 index 000000000..010eed35c --- /dev/null +++ b/fpga/src/ip_eth_tx.sv @@ -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 diff --git a/fpga/src/oddr.sv b/fpga/src/oddr.sv new file mode 100644 index 000000000..db2d14429 --- /dev/null +++ b/fpga/src/oddr.sv @@ -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 diff --git a/fpga/src/priority_encoder.sv b/fpga/src/priority_encoder.sv new file mode 100644 index 000000000..e40b27293 --- /dev/null +++ b/fpga/src/priority_encoder.sv @@ -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 diff --git a/fpga/src/rgmii_arbiter.sv b/fpga/src/rgmii_arbiter.sv new file mode 100644 index 000000000..171615d98 --- /dev/null +++ b/fpga/src/rgmii_arbiter.sv @@ -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 diff --git a/fpga/src/rgmii_core.sv b/fpga/src/rgmii_core.sv new file mode 100644 index 000000000..e46335aaa --- /dev/null +++ b/fpga/src/rgmii_core.sv @@ -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 diff --git a/fpga/src/rgmii_lfsr.sv b/fpga/src/rgmii_lfsr.sv new file mode 100644 index 000000000..dc35add5c --- /dev/null +++ b/fpga/src/rgmii_lfsr.sv @@ -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 diff --git a/fpga/src/rgmii_phy_if.sv b/fpga/src/rgmii_phy_if.sv new file mode 100644 index 000000000..15f5943f5 --- /dev/null +++ b/fpga/src/rgmii_phy_if.sv @@ -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 diff --git a/fpga/src/rgmii_soc.sv b/fpga/src/rgmii_soc.sv new file mode 100644 index 000000000..a582ad36d --- /dev/null +++ b/fpga/src/rgmii_soc.sv @@ -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 diff --git a/fpga/src/ssio_ddr_in.sv b/fpga/src/ssio_ddr_in.sv new file mode 100644 index 000000000..4c2f6f421 --- /dev/null +++ b/fpga/src/ssio_ddr_in.sv @@ -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 diff --git a/fpga/src/sync_reset.sv b/fpga/src/sync_reset.sv new file mode 100644 index 000000000..acbcf1c6e --- /dev/null +++ b/fpga/src/sync_reset.sv @@ -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 diff --git a/fpga/src/udp.sv b/fpga/src/udp.sv new file mode 100644 index 000000000..94e6ad614 --- /dev/null +++ b/fpga/src/udp.sv @@ -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 diff --git a/fpga/src/udp_checksum_gen.sv b/fpga/src/udp_checksum_gen.sv new file mode 100644 index 000000000..41c5f1591 --- /dev/null +++ b/fpga/src/udp_checksum_gen.sv @@ -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 diff --git a/fpga/src/udp_complete.sv b/fpga/src/udp_complete.sv new file mode 100644 index 000000000..3ff5816cf --- /dev/null +++ b/fpga/src/udp_complete.sv @@ -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 diff --git a/fpga/src/udp_ip_rx.sv b/fpga/src/udp_ip_rx.sv new file mode 100644 index 000000000..c143d4d8b --- /dev/null +++ b/fpga/src/udp_ip_rx.sv @@ -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 diff --git a/fpga/src/udp_ip_tx.sv b/fpga/src/udp_ip_tx.sv new file mode 100644 index 000000000..096e9444c --- /dev/null +++ b/fpga/src/udp_ip_tx.sv @@ -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 diff --git a/fpga/xilinx/xlnx_clk_gen/tcl/run.tcl b/fpga/xilinx/xlnx_clk_gen/tcl/run.tcl index 246e4d2ea..094f9c778 100644 --- a/fpga/xilinx/xlnx_clk_gen/tcl/run.tcl +++ b/fpga/xilinx/xlnx_clk_gen/tcl/run.tcl @@ -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 \ No newline at end of file +wait_on_run ${ipName}_synth_1