mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 21:57:11 -04:00
Adapt rgmii ethernet core from Alex Forencich for Ariane on Genesys2
This commit is contained in:
parent
a3044dbfce
commit
ef37d20fad
42 changed files with 11724 additions and 305 deletions
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -536,9 +536,9 @@ xlnx_axi_clock_converter i_xlnx_axi_clock_converter_ddr (
|
|||
);
|
||||
|
||||
xlnx_clk_gen i_xlnx_clk_gen (
|
||||
.clk_out1 ( clk ), // 50MHz
|
||||
.clk_out2 ( phy_tx_clk ), // 25 MHz
|
||||
.clk_out3 ( eth_clk ), // 100 MHz
|
||||
.clk_out1 ( clk ), // 50 MHz
|
||||
.clk_out2 ( phy_tx_clk ), // 125 MHz (for RGMII PHY)
|
||||
.clk_out3 ( eth_clk ), // 125 MHz quadrature
|
||||
.reset ( cpu_reset ),
|
||||
.locked ( pll_locked ),
|
||||
.clk_in1 ( ddr_clock_out )
|
||||
|
|
416
fpga/src/arp.sv
Normal file
416
fpga/src/arp.sv
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* ARP block for IPv4, ethernet frame interface
|
||||
*/
|
||||
module arp #(
|
||||
parameter CACHE_ADDR_WIDTH = 9,
|
||||
parameter REQUEST_RETRY_COUNT = 4,
|
||||
parameter REQUEST_RETRY_INTERVAL = 125000000*2,
|
||||
parameter REQUEST_TIMEOUT = 125000000*30
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame input
|
||||
*/
|
||||
input wire s_eth_hdr_valid,
|
||||
output wire s_eth_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [7:0] s_eth_payload_axis_tdata,
|
||||
input wire s_eth_payload_axis_tvalid,
|
||||
output wire s_eth_payload_axis_tready,
|
||||
input wire s_eth_payload_axis_tlast,
|
||||
input wire s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [7:0] m_eth_payload_axis_tdata,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire m_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* ARP requests
|
||||
*/
|
||||
input wire arp_request_valid,
|
||||
output wire arp_request_ready,
|
||||
input wire [31:0] arp_request_ip,
|
||||
output wire arp_response_valid,
|
||||
input wire arp_response_ready,
|
||||
output wire arp_response_error,
|
||||
output wire [47:0] arp_response_mac,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [47:0] local_mac,
|
||||
input wire [31:0] local_ip,
|
||||
input wire [31:0] gateway_ip,
|
||||
input wire [31:0] subnet_mask,
|
||||
input wire clear_cache
|
||||
);
|
||||
|
||||
localparam [15:0]
|
||||
ARP_OPER_ARP_REQUEST = 16'h0001,
|
||||
ARP_OPER_ARP_REPLY = 16'h0002,
|
||||
ARP_OPER_INARP_REQUEST = 16'h0008,
|
||||
ARP_OPER_INARP_REPLY = 16'h0009;
|
||||
|
||||
wire incoming_frame_valid;
|
||||
reg incoming_frame_ready;
|
||||
wire [47:0] incoming_eth_dest_mac;
|
||||
wire [47:0] incoming_eth_src_mac;
|
||||
wire [15:0] incoming_eth_type;
|
||||
wire [15:0] incoming_arp_htype;
|
||||
wire [15:0] incoming_arp_ptype;
|
||||
wire [7:0] incoming_arp_hlen;
|
||||
wire [7:0] incoming_arp_plen;
|
||||
wire [15:0] incoming_arp_oper;
|
||||
wire [47:0] incoming_arp_sha;
|
||||
wire [31:0] incoming_arp_spa;
|
||||
wire [47:0] incoming_arp_tha;
|
||||
wire [31:0] incoming_arp_tpa;
|
||||
|
||||
/*
|
||||
* ARP frame processing
|
||||
*/
|
||||
arp_eth_rx
|
||||
arp_eth_rx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// Ethernet frame input
|
||||
.s_eth_hdr_valid(s_eth_hdr_valid),
|
||||
.s_eth_hdr_ready(s_eth_hdr_ready),
|
||||
.s_eth_dest_mac(s_eth_dest_mac),
|
||||
.s_eth_src_mac(s_eth_src_mac),
|
||||
.s_eth_type(s_eth_type),
|
||||
.s_eth_payload_axis_tdata(s_eth_payload_axis_tdata),
|
||||
.s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid),
|
||||
.s_eth_payload_axis_tready(s_eth_payload_axis_tready),
|
||||
.s_eth_payload_axis_tlast(s_eth_payload_axis_tlast),
|
||||
.s_eth_payload_axis_tuser(s_eth_payload_axis_tuser),
|
||||
// ARP frame output
|
||||
.m_frame_valid(incoming_frame_valid),
|
||||
.m_frame_ready(incoming_frame_ready),
|
||||
.m_eth_dest_mac(incoming_eth_dest_mac),
|
||||
.m_eth_src_mac(incoming_eth_src_mac),
|
||||
.m_eth_type(incoming_eth_type),
|
||||
.m_arp_htype(incoming_arp_htype),
|
||||
.m_arp_ptype(incoming_arp_ptype),
|
||||
.m_arp_hlen(incoming_arp_hlen),
|
||||
.m_arp_plen(incoming_arp_plen),
|
||||
.m_arp_oper(incoming_arp_oper),
|
||||
.m_arp_sha(incoming_arp_sha),
|
||||
.m_arp_spa(incoming_arp_spa),
|
||||
.m_arp_tha(incoming_arp_tha),
|
||||
.m_arp_tpa(incoming_arp_tpa),
|
||||
// Status signals
|
||||
.busy(),
|
||||
.error_header_early_termination(),
|
||||
.error_invalid_header()
|
||||
);
|
||||
|
||||
reg outgoing_frame_valid_reg = 1'b0, outgoing_frame_valid_next;
|
||||
wire outgoing_frame_ready;
|
||||
reg [47:0] outgoing_eth_dest_mac_reg = 48'd0, outgoing_eth_dest_mac_next;
|
||||
reg [15:0] outgoing_arp_oper_reg = 16'd0, outgoing_arp_oper_next;
|
||||
reg [47:0] outgoing_arp_tha_reg = 48'd0, outgoing_arp_tha_next;
|
||||
reg [31:0] outgoing_arp_tpa_reg = 32'd0, outgoing_arp_tpa_next;
|
||||
|
||||
arp_eth_tx
|
||||
arp_eth_tx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// ARP frame input
|
||||
.s_frame_valid(outgoing_frame_valid_reg),
|
||||
.s_frame_ready(outgoing_frame_ready),
|
||||
.s_eth_dest_mac(outgoing_eth_dest_mac_reg),
|
||||
.s_eth_src_mac(local_mac),
|
||||
.s_eth_type(16'h0806),
|
||||
.s_arp_htype(16'h0001),
|
||||
.s_arp_ptype(16'h0800),
|
||||
.s_arp_oper(outgoing_arp_oper_reg),
|
||||
.s_arp_sha(local_mac),
|
||||
.s_arp_spa(local_ip),
|
||||
.s_arp_tha(outgoing_arp_tha_reg),
|
||||
.s_arp_tpa(outgoing_arp_tpa_reg),
|
||||
// Ethernet frame output
|
||||
.m_eth_hdr_valid(m_eth_hdr_valid),
|
||||
.m_eth_hdr_ready(m_eth_hdr_ready),
|
||||
.m_eth_dest_mac(m_eth_dest_mac),
|
||||
.m_eth_src_mac(m_eth_src_mac),
|
||||
.m_eth_type(m_eth_type),
|
||||
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
|
||||
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
|
||||
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
|
||||
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
|
||||
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser),
|
||||
// Status signals
|
||||
.busy()
|
||||
);
|
||||
|
||||
reg cache_query_request_valid_reg = 1'b0, cache_query_request_valid_next;
|
||||
reg [31:0] cache_query_request_ip_reg = 32'd0, cache_query_request_ip_next;
|
||||
wire cache_query_response_valid;
|
||||
wire cache_query_response_error;
|
||||
wire [47:0] cache_query_response_mac;
|
||||
|
||||
reg cache_write_request_valid_reg = 1'b0, cache_write_request_valid_next;
|
||||
reg [31:0] cache_write_request_ip_reg = 32'd0, cache_write_request_ip_next;
|
||||
reg [47:0] cache_write_request_mac_reg = 48'd0, cache_write_request_mac_next;
|
||||
|
||||
/*
|
||||
* ARP cache
|
||||
*/
|
||||
arp_cache #(
|
||||
.CACHE_ADDR_WIDTH(CACHE_ADDR_WIDTH)
|
||||
)
|
||||
arp_cache_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// Query cache
|
||||
.query_request_valid(cache_query_request_valid_reg),
|
||||
.query_request_ready(),
|
||||
.query_request_ip(cache_query_request_ip_reg),
|
||||
.query_response_valid(cache_query_response_valid),
|
||||
.query_response_ready(1'b1),
|
||||
.query_response_error(cache_query_response_error),
|
||||
.query_response_mac(cache_query_response_mac),
|
||||
// Write cache
|
||||
.write_request_valid(cache_write_request_valid_reg),
|
||||
.write_request_ready(),
|
||||
.write_request_ip(cache_write_request_ip_reg),
|
||||
.write_request_mac(cache_write_request_mac_reg),
|
||||
// Configuration
|
||||
.clear_cache(clear_cache)
|
||||
);
|
||||
|
||||
reg arp_request_operation_reg = 1'b0, arp_request_operation_next;
|
||||
|
||||
reg arp_request_ready_reg = 1'b0, arp_request_ready_next;
|
||||
reg [31:0] arp_request_ip_reg = 32'd0, arp_request_ip_next;
|
||||
|
||||
reg arp_response_valid_reg = 1'b0, arp_response_valid_next;
|
||||
reg arp_response_error_reg = 1'b0, arp_response_error_next;
|
||||
reg [47:0] arp_response_mac_reg = 48'd0, arp_response_mac_next;
|
||||
|
||||
reg [5:0] arp_request_retry_cnt_reg = 6'd0, arp_request_retry_cnt_next;
|
||||
reg [35:0] arp_request_timer_reg = 36'd0, arp_request_timer_next;
|
||||
|
||||
assign arp_request_ready = arp_request_ready_reg;
|
||||
|
||||
assign arp_response_valid = arp_response_valid_reg;
|
||||
assign arp_response_error = arp_response_error_reg;
|
||||
assign arp_response_mac = arp_response_mac_reg;
|
||||
|
||||
always @* begin
|
||||
incoming_frame_ready = 1'b0;
|
||||
|
||||
outgoing_frame_valid_next = outgoing_frame_valid_reg && !outgoing_frame_ready;
|
||||
outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg;
|
||||
outgoing_arp_oper_next = outgoing_arp_oper_reg;
|
||||
outgoing_arp_tha_next = outgoing_arp_tha_reg;
|
||||
outgoing_arp_tpa_next = outgoing_arp_tpa_reg;
|
||||
|
||||
cache_query_request_valid_next = 1'b0;
|
||||
cache_query_request_ip_next = cache_query_request_ip_reg;
|
||||
|
||||
cache_write_request_valid_next = 1'b0;
|
||||
cache_write_request_mac_next = cache_write_request_mac_reg;
|
||||
cache_write_request_ip_next = cache_write_request_ip_reg;
|
||||
|
||||
arp_request_ready_next = 1'b0;
|
||||
arp_request_ip_next = arp_request_ip_reg;
|
||||
arp_request_operation_next = arp_request_operation_reg;
|
||||
arp_request_retry_cnt_next = arp_request_retry_cnt_reg;
|
||||
arp_request_timer_next = arp_request_timer_reg;
|
||||
arp_response_valid_next = arp_response_valid_reg && !arp_response_ready;
|
||||
arp_response_error_next = 1'b0;
|
||||
arp_response_mac_next = 48'd0;
|
||||
|
||||
// manage incoming frames
|
||||
incoming_frame_ready = outgoing_frame_ready;
|
||||
if (incoming_frame_valid && incoming_frame_ready) begin
|
||||
if (incoming_eth_type == 16'h0806 && incoming_arp_htype == 16'h0001 && incoming_arp_ptype == 16'h0800) begin
|
||||
// store sender addresses in cache
|
||||
cache_write_request_valid_next = 1'b1;
|
||||
cache_write_request_ip_next = incoming_arp_spa;
|
||||
cache_write_request_mac_next = incoming_arp_sha;
|
||||
if (incoming_arp_oper == ARP_OPER_ARP_REQUEST) begin
|
||||
// ARP request
|
||||
if (incoming_arp_tpa == local_ip) begin
|
||||
// send reply frame to valid incoming request
|
||||
outgoing_frame_valid_next = 1'b1;
|
||||
outgoing_eth_dest_mac_next = incoming_eth_src_mac;
|
||||
outgoing_arp_oper_next = ARP_OPER_ARP_REPLY;
|
||||
outgoing_arp_tha_next = incoming_arp_sha;
|
||||
outgoing_arp_tpa_next = incoming_arp_spa;
|
||||
end
|
||||
end else if (incoming_arp_oper == ARP_OPER_INARP_REPLY) begin
|
||||
// INARP request
|
||||
if (incoming_arp_tha == local_mac) begin
|
||||
// send reply frame to valid incoming request
|
||||
outgoing_frame_valid_next = 1'b1;
|
||||
outgoing_eth_dest_mac_next = incoming_eth_src_mac;
|
||||
outgoing_arp_oper_next = ARP_OPER_INARP_REPLY;
|
||||
outgoing_arp_tha_next = incoming_arp_sha;
|
||||
outgoing_arp_tpa_next = incoming_arp_spa;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// manage ARP lookup requests
|
||||
if (arp_request_operation_reg) begin
|
||||
arp_request_ready_next = 1'b0;
|
||||
cache_query_request_valid_next = 1'b1;
|
||||
arp_request_timer_next = arp_request_timer_reg - 1;
|
||||
// if we got a response, it will go in the cache, so when the query succeds, we're done
|
||||
if (cache_query_response_valid && !cache_query_response_error) begin
|
||||
arp_request_operation_next = 1'b0;
|
||||
cache_query_request_valid_next = 1'b0;
|
||||
arp_response_valid_next = 1'b1;
|
||||
arp_response_error_next = 1'b0;
|
||||
arp_response_mac_next = cache_query_response_mac;
|
||||
end
|
||||
// timer timeout
|
||||
if (arp_request_timer_reg == 0) begin
|
||||
if (arp_request_retry_cnt_reg > 0) begin
|
||||
// have more retries
|
||||
// send ARP request frame
|
||||
outgoing_frame_valid_next = 1'b1;
|
||||
outgoing_eth_dest_mac_next = 48'hffffffffffff;
|
||||
outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST;
|
||||
outgoing_arp_tha_next = 48'h000000000000;
|
||||
outgoing_arp_tpa_next = arp_request_ip_reg;
|
||||
arp_request_retry_cnt_next = arp_request_retry_cnt_reg - 1;
|
||||
if (arp_request_retry_cnt_reg > 1) begin
|
||||
arp_request_timer_next = REQUEST_RETRY_INTERVAL;
|
||||
end else begin
|
||||
arp_request_timer_next = REQUEST_TIMEOUT;
|
||||
end
|
||||
end else begin
|
||||
// out of retries
|
||||
arp_request_operation_next = 1'b0;
|
||||
arp_response_valid_next = 1'b1;
|
||||
arp_response_error_next = 1'b1;
|
||||
cache_query_request_valid_next = 1'b0;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
arp_request_ready_next = !arp_response_valid_next;
|
||||
if (cache_query_request_valid_reg) begin
|
||||
cache_query_request_valid_next = 1'b1;
|
||||
if (cache_query_response_valid) begin
|
||||
if (cache_query_response_error) begin
|
||||
arp_request_operation_next = 1'b1;
|
||||
// send ARP request frame
|
||||
outgoing_frame_valid_next = 1'b1;
|
||||
outgoing_eth_dest_mac_next = 48'hffffffffffff;
|
||||
outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST;
|
||||
outgoing_arp_tha_next = 48'h000000000000;
|
||||
outgoing_arp_tpa_next = arp_request_ip_reg;
|
||||
arp_request_retry_cnt_next = REQUEST_RETRY_COUNT-1;
|
||||
arp_request_timer_next = REQUEST_RETRY_INTERVAL;
|
||||
end else begin
|
||||
cache_query_request_valid_next = 1'b0;
|
||||
arp_response_valid_next = 1'b1;
|
||||
arp_response_error_next = 1'b0;
|
||||
arp_response_mac_next = cache_query_response_mac;
|
||||
end
|
||||
end
|
||||
end else if (arp_request_valid && arp_request_ready) begin
|
||||
if (~(arp_request_ip | subnet_mask) == 0) begin
|
||||
// broadcast address
|
||||
// (all bits in request IP set where subnet mask is clear)
|
||||
arp_response_valid_next = 1'b1;
|
||||
arp_response_error_next = 1'b0;
|
||||
arp_response_mac_next = 48'hffffffffffff;
|
||||
end else if (((arp_request_ip ^ gateway_ip) & subnet_mask) == 0) begin
|
||||
// within subnet, look up IP directly
|
||||
// (no bits differ between request IP and gateway IP where subnet mask is set)
|
||||
cache_query_request_valid_next = 1'b1;
|
||||
cache_query_request_ip_next = arp_request_ip;
|
||||
arp_request_ip_next = arp_request_ip;
|
||||
end else begin
|
||||
// outside of subnet, so look up gateway address
|
||||
cache_query_request_valid_next = 1'b1;
|
||||
cache_query_request_ip_next = gateway_ip;
|
||||
arp_request_ip_next = gateway_ip;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
outgoing_frame_valid_reg <= 1'b0;
|
||||
cache_query_request_valid_reg <= 1'b0;
|
||||
cache_write_request_valid_reg <= 1'b0;
|
||||
arp_request_ready_reg <= 1'b0;
|
||||
arp_request_operation_reg <= 1'b0;
|
||||
arp_request_retry_cnt_reg <= 6'd0;
|
||||
arp_request_timer_reg <= 36'd0;
|
||||
arp_response_valid_reg <= 1'b0;
|
||||
end else begin
|
||||
outgoing_frame_valid_reg <= outgoing_frame_valid_next;
|
||||
cache_query_request_valid_reg <= cache_query_request_valid_next;
|
||||
cache_write_request_valid_reg <= cache_write_request_valid_next;
|
||||
arp_request_ready_reg <= arp_request_ready_next;
|
||||
arp_request_operation_reg <= arp_request_operation_next;
|
||||
arp_request_retry_cnt_reg <= arp_request_retry_cnt_next;
|
||||
arp_request_timer_reg <= arp_request_timer_next;
|
||||
arp_response_valid_reg <= arp_response_valid_next;
|
||||
end
|
||||
|
||||
cache_query_request_ip_reg <= cache_query_request_ip_next;
|
||||
outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next;
|
||||
outgoing_arp_oper_reg <= outgoing_arp_oper_next;
|
||||
outgoing_arp_tha_reg <= outgoing_arp_tha_next;
|
||||
outgoing_arp_tpa_reg <= outgoing_arp_tpa_next;
|
||||
cache_write_request_mac_reg <= cache_write_request_mac_next;
|
||||
cache_write_request_ip_reg <= cache_write_request_ip_next;
|
||||
arp_request_ip_reg <= arp_request_ip_next;
|
||||
arp_response_error_reg <= arp_response_error_next;
|
||||
arp_response_mac_reg <= arp_response_mac_next;
|
||||
end
|
||||
|
||||
endmodule
|
218
fpga/src/arp_cache.sv
Normal file
218
fpga/src/arp_cache.sv
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* ARP cache
|
||||
*/
|
||||
module arp_cache #(
|
||||
parameter CACHE_ADDR_WIDTH = 9
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Cache query
|
||||
*/
|
||||
input wire query_request_valid,
|
||||
output wire query_request_ready,
|
||||
input wire [31:0] query_request_ip,
|
||||
|
||||
output wire query_response_valid,
|
||||
input wire query_response_ready,
|
||||
output wire query_response_error,
|
||||
output wire [47:0] query_response_mac,
|
||||
|
||||
/*
|
||||
* Cache write
|
||||
*/
|
||||
input wire write_request_valid,
|
||||
output wire write_request_ready,
|
||||
input wire [31:0] write_request_ip,
|
||||
input wire [47:0] write_request_mac,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire clear_cache
|
||||
);
|
||||
|
||||
reg mem_write = 0;
|
||||
reg store_query = 0;
|
||||
reg store_write = 0;
|
||||
|
||||
reg query_ip_valid_reg = 0, query_ip_valid_next;
|
||||
reg [31:0] query_ip_reg = 0;
|
||||
reg write_ip_valid_reg = 0, write_ip_valid_next;
|
||||
reg [31:0] write_ip_reg = 0;
|
||||
reg [47:0] write_mac_reg = 0;
|
||||
|
||||
reg [CACHE_ADDR_WIDTH-1:0] wr_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, wr_ptr_next;
|
||||
reg [CACHE_ADDR_WIDTH-1:0] rd_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, rd_ptr_next;
|
||||
|
||||
reg valid_mem[(2**CACHE_ADDR_WIDTH)-1:0];
|
||||
reg [31:0] ip_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0];
|
||||
reg [47:0] mac_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0];
|
||||
|
||||
reg query_request_ready_reg = 0, query_request_ready_next;
|
||||
|
||||
reg query_response_valid_reg = 0, query_response_valid_next;
|
||||
reg query_response_error_reg = 0, query_response_error_next;
|
||||
reg [47:0] query_response_mac_reg = 0;
|
||||
|
||||
reg write_request_ready_reg = 0, write_request_ready_next;
|
||||
|
||||
wire [31:0] query_request_hash;
|
||||
wire [31:0] write_request_hash;
|
||||
|
||||
assign query_request_ready = query_request_ready_reg;
|
||||
|
||||
assign query_response_valid = query_response_valid_reg;
|
||||
assign query_response_error = query_response_error_reg;
|
||||
assign query_response_mac = query_response_mac_reg;
|
||||
|
||||
assign write_request_ready = write_request_ready_reg;
|
||||
|
||||
rgmii_lfsr #(
|
||||
.LFSR_WIDTH(32),
|
||||
.LFSR_POLY(32'h4c11db7),
|
||||
.LFSR_CONFIG("GALOIS"),
|
||||
.LFSR_FEED_FORWARD(0),
|
||||
.REVERSE(1),
|
||||
.DATA_WIDTH(32),
|
||||
.STYLE("AUTO")
|
||||
)
|
||||
rd_hash (
|
||||
.data_in(query_request_ip),
|
||||
.state_in(32'hffffffff),
|
||||
.data_out(),
|
||||
.state_out(query_request_hash)
|
||||
);
|
||||
|
||||
rgmii_lfsr #(
|
||||
.LFSR_WIDTH(32),
|
||||
.LFSR_POLY(32'h4c11db7),
|
||||
.LFSR_CONFIG("GALOIS"),
|
||||
.LFSR_FEED_FORWARD(0),
|
||||
.REVERSE(1),
|
||||
.DATA_WIDTH(32),
|
||||
.STYLE("AUTO")
|
||||
)
|
||||
wr_hash (
|
||||
.data_in(write_request_ip),
|
||||
.state_in(32'hffffffff),
|
||||
.data_out(),
|
||||
.state_out(write_request_hash)
|
||||
);
|
||||
|
||||
always @* begin
|
||||
mem_write = 1'b0;
|
||||
store_query = 1'b0;
|
||||
store_write = 1'b0;
|
||||
|
||||
wr_ptr_next = wr_ptr_reg;
|
||||
rd_ptr_next = rd_ptr_reg;
|
||||
|
||||
query_ip_valid_next = query_ip_valid_reg;
|
||||
|
||||
query_request_ready_next = ~query_ip_valid_reg || ~query_request_valid || query_response_ready;
|
||||
|
||||
query_response_valid_next = query_response_valid_reg & ~query_response_ready;
|
||||
query_response_error_next = query_response_error_reg;
|
||||
|
||||
if (query_ip_valid_reg && (~query_request_valid || query_response_ready)) begin
|
||||
query_response_valid_next = 1;
|
||||
query_ip_valid_next = 0;
|
||||
if (valid_mem[rd_ptr_reg] && ip_addr_mem[rd_ptr_reg] == query_ip_reg) begin
|
||||
query_response_error_next = 0;
|
||||
end else begin
|
||||
query_response_error_next = 1;
|
||||
end
|
||||
end
|
||||
|
||||
if (query_request_valid && query_request_ready && (~query_ip_valid_reg || ~query_request_valid || query_response_ready)) begin
|
||||
store_query = 1;
|
||||
query_ip_valid_next = 1;
|
||||
rd_ptr_next = query_request_hash[CACHE_ADDR_WIDTH-1:0];
|
||||
end
|
||||
|
||||
write_ip_valid_next = write_ip_valid_reg;
|
||||
|
||||
write_request_ready_next = 1'b1;
|
||||
|
||||
if (write_ip_valid_reg) begin
|
||||
write_ip_valid_next = 0;
|
||||
mem_write = 1;
|
||||
end
|
||||
|
||||
if (write_request_valid && write_request_ready) begin
|
||||
store_write = 1;
|
||||
write_ip_valid_next = 1;
|
||||
wr_ptr_next = write_request_hash[CACHE_ADDR_WIDTH-1:0];
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
query_ip_valid_reg <= 1'b0;
|
||||
query_request_ready_reg <= 1'b0;
|
||||
query_response_valid_reg <= 1'b0;
|
||||
write_ip_valid_reg <= 1'b0;
|
||||
write_request_ready_reg <= 1'b0;
|
||||
end else begin
|
||||
query_ip_valid_reg <= query_ip_valid_next;
|
||||
query_request_ready_reg <= query_request_ready_next;
|
||||
query_response_valid_reg <= query_response_valid_next;
|
||||
write_ip_valid_reg <= write_ip_valid_next;
|
||||
write_request_ready_reg <= write_request_ready_next;
|
||||
end
|
||||
|
||||
query_response_error_reg <= query_response_error_next;
|
||||
|
||||
if (store_query) begin
|
||||
query_ip_reg <= query_request_ip;
|
||||
end
|
||||
|
||||
if (store_write) begin
|
||||
write_ip_reg <= write_request_ip;
|
||||
write_mac_reg <= write_request_mac;
|
||||
end
|
||||
|
||||
wr_ptr_reg <= wr_ptr_next;
|
||||
rd_ptr_reg <= rd_ptr_next;
|
||||
|
||||
query_response_mac_reg <= mac_addr_mem[rd_ptr_reg];
|
||||
|
||||
if (mem_write) begin
|
||||
valid_mem[wr_ptr_reg] <= 1'b1;
|
||||
ip_addr_mem[wr_ptr_reg] <= write_ip_reg;
|
||||
mac_addr_mem[wr_ptr_reg] <= write_mac_reg;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
391
fpga/src/arp_eth_rx.sv
Normal file
391
fpga/src/arp_eth_rx.sv
Normal file
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* ARP ethernet frame receiver (Ethernet frame in, ARP frame out)
|
||||
*/
|
||||
module arp_eth_rx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame input
|
||||
*/
|
||||
input wire s_eth_hdr_valid,
|
||||
output wire s_eth_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [7:0] s_eth_payload_axis_tdata,
|
||||
input wire s_eth_payload_axis_tvalid,
|
||||
output wire s_eth_payload_axis_tready,
|
||||
input wire s_eth_payload_axis_tlast,
|
||||
input wire s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* ARP frame output
|
||||
*/
|
||||
output wire m_frame_valid,
|
||||
input wire m_frame_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [15:0] m_arp_htype,
|
||||
output wire [15:0] m_arp_ptype,
|
||||
output wire [7:0] m_arp_hlen,
|
||||
output wire [7:0] m_arp_plen,
|
||||
output wire [15:0] m_arp_oper,
|
||||
output wire [47:0] m_arp_sha,
|
||||
output wire [31:0] m_arp_spa,
|
||||
output wire [47:0] m_arp_tha,
|
||||
output wire [31:0] m_arp_tpa,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy,
|
||||
output wire error_header_early_termination,
|
||||
output wire error_invalid_header
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
ARP Frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype (0x0806) 2 octets
|
||||
HTYPE (1) 2 octets
|
||||
PTYPE (0x0800) 2 octets
|
||||
HLEN (6) 1 octets
|
||||
PLEN (4) 1 octets
|
||||
OPER 2 octets
|
||||
SHA Sender MAC 6 octets
|
||||
SPA Sender IP 4 octets
|
||||
THA Target MAC 6 octets
|
||||
TPA Target IP 4 octets
|
||||
|
||||
This module receives an Ethernet frame with header fields in parallel and
|
||||
payload on an AXI stream interface, decodes the ARP packet fields, and
|
||||
produces the frame fields in parallel.
|
||||
|
||||
*/
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_READ_HEADER = 3'd1,
|
||||
STATE_WAIT_LAST = 3'd2;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_eth_hdr;
|
||||
reg store_arp_htype_0;
|
||||
reg store_arp_htype_1;
|
||||
reg store_arp_ptype_0;
|
||||
reg store_arp_ptype_1;
|
||||
reg store_arp_hlen;
|
||||
reg store_arp_plen;
|
||||
reg store_arp_oper_0;
|
||||
reg store_arp_oper_1;
|
||||
reg store_arp_sha_0;
|
||||
reg store_arp_sha_1;
|
||||
reg store_arp_sha_2;
|
||||
reg store_arp_sha_3;
|
||||
reg store_arp_sha_4;
|
||||
reg store_arp_sha_5;
|
||||
reg store_arp_spa_0;
|
||||
reg store_arp_spa_1;
|
||||
reg store_arp_spa_2;
|
||||
reg store_arp_spa_3;
|
||||
reg store_arp_tha_0;
|
||||
reg store_arp_tha_1;
|
||||
reg store_arp_tha_2;
|
||||
reg store_arp_tha_3;
|
||||
reg store_arp_tha_4;
|
||||
reg store_arp_tha_5;
|
||||
reg store_arp_tpa_0;
|
||||
reg store_arp_tpa_1;
|
||||
reg store_arp_tpa_2;
|
||||
reg store_arp_tpa_3;
|
||||
|
||||
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
|
||||
|
||||
reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next;
|
||||
reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next;
|
||||
|
||||
reg m_frame_valid_reg = 1'b0, m_frame_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
reg [15:0] m_arp_htype_reg = 16'd0;
|
||||
reg [15:0] m_arp_ptype_reg = 16'd0;
|
||||
reg [7:0] m_arp_hlen_reg = 8'd0;
|
||||
reg [7:0] m_arp_plen_reg = 8'd0;
|
||||
reg [15:0] m_arp_oper_reg = 16'd0;
|
||||
reg [47:0] m_arp_sha_reg = 48'd0;
|
||||
reg [31:0] m_arp_spa_reg = 32'd0;
|
||||
reg [47:0] m_arp_tha_reg = 48'd0;
|
||||
reg [31:0] m_arp_tpa_reg = 32'd0;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
|
||||
reg error_invalid_header_reg = 1'b0, error_invalid_header_next;
|
||||
|
||||
assign s_eth_hdr_ready = s_eth_hdr_ready_reg;
|
||||
assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg;
|
||||
|
||||
assign m_frame_valid = m_frame_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
assign m_arp_htype = m_arp_htype_reg;
|
||||
assign m_arp_ptype = m_arp_ptype_reg;
|
||||
assign m_arp_hlen = m_arp_hlen_reg;
|
||||
assign m_arp_plen = m_arp_plen_reg;
|
||||
assign m_arp_oper = m_arp_oper_reg;
|
||||
assign m_arp_sha = m_arp_sha_reg;
|
||||
assign m_arp_spa = m_arp_spa_reg;
|
||||
assign m_arp_tha = m_arp_tha_reg;
|
||||
assign m_arp_tpa = m_arp_tpa_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
assign error_header_early_termination = error_header_early_termination_reg;
|
||||
assign error_invalid_header = error_invalid_header_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_eth_hdr_ready_next = 1'b0;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
|
||||
store_eth_hdr = 1'b0;
|
||||
store_arp_htype_0 = 1'b0;
|
||||
store_arp_htype_1 = 1'b0;
|
||||
store_arp_ptype_0 = 1'b0;
|
||||
store_arp_ptype_1 = 1'b0;
|
||||
store_arp_hlen = 1'b0;
|
||||
store_arp_plen = 1'b0;
|
||||
store_arp_oper_0 = 1'b0;
|
||||
store_arp_oper_1 = 1'b0;
|
||||
store_arp_sha_0 = 1'b0;
|
||||
store_arp_sha_1 = 1'b0;
|
||||
store_arp_sha_2 = 1'b0;
|
||||
store_arp_sha_3 = 1'b0;
|
||||
store_arp_sha_4 = 1'b0;
|
||||
store_arp_sha_5 = 1'b0;
|
||||
store_arp_spa_0 = 1'b0;
|
||||
store_arp_spa_1 = 1'b0;
|
||||
store_arp_spa_2 = 1'b0;
|
||||
store_arp_spa_3 = 1'b0;
|
||||
store_arp_tha_0 = 1'b0;
|
||||
store_arp_tha_1 = 1'b0;
|
||||
store_arp_tha_2 = 1'b0;
|
||||
store_arp_tha_3 = 1'b0;
|
||||
store_arp_tha_4 = 1'b0;
|
||||
store_arp_tha_5 = 1'b0;
|
||||
store_arp_tpa_0 = 1'b0;
|
||||
store_arp_tpa_1 = 1'b0;
|
||||
store_arp_tpa_2 = 1'b0;
|
||||
store_arp_tpa_3 = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
m_frame_valid_next = m_frame_valid_reg && !m_frame_ready;
|
||||
|
||||
error_header_early_termination_next = 1'b0;
|
||||
error_invalid_header_next = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for data
|
||||
frame_ptr_next = 8'd0;
|
||||
s_eth_hdr_ready_next = !m_frame_valid_next;
|
||||
|
||||
if (s_eth_hdr_ready && s_eth_hdr_valid) begin
|
||||
s_eth_hdr_ready_next = 1'b0;
|
||||
s_eth_payload_axis_tready_next = 1'b1;
|
||||
store_eth_hdr = 1'b1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_READ_HEADER: begin
|
||||
// read header state
|
||||
s_eth_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_eth_payload_axis_tvalid) begin
|
||||
// word transfer in - store it
|
||||
frame_ptr_next = frame_ptr_reg + 8'd1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
case (frame_ptr_reg)
|
||||
8'h00: store_arp_htype_1 = 1'b1;
|
||||
8'h01: store_arp_htype_0 = 1'b1;
|
||||
8'h02: store_arp_ptype_1 = 1'b1;
|
||||
8'h03: store_arp_ptype_0 = 1'b1;
|
||||
8'h04: store_arp_hlen = 1'b1;
|
||||
8'h05: store_arp_plen = 1'b1;
|
||||
8'h06: store_arp_oper_1 = 1'b1;
|
||||
8'h07: store_arp_oper_0 = 1'b1;
|
||||
8'h08: store_arp_sha_5 = 1'b1;
|
||||
8'h09: store_arp_sha_4 = 1'b1;
|
||||
8'h0A: store_arp_sha_3 = 1'b1;
|
||||
8'h0B: store_arp_sha_2 = 1'b1;
|
||||
8'h0C: store_arp_sha_1 = 1'b1;
|
||||
8'h0D: store_arp_sha_0 = 1'b1;
|
||||
8'h0E: store_arp_spa_3 = 1'b1;
|
||||
8'h0F: store_arp_spa_2 = 1'b1;
|
||||
8'h10: store_arp_spa_1 = 1'b1;
|
||||
8'h11: store_arp_spa_0 = 1'b1;
|
||||
8'h12: store_arp_tha_5 = 1'b1;
|
||||
8'h13: store_arp_tha_4 = 1'b1;
|
||||
8'h14: store_arp_tha_3 = 1'b1;
|
||||
8'h15: store_arp_tha_2 = 1'b1;
|
||||
8'h16: store_arp_tha_1 = 1'b1;
|
||||
8'h17: store_arp_tha_0 = 1'b1;
|
||||
8'h18: store_arp_tpa_3 = 1'b1;
|
||||
8'h19: store_arp_tpa_2 = 1'b1;
|
||||
8'h1A: store_arp_tpa_1 = 1'b1;
|
||||
8'h1B: begin
|
||||
store_arp_tpa_0 = 1'b1;
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
endcase
|
||||
if (s_eth_payload_axis_tlast) begin
|
||||
// end of frame
|
||||
if (frame_ptr_reg != 8'h1B) begin
|
||||
// don't have the whole header
|
||||
error_header_early_termination_next = 1'b1;
|
||||
end else if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin
|
||||
// lengths not valid
|
||||
error_invalid_header_next = 1'b1;
|
||||
end else begin
|
||||
// otherwise, transfer tuser
|
||||
m_frame_valid_next = !s_eth_payload_axis_tuser;
|
||||
end
|
||||
s_eth_hdr_ready_next = !m_frame_valid_next;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_READ_HEADER;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_LAST: begin
|
||||
// wait for end of frame; read and discard
|
||||
s_eth_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_eth_payload_axis_tvalid) begin
|
||||
if (s_eth_payload_axis_tlast) begin
|
||||
if (m_arp_hlen != 4'd6 || m_arp_plen != 4'd4) begin
|
||||
// lengths not valid
|
||||
error_invalid_header_next = 1'b1;
|
||||
end else begin
|
||||
// otherwise, transfer tuser
|
||||
m_frame_valid_next = !s_eth_payload_axis_tuser;
|
||||
end
|
||||
s_eth_hdr_ready_next = !m_frame_valid_next;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end else begin
|
||||
// wait for end of frame; read and discard
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 8'd0;
|
||||
s_eth_payload_axis_tready_reg <= 1'b0;
|
||||
m_frame_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
error_header_early_termination_reg <= 1'b0;
|
||||
error_invalid_header_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
|
||||
s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
m_frame_valid_reg <= m_frame_valid_next;
|
||||
|
||||
error_header_early_termination_reg <= error_header_early_termination_next;
|
||||
error_invalid_header_reg <= error_invalid_header_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_eth_hdr) begin
|
||||
m_eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
m_eth_src_mac_reg <= s_eth_src_mac;
|
||||
m_eth_type_reg <= s_eth_type;
|
||||
end
|
||||
|
||||
if (store_arp_htype_0) m_arp_htype_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_htype_1) m_arp_htype_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_ptype_0) m_arp_ptype_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_ptype_1) m_arp_ptype_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_hlen) m_arp_hlen_reg <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_plen) m_arp_plen_reg <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_oper_0) m_arp_oper_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_oper_1) m_arp_oper_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_sha_0) m_arp_sha_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_sha_1) m_arp_sha_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_sha_2) m_arp_sha_reg[23:16] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_sha_3) m_arp_sha_reg[31:24] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_sha_4) m_arp_sha_reg[39:32] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_sha_5) m_arp_sha_reg[47:40] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_spa_0) m_arp_spa_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_spa_1) m_arp_spa_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_spa_2) m_arp_spa_reg[23:16] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_spa_3) m_arp_spa_reg[31:24] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tha_0) m_arp_tha_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tha_1) m_arp_tha_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tha_2) m_arp_tha_reg[23:16] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tha_3) m_arp_tha_reg[31:24] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tha_4) m_arp_tha_reg[39:32] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tha_5) m_arp_tha_reg[47:40] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tpa_0) m_arp_tpa_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tpa_1) m_arp_tpa_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tpa_2) m_arp_tpa_reg[23:16] <= s_eth_payload_axis_tdata;
|
||||
if (store_arp_tpa_3) m_arp_tpa_reg[31:24] <= s_eth_payload_axis_tdata;
|
||||
end
|
||||
|
||||
endmodule
|
340
fpga/src/arp_eth_tx.sv
Normal file
340
fpga/src/arp_eth_tx.sv
Normal file
|
@ -0,0 +1,340 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* ARP ethernet frame transmitter (ARP frame in, Ethernet frame out)
|
||||
*/
|
||||
module arp_eth_tx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* ARP frame input
|
||||
*/
|
||||
input wire s_frame_valid,
|
||||
output wire s_frame_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [15:0] s_arp_htype,
|
||||
input wire [15:0] s_arp_ptype,
|
||||
input wire [15:0] s_arp_oper,
|
||||
input wire [47:0] s_arp_sha,
|
||||
input wire [31:0] s_arp_spa,
|
||||
input wire [47:0] s_arp_tha,
|
||||
input wire [31:0] s_arp_tpa,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [7:0] m_eth_payload_axis_tdata,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire m_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
ARP Frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype (0x0806) 2 octets
|
||||
HTYPE (1) 2 octets
|
||||
PTYPE (0x0800) 2 octets
|
||||
HLEN (6) 1 octets
|
||||
PLEN (4) 1 octets
|
||||
OPER 2 octets
|
||||
SHA Sender MAC 6 octets
|
||||
SPA Sender IP 4 octets
|
||||
THA Target MAC 6 octets
|
||||
TPA Target IP 4 octets
|
||||
|
||||
This module receives an ARP frame with header fields in parallel and
|
||||
transmits the complete Ethernet payload on an AXI interface.
|
||||
|
||||
*/
|
||||
|
||||
localparam [1:0]
|
||||
STATE_IDLE = 2'd0,
|
||||
STATE_WRITE_HEADER = 2'd1;
|
||||
|
||||
reg [1:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_frame;
|
||||
|
||||
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
|
||||
|
||||
reg [15:0] arp_htype_reg = 16'd0;
|
||||
reg [15:0] arp_ptype_reg = 16'd0;
|
||||
reg [15:0] arp_oper_reg = 16'd0;
|
||||
reg [47:0] arp_sha_reg = 48'd0;
|
||||
reg [31:0] arp_spa_reg = 32'd0;
|
||||
reg [47:0] arp_tha_reg = 48'd0;
|
||||
reg [31:0] arp_tpa_reg = 32'd0;
|
||||
|
||||
reg s_frame_ready_reg = 1'b0, s_frame_ready_next;
|
||||
|
||||
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] m_eth_payload_axis_tdata_int;
|
||||
reg m_eth_payload_axis_tvalid_int;
|
||||
reg m_eth_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_eth_payload_axis_tlast_int;
|
||||
reg m_eth_payload_axis_tuser_int;
|
||||
wire m_eth_payload_axis_tready_int_early;
|
||||
|
||||
assign s_frame_ready = s_frame_ready_reg;
|
||||
|
||||
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_frame_ready_next = 1'b0;
|
||||
|
||||
store_frame = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
|
||||
|
||||
m_eth_payload_axis_tdata_int = 8'd0;
|
||||
m_eth_payload_axis_tvalid_int = 1'b0;
|
||||
m_eth_payload_axis_tlast_int = 1'b0;
|
||||
m_eth_payload_axis_tuser_int = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for data
|
||||
frame_ptr_next = 8'd0;
|
||||
s_frame_ready_next = !m_eth_hdr_valid_next;
|
||||
|
||||
if (s_frame_ready && s_frame_valid) begin
|
||||
store_frame = 1'b1;
|
||||
s_frame_ready_next = 1'b0;
|
||||
m_eth_hdr_valid_next = 1'b1;
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
m_eth_payload_axis_tvalid_int = 1'b1;
|
||||
m_eth_payload_axis_tdata_int = s_arp_htype[15: 8];
|
||||
frame_ptr_next = 8'd1;
|
||||
end
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_HEADER: begin
|
||||
// read header state
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
// word transfer out
|
||||
frame_ptr_next = frame_ptr_reg + 8'd1;
|
||||
m_eth_payload_axis_tvalid_int = 1'b1;
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
case (frame_ptr_reg)
|
||||
8'h00: m_eth_payload_axis_tdata_int = arp_htype_reg[15: 8];
|
||||
8'h01: m_eth_payload_axis_tdata_int = arp_htype_reg[ 7: 0];
|
||||
8'h02: m_eth_payload_axis_tdata_int = arp_ptype_reg[15: 8];
|
||||
8'h03: m_eth_payload_axis_tdata_int = arp_ptype_reg[ 7: 0];
|
||||
8'h04: m_eth_payload_axis_tdata_int = 8'd6; // hlen
|
||||
8'h05: m_eth_payload_axis_tdata_int = 8'd4; // plen
|
||||
8'h06: m_eth_payload_axis_tdata_int = arp_oper_reg[15: 8];
|
||||
8'h07: m_eth_payload_axis_tdata_int = arp_oper_reg[ 7: 0];
|
||||
8'h08: m_eth_payload_axis_tdata_int = arp_sha_reg[47:40];
|
||||
8'h09: m_eth_payload_axis_tdata_int = arp_sha_reg[39:32];
|
||||
8'h0A: m_eth_payload_axis_tdata_int = arp_sha_reg[31:24];
|
||||
8'h0B: m_eth_payload_axis_tdata_int = arp_sha_reg[23:16];
|
||||
8'h0C: m_eth_payload_axis_tdata_int = arp_sha_reg[15: 8];
|
||||
8'h0D: m_eth_payload_axis_tdata_int = arp_sha_reg[ 7: 0];
|
||||
8'h0E: m_eth_payload_axis_tdata_int = arp_spa_reg[31:24];
|
||||
8'h0F: m_eth_payload_axis_tdata_int = arp_spa_reg[23:16];
|
||||
8'h10: m_eth_payload_axis_tdata_int = arp_spa_reg[15: 8];
|
||||
8'h11: m_eth_payload_axis_tdata_int = arp_spa_reg[ 7: 0];
|
||||
8'h12: m_eth_payload_axis_tdata_int = arp_tha_reg[47:40];
|
||||
8'h13: m_eth_payload_axis_tdata_int = arp_tha_reg[39:32];
|
||||
8'h14: m_eth_payload_axis_tdata_int = arp_tha_reg[31:24];
|
||||
8'h15: m_eth_payload_axis_tdata_int = arp_tha_reg[23:16];
|
||||
8'h16: m_eth_payload_axis_tdata_int = arp_tha_reg[15: 8];
|
||||
8'h17: m_eth_payload_axis_tdata_int = arp_tha_reg[ 7: 0];
|
||||
8'h18: m_eth_payload_axis_tdata_int = arp_tpa_reg[31:24];
|
||||
8'h19: m_eth_payload_axis_tdata_int = arp_tpa_reg[23:16];
|
||||
8'h1A: m_eth_payload_axis_tdata_int = arp_tpa_reg[15: 8];
|
||||
8'h1B: begin
|
||||
m_eth_payload_axis_tdata_int = arp_tpa_reg[ 7: 0];
|
||||
m_eth_payload_axis_tlast_int = 1'b1;
|
||||
s_frame_ready_next = !m_eth_hdr_valid_next;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
end else begin
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 8'd0;
|
||||
s_frame_ready_reg <= 1'b0;
|
||||
m_eth_hdr_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
s_frame_ready_reg <= s_frame_ready_next;
|
||||
|
||||
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
if (store_frame) begin
|
||||
m_eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
m_eth_src_mac_reg <= s_eth_src_mac;
|
||||
m_eth_type_reg <= s_eth_type;
|
||||
arp_htype_reg <= s_arp_htype;
|
||||
arp_ptype_reg <= s_arp_ptype;
|
||||
arp_oper_reg <= s_arp_oper;
|
||||
arp_sha_reg <= s_arp_sha;
|
||||
arp_spa_reg <= s_arp_spa;
|
||||
arp_tha_reg <= s_arp_tha;
|
||||
arp_tpa_reg <= s_arp_tpa;
|
||||
end
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0;
|
||||
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
|
||||
reg m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg m_eth_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0;
|
||||
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
|
||||
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg temp_m_eth_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_eth_payload_int_to_output;
|
||||
reg store_eth_payload_int_to_temp;
|
||||
reg store_eth_payload_axis_temp_to_output;
|
||||
|
||||
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
|
||||
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
|
||||
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
|
||||
assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
|
||||
store_eth_payload_int_to_output = 1'b0;
|
||||
store_eth_payload_int_to_temp = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_eth_payload_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_eth_payload_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_eth_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_eth_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
|
||||
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_eth_payload_int_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end else if (store_eth_payload_axis_temp_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
|
||||
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
|
||||
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_eth_payload_int_to_temp) begin
|
||||
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
453
fpga/src/axis_async_fifo.sv
Normal file
453
fpga/src/axis_async_fifo.sv
Normal file
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream asynchronous FIFO
|
||||
*/
|
||||
module axis_async_fifo #
|
||||
(
|
||||
parameter ADDR_WIDTH = 12,
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter KEEP_ENABLE = (DATA_WIDTH>8),
|
||||
parameter KEEP_WIDTH = (DATA_WIDTH/8),
|
||||
parameter LAST_ENABLE = 1,
|
||||
parameter ID_ENABLE = 0,
|
||||
parameter ID_WIDTH = 8,
|
||||
parameter DEST_ENABLE = 0,
|
||||
parameter DEST_WIDTH = 8,
|
||||
parameter USER_ENABLE = 1,
|
||||
parameter USER_WIDTH = 1,
|
||||
parameter FRAME_FIFO = 0,
|
||||
parameter USER_BAD_FRAME_VALUE = 1'b1,
|
||||
parameter USER_BAD_FRAME_MASK = 1'b1,
|
||||
parameter DROP_BAD_FRAME = 0,
|
||||
parameter DROP_WHEN_FULL = 0
|
||||
)
|
||||
(
|
||||
/*
|
||||
* Common asynchronous reset
|
||||
*/
|
||||
input wire async_rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire s_clk,
|
||||
input wire [DATA_WIDTH-1:0] s_axis_tdata,
|
||||
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
input wire s_axis_tlast,
|
||||
input wire [ID_WIDTH-1:0] s_axis_tid,
|
||||
input wire [DEST_WIDTH-1:0] s_axis_tdest,
|
||||
input wire [USER_WIDTH-1:0] s_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
input wire m_clk,
|
||||
output wire [DATA_WIDTH-1:0] m_axis_tdata,
|
||||
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
|
||||
output wire m_axis_tvalid,
|
||||
input wire m_axis_tready,
|
||||
output wire m_axis_tlast,
|
||||
output wire [ID_WIDTH-1:0] m_axis_tid,
|
||||
output wire [DEST_WIDTH-1:0] m_axis_tdest,
|
||||
output wire [USER_WIDTH-1:0] m_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire s_status_overflow,
|
||||
output wire s_status_bad_frame,
|
||||
output wire s_status_good_frame,
|
||||
output wire m_status_overflow,
|
||||
output wire m_status_bad_frame,
|
||||
output wire m_status_good_frame
|
||||
);
|
||||
|
||||
// check configuration
|
||||
initial begin
|
||||
if (FRAME_FIFO && !LAST_ENABLE) begin
|
||||
$error("Error: FRAME_FIFO set requires LAST_ENABLE set");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (DROP_BAD_FRAME && !FRAME_FIFO) begin
|
||||
$error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (DROP_WHEN_FULL && !FRAME_FIFO) begin
|
||||
$error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
|
||||
$error("Error: Invalid USER_BAD_FRAME_MASK value");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
localparam KEEP_OFFSET = DATA_WIDTH;
|
||||
localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0);
|
||||
localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0);
|
||||
localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0);
|
||||
localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0);
|
||||
localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0);
|
||||
|
||||
reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next;
|
||||
reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next;
|
||||
reg [ADDR_WIDTH:0] wr_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_gray_next;
|
||||
reg [ADDR_WIDTH:0] wr_ptr_cur_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_gray_next;
|
||||
reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next;
|
||||
reg [ADDR_WIDTH:0] rd_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_gray_next;
|
||||
reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
|
||||
reg [ADDR_WIDTH:0] wr_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
reg [ADDR_WIDTH:0] wr_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
reg [ADDR_WIDTH:0] rd_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
reg [ADDR_WIDTH:0] rd_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
|
||||
reg s_rst_sync1_reg = 1'b1;
|
||||
reg s_rst_sync2_reg = 1'b1;
|
||||
reg s_rst_sync3_reg = 1'b1;
|
||||
reg m_rst_sync1_reg = 1'b1;
|
||||
reg m_rst_sync2_reg = 1'b1;
|
||||
reg m_rst_sync3_reg = 1'b1;
|
||||
|
||||
reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0];
|
||||
reg [WIDTH-1:0] mem_read_data_reg;
|
||||
reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next;
|
||||
|
||||
wire [WIDTH-1:0] s_axis;
|
||||
|
||||
reg [WIDTH-1:0] m_axis_reg;
|
||||
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
|
||||
|
||||
// full when first TWO MSBs do NOT match, but rest matches
|
||||
// (gray code equivalent of first MSB different but rest same)
|
||||
wire full = ((wr_ptr_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) &&
|
||||
(wr_ptr_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) &&
|
||||
(wr_ptr_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0]));
|
||||
wire full_cur = ((wr_ptr_cur_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) &&
|
||||
(wr_ptr_cur_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) &&
|
||||
(wr_ptr_cur_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0]));
|
||||
// empty when pointers match exactly
|
||||
wire empty = rd_ptr_gray_reg == wr_ptr_gray_sync2_reg;
|
||||
// overflow within packet
|
||||
wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) &&
|
||||
(wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0]));
|
||||
|
||||
// control signals
|
||||
reg write;
|
||||
reg read;
|
||||
reg store_output;
|
||||
|
||||
reg drop_frame_reg = 1'b0, drop_frame_next;
|
||||
reg overflow_reg = 1'b0, overflow_next;
|
||||
reg bad_frame_reg = 1'b0, bad_frame_next;
|
||||
reg good_frame_reg = 1'b0, good_frame_next;
|
||||
|
||||
reg overflow_sync1_reg = 1'b0;
|
||||
reg overflow_sync2_reg = 1'b0;
|
||||
reg overflow_sync3_reg = 1'b0;
|
||||
reg overflow_sync4_reg = 1'b0;
|
||||
reg bad_frame_sync1_reg = 1'b0;
|
||||
reg bad_frame_sync2_reg = 1'b0;
|
||||
reg bad_frame_sync3_reg = 1'b0;
|
||||
reg bad_frame_sync4_reg = 1'b0;
|
||||
reg good_frame_sync1_reg = 1'b0;
|
||||
reg good_frame_sync2_reg = 1'b0;
|
||||
reg good_frame_sync3_reg = 1'b0;
|
||||
reg good_frame_sync4_reg = 1'b0;
|
||||
|
||||
assign s_axis_tready = (FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full) && !s_rst_sync3_reg;
|
||||
|
||||
generate
|
||||
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
|
||||
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
|
||||
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
|
||||
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
|
||||
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
|
||||
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
|
||||
endgenerate
|
||||
|
||||
assign m_axis_tvalid = m_axis_tvalid_reg;
|
||||
|
||||
assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0];
|
||||
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}};
|
||||
assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1;
|
||||
assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}};
|
||||
assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
|
||||
assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}};
|
||||
|
||||
assign s_status_overflow = overflow_reg;
|
||||
assign s_status_bad_frame = bad_frame_reg;
|
||||
assign s_status_good_frame = good_frame_reg;
|
||||
|
||||
assign m_status_overflow = overflow_sync3_reg ^ overflow_sync4_reg;
|
||||
assign m_status_bad_frame = bad_frame_sync3_reg ^ bad_frame_sync4_reg;
|
||||
assign m_status_good_frame = good_frame_sync3_reg ^ good_frame_sync4_reg;
|
||||
|
||||
// reset synchronization
|
||||
always @(posedge s_clk or posedge async_rst) begin
|
||||
if (async_rst) begin
|
||||
s_rst_sync1_reg <= 1'b1;
|
||||
s_rst_sync2_reg <= 1'b1;
|
||||
s_rst_sync3_reg <= 1'b1;
|
||||
end else begin
|
||||
s_rst_sync1_reg <= 1'b0;
|
||||
s_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg;
|
||||
s_rst_sync3_reg <= s_rst_sync2_reg;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge m_clk or posedge async_rst) begin
|
||||
if (async_rst) begin
|
||||
m_rst_sync1_reg <= 1'b1;
|
||||
m_rst_sync2_reg <= 1'b1;
|
||||
m_rst_sync3_reg <= 1'b1;
|
||||
end else begin
|
||||
m_rst_sync1_reg <= 1'b0;
|
||||
m_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg;
|
||||
m_rst_sync3_reg <= m_rst_sync2_reg;
|
||||
end
|
||||
end
|
||||
|
||||
// Write logic
|
||||
always @* begin
|
||||
write = 1'b0;
|
||||
|
||||
drop_frame_next = 1'b0;
|
||||
overflow_next = 1'b0;
|
||||
bad_frame_next = 1'b0;
|
||||
good_frame_next = 1'b0;
|
||||
|
||||
wr_ptr_next = wr_ptr_reg;
|
||||
wr_ptr_cur_next = wr_ptr_cur_reg;
|
||||
wr_ptr_gray_next = wr_ptr_gray_reg;
|
||||
wr_ptr_cur_gray_next = wr_ptr_cur_gray_reg;
|
||||
|
||||
if (s_axis_tready && s_axis_tvalid) begin
|
||||
// transfer in
|
||||
if (!FRAME_FIFO) begin
|
||||
// normal FIFO mode
|
||||
write = 1'b1;
|
||||
wr_ptr_next = wr_ptr_reg + 1;
|
||||
wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1);
|
||||
end else if (full_cur || full_wr || drop_frame_reg) begin
|
||||
// full, packet overflow, or currently dropping frame
|
||||
// drop frame
|
||||
drop_frame_next = 1'b1;
|
||||
if (s_axis_tlast) begin
|
||||
// end of frame, reset write pointer
|
||||
wr_ptr_cur_next = wr_ptr_reg;
|
||||
wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1);
|
||||
drop_frame_next = 1'b0;
|
||||
overflow_next = 1'b1;
|
||||
end
|
||||
end else begin
|
||||
write = 1'b1;
|
||||
wr_ptr_cur_next = wr_ptr_cur_reg + 1;
|
||||
wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1);
|
||||
if (s_axis_tlast) begin
|
||||
// end of frame
|
||||
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & s_axis_tuser == USER_BAD_FRAME_VALUE)) begin
|
||||
// bad packet, reset write pointer
|
||||
wr_ptr_cur_next = wr_ptr_reg;
|
||||
wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1);
|
||||
bad_frame_next = 1'b1;
|
||||
end else begin
|
||||
// good packet, update write pointer
|
||||
wr_ptr_next = wr_ptr_cur_reg + 1;
|
||||
wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1);
|
||||
good_frame_next = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge s_clk) begin
|
||||
if (s_rst_sync3_reg) begin
|
||||
wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
wr_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
wr_ptr_cur_gray_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
|
||||
drop_frame_reg <= 1'b0;
|
||||
overflow_reg <= 1'b0;
|
||||
bad_frame_reg <= 1'b0;
|
||||
good_frame_reg <= 1'b0;
|
||||
end else begin
|
||||
wr_ptr_reg <= wr_ptr_next;
|
||||
wr_ptr_cur_reg <= wr_ptr_cur_next;
|
||||
wr_ptr_gray_reg <= wr_ptr_gray_next;
|
||||
wr_ptr_cur_gray_reg <= wr_ptr_cur_gray_next;
|
||||
|
||||
drop_frame_reg <= drop_frame_next;
|
||||
overflow_reg <= overflow_next;
|
||||
bad_frame_reg <= bad_frame_next;
|
||||
good_frame_reg <= good_frame_next;
|
||||
end
|
||||
|
||||
if (FRAME_FIFO) begin
|
||||
wr_addr_reg <= wr_ptr_cur_next;
|
||||
end else begin
|
||||
wr_addr_reg <= wr_ptr_next;
|
||||
end
|
||||
|
||||
if (write) begin
|
||||
mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis;
|
||||
end
|
||||
end
|
||||
|
||||
// pointer synchronization
|
||||
always @(posedge s_clk) begin
|
||||
if (s_rst_sync3_reg) begin
|
||||
rd_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
rd_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
end else begin
|
||||
rd_ptr_gray_sync1_reg <= rd_ptr_gray_reg;
|
||||
rd_ptr_gray_sync2_reg <= rd_ptr_gray_sync1_reg;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge m_clk) begin
|
||||
if (m_rst_sync3_reg) begin
|
||||
wr_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
wr_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
end else begin
|
||||
wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg;
|
||||
wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg;
|
||||
end
|
||||
end
|
||||
|
||||
// status synchronization
|
||||
always @(posedge s_clk) begin
|
||||
if (s_rst_sync3_reg) begin
|
||||
overflow_sync1_reg <= 1'b0;
|
||||
bad_frame_sync1_reg <= 1'b0;
|
||||
good_frame_sync1_reg <= 1'b0;
|
||||
end else begin
|
||||
overflow_sync1_reg <= overflow_sync1_reg ^ overflow_reg;
|
||||
bad_frame_sync1_reg <= bad_frame_sync1_reg ^ bad_frame_reg;
|
||||
good_frame_sync1_reg <= good_frame_sync1_reg ^ good_frame_reg;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge m_clk) begin
|
||||
if (m_rst_sync3_reg) begin
|
||||
overflow_sync2_reg <= 1'b0;
|
||||
overflow_sync3_reg <= 1'b0;
|
||||
bad_frame_sync2_reg <= 1'b0;
|
||||
bad_frame_sync3_reg <= 1'b0;
|
||||
good_frame_sync2_reg <= 1'b0;
|
||||
good_frame_sync3_reg <= 1'b0;
|
||||
end else begin
|
||||
overflow_sync2_reg <= overflow_sync1_reg;
|
||||
overflow_sync3_reg <= overflow_sync2_reg;
|
||||
overflow_sync4_reg <= overflow_sync3_reg;
|
||||
bad_frame_sync2_reg <= bad_frame_sync1_reg;
|
||||
bad_frame_sync3_reg <= bad_frame_sync2_reg;
|
||||
bad_frame_sync4_reg <= bad_frame_sync3_reg;
|
||||
good_frame_sync2_reg <= good_frame_sync1_reg;
|
||||
good_frame_sync3_reg <= good_frame_sync2_reg;
|
||||
good_frame_sync4_reg <= good_frame_sync3_reg;
|
||||
end
|
||||
end
|
||||
|
||||
// Read logic
|
||||
always @* begin
|
||||
read = 1'b0;
|
||||
|
||||
rd_ptr_next = rd_ptr_reg;
|
||||
rd_ptr_gray_next = rd_ptr_gray_reg;
|
||||
|
||||
mem_read_data_valid_next = mem_read_data_valid_reg;
|
||||
|
||||
if (store_output || !mem_read_data_valid_reg) begin
|
||||
// output data not valid OR currently being transferred
|
||||
if (!empty) begin
|
||||
// not empty, perform read
|
||||
read = 1'b1;
|
||||
mem_read_data_valid_next = 1'b1;
|
||||
rd_ptr_next = rd_ptr_reg + 1;
|
||||
rd_ptr_gray_next = rd_ptr_next ^ (rd_ptr_next >> 1);
|
||||
end else begin
|
||||
// empty, invalidate
|
||||
mem_read_data_valid_next = 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge m_clk) begin
|
||||
if (m_rst_sync3_reg) begin
|
||||
rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
rd_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
mem_read_data_valid_reg <= 1'b0;
|
||||
end else begin
|
||||
rd_ptr_reg <= rd_ptr_next;
|
||||
rd_ptr_gray_reg <= rd_ptr_gray_next;
|
||||
mem_read_data_valid_reg <= mem_read_data_valid_next;
|
||||
end
|
||||
|
||||
rd_addr_reg <= rd_ptr_next;
|
||||
|
||||
if (read) begin
|
||||
mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]];
|
||||
end
|
||||
end
|
||||
|
||||
// Output register
|
||||
always @* begin
|
||||
store_output = 1'b0;
|
||||
|
||||
m_axis_tvalid_next = m_axis_tvalid_reg;
|
||||
|
||||
if (m_axis_tready || !m_axis_tvalid) begin
|
||||
store_output = 1'b1;
|
||||
m_axis_tvalid_next = mem_read_data_valid_reg;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge m_clk) begin
|
||||
if (m_rst_sync3_reg) begin
|
||||
m_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_axis_tvalid_reg <= m_axis_tvalid_next;
|
||||
end
|
||||
|
||||
if (store_output) begin
|
||||
m_axis_reg <= mem_read_data_reg;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
316
fpga/src/axis_fifo.sv
Normal file
316
fpga/src/axis_fifo.sv
Normal file
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2013-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream FIFO
|
||||
*/
|
||||
module axis_fifo #
|
||||
(
|
||||
parameter ADDR_WIDTH = 12,
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter KEEP_ENABLE = (DATA_WIDTH>8),
|
||||
parameter KEEP_WIDTH = (DATA_WIDTH/8),
|
||||
parameter LAST_ENABLE = 1,
|
||||
parameter ID_ENABLE = 0,
|
||||
parameter ID_WIDTH = 8,
|
||||
parameter DEST_ENABLE = 0,
|
||||
parameter DEST_WIDTH = 8,
|
||||
parameter USER_ENABLE = 1,
|
||||
parameter USER_WIDTH = 1,
|
||||
parameter FRAME_FIFO = 0,
|
||||
parameter USER_BAD_FRAME_VALUE = 1'b1,
|
||||
parameter USER_BAD_FRAME_MASK = 1'b1,
|
||||
parameter DROP_BAD_FRAME = 0,
|
||||
parameter DROP_WHEN_FULL = 0
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [DATA_WIDTH-1:0] s_axis_tdata,
|
||||
input wire [KEEP_WIDTH-1:0] s_axis_tkeep,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
input wire s_axis_tlast,
|
||||
input wire [ID_WIDTH-1:0] s_axis_tid,
|
||||
input wire [DEST_WIDTH-1:0] s_axis_tdest,
|
||||
input wire [USER_WIDTH-1:0] s_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [DATA_WIDTH-1:0] m_axis_tdata,
|
||||
output wire [KEEP_WIDTH-1:0] m_axis_tkeep,
|
||||
output wire m_axis_tvalid,
|
||||
input wire m_axis_tready,
|
||||
output wire m_axis_tlast,
|
||||
output wire [ID_WIDTH-1:0] m_axis_tid,
|
||||
output wire [DEST_WIDTH-1:0] m_axis_tdest,
|
||||
output wire [USER_WIDTH-1:0] m_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire status_overflow,
|
||||
output wire status_bad_frame,
|
||||
output wire status_good_frame
|
||||
);
|
||||
|
||||
// check configuration
|
||||
initial begin
|
||||
if (FRAME_FIFO && !LAST_ENABLE) begin
|
||||
$error("Error: FRAME_FIFO set requires LAST_ENABLE set");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (DROP_BAD_FRAME && !FRAME_FIFO) begin
|
||||
$error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (DROP_WHEN_FULL && !FRAME_FIFO) begin
|
||||
$error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set");
|
||||
$finish;
|
||||
end
|
||||
|
||||
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin
|
||||
$error("Error: Invalid USER_BAD_FRAME_MASK value");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
localparam KEEP_OFFSET = DATA_WIDTH;
|
||||
localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0);
|
||||
localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0);
|
||||
localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0);
|
||||
localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0);
|
||||
localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0);
|
||||
|
||||
reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next;
|
||||
reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next;
|
||||
reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next;
|
||||
reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}};
|
||||
|
||||
reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0];
|
||||
reg [WIDTH-1:0] mem_read_data_reg;
|
||||
reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next;
|
||||
|
||||
wire [WIDTH-1:0] s_axis;
|
||||
|
||||
reg [WIDTH-1:0] m_axis_reg;
|
||||
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
|
||||
|
||||
// full when first MSB different but rest same
|
||||
wire full = ((wr_ptr_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) &&
|
||||
(wr_ptr_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0]));
|
||||
wire full_cur = ((wr_ptr_cur_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) &&
|
||||
(wr_ptr_cur_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0]));
|
||||
// empty when pointers match exactly
|
||||
wire empty = wr_ptr_reg == rd_ptr_reg;
|
||||
// overflow within packet
|
||||
wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) &&
|
||||
(wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0]));
|
||||
|
||||
// control signals
|
||||
reg write;
|
||||
reg read;
|
||||
reg store_output;
|
||||
|
||||
reg drop_frame_reg = 1'b0, drop_frame_next;
|
||||
reg overflow_reg = 1'b0, overflow_next;
|
||||
reg bad_frame_reg = 1'b0, bad_frame_next;
|
||||
reg good_frame_reg = 1'b0, good_frame_next;
|
||||
|
||||
assign s_axis_tready = FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full;
|
||||
|
||||
generate
|
||||
assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata;
|
||||
if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep;
|
||||
if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast;
|
||||
if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid;
|
||||
if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest;
|
||||
if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser;
|
||||
endgenerate
|
||||
|
||||
assign m_axis_tvalid = m_axis_tvalid_reg;
|
||||
|
||||
assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0];
|
||||
assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}};
|
||||
assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1;
|
||||
assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}};
|
||||
assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
|
||||
assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}};
|
||||
|
||||
assign status_overflow = overflow_reg;
|
||||
assign status_bad_frame = bad_frame_reg;
|
||||
assign status_good_frame = good_frame_reg;
|
||||
|
||||
// Write logic
|
||||
always @* begin
|
||||
write = 1'b0;
|
||||
|
||||
drop_frame_next = 1'b0;
|
||||
overflow_next = 1'b0;
|
||||
bad_frame_next = 1'b0;
|
||||
good_frame_next = 1'b0;
|
||||
|
||||
wr_ptr_next = wr_ptr_reg;
|
||||
wr_ptr_cur_next = wr_ptr_cur_reg;
|
||||
|
||||
if (s_axis_tready && s_axis_tvalid) begin
|
||||
// transfer in
|
||||
if (!FRAME_FIFO) begin
|
||||
// normal FIFO mode
|
||||
write = 1'b1;
|
||||
wr_ptr_next = wr_ptr_reg + 1;
|
||||
end else if (full_cur || full_wr || drop_frame_reg) begin
|
||||
// full, packet overflow, or currently dropping frame
|
||||
// drop frame
|
||||
drop_frame_next = 1'b1;
|
||||
if (s_axis_tlast) begin
|
||||
// end of frame, reset write pointer
|
||||
wr_ptr_cur_next = wr_ptr_reg;
|
||||
drop_frame_next = 1'b0;
|
||||
overflow_next = 1'b1;
|
||||
end
|
||||
end else begin
|
||||
write = 1'b1;
|
||||
wr_ptr_cur_next = wr_ptr_cur_reg + 1;
|
||||
if (s_axis_tlast) begin
|
||||
// end of frame
|
||||
if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & s_axis_tuser == USER_BAD_FRAME_VALUE)) begin
|
||||
// bad packet, reset write pointer
|
||||
wr_ptr_cur_next = wr_ptr_reg;
|
||||
bad_frame_next = 1'b1;
|
||||
end else begin
|
||||
// good packet, update write pointer
|
||||
wr_ptr_next = wr_ptr_cur_reg + 1;
|
||||
good_frame_next = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
|
||||
drop_frame_reg <= 1'b0;
|
||||
overflow_reg <= 1'b0;
|
||||
bad_frame_reg <= 1'b0;
|
||||
good_frame_reg <= 1'b0;
|
||||
end else begin
|
||||
wr_ptr_reg <= wr_ptr_next;
|
||||
wr_ptr_cur_reg <= wr_ptr_cur_next;
|
||||
|
||||
drop_frame_reg <= drop_frame_next;
|
||||
overflow_reg <= overflow_next;
|
||||
bad_frame_reg <= bad_frame_next;
|
||||
good_frame_reg <= good_frame_next;
|
||||
end
|
||||
|
||||
if (FRAME_FIFO) begin
|
||||
wr_addr_reg <= wr_ptr_cur_next;
|
||||
end else begin
|
||||
wr_addr_reg <= wr_ptr_next;
|
||||
end
|
||||
|
||||
if (write) begin
|
||||
mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis;
|
||||
end
|
||||
end
|
||||
|
||||
// Read logic
|
||||
always @* begin
|
||||
read = 1'b0;
|
||||
|
||||
rd_ptr_next = rd_ptr_reg;
|
||||
|
||||
mem_read_data_valid_next = mem_read_data_valid_reg;
|
||||
|
||||
if (store_output || !mem_read_data_valid_reg) begin
|
||||
// output data not valid OR currently being transferred
|
||||
if (!empty) begin
|
||||
// not empty, perform read
|
||||
read = 1'b1;
|
||||
mem_read_data_valid_next = 1'b1;
|
||||
rd_ptr_next = rd_ptr_reg + 1;
|
||||
end else begin
|
||||
// empty, invalidate
|
||||
mem_read_data_valid_next = 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
|
||||
mem_read_data_valid_reg <= 1'b0;
|
||||
end else begin
|
||||
rd_ptr_reg <= rd_ptr_next;
|
||||
mem_read_data_valid_reg <= mem_read_data_valid_next;
|
||||
end
|
||||
|
||||
rd_addr_reg <= rd_ptr_next;
|
||||
|
||||
if (read) begin
|
||||
mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]];
|
||||
end
|
||||
end
|
||||
|
||||
// Output register
|
||||
always @* begin
|
||||
store_output = 1'b0;
|
||||
|
||||
m_axis_tvalid_next = m_axis_tvalid_reg;
|
||||
|
||||
if (m_axis_tready || !m_axis_tvalid) begin
|
||||
store_output = 1'b1;
|
||||
m_axis_tvalid_next = mem_read_data_valid_reg;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_axis_tvalid_reg <= m_axis_tvalid_next;
|
||||
end
|
||||
|
||||
if (store_output) begin
|
||||
m_axis_reg <= mem_read_data_reg;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
318
fpga/src/axis_gmii_rx.sv
Normal file
318
fpga/src/axis_gmii_rx.sv
Normal file
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream GMII frame receiver (GMII in, AXI out)
|
||||
*/
|
||||
module axis_gmii_rx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* GMII input
|
||||
*/
|
||||
input wire [7:0] gmii_rxd,
|
||||
input wire gmii_rx_dv,
|
||||
input wire gmii_rx_er,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] m_axis_tdata,
|
||||
output wire m_axis_tvalid,
|
||||
output wire m_axis_tlast,
|
||||
output wire m_axis_tuser,
|
||||
|
||||
/*
|
||||
* Control
|
||||
*/
|
||||
input wire clk_enable,
|
||||
input wire mii_select,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire error_bad_frame,
|
||||
output wire error_bad_fcs
|
||||
);
|
||||
|
||||
localparam [7:0]
|
||||
ETH_PRE = 8'h55,
|
||||
ETH_SFD = 8'hD5;
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_PAYLOAD = 3'd1,
|
||||
STATE_WAIT_LAST = 3'd2;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg reset_crc;
|
||||
reg update_crc;
|
||||
|
||||
reg mii_odd = 1'b0;
|
||||
reg mii_locked = 1'b0;
|
||||
|
||||
reg [7:0] gmii_rxd_d0 = 8'd0;
|
||||
reg [7:0] gmii_rxd_d1 = 8'd0;
|
||||
reg [7:0] gmii_rxd_d2 = 8'd0;
|
||||
reg [7:0] gmii_rxd_d3 = 8'd0;
|
||||
reg [7:0] gmii_rxd_d4 = 8'd0;
|
||||
|
||||
reg gmii_rx_dv_d0 = 1'b0;
|
||||
reg gmii_rx_dv_d1 = 1'b0;
|
||||
reg gmii_rx_dv_d2 = 1'b0;
|
||||
reg gmii_rx_dv_d3 = 1'b0;
|
||||
reg gmii_rx_dv_d4 = 1'b0;
|
||||
|
||||
reg gmii_rx_er_d0 = 1'b0;
|
||||
reg gmii_rx_er_d1 = 1'b0;
|
||||
reg gmii_rx_er_d2 = 1'b0;
|
||||
reg gmii_rx_er_d3 = 1'b0;
|
||||
reg gmii_rx_er_d4 = 1'b0;
|
||||
|
||||
reg [7:0] m_axis_tdata_reg = 8'd0, m_axis_tdata_next;
|
||||
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
|
||||
reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next;
|
||||
reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next;
|
||||
|
||||
reg error_bad_frame_reg = 1'b0, error_bad_frame_next;
|
||||
reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next;
|
||||
|
||||
reg [31:0] crc_state = 32'hFFFFFFFF;
|
||||
wire [31:0] crc_next;
|
||||
|
||||
assign m_axis_tdata = m_axis_tdata_reg;
|
||||
assign m_axis_tvalid = m_axis_tvalid_reg;
|
||||
assign m_axis_tlast = m_axis_tlast_reg;
|
||||
assign m_axis_tuser = m_axis_tuser_reg;
|
||||
|
||||
assign error_bad_frame = error_bad_frame_reg;
|
||||
assign error_bad_fcs = error_bad_fcs_reg;
|
||||
|
||||
rgmii_lfsr #(
|
||||
.LFSR_WIDTH(32),
|
||||
.LFSR_POLY(32'h4c11db7),
|
||||
.LFSR_CONFIG("GALOIS"),
|
||||
.LFSR_FEED_FORWARD(0),
|
||||
.REVERSE(1),
|
||||
.DATA_WIDTH(8),
|
||||
.STYLE("AUTO")
|
||||
)
|
||||
eth_crc_8 (
|
||||
.data_in(gmii_rxd_d4),
|
||||
.state_in(crc_state),
|
||||
.data_out(),
|
||||
.state_out(crc_next)
|
||||
);
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
reset_crc = 1'b0;
|
||||
update_crc = 1'b0;
|
||||
|
||||
m_axis_tdata_next = 8'd0;
|
||||
m_axis_tvalid_next = 1'b0;
|
||||
m_axis_tlast_next = 1'b0;
|
||||
m_axis_tuser_next = 1'b0;
|
||||
|
||||
error_bad_frame_next = 1'b0;
|
||||
error_bad_fcs_next = 1'b0;
|
||||
|
||||
if (!clk_enable) begin
|
||||
// clock disabled - hold state
|
||||
state_next = state_reg;
|
||||
end else if (mii_select && !mii_odd) begin
|
||||
// MII even cycle - hold state
|
||||
state_next = state_reg;
|
||||
end else begin
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for packet
|
||||
reset_crc = 1'b1;
|
||||
|
||||
if (gmii_rx_dv_d4 && !gmii_rx_er_d4 && gmii_rxd_d4 == ETH_SFD) begin
|
||||
state_next = STATE_PAYLOAD;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_PAYLOAD: begin
|
||||
// read payload
|
||||
update_crc = 1'b1;
|
||||
|
||||
m_axis_tdata_next = gmii_rxd_d4;
|
||||
m_axis_tvalid_next = 1'b1;
|
||||
|
||||
if (gmii_rx_dv_d4 && gmii_rx_er_d4) begin
|
||||
// error
|
||||
m_axis_tlast_next = 1'b1;
|
||||
m_axis_tuser_next = 1'b1;
|
||||
error_bad_frame_next = 1'b1;
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end else if (!gmii_rx_dv) begin
|
||||
// end of packet
|
||||
m_axis_tlast_next = 1'b1;
|
||||
if (gmii_rx_er_d0 || gmii_rx_er_d1 || gmii_rx_er_d2 || gmii_rx_er_d3) begin
|
||||
// error received in FCS bytes
|
||||
m_axis_tuser_next = 1'b1;
|
||||
error_bad_frame_next = 1'b1;
|
||||
end else if ({gmii_rxd_d0, gmii_rxd_d1, gmii_rxd_d2, gmii_rxd_d3} == ~crc_next) begin
|
||||
// FCS good
|
||||
m_axis_tuser_next = 1'b0;
|
||||
end else begin
|
||||
// FCS bad
|
||||
m_axis_tuser_next = 1'b1;
|
||||
error_bad_frame_next = 1'b1;
|
||||
error_bad_fcs_next = 1'b1;
|
||||
end
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_PAYLOAD;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_LAST: begin
|
||||
// wait for end of packet
|
||||
|
||||
if (~gmii_rx_dv) begin
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
|
||||
m_axis_tvalid_reg <= 1'b0;
|
||||
|
||||
error_bad_frame_reg <= 1'b0;
|
||||
error_bad_fcs_reg <= 1'b0;
|
||||
|
||||
crc_state <= 32'hFFFFFFFF;
|
||||
|
||||
mii_locked <= 1'b0;
|
||||
mii_odd <= 1'b0;
|
||||
|
||||
gmii_rx_dv_d0 <= 1'b0;
|
||||
gmii_rx_dv_d1 <= 1'b0;
|
||||
gmii_rx_dv_d2 <= 1'b0;
|
||||
gmii_rx_dv_d3 <= 1'b0;
|
||||
gmii_rx_dv_d4 <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
m_axis_tvalid_reg <= m_axis_tvalid_next;
|
||||
|
||||
error_bad_frame_reg <= error_bad_frame_next;
|
||||
error_bad_fcs_reg <= error_bad_fcs_next;
|
||||
|
||||
// datapath
|
||||
if (reset_crc) begin
|
||||
crc_state <= 32'hFFFFFFFF;
|
||||
end else if (update_crc) begin
|
||||
crc_state <= crc_next;
|
||||
end
|
||||
|
||||
if (clk_enable) begin
|
||||
if (mii_select) begin
|
||||
mii_odd <= !mii_odd;
|
||||
|
||||
if (mii_locked) begin
|
||||
mii_locked <= gmii_rx_dv;
|
||||
end else if (gmii_rx_dv && {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == ETH_SFD) begin
|
||||
mii_locked <= 1'b1;
|
||||
mii_odd <= 1'b1;
|
||||
end
|
||||
|
||||
if (mii_odd) begin
|
||||
gmii_rx_dv_d0 <= gmii_rx_dv & gmii_rx_dv_d0;
|
||||
gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv;
|
||||
gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv;
|
||||
gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv;
|
||||
gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv;
|
||||
end else begin
|
||||
gmii_rx_dv_d0 <= gmii_rx_dv;
|
||||
end
|
||||
end else begin
|
||||
gmii_rx_dv_d0 <= gmii_rx_dv;
|
||||
gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv;
|
||||
gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv;
|
||||
gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv;
|
||||
gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
m_axis_tdata_reg <= m_axis_tdata_next;
|
||||
m_axis_tlast_reg <= m_axis_tlast_next;
|
||||
m_axis_tuser_reg <= m_axis_tuser_next;
|
||||
|
||||
// delay input
|
||||
if (clk_enable) begin
|
||||
if (mii_select) begin
|
||||
gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]};
|
||||
|
||||
if (mii_odd) begin
|
||||
gmii_rxd_d1 <= gmii_rxd_d0;
|
||||
gmii_rxd_d2 <= gmii_rxd_d1;
|
||||
gmii_rxd_d3 <= gmii_rxd_d2;
|
||||
gmii_rxd_d4 <= gmii_rxd_d3;
|
||||
|
||||
gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0;
|
||||
gmii_rx_er_d1 <= gmii_rx_er_d0;
|
||||
gmii_rx_er_d2 <= gmii_rx_er_d1;
|
||||
gmii_rx_er_d3 <= gmii_rx_er_d2;
|
||||
gmii_rx_er_d4 <= gmii_rx_er_d3;
|
||||
end else begin
|
||||
gmii_rx_er_d0 <= gmii_rx_er;
|
||||
end
|
||||
end else begin
|
||||
gmii_rxd_d0 <= gmii_rxd;
|
||||
gmii_rxd_d1 <= gmii_rxd_d0;
|
||||
gmii_rxd_d2 <= gmii_rxd_d1;
|
||||
gmii_rxd_d3 <= gmii_rxd_d2;
|
||||
gmii_rxd_d4 <= gmii_rxd_d3;
|
||||
|
||||
gmii_rx_er_d0 <= gmii_rx_er;
|
||||
gmii_rx_er_d1 <= gmii_rx_er_d0;
|
||||
gmii_rx_er_d2 <= gmii_rx_er_d1;
|
||||
gmii_rx_er_d3 <= gmii_rx_er_d2;
|
||||
gmii_rx_er_d4 <= gmii_rx_er_d3;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
381
fpga/src/axis_gmii_tx.sv
Normal file
381
fpga/src/axis_gmii_tx.sv
Normal file
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream GMII frame transmitter (AXI in, GMII out)
|
||||
*/
|
||||
module axis_gmii_tx #
|
||||
(
|
||||
parameter ENABLE_PADDING = 1,
|
||||
parameter MIN_FRAME_LENGTH = 64
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] s_axis_tdata,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
input wire s_axis_tlast,
|
||||
input wire s_axis_tuser,
|
||||
|
||||
/*
|
||||
* GMII output
|
||||
*/
|
||||
output wire [7:0] gmii_txd,
|
||||
output wire gmii_tx_en,
|
||||
output wire gmii_tx_er,
|
||||
|
||||
/*
|
||||
* Control
|
||||
*/
|
||||
input wire clk_enable,
|
||||
input wire mii_select,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [7:0] ifg_delay
|
||||
);
|
||||
|
||||
localparam [7:0]
|
||||
ETH_PRE = 8'h55,
|
||||
ETH_SFD = 8'hD5;
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_PREAMBLE = 3'd1,
|
||||
STATE_PAYLOAD = 3'd2,
|
||||
STATE_LAST = 3'd3,
|
||||
STATE_PAD = 3'd4,
|
||||
STATE_FCS = 3'd5,
|
||||
STATE_WAIT_END = 3'd6,
|
||||
STATE_IFG = 3'd7;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg reset_crc;
|
||||
reg update_crc;
|
||||
|
||||
reg [7:0] s_tdata_reg = 8'd0, s_tdata_next;
|
||||
|
||||
reg mii_odd_reg = 1'b0, mii_odd_next;
|
||||
reg [3:0] mii_msn_reg = 4'b0, mii_msn_next;
|
||||
|
||||
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
|
||||
|
||||
reg [7:0] gmii_txd_reg = 8'd0, gmii_txd_next;
|
||||
reg gmii_tx_en_reg = 1'b0, gmii_tx_en_next;
|
||||
reg gmii_tx_er_reg = 1'b0, gmii_tx_er_next;
|
||||
|
||||
reg s_axis_tready_reg = 1'b0, s_axis_tready_next;
|
||||
|
||||
reg [31:0] crc_state = 32'hFFFFFFFF;
|
||||
wire [31:0] crc_next;
|
||||
|
||||
assign s_axis_tready = s_axis_tready_reg;
|
||||
|
||||
assign gmii_txd = gmii_txd_reg;
|
||||
assign gmii_tx_en = gmii_tx_en_reg;
|
||||
assign gmii_tx_er = gmii_tx_er_reg;
|
||||
|
||||
rgmii_lfsr #(
|
||||
.LFSR_WIDTH(32),
|
||||
.LFSR_POLY(32'h4c11db7),
|
||||
.LFSR_CONFIG("GALOIS"),
|
||||
.LFSR_FEED_FORWARD(0),
|
||||
.REVERSE(1),
|
||||
.DATA_WIDTH(8),
|
||||
.STYLE("AUTO")
|
||||
)
|
||||
eth_crc_8 (
|
||||
.data_in(s_tdata_reg),
|
||||
.state_in(crc_state),
|
||||
.data_out(),
|
||||
.state_out(crc_next)
|
||||
);
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
reset_crc = 1'b0;
|
||||
update_crc = 1'b0;
|
||||
|
||||
mii_odd_next = mii_odd_reg;
|
||||
mii_msn_next = mii_msn_reg;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
s_axis_tready_next = 1'b0;
|
||||
|
||||
s_tdata_next = s_tdata_reg;
|
||||
|
||||
gmii_txd_next = 8'd0;
|
||||
gmii_tx_en_next = 1'b0;
|
||||
gmii_tx_er_next = 1'b0;
|
||||
|
||||
if (!clk_enable) begin
|
||||
// clock disabled - hold state and outputs
|
||||
gmii_txd_next = gmii_txd_reg;
|
||||
gmii_tx_en_next = gmii_tx_en_reg;
|
||||
gmii_tx_er_next = gmii_tx_er_reg;
|
||||
state_next = state_reg;
|
||||
end else if (mii_select && mii_odd_reg) begin
|
||||
// MII odd cycle - hold state, output MSN
|
||||
mii_odd_next = 1'b0;
|
||||
gmii_txd_next = {4'd0, mii_msn_reg};
|
||||
gmii_tx_en_next = gmii_tx_en_reg;
|
||||
gmii_tx_er_next = gmii_tx_er_reg;
|
||||
state_next = state_reg;
|
||||
end else begin
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for packet
|
||||
reset_crc = 1'b1;
|
||||
mii_odd_next = 1'b0;
|
||||
|
||||
if (s_axis_tvalid) begin
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = 16'd1;
|
||||
gmii_txd_next = ETH_PRE;
|
||||
gmii_tx_en_next = 1'b1;
|
||||
state_next = STATE_PREAMBLE;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_PREAMBLE: begin
|
||||
// send preamble
|
||||
reset_crc = 1'b1;
|
||||
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
|
||||
gmii_txd_next = ETH_PRE;
|
||||
gmii_tx_en_next = 1'b1;
|
||||
|
||||
if (frame_ptr_reg == 16'd6) begin
|
||||
s_axis_tready_next = 1'b1;
|
||||
s_tdata_next = s_axis_tdata;
|
||||
state_next = STATE_PREAMBLE;
|
||||
end else if (frame_ptr_reg == 16'd7) begin
|
||||
// end of preamble; start payload
|
||||
frame_ptr_next = 16'd0;
|
||||
if (s_axis_tready_reg) begin
|
||||
s_axis_tready_next = 1'b1;
|
||||
s_tdata_next = s_axis_tdata;
|
||||
end
|
||||
gmii_txd_next = ETH_SFD;
|
||||
state_next = STATE_PAYLOAD;
|
||||
end else begin
|
||||
state_next = STATE_PREAMBLE;
|
||||
end
|
||||
end
|
||||
STATE_PAYLOAD: begin
|
||||
// send payload
|
||||
|
||||
update_crc = 1'b1;
|
||||
s_axis_tready_next = 1'b1;
|
||||
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
|
||||
gmii_txd_next = s_tdata_reg;
|
||||
gmii_tx_en_next = 1'b1;
|
||||
|
||||
s_tdata_next = s_axis_tdata;
|
||||
|
||||
if (s_axis_tvalid) begin
|
||||
if (s_axis_tlast) begin
|
||||
s_axis_tready_next = !s_axis_tready_reg;
|
||||
if (s_axis_tuser) begin
|
||||
gmii_tx_er_next = 1'b1;
|
||||
frame_ptr_next = 1'b0;
|
||||
state_next = STATE_IFG;
|
||||
end else begin
|
||||
state_next = STATE_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_PAYLOAD;
|
||||
end
|
||||
end else begin
|
||||
// tvalid deassert, fail frame
|
||||
gmii_tx_er_next = 1'b1;
|
||||
frame_ptr_next = 16'd0;
|
||||
state_next = STATE_WAIT_END;
|
||||
end
|
||||
end
|
||||
STATE_LAST: begin
|
||||
// last payload word
|
||||
|
||||
update_crc = 1'b1;
|
||||
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
|
||||
gmii_txd_next = s_tdata_reg;
|
||||
gmii_tx_en_next = 1'b1;
|
||||
|
||||
if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin
|
||||
s_tdata_next = 8'd0;
|
||||
state_next = STATE_PAD;
|
||||
end else begin
|
||||
frame_ptr_next = 16'd0;
|
||||
state_next = STATE_FCS;
|
||||
end
|
||||
end
|
||||
STATE_PAD: begin
|
||||
// send padding
|
||||
|
||||
update_crc = 1'b1;
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
|
||||
gmii_txd_next = 8'd0;
|
||||
gmii_tx_en_next = 1'b1;
|
||||
|
||||
s_tdata_next = 8'd0;
|
||||
|
||||
if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin
|
||||
state_next = STATE_PAD;
|
||||
end else begin
|
||||
frame_ptr_next = 16'd0;
|
||||
state_next = STATE_FCS;
|
||||
end
|
||||
end
|
||||
STATE_FCS: begin
|
||||
// send FCS
|
||||
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
|
||||
case (frame_ptr_reg)
|
||||
2'd0: gmii_txd_next = ~crc_state[7:0];
|
||||
2'd1: gmii_txd_next = ~crc_state[15:8];
|
||||
2'd2: gmii_txd_next = ~crc_state[23:16];
|
||||
2'd3: gmii_txd_next = ~crc_state[31:24];
|
||||
endcase
|
||||
gmii_tx_en_next = 1'b1;
|
||||
|
||||
if (frame_ptr_reg < 3) begin
|
||||
state_next = STATE_FCS;
|
||||
end else begin
|
||||
frame_ptr_next = 16'd0;
|
||||
state_next = STATE_IFG;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_END: begin
|
||||
// wait for end of frame
|
||||
|
||||
reset_crc = 1'b1;
|
||||
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
s_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_axis_tvalid) begin
|
||||
if (s_axis_tlast) begin
|
||||
s_axis_tready_next = 1'b0;
|
||||
if (frame_ptr_reg < ifg_delay-1) begin
|
||||
state_next = STATE_IFG;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WAIT_END;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WAIT_END;
|
||||
end
|
||||
end
|
||||
STATE_IFG: begin
|
||||
// send IFG
|
||||
|
||||
reset_crc = 1'b1;
|
||||
|
||||
mii_odd_next = 1'b1;
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
|
||||
if (frame_ptr_reg < ifg_delay-1) begin
|
||||
state_next = STATE_IFG;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
if (mii_select) begin
|
||||
mii_msn_next = gmii_txd_next[7:4];
|
||||
gmii_txd_next[7:4] = 4'd0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
|
||||
frame_ptr_reg <= 16'd0;
|
||||
|
||||
s_axis_tready_reg <= 1'b0;
|
||||
|
||||
gmii_tx_en_reg <= 1'b0;
|
||||
gmii_tx_er_reg <= 1'b0;
|
||||
|
||||
crc_state <= 32'hFFFFFFFF;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
s_axis_tready_reg <= s_axis_tready_next;
|
||||
|
||||
gmii_tx_en_reg <= gmii_tx_en_next;
|
||||
gmii_tx_er_reg <= gmii_tx_er_next;
|
||||
|
||||
// datapath
|
||||
if (reset_crc) begin
|
||||
crc_state <= 32'hFFFFFFFF;
|
||||
end else if (update_crc) begin
|
||||
crc_state <= crc_next;
|
||||
end
|
||||
end
|
||||
|
||||
mii_odd_reg <= mii_odd_next;
|
||||
mii_msn_reg <= mii_msn_next;
|
||||
|
||||
s_tdata_reg <= s_tdata_next;
|
||||
|
||||
gmii_txd_reg <= gmii_txd_next;
|
||||
end
|
||||
|
||||
endmodule
|
0
fpga/src/defines.sv
Normal file
0
fpga/src/defines.sv
Normal file
67
fpga/src/dualmem_widen.sv
Normal file
67
fpga/src/dualmem_widen.sv
Normal file
|
@ -0,0 +1,67 @@
|
|||
|
||||
module dualmem_widen(clka, clkb, dina, dinb, addra, addrb, wea, web, douta, doutb, ena, enb);
|
||||
|
||||
input wire clka, clkb;
|
||||
input [15:0] dina;
|
||||
input [63:0] dinb;
|
||||
input [10:0] addra;
|
||||
input [8:0] addrb;
|
||||
input [1:0] wea;
|
||||
input [1:0] web;
|
||||
input [0:0] ena, enb;
|
||||
output [15:0] douta;
|
||||
output [63:0] doutb;
|
||||
|
||||
genvar r;
|
||||
wire [47:0] dout;
|
||||
|
||||
`ifdef GENESYSII
|
||||
|
||||
generate for (r = 0; r < 2; r=r+1)
|
||||
RAMB16_S9_S36
|
||||
RAMB16_S9_S36_inst
|
||||
(
|
||||
.CLKA ( clka ), // Port A Clock
|
||||
.DOA ( douta[r*8 +: 8] ), // Port A 1-bit Data Output
|
||||
.DOPA ( ),
|
||||
.ADDRA ( addra ), // Port A 14-bit Address Input
|
||||
.DIA ( dina[r*8 +: 8] ), // Port A 1-bit Data Input
|
||||
.DIPA ( 1'b0 ),
|
||||
.ENA ( ena ), // Port A RAM Enable Input
|
||||
.SSRA ( 1'b0 ), // Port A Synchronous Set/Reset Input
|
||||
.WEA ( wea[r] ), // Port A Write Enable Input
|
||||
.CLKB ( clkb ), // Port B Clock
|
||||
.DOB ( doutb[r*32 +: 32] ), // Port B 1-bit Data Output
|
||||
.DOPB ( ),
|
||||
.ADDRB ( addrb ), // Port B 14-bit Address Input
|
||||
.DIB ( dinb[r*32 +: 32] ), // Port B 1-bit Data Input
|
||||
.DIPB ( 4'b0 ),
|
||||
.ENB ( enb ), // Port B RAM Enable Input
|
||||
.SSRB ( 1'b0 ), // Port B Synchronous Set/Reset Input
|
||||
.WEB ( web[r] ) // Port B Write Enable Input
|
||||
);
|
||||
endgenerate
|
||||
|
||||
`else // !`ifdef FPGA
|
||||
|
||||
// This bit is a placeholder
|
||||
|
||||
infer_dpram #(.RAM_SIZE(11), .BYTE_WIDTH(8)) ram1 // RAM_SIZE is in words
|
||||
(
|
||||
.ram_clk_a(clka),
|
||||
.ram_en_a(|ena),
|
||||
.ram_we_a({wea[1],wea[1],wea[1],wea[1],wea[0],wea[0],wea[0],wea[0]}),
|
||||
.ram_addr_a(addra),
|
||||
.ram_wrdata_a({dina,dina,dina,dina}),
|
||||
.ram_rddata_a({dout,douta}),
|
||||
.ram_clk_b(clkb),
|
||||
.ram_en_b(|enb),
|
||||
.ram_we_b({web[1],web[1],web[1],web[1],web[0],web[0],web[0],web[0]}),
|
||||
.ram_addr_b({2'b0,addrb}),
|
||||
.ram_wrdata_b(dinb),
|
||||
.ram_rddata_b(doutb)
|
||||
);
|
||||
|
||||
`endif
|
||||
|
||||
endmodule // dualmem
|
89
fpga/src/dualmem_widen8.sv
Normal file
89
fpga/src/dualmem_widen8.sv
Normal file
|
@ -0,0 +1,89 @@
|
|||
|
||||
module dualmem_widen8(clka, clkb, dina, dinb, addra, addrb, wea, web, douta, doutb, ena, enb);
|
||||
|
||||
input wire clka, clkb;
|
||||
input [15:0] dina;
|
||||
input [63:0] dinb;
|
||||
input [12:0] addra;
|
||||
input [10:0] addrb;
|
||||
input [1:0] wea;
|
||||
input [1:0] web;
|
||||
input [0:0] ena, enb;
|
||||
output [15:0] douta;
|
||||
output [63:0] doutb;
|
||||
|
||||
genvar r;
|
||||
wire [63:0] dout0;
|
||||
wire [255:0] dout1;
|
||||
wire [7:0] we0, we1, en0, en1;
|
||||
wire [63:0] din0;
|
||||
wire [255:0] din1;
|
||||
|
||||
reg [12:0] addra_dly;
|
||||
reg [10:0] addrb_dly;
|
||||
|
||||
`ifdef GENESYSII
|
||||
|
||||
assign douta = dout0 >> {addra_dly[12:11],4'b0000};
|
||||
assign doutb = dout1 >> {addrb_dly[10:9],6'b000000};
|
||||
assign we0 = wea << {addra[12:11],1'b0};
|
||||
assign we1 = web << {addrb[10:9],1'b0};
|
||||
assign en0 = {ena,ena} << {addra[12:11],1'b0};
|
||||
assign en1 = {enb,enb} << {addrb[10:9],1'b0};
|
||||
assign din0 = {dina,dina,dina,dina};
|
||||
assign din1 = {dinb,dinb,dinb,dinb};
|
||||
|
||||
always @(posedge clka)
|
||||
begin
|
||||
addra_dly <= addra;
|
||||
addrb_dly <= addrb;
|
||||
end
|
||||
|
||||
generate for (r = 0; r < 8; r=r+1)
|
||||
RAMB16_S9_S36
|
||||
RAMB16_S9_S36_inst
|
||||
(
|
||||
.CLKA ( clka ), // Port A Clock
|
||||
.DOA ( dout0[r*8 +: 8] ), // Port A 1-bit Data Output
|
||||
.DOPA ( ),
|
||||
.ADDRA ( addra[10:0] ), // Port A 14-bit Address Input
|
||||
.DIA ( din0[r*8 +: 8] ), // Port A 1-bit Data Input
|
||||
.DIPA ( 1'b0 ),
|
||||
.ENA ( en0[r] ), // Port A RAM Enable Input
|
||||
.SSRA ( 1'b0 ), // Port A Synchronous Set/Reset Input
|
||||
.WEA ( we0[r] ), // Port A Write Enable Input
|
||||
.CLKB ( clkb ), // Port B Clock
|
||||
.DOB ( dout1[r*32 +: 32] ), // Port B 1-bit Data Output
|
||||
.DOPB ( ),
|
||||
.ADDRB ( addrb[8:0] ), // Port B 14-bit Address Input
|
||||
.DIB ( din1[r*32 +: 32] ), // Port B 1-bit Data Input
|
||||
.DIPB ( 4'b0 ),
|
||||
.ENB ( en1[r] ), // Port B RAM Enable Input
|
||||
.SSRB ( 1'b0 ), // Port B Synchronous Set/Reset Input
|
||||
.WEB ( we1[r] ) // Port B Write Enable Input
|
||||
);
|
||||
endgenerate
|
||||
|
||||
`else // !`ifdef FPGA
|
||||
|
||||
// This bit is a placeholder
|
||||
|
||||
infer_dpram #(.RAM_SIZE(11), .BYTE_WIDTH(8)) ram1 // RAM_SIZE is in words
|
||||
(
|
||||
.ram_clk_a(clka),
|
||||
.ram_en_a(|ena),
|
||||
.ram_we_a({wea[1],wea[1],wea[1],wea[1],wea[0],wea[0],wea[0],wea[0]}),
|
||||
.ram_addr_a(addra),
|
||||
.ram_wrdata_a({dina,dina,dina,dina}),
|
||||
.ram_rddata_a({dout,douta}),
|
||||
.ram_clk_b(clkb),
|
||||
.ram_en_b(|enb),
|
||||
.ram_we_b({web[1],web[1],web[1],web[1],web[0],web[0],web[0],web[0]}),
|
||||
.ram_addr_b({2'b0,addrb}),
|
||||
.ram_wrdata_b(dinb),
|
||||
.ram_rddata_b(doutb)
|
||||
);
|
||||
|
||||
`endif
|
||||
|
||||
endmodule // dualmem
|
316
fpga/src/eth_arb_mux.sv
Normal file
316
fpga/src/eth_arb_mux.sv
Normal file
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Ethernet arbitrated multiplexer
|
||||
*/
|
||||
module eth_arb_mux #
|
||||
(
|
||||
parameter S_COUNT = 4,
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter KEEP_ENABLE = (DATA_WIDTH>8),
|
||||
parameter KEEP_WIDTH = (DATA_WIDTH/8),
|
||||
parameter ID_ENABLE = 0,
|
||||
parameter ID_WIDTH = 8,
|
||||
parameter DEST_ENABLE = 0,
|
||||
parameter DEST_WIDTH = 8,
|
||||
parameter USER_ENABLE = 1,
|
||||
parameter USER_WIDTH = 1,
|
||||
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
|
||||
parameter ARB_TYPE = "PRIORITY",
|
||||
// LSB priority: "LOW", "HIGH"
|
||||
parameter LSB_PRIORITY = "HIGH"
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame inputs
|
||||
*/
|
||||
input wire [S_COUNT-1:0] s_eth_hdr_valid,
|
||||
output wire [S_COUNT-1:0] s_eth_hdr_ready,
|
||||
input wire [S_COUNT*48-1:0] s_eth_dest_mac,
|
||||
input wire [S_COUNT*48-1:0] s_eth_src_mac,
|
||||
input wire [S_COUNT*16-1:0] s_eth_type,
|
||||
input wire [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata,
|
||||
input wire [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep,
|
||||
input wire [S_COUNT-1:0] s_eth_payload_axis_tvalid,
|
||||
output wire [S_COUNT-1:0] s_eth_payload_axis_tready,
|
||||
input wire [S_COUNT-1:0] s_eth_payload_axis_tlast,
|
||||
input wire [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid,
|
||||
input wire [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest,
|
||||
input wire [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata,
|
||||
output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire [ID_WIDTH-1:0] m_eth_payload_axis_tid,
|
||||
output wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest,
|
||||
output wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser
|
||||
);
|
||||
|
||||
parameter CL_S_COUNT = $clog2(S_COUNT);
|
||||
|
||||
reg frame_reg = 1'b0, frame_next;
|
||||
|
||||
reg s_eth_hdr_ready_mask_reg = 1'b0, s_eth_hdr_ready_mask_next;
|
||||
|
||||
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next;
|
||||
reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next;
|
||||
|
||||
wire [S_COUNT-1:0] request;
|
||||
wire [S_COUNT-1:0] acknowledge;
|
||||
wire [S_COUNT-1:0] grant;
|
||||
wire grant_valid;
|
||||
wire [CL_S_COUNT-1:0] grant_encoded;
|
||||
|
||||
// internal datapath
|
||||
reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int;
|
||||
reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int;
|
||||
reg m_eth_payload_axis_tvalid_int;
|
||||
reg m_eth_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_eth_payload_axis_tlast_int;
|
||||
reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int;
|
||||
reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int;
|
||||
reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int;
|
||||
wire m_eth_payload_axis_tready_int_early;
|
||||
|
||||
assign s_eth_hdr_ready = (!s_eth_hdr_ready_mask_reg && grant_valid) << grant_encoded;
|
||||
|
||||
assign s_eth_payload_axis_tready = (m_eth_payload_axis_tready_int_reg && grant_valid) << grant_encoded;
|
||||
|
||||
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
|
||||
// mux for incoming packet
|
||||
wire [DATA_WIDTH-1:0] current_s_tdata = s_eth_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH];
|
||||
wire [KEEP_WIDTH-1:0] current_s_tkeep = s_eth_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH];
|
||||
wire current_s_tvalid = s_eth_payload_axis_tvalid[grant_encoded];
|
||||
wire current_s_tready = s_eth_payload_axis_tready[grant_encoded];
|
||||
wire current_s_tlast = s_eth_payload_axis_tlast[grant_encoded];
|
||||
wire [ID_WIDTH-1:0] current_s_tid = s_eth_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH];
|
||||
wire [DEST_WIDTH-1:0] current_s_tdest = s_eth_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH];
|
||||
wire [USER_WIDTH-1:0] current_s_tuser = s_eth_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH];
|
||||
|
||||
// arbiter instance
|
||||
rgmii_arbiter #(
|
||||
.PORTS(S_COUNT),
|
||||
.TYPE(ARB_TYPE),
|
||||
.BLOCK("ACKNOWLEDGE"),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
arb_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.request(request),
|
||||
.acknowledge(acknowledge),
|
||||
.grant(grant),
|
||||
.grant_valid(grant_valid),
|
||||
.grant_encoded(grant_encoded)
|
||||
);
|
||||
|
||||
generate
|
||||
genvar n;
|
||||
|
||||
for (n = 0; n < S_COUNT; n = n + 1) begin
|
||||
assign request[n] = s_eth_hdr_valid[n] && !grant[n];
|
||||
assign acknowledge[n] = grant[n] && s_eth_payload_axis_tvalid[n] && s_eth_payload_axis_tready[n] && s_eth_payload_axis_tlast[n];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @* begin
|
||||
frame_next = frame_reg;
|
||||
|
||||
s_eth_hdr_ready_mask_next = s_eth_hdr_ready_mask_reg;
|
||||
|
||||
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
|
||||
m_eth_dest_mac_next = m_eth_dest_mac_reg;
|
||||
m_eth_src_mac_next = m_eth_src_mac_reg;
|
||||
m_eth_type_next = m_eth_type_reg;
|
||||
|
||||
if (s_eth_payload_axis_tvalid[grant_encoded] && s_eth_payload_axis_tready[grant_encoded]) begin
|
||||
// end of frame detection
|
||||
if (s_eth_payload_axis_tlast[grant_encoded]) begin
|
||||
frame_next = 1'b0;
|
||||
s_eth_hdr_ready_mask_next = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
if (!frame_reg && grant_valid) begin
|
||||
// start of frame
|
||||
frame_next = 1'b1;
|
||||
|
||||
s_eth_hdr_ready_mask_next = 1'b1;
|
||||
|
||||
m_eth_hdr_valid_next = 1'b1;
|
||||
m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48];
|
||||
m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48];
|
||||
m_eth_type_next = s_eth_type[grant_encoded*16 +: 16];
|
||||
end
|
||||
|
||||
// pass through selected packet data
|
||||
m_eth_payload_axis_tdata_int = current_s_tdata;
|
||||
m_eth_payload_axis_tkeep_int = current_s_tkeep;
|
||||
m_eth_payload_axis_tvalid_int = current_s_tvalid && m_eth_payload_axis_tready_int_reg && grant_valid;
|
||||
m_eth_payload_axis_tlast_int = current_s_tlast;
|
||||
m_eth_payload_axis_tid_int = current_s_tid;
|
||||
m_eth_payload_axis_tdest_int = current_s_tdest;
|
||||
m_eth_payload_axis_tuser_int = current_s_tuser;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
frame_reg <= 1'b0;
|
||||
s_eth_hdr_ready_mask_reg <= 1'b0;
|
||||
m_eth_hdr_valid_reg <= 1'b0;
|
||||
end else begin
|
||||
frame_reg <= frame_next;
|
||||
s_eth_hdr_ready_mask_reg <= s_eth_hdr_ready_mask_next;
|
||||
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
|
||||
end
|
||||
|
||||
m_eth_dest_mac_reg <= m_eth_dest_mac_next;
|
||||
m_eth_src_mac_reg <= m_eth_src_mac_next;
|
||||
m_eth_type_reg <= m_eth_type_next;
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
|
||||
reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
|
||||
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
|
||||
reg m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
|
||||
reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
|
||||
reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
|
||||
|
||||
reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
|
||||
reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
|
||||
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
|
||||
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
|
||||
reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
|
||||
reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
|
||||
|
||||
// datapath control
|
||||
reg store_axis_int_to_output;
|
||||
reg store_axis_int_to_temp;
|
||||
reg store_eth_payload_axis_temp_to_output;
|
||||
|
||||
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
|
||||
assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
|
||||
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
|
||||
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
|
||||
assign m_eth_payload_axis_tid = ID_ENABLE ? m_eth_payload_axis_tid_reg : {ID_WIDTH{1'b0}};
|
||||
assign m_eth_payload_axis_tdest = DEST_ENABLE ? m_eth_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}};
|
||||
assign m_eth_payload_axis_tuser = USER_ENABLE ? m_eth_payload_axis_tuser_reg : {USER_WIDTH{1'b0}};
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
|
||||
store_axis_int_to_output = 1'b0;
|
||||
store_axis_int_to_temp = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_axis_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_axis_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_eth_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_eth_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
|
||||
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_axis_int_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int;
|
||||
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int;
|
||||
m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int;
|
||||
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end else if (store_eth_payload_axis_temp_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
|
||||
m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg;
|
||||
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
|
||||
m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg;
|
||||
m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg;
|
||||
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_axis_int_to_temp) begin
|
||||
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int;
|
||||
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int;
|
||||
temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int;
|
||||
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
370
fpga/src/eth_axis_rx.sv
Normal file
370
fpga/src/eth_axis_rx.sv
Normal file
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream ethernet frame receiver (AXI in, Ethernet frame out)
|
||||
*/
|
||||
module eth_axis_rx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] s_axis_tdata,
|
||||
input wire s_axis_tvalid,
|
||||
output wire s_axis_tready,
|
||||
input wire s_axis_tlast,
|
||||
input wire s_axis_tuser,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [7:0] m_eth_payload_axis_tdata,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire m_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy,
|
||||
output wire error_header_early_termination
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
Ethernet frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype 2 octets
|
||||
|
||||
This module receives an Ethernet frame on an AXI stream interface, decodes
|
||||
and strips the headers, then produces the header fields in parallel along
|
||||
with the payload in a separate AXI stream.
|
||||
|
||||
*/
|
||||
|
||||
localparam [1:0]
|
||||
STATE_IDLE = 2'd0,
|
||||
STATE_READ_HEADER = 2'd1,
|
||||
STATE_READ_PAYLOAD = 2'd2;
|
||||
|
||||
reg [1:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_eth_dest_mac_0;
|
||||
reg store_eth_dest_mac_1;
|
||||
reg store_eth_dest_mac_2;
|
||||
reg store_eth_dest_mac_3;
|
||||
reg store_eth_dest_mac_4;
|
||||
reg store_eth_dest_mac_5;
|
||||
reg store_eth_src_mac_0;
|
||||
reg store_eth_src_mac_1;
|
||||
reg store_eth_src_mac_2;
|
||||
reg store_eth_src_mac_3;
|
||||
reg store_eth_src_mac_4;
|
||||
reg store_eth_src_mac_5;
|
||||
reg store_eth_type_0;
|
||||
reg store_eth_type_1;
|
||||
|
||||
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
|
||||
|
||||
reg s_axis_tready_reg = 1'b0, s_axis_tready_next;
|
||||
|
||||
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] m_eth_payload_axis_tdata_int;
|
||||
reg m_eth_payload_axis_tvalid_int;
|
||||
reg m_eth_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_eth_payload_axis_tlast_int;
|
||||
reg m_eth_payload_axis_tuser_int;
|
||||
wire m_eth_payload_axis_tready_int_early;
|
||||
|
||||
assign s_axis_tready = s_axis_tready_reg;
|
||||
|
||||
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
assign error_header_early_termination = error_header_early_termination_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_axis_tready_next = 1'b0;
|
||||
|
||||
store_eth_dest_mac_0 = 1'b0;
|
||||
store_eth_dest_mac_1 = 1'b0;
|
||||
store_eth_dest_mac_2 = 1'b0;
|
||||
store_eth_dest_mac_3 = 1'b0;
|
||||
store_eth_dest_mac_4 = 1'b0;
|
||||
store_eth_dest_mac_5 = 1'b0;
|
||||
store_eth_src_mac_0 = 1'b0;
|
||||
store_eth_src_mac_1 = 1'b0;
|
||||
store_eth_src_mac_2 = 1'b0;
|
||||
store_eth_src_mac_3 = 1'b0;
|
||||
store_eth_src_mac_4 = 1'b0;
|
||||
store_eth_src_mac_5 = 1'b0;
|
||||
store_eth_type_0 = 1'b0;
|
||||
store_eth_type_1 = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
|
||||
|
||||
error_header_early_termination_next = 1'b0;
|
||||
|
||||
m_eth_payload_axis_tdata_int = 8'd0;
|
||||
m_eth_payload_axis_tvalid_int = 1'b0;
|
||||
m_eth_payload_axis_tlast_int = 1'b0;
|
||||
m_eth_payload_axis_tuser_int = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for data
|
||||
frame_ptr_next = 8'd0;
|
||||
s_axis_tready_next = !m_eth_hdr_valid_reg;
|
||||
|
||||
if (s_axis_tready && s_axis_tvalid) begin
|
||||
// got first word of packet
|
||||
if (s_axis_tlast) begin
|
||||
// tlast asserted on first word
|
||||
error_header_early_termination_next = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
// move to read header state
|
||||
frame_ptr_next = 1'b1;
|
||||
store_eth_dest_mac_5 = 1'b1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_READ_HEADER: begin
|
||||
// read header
|
||||
s_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_axis_tready && s_axis_tvalid) begin
|
||||
// word transfer in - store it
|
||||
frame_ptr_next = frame_ptr_reg + 8'd1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
case (frame_ptr_reg)
|
||||
8'h00: store_eth_dest_mac_5 = 1'b1;
|
||||
8'h01: store_eth_dest_mac_4 = 1'b1;
|
||||
8'h02: store_eth_dest_mac_3 = 1'b1;
|
||||
8'h03: store_eth_dest_mac_2 = 1'b1;
|
||||
8'h04: store_eth_dest_mac_1 = 1'b1;
|
||||
8'h05: store_eth_dest_mac_0 = 1'b1;
|
||||
8'h06: store_eth_src_mac_5 = 1'b1;
|
||||
8'h07: store_eth_src_mac_4 = 1'b1;
|
||||
8'h08: store_eth_src_mac_3 = 1'b1;
|
||||
8'h09: store_eth_src_mac_2 = 1'b1;
|
||||
8'h0A: store_eth_src_mac_1 = 1'b1;
|
||||
8'h0B: store_eth_src_mac_0 = 1'b1;
|
||||
8'h0C: store_eth_type_1 = 1'b1;
|
||||
8'h0D: begin
|
||||
store_eth_type_0 = 1'b1;
|
||||
m_eth_hdr_valid_next = 1'b1;
|
||||
s_axis_tready_next = m_eth_payload_axis_tready_int_early;
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
endcase
|
||||
if (s_axis_tlast) begin
|
||||
error_header_early_termination_next = 1'b1;
|
||||
s_axis_tready_next = !m_eth_hdr_valid_reg;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_READ_HEADER;
|
||||
end
|
||||
end
|
||||
STATE_READ_PAYLOAD: begin
|
||||
// read payload
|
||||
s_axis_tready_next = m_eth_payload_axis_tready_int_early;
|
||||
|
||||
m_eth_payload_axis_tdata_int = s_axis_tdata;
|
||||
m_eth_payload_axis_tvalid_int = s_axis_tvalid;
|
||||
m_eth_payload_axis_tlast_int = s_axis_tlast;
|
||||
m_eth_payload_axis_tuser_int = s_axis_tuser;
|
||||
|
||||
if (s_axis_tready && s_axis_tvalid) begin
|
||||
// word transfer through
|
||||
if (s_axis_tlast) begin
|
||||
s_axis_tready_next = !m_eth_hdr_valid_reg;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 8'd0;
|
||||
s_axis_tready_reg <= 1'b0;
|
||||
m_eth_hdr_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
error_header_early_termination_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
s_axis_tready_reg <= s_axis_tready_next;
|
||||
|
||||
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
|
||||
|
||||
error_header_early_termination_reg <= error_header_early_termination_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_eth_dest_mac_0) m_eth_dest_mac_reg[ 7: 0] <= s_axis_tdata;
|
||||
if (store_eth_dest_mac_1) m_eth_dest_mac_reg[15: 8] <= s_axis_tdata;
|
||||
if (store_eth_dest_mac_2) m_eth_dest_mac_reg[23:16] <= s_axis_tdata;
|
||||
if (store_eth_dest_mac_3) m_eth_dest_mac_reg[31:24] <= s_axis_tdata;
|
||||
if (store_eth_dest_mac_4) m_eth_dest_mac_reg[39:32] <= s_axis_tdata;
|
||||
if (store_eth_dest_mac_5) m_eth_dest_mac_reg[47:40] <= s_axis_tdata;
|
||||
if (store_eth_src_mac_0) m_eth_src_mac_reg[ 7: 0] <= s_axis_tdata;
|
||||
if (store_eth_src_mac_1) m_eth_src_mac_reg[15: 8] <= s_axis_tdata;
|
||||
if (store_eth_src_mac_2) m_eth_src_mac_reg[23:16] <= s_axis_tdata;
|
||||
if (store_eth_src_mac_3) m_eth_src_mac_reg[31:24] <= s_axis_tdata;
|
||||
if (store_eth_src_mac_4) m_eth_src_mac_reg[39:32] <= s_axis_tdata;
|
||||
if (store_eth_src_mac_5) m_eth_src_mac_reg[47:40] <= s_axis_tdata;
|
||||
if (store_eth_type_0) m_eth_type_reg[ 7: 0] <= s_axis_tdata;
|
||||
if (store_eth_type_1) m_eth_type_reg[15: 8] <= s_axis_tdata;
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0;
|
||||
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
|
||||
reg m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg m_eth_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0;
|
||||
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
|
||||
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg temp_m_eth_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_eth_payload_int_to_output;
|
||||
reg store_eth_payload_int_to_temp;
|
||||
reg store_eth_payload_axis_temp_to_output;
|
||||
|
||||
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
|
||||
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
|
||||
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
|
||||
assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
|
||||
store_eth_payload_int_to_output = 1'b0;
|
||||
store_eth_payload_int_to_temp = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_eth_payload_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_eth_payload_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_eth_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_eth_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
|
||||
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_eth_payload_int_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end else if (store_eth_payload_axis_temp_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
|
||||
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
|
||||
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_eth_payload_int_to_temp) begin
|
||||
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
312
fpga/src/eth_axis_tx.sv
Normal file
312
fpga/src/eth_axis_tx.sv
Normal file
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream ethernet frame transmitter (Ethernet frame in, AXI out)
|
||||
*/
|
||||
module eth_axis_tx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame input
|
||||
*/
|
||||
input wire s_eth_hdr_valid,
|
||||
output wire s_eth_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [7:0] s_eth_payload_axis_tdata,
|
||||
input wire s_eth_payload_axis_tvalid,
|
||||
output wire s_eth_payload_axis_tready,
|
||||
input wire s_eth_payload_axis_tlast,
|
||||
input wire s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] m_axis_tdata,
|
||||
output wire m_axis_tvalid,
|
||||
input wire m_axis_tready,
|
||||
output wire m_axis_tlast,
|
||||
output wire m_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
Ethernet frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype 2 octets
|
||||
|
||||
This module receives an Ethernet frame with header fields in parallel along
|
||||
with the payload in an AXI stream, combines the header with the payload, and
|
||||
transmits the complete Ethernet frame on the output AXI stream interface.
|
||||
|
||||
*/
|
||||
|
||||
localparam [1:0]
|
||||
STATE_IDLE = 2'd0,
|
||||
STATE_WRITE_HEADER = 2'd1,
|
||||
STATE_WRITE_PAYLOAD = 2'd2;
|
||||
|
||||
reg [1:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_eth_hdr;
|
||||
|
||||
reg [7:0] frame_ptr_reg = 8'd0, frame_ptr_next;
|
||||
|
||||
reg [47:0] eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] eth_type_reg = 16'd0;
|
||||
|
||||
reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next;
|
||||
reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] m_axis_tdata_int;
|
||||
reg m_axis_tvalid_int;
|
||||
reg m_axis_tready_int_reg = 1'b0;
|
||||
reg m_axis_tlast_int;
|
||||
reg m_axis_tuser_int;
|
||||
wire m_axis_tready_int_early;
|
||||
|
||||
assign s_eth_hdr_ready = s_eth_hdr_ready_reg;
|
||||
assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_eth_hdr_ready_next = 1'b0;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
|
||||
store_eth_hdr = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
m_axis_tdata_int = 8'd0;
|
||||
m_axis_tvalid_int = 1'b0;
|
||||
m_axis_tlast_int = 1'b0;
|
||||
m_axis_tuser_int = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for data
|
||||
frame_ptr_next = 8'd0;
|
||||
s_eth_hdr_ready_next = 1'b1;
|
||||
|
||||
if (s_eth_hdr_ready && s_eth_hdr_valid) begin
|
||||
store_eth_hdr = 1'b1;
|
||||
s_eth_hdr_ready_next = 1'b0;
|
||||
if (m_axis_tready_int_reg) begin
|
||||
m_axis_tvalid_int = 1'b1;
|
||||
m_axis_tdata_int = s_eth_dest_mac[47:40];
|
||||
frame_ptr_next = 1'b1;
|
||||
end
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_HEADER: begin
|
||||
// write header
|
||||
if (m_axis_tready_int_reg) begin
|
||||
frame_ptr_next = frame_ptr_reg+1;
|
||||
m_axis_tvalid_int = 1'b1;
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
case (frame_ptr_reg)
|
||||
8'h00: m_axis_tdata_int = eth_dest_mac_reg[47:40];
|
||||
8'h01: m_axis_tdata_int = eth_dest_mac_reg[39:32];
|
||||
8'h02: m_axis_tdata_int = eth_dest_mac_reg[31:24];
|
||||
8'h03: m_axis_tdata_int = eth_dest_mac_reg[23:16];
|
||||
8'h04: m_axis_tdata_int = eth_dest_mac_reg[15: 8];
|
||||
8'h05: m_axis_tdata_int = eth_dest_mac_reg[ 7: 0];
|
||||
8'h06: m_axis_tdata_int = eth_src_mac_reg[47:40];
|
||||
8'h07: m_axis_tdata_int = eth_src_mac_reg[39:32];
|
||||
8'h08: m_axis_tdata_int = eth_src_mac_reg[31:24];
|
||||
8'h09: m_axis_tdata_int = eth_src_mac_reg[23:16];
|
||||
8'h0A: m_axis_tdata_int = eth_src_mac_reg[15: 8];
|
||||
8'h0B: m_axis_tdata_int = eth_src_mac_reg[ 7: 0];
|
||||
8'h0C: m_axis_tdata_int = eth_type_reg[15: 8];
|
||||
8'h0D: begin
|
||||
m_axis_tdata_int = eth_type_reg[ 7: 0];
|
||||
s_eth_payload_axis_tready_next = m_axis_tready_int_early;
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
endcase
|
||||
end else begin
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_PAYLOAD: begin
|
||||
// write payload
|
||||
s_eth_payload_axis_tready_next = m_axis_tready_int_early;
|
||||
|
||||
m_axis_tdata_int = s_eth_payload_axis_tdata;
|
||||
m_axis_tvalid_int = s_eth_payload_axis_tvalid;
|
||||
m_axis_tlast_int = s_eth_payload_axis_tlast;
|
||||
m_axis_tuser_int = s_eth_payload_axis_tuser;
|
||||
|
||||
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
|
||||
// word transfer through
|
||||
if (s_eth_payload_axis_tlast) begin
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
s_eth_hdr_ready_next = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 8'd0;
|
||||
s_eth_hdr_ready_reg <= 1'b0;
|
||||
s_eth_payload_axis_tready_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
|
||||
|
||||
s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_eth_hdr) begin
|
||||
eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
eth_src_mac_reg <= s_eth_src_mac;
|
||||
eth_type_reg <= s_eth_type;
|
||||
end
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] m_axis_tdata_reg = 8'd0;
|
||||
reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next;
|
||||
reg m_axis_tlast_reg = 1'b0;
|
||||
reg m_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_m_axis_tdata_reg = 8'd0;
|
||||
reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next;
|
||||
reg temp_m_axis_tlast_reg = 1'b0;
|
||||
reg temp_m_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_axis_int_to_output;
|
||||
reg store_axis_int_to_temp;
|
||||
reg store_axis_temp_to_output;
|
||||
|
||||
assign m_axis_tdata = m_axis_tdata_reg;
|
||||
assign m_axis_tvalid = m_axis_tvalid_reg;
|
||||
assign m_axis_tlast = m_axis_tlast_reg;
|
||||
assign m_axis_tuser = m_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_axis_tvalid_next = m_axis_tvalid_reg;
|
||||
temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg;
|
||||
|
||||
store_axis_int_to_output = 1'b0;
|
||||
store_axis_int_to_temp = 1'b0;
|
||||
store_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_axis_tready || !m_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_axis_tvalid_next = m_axis_tvalid_int;
|
||||
store_axis_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_axis_tvalid_next = m_axis_tvalid_int;
|
||||
store_axis_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_axis_tvalid_next = temp_m_axis_tvalid_reg;
|
||||
temp_m_axis_tvalid_next = 1'b0;
|
||||
store_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_axis_tvalid_reg <= 1'b0;
|
||||
m_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_axis_tvalid_reg <= m_axis_tvalid_next;
|
||||
m_axis_tready_int_reg <= m_axis_tready_int_early;
|
||||
temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_axis_int_to_output) begin
|
||||
m_axis_tdata_reg <= m_axis_tdata_int;
|
||||
m_axis_tlast_reg <= m_axis_tlast_int;
|
||||
m_axis_tuser_reg <= m_axis_tuser_int;
|
||||
end else if (store_axis_temp_to_output) begin
|
||||
m_axis_tdata_reg <= temp_m_axis_tdata_reg;
|
||||
m_axis_tlast_reg <= temp_m_axis_tlast_reg;
|
||||
m_axis_tuser_reg <= temp_m_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_axis_int_to_temp) begin
|
||||
temp_m_axis_tdata_reg <= m_axis_tdata_int;
|
||||
temp_m_axis_tlast_reg <= m_axis_tlast_int;
|
||||
temp_m_axis_tuser_reg <= m_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
127
fpga/src/eth_mac_1g.sv
Normal file
127
fpga/src/eth_mac_1g.sv
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* 1G Ethernet MAC
|
||||
*/
|
||||
module eth_mac_1g #
|
||||
(
|
||||
parameter ENABLE_PADDING = 1,
|
||||
parameter MIN_FRAME_LENGTH = 64
|
||||
)
|
||||
(
|
||||
input wire rx_clk,
|
||||
input wire rx_rst,
|
||||
input wire tx_clk,
|
||||
input wire tx_rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] tx_axis_tdata,
|
||||
input wire tx_axis_tvalid,
|
||||
output wire tx_axis_tready,
|
||||
input wire tx_axis_tlast,
|
||||
input wire tx_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] rx_axis_tdata,
|
||||
output wire rx_axis_tvalid,
|
||||
output wire rx_axis_tlast,
|
||||
output wire rx_axis_tuser,
|
||||
|
||||
/*
|
||||
* GMII interface
|
||||
*/
|
||||
input wire [7:0] gmii_rxd,
|
||||
input wire gmii_rx_dv,
|
||||
input wire gmii_rx_er,
|
||||
output wire [7:0] gmii_txd,
|
||||
output wire gmii_tx_en,
|
||||
output wire gmii_tx_er,
|
||||
|
||||
/*
|
||||
* Control
|
||||
*/
|
||||
input wire rx_clk_enable,
|
||||
input wire tx_clk_enable,
|
||||
input wire rx_mii_select,
|
||||
input wire tx_mii_select,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire rx_error_bad_frame,
|
||||
output wire rx_error_bad_fcs,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [7:0] ifg_delay
|
||||
);
|
||||
|
||||
axis_gmii_rx
|
||||
axis_gmii_rx_inst (
|
||||
.clk(rx_clk),
|
||||
.rst(rx_rst),
|
||||
.gmii_rxd(gmii_rxd),
|
||||
.gmii_rx_dv(gmii_rx_dv),
|
||||
.gmii_rx_er(gmii_rx_er),
|
||||
.m_axis_tdata(rx_axis_tdata),
|
||||
.m_axis_tvalid(rx_axis_tvalid),
|
||||
.m_axis_tlast(rx_axis_tlast),
|
||||
.m_axis_tuser(rx_axis_tuser),
|
||||
.clk_enable(rx_clk_enable),
|
||||
.mii_select(rx_mii_select),
|
||||
.error_bad_frame(rx_error_bad_frame),
|
||||
.error_bad_fcs(rx_error_bad_fcs)
|
||||
);
|
||||
|
||||
axis_gmii_tx #(
|
||||
.ENABLE_PADDING(ENABLE_PADDING),
|
||||
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
|
||||
)
|
||||
axis_gmii_tx_inst (
|
||||
.clk(tx_clk),
|
||||
.rst(tx_rst),
|
||||
.s_axis_tdata(tx_axis_tdata),
|
||||
.s_axis_tvalid(tx_axis_tvalid),
|
||||
.s_axis_tready(tx_axis_tready),
|
||||
.s_axis_tlast(tx_axis_tlast),
|
||||
.s_axis_tuser(tx_axis_tuser),
|
||||
.gmii_txd(gmii_txd),
|
||||
.gmii_tx_en(gmii_tx_en),
|
||||
.gmii_tx_er(gmii_tx_er),
|
||||
.clk_enable(tx_clk_enable),
|
||||
.mii_select(tx_mii_select),
|
||||
.ifg_delay(ifg_delay)
|
||||
);
|
||||
|
||||
endmodule
|
253
fpga/src/eth_mac_1g_rgmii.sv
Normal file
253
fpga/src/eth_mac_1g_rgmii.sv
Normal file
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* 1G Ethernet MAC with RGMII interface
|
||||
*/
|
||||
module eth_mac_1g_rgmii #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// IODDR style ("IODDR", "IODDR2")
|
||||
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
|
||||
// Use IODDR2 for Spartan-6
|
||||
parameter IODDR_STYLE = "IODDR2",
|
||||
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
|
||||
// Use BUFR for Virtex-5, Virtex-6, 7-series
|
||||
// Use BUFG for Ultrascale
|
||||
// Use BUFIO2 for Spartan-6
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2",
|
||||
// Use 90 degree clock for RGMII transmit ("TRUE", "FALSE")
|
||||
parameter USE_CLK90 = "TRUE",
|
||||
parameter ENABLE_PADDING = 1,
|
||||
parameter MIN_FRAME_LENGTH = 64
|
||||
)
|
||||
(
|
||||
input wire gtx_clk,
|
||||
input wire gtx_clk90,
|
||||
input wire gtx_rst,
|
||||
output wire rx_clk,
|
||||
output wire rx_rst,
|
||||
output wire tx_clk,
|
||||
output wire tx_rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] tx_axis_tdata,
|
||||
input wire tx_axis_tvalid,
|
||||
output wire tx_axis_tready,
|
||||
input wire tx_axis_tlast,
|
||||
input wire tx_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] rx_axis_tdata,
|
||||
output wire rx_axis_tvalid,
|
||||
output wire rx_axis_tlast,
|
||||
output wire rx_axis_tuser,
|
||||
|
||||
/*
|
||||
* RGMII interface
|
||||
*/
|
||||
input wire rgmii_rx_clk,
|
||||
input wire [3:0] rgmii_rxd,
|
||||
input wire rgmii_rx_ctl,
|
||||
output wire rgmii_tx_clk,
|
||||
output wire [3:0] rgmii_txd,
|
||||
output wire rgmii_tx_ctl,
|
||||
output wire mac_gmii_tx_en,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire rx_error_bad_frame,
|
||||
output wire rx_error_bad_fcs,
|
||||
output wire [1:0] speed,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [7:0] ifg_delay
|
||||
);
|
||||
|
||||
wire [7:0] mac_gmii_rxd;
|
||||
wire mac_gmii_rx_dv;
|
||||
wire mac_gmii_rx_er;
|
||||
wire mac_gmii_tx_clk_en;
|
||||
wire [7:0] mac_gmii_txd;
|
||||
wire mac_gmii_tx_er;
|
||||
|
||||
reg [1:0] speed_reg = 2'b10;
|
||||
wire mii_select;
|
||||
|
||||
reg tx_mii_select_1 = 1'b0;
|
||||
reg tx_mii_select_2 = 1'b0;
|
||||
reg tx_mii_select_3 = 1'b0;
|
||||
|
||||
always @(posedge tx_clk) begin
|
||||
tx_mii_select_1 <= mii_select;
|
||||
tx_mii_select_2 <= tx_mii_select_1;
|
||||
tx_mii_select_3 <= tx_mii_select_2;
|
||||
end
|
||||
|
||||
reg rx_mii_select_1 = 1'b0;
|
||||
reg rx_mii_select_2 = 1'b0;
|
||||
reg rx_mii_select_3 = 1'b0;
|
||||
|
||||
always @(posedge rx_clk) begin
|
||||
rx_mii_select_1 <= mii_select;
|
||||
rx_mii_select_2 <= rx_mii_select_1;
|
||||
rx_mii_select_3 <= rx_mii_select_2;
|
||||
end
|
||||
|
||||
// PHY speed detection
|
||||
reg [2:0] rx_prescale = 3'd0;
|
||||
|
||||
always @(posedge rx_clk) begin
|
||||
rx_prescale <= rx_prescale + 3'd1;
|
||||
end
|
||||
|
||||
reg rx_prescale_sync_1 = 1'b0;
|
||||
reg rx_prescale_sync_2 = 1'b0;
|
||||
reg rx_prescale_sync_3 = 1'b0;
|
||||
|
||||
always @(posedge gtx_clk) begin
|
||||
rx_prescale_sync_1 <= rx_prescale[2];
|
||||
rx_prescale_sync_2 <= rx_prescale_sync_1;
|
||||
rx_prescale_sync_3 <= rx_prescale_sync_2;
|
||||
end
|
||||
|
||||
reg [6:0] rx_speed_count_1 = 0;
|
||||
reg [1:0] rx_speed_count_2 = 0;
|
||||
|
||||
always @(posedge gtx_clk) begin
|
||||
if (gtx_rst) begin
|
||||
rx_speed_count_1 <= 0;
|
||||
rx_speed_count_2 <= 0;
|
||||
speed_reg <= 2'b10;
|
||||
end else begin
|
||||
rx_speed_count_1 <= rx_speed_count_1 + 1;
|
||||
|
||||
if (rx_prescale_sync_2 ^ rx_prescale_sync_3) begin
|
||||
rx_speed_count_2 <= rx_speed_count_2 + 1;
|
||||
end
|
||||
|
||||
if (&rx_speed_count_1) begin
|
||||
// reference count overflow - 10M
|
||||
rx_speed_count_1 <= 0;
|
||||
rx_speed_count_2 <= 0;
|
||||
speed_reg <= 2'b00;
|
||||
end
|
||||
|
||||
if (&rx_speed_count_2) begin
|
||||
// prescaled count overflow - 100M or 1000M
|
||||
rx_speed_count_1 <= 0;
|
||||
rx_speed_count_2 <= 0;
|
||||
if (rx_speed_count_1[6:5]) begin
|
||||
// large reference count - 100M
|
||||
speed_reg <= 2'b01;
|
||||
end else begin
|
||||
// small reference count - 1000M
|
||||
speed_reg <= 2'b10;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign speed = speed_reg;
|
||||
assign mii_select = speed != 2'b10;
|
||||
|
||||
rgmii_phy_if #(
|
||||
.TARGET(TARGET),
|
||||
.IODDR_STYLE(IODDR_STYLE),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
|
||||
.USE_CLK90(USE_CLK90)
|
||||
)
|
||||
rgmii_phy_if_inst (
|
||||
.clk(gtx_clk),
|
||||
.clk90(gtx_clk90),
|
||||
.rst(gtx_rst),
|
||||
|
||||
.mac_gmii_rx_clk(rx_clk),
|
||||
.mac_gmii_rx_rst(rx_rst),
|
||||
.mac_gmii_rxd(mac_gmii_rxd),
|
||||
.mac_gmii_rx_dv(mac_gmii_rx_dv),
|
||||
.mac_gmii_rx_er(mac_gmii_rx_er),
|
||||
.mac_gmii_tx_clk(tx_clk),
|
||||
.mac_gmii_tx_rst(tx_rst),
|
||||
.mac_gmii_tx_clk_en(mac_gmii_tx_clk_en),
|
||||
.mac_gmii_txd(mac_gmii_txd),
|
||||
.mac_gmii_tx_en(mac_gmii_tx_en),
|
||||
.mac_gmii_tx_er(mac_gmii_tx_er),
|
||||
|
||||
.phy_rgmii_rx_clk(rgmii_rx_clk),
|
||||
.phy_rgmii_rxd(rgmii_rxd),
|
||||
.phy_rgmii_rx_ctl(rgmii_rx_ctl),
|
||||
.phy_rgmii_tx_clk(rgmii_tx_clk),
|
||||
.phy_rgmii_txd(rgmii_txd),
|
||||
.phy_rgmii_tx_ctl(rgmii_tx_ctl),
|
||||
|
||||
.speed(speed)
|
||||
);
|
||||
|
||||
eth_mac_1g #(
|
||||
.ENABLE_PADDING(ENABLE_PADDING),
|
||||
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
|
||||
)
|
||||
eth_mac_1g_inst (
|
||||
.tx_clk(tx_clk),
|
||||
.tx_rst(tx_rst),
|
||||
.rx_clk(rx_clk),
|
||||
.rx_rst(rx_rst),
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser),
|
||||
.gmii_rxd(mac_gmii_rxd),
|
||||
.gmii_rx_dv(mac_gmii_rx_dv),
|
||||
.gmii_rx_er(mac_gmii_rx_er),
|
||||
.gmii_txd(mac_gmii_txd),
|
||||
.gmii_tx_en(mac_gmii_tx_en),
|
||||
.gmii_tx_er(mac_gmii_tx_er),
|
||||
.rx_clk_enable(1'b1),
|
||||
.tx_clk_enable(mac_gmii_tx_clk_en),
|
||||
.rx_mii_select(rx_mii_select_3),
|
||||
.tx_mii_select(tx_mii_select_3),
|
||||
.rx_error_bad_frame(rx_error_bad_frame),
|
||||
.rx_error_bad_fcs(rx_error_bad_fcs),
|
||||
.ifg_delay(ifg_delay)
|
||||
);
|
||||
|
||||
endmodule
|
199
fpga/src/eth_mac_1g_rgmii_fifo.sv
Normal file
199
fpga/src/eth_mac_1g_rgmii_fifo.sv
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* 1G Ethernet MAC with RGMII interface and TX and RX FIFOs
|
||||
*/
|
||||
module eth_mac_1g_rgmii_fifo #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// IODDR style ("IODDR", "IODDR2")
|
||||
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
|
||||
// Use IODDR2 for Spartan-6
|
||||
parameter IODDR_STYLE = "IODDR2",
|
||||
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
|
||||
// Use BUFR for Virtex-5, Virtex-6, 7-series
|
||||
// Use BUFG for Ultrascale
|
||||
// Use BUFIO2 for Spartan-6
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2",
|
||||
// Use 90 degree clock for RGMII transmit ("TRUE", "FALSE")
|
||||
parameter USE_CLK90 = "TRUE",
|
||||
parameter ENABLE_PADDING = 1,
|
||||
parameter MIN_FRAME_LENGTH = 64,
|
||||
parameter TX_FIFO_ADDR_WIDTH = 12,
|
||||
parameter TX_FRAME_FIFO = 1,
|
||||
parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO,
|
||||
parameter TX_DROP_WHEN_FULL = 0,
|
||||
parameter RX_FIFO_ADDR_WIDTH = 12,
|
||||
parameter RX_FRAME_FIFO = 1,
|
||||
parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO,
|
||||
parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO
|
||||
)
|
||||
(
|
||||
input wire gtx_clk,
|
||||
input wire gtx_clk90,
|
||||
input wire gtx_rst,
|
||||
input wire logic_clk,
|
||||
input wire logic_rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] tx_axis_tdata,
|
||||
input wire tx_axis_tvalid,
|
||||
output wire tx_axis_tready,
|
||||
input wire tx_axis_tlast,
|
||||
input wire tx_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] rx_axis_tdata,
|
||||
output wire rx_axis_tvalid,
|
||||
output wire rx_axis_tlast,
|
||||
output wire rx_axis_tuser,
|
||||
|
||||
/*
|
||||
* RGMII interface
|
||||
*/
|
||||
input wire rgmii_rx_clk,
|
||||
input wire [3:0] rgmii_rxd,
|
||||
input wire rgmii_rx_ctl,
|
||||
output wire rgmii_tx_clk,
|
||||
output wire [3:0] rgmii_txd,
|
||||
output wire rgmii_tx_ctl,
|
||||
output wire mac_gmii_tx_en,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire tx_fifo_overflow,
|
||||
output wire tx_fifo_bad_frame,
|
||||
output wire tx_fifo_good_frame,
|
||||
output wire rx_error_bad_frame,
|
||||
output wire rx_error_bad_fcs,
|
||||
output wire rx_fifo_overflow,
|
||||
output wire rx_fifo_bad_frame,
|
||||
output wire rx_fifo_good_frame,
|
||||
output wire [1:0] speed,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [7:0] ifg_delay
|
||||
);
|
||||
|
||||
wire tx_clk;
|
||||
wire rx_clk;
|
||||
wire tx_rst;
|
||||
wire rx_rst;
|
||||
|
||||
// synchronize MAC status signals into logic clock domain
|
||||
wire rx_error_bad_frame_int;
|
||||
wire rx_error_bad_fcs_int;
|
||||
|
||||
reg [1:0] rx_sync_reg_1 = 2'd0;
|
||||
reg [1:0] rx_sync_reg_2 = 2'd0;
|
||||
reg [1:0] rx_sync_reg_3 = 2'd0;
|
||||
reg [1:0] rx_sync_reg_4 = 2'd0;
|
||||
|
||||
assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0];
|
||||
assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1];
|
||||
|
||||
always @(posedge rx_clk or posedge rx_rst) begin
|
||||
if (rx_rst) begin
|
||||
rx_sync_reg_1 <= 2'd0;
|
||||
end else begin
|
||||
rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int};
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge logic_clk or posedge logic_rst) begin
|
||||
if (logic_rst) begin
|
||||
rx_sync_reg_2 <= 2'd0;
|
||||
rx_sync_reg_3 <= 2'd0;
|
||||
rx_sync_reg_4 <= 2'd0;
|
||||
end else begin
|
||||
rx_sync_reg_2 <= rx_sync_reg_1;
|
||||
rx_sync_reg_3 <= rx_sync_reg_2;
|
||||
rx_sync_reg_4 <= rx_sync_reg_3;
|
||||
end
|
||||
end
|
||||
|
||||
wire [1:0] speed_int;
|
||||
|
||||
reg [1:0] speed_sync_reg_1 = 2'b10;
|
||||
reg [1:0] speed_sync_reg_2 = 2'b10;
|
||||
|
||||
assign speed = speed_sync_reg_2;
|
||||
|
||||
always @(posedge logic_clk) begin
|
||||
speed_sync_reg_1 <= speed_int;
|
||||
speed_sync_reg_2 <= speed_sync_reg_1;
|
||||
end
|
||||
|
||||
eth_mac_1g_rgmii #(
|
||||
.TARGET(TARGET),
|
||||
.IODDR_STYLE(IODDR_STYLE),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
|
||||
.USE_CLK90(USE_CLK90),
|
||||
.ENABLE_PADDING(ENABLE_PADDING),
|
||||
.MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
|
||||
)
|
||||
eth_mac_1g_rgmii_inst (
|
||||
.gtx_clk(gtx_clk),
|
||||
.gtx_clk90(gtx_clk90),
|
||||
.gtx_rst(gtx_rst),
|
||||
.tx_clk(tx_clk),
|
||||
.tx_rst(tx_rst),
|
||||
.rx_clk(rx_clk),
|
||||
.rx_rst(rx_rst),
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser),
|
||||
.rgmii_rx_clk(rgmii_rx_clk),
|
||||
.rgmii_rxd(rgmii_rxd),
|
||||
.rgmii_rx_ctl(rgmii_rx_ctl),
|
||||
.rgmii_tx_clk(rgmii_tx_clk),
|
||||
.rgmii_txd(rgmii_txd),
|
||||
.rgmii_tx_ctl(rgmii_tx_ctl),
|
||||
.mac_gmii_tx_en(mac_gmii_tx_en),
|
||||
.rx_error_bad_frame(rx_error_bad_frame_int),
|
||||
.rx_error_bad_fcs(rx_error_bad_fcs_int),
|
||||
.speed(speed_int),
|
||||
.ifg_delay(ifg_delay)
|
||||
);
|
||||
|
||||
endmodule
|
378
fpga/src/framing_top.sv
Normal file
378
fpga/src/framing_top.sv
Normal file
|
@ -0,0 +1,378 @@
|
|||
// See LICENSE for license details.
|
||||
`default_nettype none
|
||||
|
||||
module framing_top
|
||||
(
|
||||
input wire msoc_clk,
|
||||
input wire [14:0] core_lsu_addr,
|
||||
input wire [63:0] core_lsu_wdata,
|
||||
input wire [7:0] core_lsu_be,
|
||||
input wire ce_d,
|
||||
input wire we_d,
|
||||
input wire framing_sel,
|
||||
output logic [63:0] framing_rdata,
|
||||
|
||||
// Internal 125 MHz clock
|
||||
input clk_int,
|
||||
input rst_int,
|
||||
input clk90_int,
|
||||
input clk_200_int,
|
||||
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
input wire phy_rx_clk,
|
||||
input wire [3:0] phy_rxd,
|
||||
input wire phy_rx_ctl,
|
||||
output wire phy_tx_clk,
|
||||
output wire [3:0] phy_txd,
|
||||
output wire phy_tx_ctl,
|
||||
output wire phy_reset_n,
|
||||
input wire phy_int_n,
|
||||
input wire phy_pme_n,
|
||||
|
||||
input wire phy_mdio_i,
|
||||
output reg phy_mdio_o,
|
||||
output reg phy_mdio_oen,
|
||||
output wire phy_mdc,
|
||||
|
||||
output reg eth_irq
|
||||
);
|
||||
|
||||
// obsolete signals to be removedphy_
|
||||
//
|
||||
|
||||
|
||||
logic [14:0] core_lsu_addr_dly;
|
||||
|
||||
logic tx_enable_i, mac_gmii_tx_en;
|
||||
logic [47:0] mac_address, rx_dest_mac;
|
||||
logic [10:0] tx_frame_addr, rx_length_axis[0:7], tx_packet_length;
|
||||
logic [12:0] axis_tx_frame_size;
|
||||
logic ce_d_dly, avail;
|
||||
logic [63:0] framing_rdata_pkt, framing_wdata_pkt;
|
||||
logic [3:0] tx_enable_dly, firstbuf, nextbuf, lastbuf;
|
||||
logic [2:0] last;
|
||||
|
||||
reg byte_sync, sync, irq_en, tx_busy, rx_axis_tvalid_old;
|
||||
|
||||
wire [7:0] m_enb = (we_d ? core_lsu_be : 8'hFF);
|
||||
logic phy_mdclk, cooked, tx_enable_old, loopback, promiscuous;
|
||||
logic [3:0] spare;
|
||||
logic [10:0] rx_addr_axis;
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
reg tx_axis_tvalid;
|
||||
reg tx_axis_tvalid_dly;
|
||||
reg tx_axis_tlast;
|
||||
wire [7:0] tx_axis_tdata;
|
||||
wire tx_axis_tready;
|
||||
wire tx_axis_tuser = 1'b0;
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
wire [7:0] rx_axis_tdata;
|
||||
wire rx_axis_tvalid;
|
||||
wire rx_axis_tlast;
|
||||
wire rx_axis_tuser;
|
||||
|
||||
/*
|
||||
* AXIS Status
|
||||
*/
|
||||
wire [31:0] tx_fcs_reg_rev, rx_fcs_reg_rev;
|
||||
|
||||
always @(posedge clk_int)
|
||||
if (rst_int == 1'b1)
|
||||
begin
|
||||
byte_sync <= 1'b0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (rx_axis_tvalid && (byte_sync == 0) && (nextbuf != (firstbuf+lastbuf)&15) && ~rx_axis_tvalid_old)
|
||||
begin
|
||||
byte_sync <= 1'b1;
|
||||
end
|
||||
if (rx_axis_tlast && byte_sync && ~rx_axis_tvalid_old)
|
||||
begin
|
||||
last <= 1'b1;
|
||||
end
|
||||
else if ((last > 0) && (last < 7))
|
||||
begin
|
||||
byte_sync <= 1'b0;
|
||||
last <= last + 3'b1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
last <= 3'b0;
|
||||
end
|
||||
end
|
||||
|
||||
logic [1:0] rx_wr = rx_axis_tvalid << rx_addr_axis[2];
|
||||
logic [15:0] douta;
|
||||
assign tx_axis_tdata = douta >> {tx_frame_addr[2],3'b000};
|
||||
assign phy_mdc = phy_mdclk;
|
||||
|
||||
dualmem_widen8 RAMB16_inst_rx (
|
||||
.clka(clk_int), // Port A Clock
|
||||
.clkb(msoc_clk), // Port A Clock
|
||||
.douta(), // Port A 8-bit Data Output
|
||||
.addra({nextbuf[2:0],rx_addr_axis[10:3],rx_addr_axis[1:0]}), // Port A 11-bit Address Input
|
||||
.dina({rx_axis_tdata,rx_axis_tdata}), // Port A 8-bit Data Input
|
||||
.ena(rx_axis_tvalid), // Port A RAM Enable Input
|
||||
.wea(rx_wr), // Port A Write Enable Input
|
||||
.doutb(framing_rdata_pkt), // Port B 32-bit Data Output
|
||||
.addrb(core_lsu_addr[13:3]), // Port B 9-bit Address Input
|
||||
.dinb(core_lsu_wdata), // Port B 32-bit Data Input
|
||||
.enb(ce_d & framing_sel & core_lsu_addr[14]),
|
||||
// Port B RAM Enable Input
|
||||
.web(we_d ? {(|core_lsu_be[7:4]),(|core_lsu_be[3:0])} : 2'b0) // Port B Write Enable Input
|
||||
);
|
||||
|
||||
dualmem_widen RAMB16_inst_tx (
|
||||
.clka(~clk_int), // Port A Clock
|
||||
.clkb(msoc_clk), // Port A Clock
|
||||
.douta(douta), // Port A 8-bit Data Output
|
||||
.addra({1'b0,tx_frame_addr[10:3],tx_frame_addr[1:0]}), // Port A 11-bit Address Input
|
||||
.dina(16'b0), // Port A 8-bit Data Input
|
||||
.ena(tx_axis_tvalid), // Port A RAM Enable Input
|
||||
.wea(2'b0), // Port A Write Enable Input
|
||||
.doutb(framing_wdata_pkt), // Port B 32-bit Data Output
|
||||
.addrb(core_lsu_addr[11:3]), // Port B 9-bit Address Input
|
||||
.dinb(core_lsu_wdata), // Port B 32-bit Data Input
|
||||
.enb(ce_d & framing_sel & (core_lsu_addr[14:12]==3'b001)),
|
||||
// Port B RAM Enable Input
|
||||
.web(we_d ? {(|core_lsu_be[7:4]),(|core_lsu_be[3:0])} : 2'b0) // Port B Write Enable Input
|
||||
);
|
||||
|
||||
always @(posedge msoc_clk)
|
||||
if (rst_int)
|
||||
begin
|
||||
core_lsu_addr_dly <= 0;
|
||||
mac_address <= 48'H230100890702;
|
||||
tx_packet_length <= 0;
|
||||
tx_enable_dly <= 0;
|
||||
cooked <= 1'b0;
|
||||
loopback <= 1'b0;
|
||||
spare <= 4'b0;
|
||||
promiscuous <= 1'b0;
|
||||
phy_mdio_oen <= 1'b0;
|
||||
phy_mdio_o <= 1'b0;
|
||||
phy_mdclk <= 1'b0;
|
||||
sync <= 1'b0;
|
||||
firstbuf <= 4'b0;
|
||||
lastbuf <= 4'b0;
|
||||
nextbuf <= 4'b0;
|
||||
eth_irq <= 1'b0;
|
||||
irq_en <= 1'b0;
|
||||
ce_d_dly <= 1'b0;
|
||||
tx_busy <= 1'b0;
|
||||
avail = 1'b0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
core_lsu_addr_dly <= core_lsu_addr;
|
||||
ce_d_dly <= ce_d;
|
||||
avail = nextbuf != firstbuf;
|
||||
eth_irq <= avail & irq_en; // make eth_irq go away immediately if irq_en is low
|
||||
if (framing_sel&we_d&(&core_lsu_be[3:0])&(core_lsu_addr[14:11]==4'b0001))
|
||||
case(core_lsu_addr[6:3])
|
||||
0: mac_address[31:0] <= core_lsu_wdata;
|
||||
1: {irq_en,promiscuous,spare,loopback,cooked,mac_address[47:32]} <= core_lsu_wdata;
|
||||
2: begin tx_enable_dly <= 10; tx_packet_length <= core_lsu_wdata; end /* tx payload size */
|
||||
3: begin tx_enable_dly <= 0; tx_packet_length <= 0; end
|
||||
4: begin {phy_mdio_oen,phy_mdio_o,phy_mdclk} <= core_lsu_wdata; end
|
||||
5: begin lastbuf <= core_lsu_wdata[3:0]; end
|
||||
6: begin firstbuf <= core_lsu_wdata[3:0]; end
|
||||
default:;
|
||||
endcase
|
||||
if ((last > 0) && ~sync)
|
||||
begin
|
||||
// check broadcast/multicast address
|
||||
sync <= (rx_dest_mac[47:24]==24'h01005E) | (&rx_dest_mac) | (mac_address == rx_dest_mac) | promiscuous;
|
||||
end
|
||||
else if (!last)
|
||||
begin
|
||||
if (sync) nextbuf <= nextbuf + 1'b1;
|
||||
sync <= 1'b0;
|
||||
end
|
||||
if (mac_gmii_tx_en && tx_axis_tlast)
|
||||
begin
|
||||
tx_enable_dly <= 0;
|
||||
end
|
||||
else if (1'b1 == |tx_enable_dly)
|
||||
begin
|
||||
tx_busy <= 1'b1;
|
||||
tx_enable_dly <= tx_enable_dly + 1'b1;
|
||||
end
|
||||
else if (~mac_gmii_tx_en)
|
||||
tx_busy <= 1'b0;
|
||||
end
|
||||
|
||||
always @(posedge clk_int)
|
||||
if (rst_int)
|
||||
begin
|
||||
tx_enable_i <= 1'b0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
tx_enable_old <= tx_enable_i;
|
||||
if (mac_gmii_tx_en && tx_axis_tlast)
|
||||
begin
|
||||
tx_enable_i <= 1'b0;
|
||||
end
|
||||
else if (1'b1 == &tx_enable_dly)
|
||||
tx_enable_i <= 1'b1;
|
||||
end
|
||||
|
||||
always @* casez({ce_d_dly,core_lsu_addr_dly[14:3]})
|
||||
13'b10001????0000 : framing_rdata = mac_address[31:0];
|
||||
13'b10001????0001 : framing_rdata = {irq_en, promiscuous, spare, loopback, cooked, mac_address[47:32]};
|
||||
13'b1000?????0010 : framing_rdata = {tx_busy, 4'b0, tx_frame_addr, 5'b0, tx_packet_length};
|
||||
13'b10001????0011 : framing_rdata = tx_fcs_reg_rev;
|
||||
13'b10001????0100 : framing_rdata = {phy_mdio_i,phy_mdio_oen,phy_mdio_o,phy_mdclk};
|
||||
13'b10001????0101 : framing_rdata = rx_fcs_reg_rev;
|
||||
13'b10001????0110 : framing_rdata = {eth_irq, avail, lastbuf, nextbuf, firstbuf};
|
||||
13'b10001????1??? : framing_rdata = rx_length_axis[core_lsu_addr_dly[5:3]];
|
||||
13'b10010???????? : framing_rdata = framing_wdata_pkt;
|
||||
13'b11??????????? : framing_rdata = framing_rdata_pkt;
|
||||
default: framing_rdata = 'h0;
|
||||
endcase
|
||||
|
||||
parameter dly = 0;
|
||||
|
||||
reg [31:0] tx_fcs_reg, rx_fcs_reg;
|
||||
assign tx_fcs_reg_rev = {tx_fcs_reg[0],tx_fcs_reg[1],tx_fcs_reg[2],tx_fcs_reg[3],
|
||||
tx_fcs_reg[4],tx_fcs_reg[5],tx_fcs_reg[6],tx_fcs_reg[7],
|
||||
tx_fcs_reg[8],tx_fcs_reg[9],tx_fcs_reg[10],tx_fcs_reg[11],
|
||||
tx_fcs_reg[12],tx_fcs_reg[13],tx_fcs_reg[14],tx_fcs_reg[15],
|
||||
tx_fcs_reg[16],tx_fcs_reg[17],tx_fcs_reg[18],tx_fcs_reg[19],
|
||||
tx_fcs_reg[20],tx_fcs_reg[21],tx_fcs_reg[22],tx_fcs_reg[23],
|
||||
tx_fcs_reg[24],tx_fcs_reg[25],tx_fcs_reg[26],tx_fcs_reg[27],
|
||||
tx_fcs_reg[28],tx_fcs_reg[29],tx_fcs_reg[30],tx_fcs_reg[31]};
|
||||
assign rx_fcs_reg_rev = {rx_fcs_reg[0],rx_fcs_reg[1],rx_fcs_reg[2],rx_fcs_reg[3],
|
||||
rx_fcs_reg[4],rx_fcs_reg[5],rx_fcs_reg[6],rx_fcs_reg[7],
|
||||
rx_fcs_reg[8],rx_fcs_reg[9],rx_fcs_reg[10],rx_fcs_reg[11],
|
||||
rx_fcs_reg[12],rx_fcs_reg[13],rx_fcs_reg[14],rx_fcs_reg[15],
|
||||
rx_fcs_reg[16],rx_fcs_reg[17],rx_fcs_reg[18],rx_fcs_reg[19],
|
||||
rx_fcs_reg[20],rx_fcs_reg[21],rx_fcs_reg[22],rx_fcs_reg[23],
|
||||
rx_fcs_reg[24],rx_fcs_reg[25],rx_fcs_reg[26],rx_fcs_reg[27],
|
||||
rx_fcs_reg[28],rx_fcs_reg[29],rx_fcs_reg[30],rx_fcs_reg[31]};
|
||||
wire axis_tx_byte_sent = &axis_tx_frame_size[1:0];
|
||||
|
||||
always @(posedge clk_int)
|
||||
if (rst_int)
|
||||
begin
|
||||
rx_addr_axis <= 'b0;
|
||||
tx_axis_tvalid <= 'b0;
|
||||
axis_tx_frame_size <= 0;
|
||||
tx_axis_tvalid_dly <= 'b0;
|
||||
tx_frame_addr <= 'b0;
|
||||
tx_axis_tlast <= 'b0;
|
||||
rx_dest_mac <= 'b0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (tx_enable_i & (tx_enable_old == 0))
|
||||
begin
|
||||
axis_tx_frame_size <= 'b0;
|
||||
tx_frame_addr <= 'b0;
|
||||
end
|
||||
else if (1'b0 == &axis_tx_frame_size)
|
||||
begin
|
||||
axis_tx_frame_size <= axis_tx_frame_size + 1;
|
||||
end
|
||||
if (tx_axis_tready)
|
||||
begin
|
||||
tx_frame_addr <= tx_frame_addr + 1;
|
||||
tx_axis_tlast <= (tx_frame_addr == tx_packet_length-2) & tx_axis_tvalid_dly;
|
||||
end
|
||||
if (axis_tx_byte_sent)
|
||||
begin
|
||||
tx_axis_tvalid <= tx_axis_tvalid_dly;
|
||||
if (tx_enable_old)
|
||||
tx_axis_tvalid_dly <= 1'b1;
|
||||
else if (~tx_axis_tlast)
|
||||
tx_axis_tvalid_dly <= 1'b0;
|
||||
end
|
||||
if (rx_axis_tvalid & ~rx_axis_tvalid_old)
|
||||
begin
|
||||
rx_addr_axis <= rx_addr_axis + 1;
|
||||
if (rx_addr_axis < 6)
|
||||
rx_dest_mac <= {rx_dest_mac[39:0],rx_axis_tdata};
|
||||
end
|
||||
if (rx_axis_tlast & ~rx_axis_tvalid_old)
|
||||
begin
|
||||
rx_length_axis[nextbuf[2:0]] <= rx_addr_axis + 1;
|
||||
rx_addr_axis <= 'b0;
|
||||
end
|
||||
rx_axis_tvalid_old <= rx_axis_tvalid;
|
||||
end
|
||||
|
||||
rgmii_soc rgmii_soc1
|
||||
(
|
||||
.rst_int(rst_int),
|
||||
.clk_int(clk_int),
|
||||
.clk90_int(clk90_int),
|
||||
.clk_200_int(clk_200_int),
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
.phy_rx_clk(phy_rx_clk),
|
||||
.phy_rxd(phy_rxd),
|
||||
.phy_rx_ctl(phy_rx_ctl),
|
||||
.phy_tx_clk(phy_tx_clk),
|
||||
.phy_txd(phy_txd),
|
||||
.phy_tx_ctl(phy_tx_ctl),
|
||||
.phy_reset_n(phy_reset_n),
|
||||
.phy_int_n(phy_int_n),
|
||||
.phy_pme_n(phy_pme_n),
|
||||
.mac_gmii_tx_en(mac_gmii_tx_en),
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser)
|
||||
);
|
||||
|
||||
`ifdef XILINX_ILA
|
||||
|
||||
ila_2 eth_ila_clk_int (
|
||||
.clk(clk_int), // input wire clk
|
||||
.probe0(tx_axis_tdata), // input wire [7:0] probe0
|
||||
.probe1(tx_axis_tvalid), // input wire [0:0] probe1
|
||||
.probe2(tx_axis_tready), // input wire [0:0] probe2
|
||||
.probe3(tx_axis_tlast), // input wire [0:0] probe3
|
||||
.probe4(rx_axis_tdata), // input wire [7:0] probe4
|
||||
.probe5(rx_axis_tvalid), // input wire [0:0] probe5
|
||||
.probe6(rx_axis_tlast), // input wire [0:0] probe6
|
||||
.probe7(rx_axis_tuser), // input wire [0:0] probe7
|
||||
.probe8(byte_sync),
|
||||
.probe9(last),
|
||||
.probe10(rx_addr_axis),
|
||||
.probe11(rx_dest_mac),
|
||||
.probe12(tx_enable_i),
|
||||
.probe13(axis_tx_frame_size),
|
||||
.probe14(tx_axis_tvalid_dly),
|
||||
.probe15(tx_frame_addr)
|
||||
);
|
||||
|
||||
ila_3 eth_ila_clk_msoc (
|
||||
.clk(msoc_clk), // input wire clk
|
||||
.probe0(sync),
|
||||
.probe1(avail),
|
||||
.probe2(nextbuf),
|
||||
.probe3(tx_enable_dly),
|
||||
.probe4(tx_busy)
|
||||
);
|
||||
|
||||
`endif
|
||||
|
||||
endmodule // framing_top
|
||||
`default_nettype wire
|
151
fpga/src/iddr.sv
Normal file
151
fpga/src/iddr.sv
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2016-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Generic IDDR module
|
||||
*/
|
||||
module iddr #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// IODDR style ("IODDR", "IODDR2")
|
||||
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
|
||||
// Use IODDR2 for Spartan-6
|
||||
parameter IODDR_STYLE = "IODDR2",
|
||||
// Width of register in bits
|
||||
parameter WIDTH = 1
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
|
||||
input wire [WIDTH-1:0] d,
|
||||
|
||||
output wire [WIDTH-1:0] q1,
|
||||
output wire [WIDTH-1:0] q2
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
Provides a consistent input DDR flip flop across multiple FPGA families
|
||||
_____ _____ _____ _____ ____
|
||||
clk ____/ \_____/ \_____/ \_____/ \_____/
|
||||
_ _____ _____ _____ _____ _____ _____ _____ _____ _____ _
|
||||
d _X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7__X_D8__X_
|
||||
_______ ___________ ___________ ___________ ___________ _
|
||||
q1 _______X___________X____D0_____X____D2_____X____D4_____X_
|
||||
_______ ___________ ___________ ___________ ___________ _
|
||||
q2 _______X___________X____D1_____X____D3_____X____D5_____X_
|
||||
|
||||
*/
|
||||
|
||||
genvar n;
|
||||
|
||||
generate
|
||||
|
||||
if (TARGET == "XILINX") begin
|
||||
for (n = 0; n < WIDTH; n = n + 1) begin : iddr
|
||||
if (IODDR_STYLE == "IODDR") begin
|
||||
IDDR #(
|
||||
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),
|
||||
.SRTYPE("ASYNC")
|
||||
)
|
||||
iddr_inst (
|
||||
.Q1(q1[n]),
|
||||
.Q2(q2[n]),
|
||||
.C(clk),
|
||||
.CE(1'b1),
|
||||
.D(d[n]),
|
||||
.R(1'b0),
|
||||
.S(1'b0)
|
||||
);
|
||||
end else if (IODDR_STYLE == "IODDR2") begin
|
||||
IDDR2 #(
|
||||
.DDR_ALIGNMENT("C0")
|
||||
)
|
||||
iddr_inst (
|
||||
.Q0(q1[n]),
|
||||
.Q1(q2[n]),
|
||||
.C0(clk),
|
||||
.C1(~clk),
|
||||
.CE(1'b1),
|
||||
.D(d[n]),
|
||||
.R(1'b0),
|
||||
.S(1'b0)
|
||||
);
|
||||
end
|
||||
end
|
||||
end else if (TARGET == "ALTERA") begin
|
||||
wire [WIDTH-1:0] q1_int;
|
||||
reg [WIDTH-1:0] q1_delay;
|
||||
|
||||
altddio_in #(
|
||||
.WIDTH(WIDTH),
|
||||
.POWER_UP_HIGH("OFF")
|
||||
)
|
||||
altddio_in_inst (
|
||||
.aset(1'b0),
|
||||
.datain(d),
|
||||
.inclocken(1'b1),
|
||||
.inclock(clk),
|
||||
.aclr(1'b0),
|
||||
.dataout_h(q1_int),
|
||||
.dataout_l(q2)
|
||||
);
|
||||
|
||||
always @(posedge clk) begin
|
||||
q1_delay <= q1_int;
|
||||
end
|
||||
|
||||
assign q1 = q1_delay;
|
||||
end else begin
|
||||
reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}};
|
||||
reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}};
|
||||
|
||||
reg [WIDTH-1:0] q_reg_1 = {WIDTH{1'b0}};
|
||||
reg [WIDTH-1:0] q_reg_2 = {WIDTH{1'b0}};
|
||||
|
||||
always @(posedge clk) begin
|
||||
d_reg_1 <= d;
|
||||
end
|
||||
|
||||
always @(negedge clk) begin
|
||||
d_reg_2 <= d;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
q_reg_1 <= d_reg_1;
|
||||
q_reg_2 <= d_reg_2;
|
||||
end
|
||||
|
||||
assign q1 = q_reg_1;
|
||||
assign q2 = q_reg_2;
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
endmodule
|
341
fpga/src/ip.sv
Normal file
341
fpga/src/ip.sv
Normal file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* IPv4 block, ethernet frame interface
|
||||
*/
|
||||
module ip
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame input
|
||||
*/
|
||||
input wire s_eth_hdr_valid,
|
||||
output wire s_eth_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [7:0] s_eth_payload_axis_tdata,
|
||||
input wire s_eth_payload_axis_tvalid,
|
||||
output wire s_eth_payload_axis_tready,
|
||||
input wire s_eth_payload_axis_tlast,
|
||||
input wire s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [7:0] m_eth_payload_axis_tdata,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire m_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* ARP requests
|
||||
*/
|
||||
output wire arp_request_valid,
|
||||
input wire arp_request_ready,
|
||||
output wire [31:0] arp_request_ip,
|
||||
input wire arp_response_valid,
|
||||
output wire arp_response_ready,
|
||||
input wire arp_response_error,
|
||||
input wire [47:0] arp_response_mac,
|
||||
|
||||
/*
|
||||
* IP input
|
||||
*/
|
||||
input wire s_ip_hdr_valid,
|
||||
output wire s_ip_hdr_ready,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_length,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [7:0] s_ip_protocol,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [7:0] s_ip_payload_axis_tdata,
|
||||
input wire s_ip_payload_axis_tvalid,
|
||||
output wire s_ip_payload_axis_tready,
|
||||
input wire s_ip_payload_axis_tlast,
|
||||
input wire s_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP output
|
||||
*/
|
||||
output wire m_ip_hdr_valid,
|
||||
input wire m_ip_hdr_ready,
|
||||
output wire [47:0] m_ip_eth_dest_mac,
|
||||
output wire [47:0] m_ip_eth_src_mac,
|
||||
output wire [15:0] m_ip_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [7:0] m_ip_payload_axis_tdata,
|
||||
output wire m_ip_payload_axis_tvalid,
|
||||
input wire m_ip_payload_axis_tready,
|
||||
output wire m_ip_payload_axis_tlast,
|
||||
output wire m_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire rx_busy,
|
||||
output wire tx_busy,
|
||||
output wire rx_error_header_early_termination,
|
||||
output wire rx_error_payload_early_termination,
|
||||
output wire rx_error_invalid_header,
|
||||
output wire rx_error_invalid_checksum,
|
||||
output wire tx_error_payload_early_termination,
|
||||
output wire tx_error_arp_failed,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [47:0] local_mac,
|
||||
input wire [31:0] local_ip
|
||||
);
|
||||
|
||||
localparam [1:0]
|
||||
STATE_IDLE = 2'd0,
|
||||
STATE_ARP_QUERY = 2'd1,
|
||||
STATE_WAIT_PACKET = 2'd2;
|
||||
|
||||
reg [1:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
reg outgoing_ip_hdr_valid_reg = 1'b0, outgoing_ip_hdr_valid_next;
|
||||
wire outgoing_ip_hdr_ready;
|
||||
reg [47:0] outgoing_eth_dest_mac_reg = 48'h000000000000, outgoing_eth_dest_mac_next;
|
||||
wire outgoing_ip_payload_axis_tready;
|
||||
|
||||
/*
|
||||
* IP frame processing
|
||||
*/
|
||||
ip_eth_rx
|
||||
ip_eth_rx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// Ethernet frame input
|
||||
.s_eth_hdr_valid(s_eth_hdr_valid),
|
||||
.s_eth_hdr_ready(s_eth_hdr_ready),
|
||||
.s_eth_dest_mac(s_eth_dest_mac),
|
||||
.s_eth_src_mac(s_eth_src_mac),
|
||||
.s_eth_type(s_eth_type),
|
||||
.s_eth_payload_axis_tdata(s_eth_payload_axis_tdata),
|
||||
.s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid),
|
||||
.s_eth_payload_axis_tready(s_eth_payload_axis_tready),
|
||||
.s_eth_payload_axis_tlast(s_eth_payload_axis_tlast),
|
||||
.s_eth_payload_axis_tuser(s_eth_payload_axis_tuser),
|
||||
// IP frame output
|
||||
.m_ip_hdr_valid(m_ip_hdr_valid),
|
||||
.m_ip_hdr_ready(m_ip_hdr_ready),
|
||||
.m_eth_dest_mac(m_ip_eth_dest_mac),
|
||||
.m_eth_src_mac(m_ip_eth_src_mac),
|
||||
.m_eth_type(m_ip_eth_type),
|
||||
.m_ip_version(m_ip_version),
|
||||
.m_ip_ihl(m_ip_ihl),
|
||||
.m_ip_dscp(m_ip_dscp),
|
||||
.m_ip_ecn(m_ip_ecn),
|
||||
.m_ip_length(m_ip_length),
|
||||
.m_ip_identification(m_ip_identification),
|
||||
.m_ip_flags(m_ip_flags),
|
||||
.m_ip_fragment_offset(m_ip_fragment_offset),
|
||||
.m_ip_ttl(m_ip_ttl),
|
||||
.m_ip_protocol(m_ip_protocol),
|
||||
.m_ip_header_checksum(m_ip_header_checksum),
|
||||
.m_ip_source_ip(m_ip_source_ip),
|
||||
.m_ip_dest_ip(m_ip_dest_ip),
|
||||
.m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
|
||||
.m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
|
||||
.m_ip_payload_axis_tready(m_ip_payload_axis_tready),
|
||||
.m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
|
||||
.m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
|
||||
// Status signals
|
||||
.busy(rx_busy),
|
||||
.error_header_early_termination(rx_error_header_early_termination),
|
||||
.error_payload_early_termination(rx_error_payload_early_termination),
|
||||
.error_invalid_header(rx_error_invalid_header),
|
||||
.error_invalid_checksum(rx_error_invalid_checksum)
|
||||
);
|
||||
|
||||
ip_eth_tx
|
||||
ip_eth_tx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// IP frame input
|
||||
.s_ip_hdr_valid(outgoing_ip_hdr_valid_reg),
|
||||
.s_ip_hdr_ready(outgoing_ip_hdr_ready),
|
||||
.s_eth_dest_mac(outgoing_eth_dest_mac_reg),
|
||||
.s_eth_src_mac(local_mac),
|
||||
.s_eth_type(16'h0800),
|
||||
.s_ip_dscp(s_ip_dscp),
|
||||
.s_ip_ecn(s_ip_ecn),
|
||||
.s_ip_length(s_ip_length),
|
||||
.s_ip_identification(16'd0),
|
||||
.s_ip_flags(3'b010),
|
||||
.s_ip_fragment_offset(13'd0),
|
||||
.s_ip_ttl(s_ip_ttl),
|
||||
.s_ip_protocol(s_ip_protocol),
|
||||
.s_ip_source_ip(s_ip_source_ip),
|
||||
.s_ip_dest_ip(s_ip_dest_ip),
|
||||
.s_ip_payload_axis_tdata(s_ip_payload_axis_tdata),
|
||||
.s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid),
|
||||
.s_ip_payload_axis_tready(outgoing_ip_payload_axis_tready),
|
||||
.s_ip_payload_axis_tlast(s_ip_payload_axis_tlast),
|
||||
.s_ip_payload_axis_tuser(s_ip_payload_axis_tuser),
|
||||
// Ethernet frame output
|
||||
.m_eth_hdr_valid(m_eth_hdr_valid),
|
||||
.m_eth_hdr_ready(m_eth_hdr_ready),
|
||||
.m_eth_dest_mac(m_eth_dest_mac),
|
||||
.m_eth_src_mac(m_eth_src_mac),
|
||||
.m_eth_type(m_eth_type),
|
||||
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
|
||||
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
|
||||
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
|
||||
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
|
||||
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser),
|
||||
// Status signals
|
||||
.busy(tx_busy),
|
||||
.error_payload_early_termination(tx_error_payload_early_termination)
|
||||
);
|
||||
|
||||
reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next;
|
||||
|
||||
reg arp_request_valid_reg = 1'b0, arp_request_valid_next;
|
||||
|
||||
reg arp_response_ready_reg = 1'b0, arp_response_ready_next;
|
||||
|
||||
reg drop_packet_reg = 1'b0, drop_packet_next;
|
||||
|
||||
assign s_ip_hdr_ready = s_ip_hdr_ready_reg;
|
||||
assign s_ip_payload_axis_tready = outgoing_ip_payload_axis_tready || drop_packet_reg;
|
||||
|
||||
assign arp_request_valid = arp_request_valid_reg;
|
||||
assign arp_request_ip = s_ip_dest_ip;
|
||||
assign arp_response_ready = arp_response_ready_reg;
|
||||
|
||||
assign tx_error_arp_failed = arp_response_error;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
arp_request_valid_next = arp_request_valid_reg && !arp_request_ready;
|
||||
arp_response_ready_next = 1'b0;
|
||||
drop_packet_next = 1'b0;
|
||||
|
||||
s_ip_hdr_ready_next = 1'b0;
|
||||
|
||||
outgoing_ip_hdr_valid_next = outgoing_ip_hdr_valid_reg && !outgoing_ip_hdr_ready;
|
||||
outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// wait for outgoing packet
|
||||
if (s_ip_hdr_valid) begin
|
||||
// initiate ARP request
|
||||
arp_request_valid_next = 1'b1;
|
||||
arp_response_ready_next = 1'b1;
|
||||
state_next = STATE_ARP_QUERY;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_ARP_QUERY: begin
|
||||
arp_response_ready_next = 1'b1;
|
||||
|
||||
if (arp_response_valid) begin
|
||||
// wait for ARP reponse
|
||||
if (arp_response_error) begin
|
||||
// did not get MAC address; drop packet
|
||||
s_ip_hdr_ready_next = 1'b1;
|
||||
drop_packet_next = 1'b1;
|
||||
state_next = STATE_WAIT_PACKET;
|
||||
end else begin
|
||||
// got MAC address; send packet
|
||||
s_ip_hdr_ready_next = 1'b1;
|
||||
outgoing_ip_hdr_valid_next = 1'b1;
|
||||
outgoing_eth_dest_mac_next = arp_response_mac;
|
||||
state_next = STATE_WAIT_PACKET;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_ARP_QUERY;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_PACKET: begin
|
||||
drop_packet_next = drop_packet_reg;
|
||||
|
||||
// wait for packet transfer to complete
|
||||
if (s_ip_payload_axis_tlast && s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WAIT_PACKET;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
arp_request_valid_reg <= 1'b0;
|
||||
arp_response_ready_reg <= 1'b0;
|
||||
drop_packet_reg <= 1'b0;
|
||||
s_ip_hdr_ready_reg <= 1'b0;
|
||||
outgoing_ip_hdr_valid_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
arp_request_valid_reg <= arp_request_valid_next;
|
||||
arp_response_ready_reg <= arp_response_ready_next;
|
||||
drop_packet_reg <= drop_packet_next;
|
||||
|
||||
s_ip_hdr_ready_reg <= s_ip_hdr_ready_next;
|
||||
|
||||
outgoing_ip_hdr_valid_reg <= outgoing_ip_hdr_valid_next;
|
||||
end
|
||||
|
||||
outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next;
|
||||
end
|
||||
|
||||
endmodule
|
407
fpga/src/ip_arb_mux.sv
Normal file
407
fpga/src/ip_arb_mux.sv
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* IP arbitrated multiplexer
|
||||
*/
|
||||
module ip_arb_mux #
|
||||
(
|
||||
parameter S_COUNT = 4,
|
||||
parameter DATA_WIDTH = 8,
|
||||
parameter KEEP_ENABLE = (DATA_WIDTH>8),
|
||||
parameter KEEP_WIDTH = (DATA_WIDTH/8),
|
||||
parameter ID_ENABLE = 0,
|
||||
parameter ID_WIDTH = 8,
|
||||
parameter DEST_ENABLE = 0,
|
||||
parameter DEST_WIDTH = 8,
|
||||
parameter USER_ENABLE = 1,
|
||||
parameter USER_WIDTH = 1,
|
||||
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
|
||||
parameter ARB_TYPE = "PRIORITY",
|
||||
// LSB priority: "LOW", "HIGH"
|
||||
parameter LSB_PRIORITY = "HIGH"
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* IP frame inputs
|
||||
*/
|
||||
input wire [S_COUNT-1:0] s_ip_hdr_valid,
|
||||
output wire [S_COUNT-1:0] s_ip_hdr_ready,
|
||||
input wire [S_COUNT*48-1:0] s_eth_dest_mac,
|
||||
input wire [S_COUNT*48-1:0] s_eth_src_mac,
|
||||
input wire [S_COUNT*16-1:0] s_eth_type,
|
||||
input wire [S_COUNT*4-1:0] s_ip_version,
|
||||
input wire [S_COUNT*4-1:0] s_ip_ihl,
|
||||
input wire [S_COUNT*6-1:0] s_ip_dscp,
|
||||
input wire [S_COUNT*2-1:0] s_ip_ecn,
|
||||
input wire [S_COUNT*16-1:0] s_ip_length,
|
||||
input wire [S_COUNT*16-1:0] s_ip_identification,
|
||||
input wire [S_COUNT*3-1:0] s_ip_flags,
|
||||
input wire [S_COUNT*13-1:0] s_ip_fragment_offset,
|
||||
input wire [S_COUNT*8-1:0] s_ip_ttl,
|
||||
input wire [S_COUNT*8-1:0] s_ip_protocol,
|
||||
input wire [S_COUNT*16-1:0] s_ip_header_checksum,
|
||||
input wire [S_COUNT*32-1:0] s_ip_source_ip,
|
||||
input wire [S_COUNT*32-1:0] s_ip_dest_ip,
|
||||
input wire [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata,
|
||||
input wire [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep,
|
||||
input wire [S_COUNT-1:0] s_ip_payload_axis_tvalid,
|
||||
output wire [S_COUNT-1:0] s_ip_payload_axis_tready,
|
||||
input wire [S_COUNT-1:0] s_ip_payload_axis_tlast,
|
||||
input wire [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid,
|
||||
input wire [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest,
|
||||
input wire [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP frame output
|
||||
*/
|
||||
output wire m_ip_hdr_valid,
|
||||
input wire m_ip_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata,
|
||||
output wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep,
|
||||
output wire m_ip_payload_axis_tvalid,
|
||||
input wire m_ip_payload_axis_tready,
|
||||
output wire m_ip_payload_axis_tlast,
|
||||
output wire [ID_WIDTH-1:0] m_ip_payload_axis_tid,
|
||||
output wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest,
|
||||
output wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser
|
||||
);
|
||||
|
||||
parameter CL_S_COUNT = $clog2(S_COUNT);
|
||||
|
||||
reg frame_reg = 1'b0, frame_next;
|
||||
|
||||
reg s_ip_hdr_ready_mask_reg = 1'b0, s_ip_hdr_ready_mask_next;
|
||||
|
||||
reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next;
|
||||
reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next;
|
||||
reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next;
|
||||
reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next;
|
||||
reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next;
|
||||
reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next;
|
||||
reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next;
|
||||
reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next;
|
||||
reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next;
|
||||
reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next;
|
||||
reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next;
|
||||
reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next;
|
||||
reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next;
|
||||
reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next;
|
||||
reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next;
|
||||
|
||||
wire [S_COUNT-1:0] request;
|
||||
wire [S_COUNT-1:0] acknowledge;
|
||||
wire [S_COUNT-1:0] grant;
|
||||
wire grant_valid;
|
||||
wire [CL_S_COUNT-1:0] grant_encoded;
|
||||
|
||||
// internal datapath
|
||||
reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int;
|
||||
reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int;
|
||||
reg m_ip_payload_axis_tvalid_int;
|
||||
reg m_ip_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_ip_payload_axis_tlast_int;
|
||||
reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int;
|
||||
reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int;
|
||||
reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int;
|
||||
wire m_ip_payload_axis_tready_int_early;
|
||||
|
||||
assign s_ip_hdr_ready = (!s_ip_hdr_ready_mask_reg && grant_valid) << grant_encoded;
|
||||
|
||||
assign s_ip_payload_axis_tready = (m_ip_payload_axis_tready_int_reg && grant_valid) << grant_encoded;
|
||||
|
||||
assign m_ip_hdr_valid = m_ip_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
assign m_ip_version = m_ip_version_reg;
|
||||
assign m_ip_ihl = m_ip_ihl_reg;
|
||||
assign m_ip_dscp = m_ip_dscp_reg;
|
||||
assign m_ip_ecn = m_ip_ecn_reg;
|
||||
assign m_ip_length = m_ip_length_reg;
|
||||
assign m_ip_identification = m_ip_identification_reg;
|
||||
assign m_ip_flags = m_ip_flags_reg;
|
||||
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
|
||||
assign m_ip_ttl = m_ip_ttl_reg;
|
||||
assign m_ip_protocol = m_ip_protocol_reg;
|
||||
assign m_ip_header_checksum = m_ip_header_checksum_reg;
|
||||
assign m_ip_source_ip = m_ip_source_ip_reg;
|
||||
assign m_ip_dest_ip = m_ip_dest_ip_reg;
|
||||
|
||||
// mux for incoming packet
|
||||
wire [DATA_WIDTH-1:0] current_s_tdata = s_ip_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH];
|
||||
wire [KEEP_WIDTH-1:0] current_s_tkeep = s_ip_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH];
|
||||
wire current_s_tvalid = s_ip_payload_axis_tvalid[grant_encoded];
|
||||
wire current_s_tready = s_ip_payload_axis_tready[grant_encoded];
|
||||
wire current_s_tlast = s_ip_payload_axis_tlast[grant_encoded];
|
||||
wire [ID_WIDTH-1:0] current_s_tid = s_ip_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH];
|
||||
wire [DEST_WIDTH-1:0] current_s_tdest = s_ip_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH];
|
||||
wire [USER_WIDTH-1:0] current_s_tuser = s_ip_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH];
|
||||
|
||||
// arbiter instance
|
||||
rgmii_arbiter #(
|
||||
.PORTS(S_COUNT),
|
||||
.TYPE(ARB_TYPE),
|
||||
.BLOCK("ACKNOWLEDGE"),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
arb_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.request(request),
|
||||
.acknowledge(acknowledge),
|
||||
.grant(grant),
|
||||
.grant_valid(grant_valid),
|
||||
.grant_encoded(grant_encoded)
|
||||
);
|
||||
|
||||
generate
|
||||
genvar n;
|
||||
|
||||
for (n = 0; n < S_COUNT; n = n + 1) begin
|
||||
assign request[n] = s_ip_hdr_valid[n] && !grant[n];
|
||||
assign acknowledge[n] = grant[n] && s_ip_payload_axis_tvalid[n] && s_ip_payload_axis_tready[n] && s_ip_payload_axis_tlast[n];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
always @* begin
|
||||
frame_next = frame_reg;
|
||||
|
||||
s_ip_hdr_ready_mask_next = s_ip_hdr_ready_mask_reg;
|
||||
|
||||
m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready;
|
||||
m_eth_dest_mac_next = m_eth_dest_mac_reg;
|
||||
m_eth_src_mac_next = m_eth_src_mac_reg;
|
||||
m_eth_type_next = m_eth_type_reg;
|
||||
m_ip_version_next = m_ip_version_reg;
|
||||
m_ip_ihl_next = m_ip_ihl_reg;
|
||||
m_ip_dscp_next = m_ip_dscp_reg;
|
||||
m_ip_ecn_next = m_ip_ecn_reg;
|
||||
m_ip_length_next = m_ip_length_reg;
|
||||
m_ip_identification_next = m_ip_identification_reg;
|
||||
m_ip_flags_next = m_ip_flags_reg;
|
||||
m_ip_fragment_offset_next = m_ip_fragment_offset_reg;
|
||||
m_ip_ttl_next = m_ip_ttl_reg;
|
||||
m_ip_protocol_next = m_ip_protocol_reg;
|
||||
m_ip_header_checksum_next = m_ip_header_checksum_reg;
|
||||
m_ip_source_ip_next = m_ip_source_ip_reg;
|
||||
m_ip_dest_ip_next = m_ip_dest_ip_reg;
|
||||
|
||||
if (s_ip_payload_axis_tvalid[grant_encoded] && s_ip_payload_axis_tready[grant_encoded]) begin
|
||||
// end of frame detection
|
||||
if (s_ip_payload_axis_tlast[grant_encoded]) begin
|
||||
frame_next = 1'b0;
|
||||
s_ip_hdr_ready_mask_next = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
if (!frame_reg && grant_valid) begin
|
||||
// start of frame
|
||||
frame_next = 1'b1;
|
||||
|
||||
s_ip_hdr_ready_mask_next = 1'b1;
|
||||
|
||||
m_ip_hdr_valid_next = 1'b1;
|
||||
m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48];
|
||||
m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48];
|
||||
m_eth_type_next = s_eth_type[grant_encoded*16 +: 16];
|
||||
m_ip_version_next = s_ip_version[grant_encoded*4 +: 4];
|
||||
m_ip_ihl_next = s_ip_ihl[grant_encoded*4 +: 4];
|
||||
m_ip_dscp_next = s_ip_dscp[grant_encoded*6 +: 6];
|
||||
m_ip_ecn_next = s_ip_ecn[grant_encoded*2 +: 2];
|
||||
m_ip_length_next = s_ip_length[grant_encoded*16 +: 16];
|
||||
m_ip_identification_next = s_ip_identification[grant_encoded*16 +: 16];
|
||||
m_ip_flags_next = s_ip_flags[grant_encoded*3 +: 3];
|
||||
m_ip_fragment_offset_next = s_ip_fragment_offset[grant_encoded*13 +: 13];
|
||||
m_ip_ttl_next = s_ip_ttl[grant_encoded*8 +: 8];
|
||||
m_ip_protocol_next = s_ip_protocol[grant_encoded*8 +: 8];
|
||||
m_ip_header_checksum_next = s_ip_header_checksum[grant_encoded*16 +: 16];
|
||||
m_ip_source_ip_next = s_ip_source_ip[grant_encoded*32 +: 32];
|
||||
m_ip_dest_ip_next = s_ip_dest_ip[grant_encoded*32 +: 32];
|
||||
end
|
||||
|
||||
// pass through selected packet data
|
||||
m_ip_payload_axis_tdata_int = current_s_tdata;
|
||||
m_ip_payload_axis_tkeep_int = current_s_tkeep;
|
||||
m_ip_payload_axis_tvalid_int = current_s_tvalid && m_ip_payload_axis_tready_int_reg && grant_valid;
|
||||
m_ip_payload_axis_tlast_int = current_s_tlast;
|
||||
m_ip_payload_axis_tid_int = current_s_tid;
|
||||
m_ip_payload_axis_tdest_int = current_s_tdest;
|
||||
m_ip_payload_axis_tuser_int = current_s_tuser;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
frame_reg <= 1'b0;
|
||||
s_ip_hdr_ready_mask_reg <= 1'b0;
|
||||
m_ip_hdr_valid_reg <= 1'b0;
|
||||
end else begin
|
||||
frame_reg <= frame_next;
|
||||
s_ip_hdr_ready_mask_reg <= s_ip_hdr_ready_mask_next;
|
||||
m_ip_hdr_valid_reg <= m_ip_hdr_valid_next;
|
||||
end
|
||||
|
||||
m_eth_dest_mac_reg <= m_eth_dest_mac_next;
|
||||
m_eth_src_mac_reg <= m_eth_src_mac_next;
|
||||
m_eth_type_reg <= m_eth_type_next;
|
||||
m_ip_version_reg <= m_ip_version_next;
|
||||
m_ip_ihl_reg <= m_ip_ihl_next;
|
||||
m_ip_dscp_reg <= m_ip_dscp_next;
|
||||
m_ip_ecn_reg <= m_ip_ecn_next;
|
||||
m_ip_length_reg <= m_ip_length_next;
|
||||
m_ip_identification_reg <= m_ip_identification_next;
|
||||
m_ip_flags_reg <= m_ip_flags_next;
|
||||
m_ip_fragment_offset_reg <= m_ip_fragment_offset_next;
|
||||
m_ip_ttl_reg <= m_ip_ttl_next;
|
||||
m_ip_protocol_reg <= m_ip_protocol_next;
|
||||
m_ip_header_checksum_reg <= m_ip_header_checksum_next;
|
||||
m_ip_source_ip_reg <= m_ip_source_ip_next;
|
||||
m_ip_dest_ip_reg <= m_ip_dest_ip_next;
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
|
||||
reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
|
||||
reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next;
|
||||
reg m_ip_payload_axis_tlast_reg = 1'b0;
|
||||
reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
|
||||
reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
|
||||
reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
|
||||
|
||||
reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}};
|
||||
reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}};
|
||||
reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next;
|
||||
reg temp_m_ip_payload_axis_tlast_reg = 1'b0;
|
||||
reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}};
|
||||
reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}};
|
||||
reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}};
|
||||
|
||||
// datapath control
|
||||
reg store_axis_int_to_output;
|
||||
reg store_axis_int_to_temp;
|
||||
reg store_ip_payload_axis_temp_to_output;
|
||||
|
||||
assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg;
|
||||
assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? m_ip_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
|
||||
assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg;
|
||||
assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg;
|
||||
assign m_ip_payload_axis_tid = ID_ENABLE ? m_ip_payload_axis_tid_reg : {ID_WIDTH{1'b0}};
|
||||
assign m_ip_payload_axis_tdest = DEST_ENABLE ? m_ip_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}};
|
||||
assign m_ip_payload_axis_tuser = USER_ENABLE ? m_ip_payload_axis_tuser_reg : {USER_WIDTH{1'b0}};
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg;
|
||||
temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
|
||||
|
||||
store_axis_int_to_output = 1'b0;
|
||||
store_axis_int_to_temp = 1'b0;
|
||||
store_ip_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_ip_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
|
||||
store_axis_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
|
||||
store_axis_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_ip_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
|
||||
temp_m_ip_payload_axis_tvalid_next = 1'b0;
|
||||
store_ip_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_ip_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_ip_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next;
|
||||
m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early;
|
||||
temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_axis_int_to_output) begin
|
||||
m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
|
||||
m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int;
|
||||
m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
|
||||
m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int;
|
||||
m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int;
|
||||
m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
|
||||
end else if (store_ip_payload_axis_temp_to_output) begin
|
||||
m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg;
|
||||
m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg;
|
||||
m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg;
|
||||
m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg;
|
||||
m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg;
|
||||
m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_axis_int_to_temp) begin
|
||||
temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
|
||||
temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int;
|
||||
temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
|
||||
temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int;
|
||||
temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int;
|
||||
temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
440
fpga/src/ip_complete.sv
Normal file
440
fpga/src/ip_complete.sv
Normal file
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* IPv4 and ARP block, ethernet frame interface
|
||||
*/
|
||||
module ip_complete #(
|
||||
parameter ARP_CACHE_ADDR_WIDTH = 9,
|
||||
parameter ARP_REQUEST_RETRY_COUNT = 4,
|
||||
parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2,
|
||||
parameter ARP_REQUEST_TIMEOUT = 125000000*30
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame input
|
||||
*/
|
||||
input wire s_eth_hdr_valid,
|
||||
output wire s_eth_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [7:0] s_eth_payload_axis_tdata,
|
||||
input wire s_eth_payload_axis_tvalid,
|
||||
output wire s_eth_payload_axis_tready,
|
||||
input wire s_eth_payload_axis_tlast,
|
||||
input wire s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [7:0] m_eth_payload_axis_tdata,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire m_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP input
|
||||
*/
|
||||
input wire s_ip_hdr_valid,
|
||||
output wire s_ip_hdr_ready,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_length,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [7:0] s_ip_protocol,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [7:0] s_ip_payload_axis_tdata,
|
||||
input wire s_ip_payload_axis_tvalid,
|
||||
output wire s_ip_payload_axis_tready,
|
||||
input wire s_ip_payload_axis_tlast,
|
||||
input wire s_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP output
|
||||
*/
|
||||
output wire m_ip_hdr_valid,
|
||||
input wire m_ip_hdr_ready,
|
||||
output wire [47:0] m_ip_eth_dest_mac,
|
||||
output wire [47:0] m_ip_eth_src_mac,
|
||||
output wire [15:0] m_ip_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [7:0] m_ip_payload_axis_tdata,
|
||||
output wire m_ip_payload_axis_tvalid,
|
||||
input wire m_ip_payload_axis_tready,
|
||||
output wire m_ip_payload_axis_tlast,
|
||||
output wire m_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire rx_busy,
|
||||
output wire tx_busy,
|
||||
output wire rx_error_header_early_termination,
|
||||
output wire rx_error_payload_early_termination,
|
||||
output wire rx_error_invalid_header,
|
||||
output wire rx_error_invalid_checksum,
|
||||
output wire tx_error_payload_early_termination,
|
||||
output wire tx_error_arp_failed,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [47:0] local_mac,
|
||||
input wire [31:0] local_ip,
|
||||
input wire [31:0] gateway_ip,
|
||||
input wire [31:0] subnet_mask,
|
||||
input wire clear_arp_cache
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
This module integrates the IP and ARP modules for a complete IP stack
|
||||
|
||||
*/
|
||||
|
||||
wire arp_request_valid;
|
||||
wire arp_request_ready;
|
||||
wire [31:0] arp_request_ip;
|
||||
wire arp_response_valid;
|
||||
wire arp_response_ready;
|
||||
wire arp_response_error;
|
||||
wire [47:0] arp_response_mac;
|
||||
|
||||
wire ip_rx_eth_hdr_valid;
|
||||
wire ip_rx_eth_hdr_ready;
|
||||
wire [47:0] ip_rx_eth_dest_mac;
|
||||
wire [47:0] ip_rx_eth_src_mac;
|
||||
wire [15:0] ip_rx_eth_type;
|
||||
wire [7:0] ip_rx_eth_payload_axis_tdata;
|
||||
wire ip_rx_eth_payload_axis_tvalid;
|
||||
wire ip_rx_eth_payload_axis_tready;
|
||||
wire ip_rx_eth_payload_axis_tlast;
|
||||
wire ip_rx_eth_payload_axis_tuser;
|
||||
|
||||
wire ip_tx_eth_hdr_valid;
|
||||
wire ip_tx_eth_hdr_ready;
|
||||
wire [47:0] ip_tx_eth_dest_mac;
|
||||
wire [47:0] ip_tx_eth_src_mac;
|
||||
wire [15:0] ip_tx_eth_type;
|
||||
wire [7:0] ip_tx_eth_payload_axis_tdata;
|
||||
wire ip_tx_eth_payload_axis_tvalid;
|
||||
wire ip_tx_eth_payload_axis_tready;
|
||||
wire ip_tx_eth_payload_axis_tlast;
|
||||
wire ip_tx_eth_payload_axis_tuser;
|
||||
|
||||
wire arp_rx_eth_hdr_valid;
|
||||
wire arp_rx_eth_hdr_ready;
|
||||
wire [47:0] arp_rx_eth_dest_mac;
|
||||
wire [47:0] arp_rx_eth_src_mac;
|
||||
wire [15:0] arp_rx_eth_type;
|
||||
wire [7:0] arp_rx_eth_payload_axis_tdata;
|
||||
wire arp_rx_eth_payload_axis_tvalid;
|
||||
wire arp_rx_eth_payload_axis_tready;
|
||||
wire arp_rx_eth_payload_axis_tlast;
|
||||
wire arp_rx_eth_payload_axis_tuser;
|
||||
|
||||
wire arp_tx_eth_hdr_valid;
|
||||
wire arp_tx_eth_hdr_ready;
|
||||
wire [47:0] arp_tx_eth_dest_mac;
|
||||
wire [47:0] arp_tx_eth_src_mac;
|
||||
wire [15:0] arp_tx_eth_type;
|
||||
wire [7:0] arp_tx_eth_payload_axis_tdata;
|
||||
wire arp_tx_eth_payload_axis_tvalid;
|
||||
wire arp_tx_eth_payload_axis_tready;
|
||||
wire arp_tx_eth_payload_axis_tlast;
|
||||
wire arp_tx_eth_payload_axis_tuser;
|
||||
|
||||
/*
|
||||
* Input classifier (eth_type)
|
||||
*/
|
||||
wire s_select_ip = (s_eth_type == 16'h0800);
|
||||
wire s_select_arp = (s_eth_type == 16'h0806);
|
||||
wire s_select_none = !(s_select_ip || s_select_arp);
|
||||
|
||||
reg s_select_ip_reg = 1'b0;
|
||||
reg s_select_arp_reg = 1'b0;
|
||||
reg s_select_none_reg = 1'b0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
s_select_ip_reg <= 1'b0;
|
||||
s_select_arp_reg <= 1'b0;
|
||||
s_select_none_reg <= 1'b0;
|
||||
end else begin
|
||||
if (s_eth_payload_axis_tvalid) begin
|
||||
if ((!s_select_ip_reg && !s_select_arp_reg && !s_select_none_reg) ||
|
||||
(s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && s_eth_payload_axis_tlast)) begin
|
||||
s_select_ip_reg <= s_select_ip;
|
||||
s_select_arp_reg <= s_select_arp;
|
||||
s_select_none_reg <= s_select_none;
|
||||
end
|
||||
end else begin
|
||||
s_select_ip_reg <= 1'b0;
|
||||
s_select_arp_reg <= 1'b0;
|
||||
s_select_none_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign ip_rx_eth_hdr_valid = s_select_ip && s_eth_hdr_valid;
|
||||
assign ip_rx_eth_dest_mac = s_eth_dest_mac;
|
||||
assign ip_rx_eth_src_mac = s_eth_src_mac;
|
||||
assign ip_rx_eth_type = 16'h0800;
|
||||
assign ip_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata;
|
||||
assign ip_rx_eth_payload_axis_tvalid = s_select_ip_reg && s_eth_payload_axis_tvalid;
|
||||
assign ip_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast;
|
||||
assign ip_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser;
|
||||
|
||||
assign arp_rx_eth_hdr_valid = s_select_arp && s_eth_hdr_valid;
|
||||
assign arp_rx_eth_dest_mac = s_eth_dest_mac;
|
||||
assign arp_rx_eth_src_mac = s_eth_src_mac;
|
||||
assign arp_rx_eth_type = 16'h0806;
|
||||
assign arp_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata;
|
||||
assign arp_rx_eth_payload_axis_tvalid = s_select_arp_reg && s_eth_payload_axis_tvalid;
|
||||
assign arp_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast;
|
||||
assign arp_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser;
|
||||
|
||||
assign s_eth_hdr_ready = (s_select_ip && ip_rx_eth_hdr_ready) ||
|
||||
(s_select_arp && arp_rx_eth_hdr_ready) ||
|
||||
(s_select_none);
|
||||
|
||||
assign s_eth_payload_axis_tready = (s_select_ip_reg && ip_rx_eth_payload_axis_tready) ||
|
||||
(s_select_arp_reg && arp_rx_eth_payload_axis_tready) ||
|
||||
s_select_none_reg;
|
||||
|
||||
/*
|
||||
* Output arbiter
|
||||
*/
|
||||
eth_arb_mux #(
|
||||
.S_COUNT(2),
|
||||
.DATA_WIDTH(8),
|
||||
.KEEP_ENABLE(0),
|
||||
.ID_ENABLE(0),
|
||||
.DEST_ENABLE(0),
|
||||
.USER_ENABLE(1),
|
||||
.USER_WIDTH(1),
|
||||
.ARB_TYPE("PRIORITY"),
|
||||
.LSB_PRIORITY("HIGH")
|
||||
)
|
||||
eth_arb_mux_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// Ethernet frame inputs
|
||||
.s_eth_hdr_valid({ip_tx_eth_hdr_valid, arp_tx_eth_hdr_valid}),
|
||||
.s_eth_hdr_ready({ip_tx_eth_hdr_ready, arp_tx_eth_hdr_ready}),
|
||||
.s_eth_dest_mac({ip_tx_eth_dest_mac, arp_tx_eth_dest_mac}),
|
||||
.s_eth_src_mac({ip_tx_eth_src_mac, arp_tx_eth_src_mac}),
|
||||
.s_eth_type({ip_tx_eth_type, arp_tx_eth_type}),
|
||||
.s_eth_payload_axis_tdata({ip_tx_eth_payload_axis_tdata, arp_tx_eth_payload_axis_tdata}),
|
||||
.s_eth_payload_axis_tkeep(0),
|
||||
.s_eth_payload_axis_tvalid({ip_tx_eth_payload_axis_tvalid, arp_tx_eth_payload_axis_tvalid}),
|
||||
.s_eth_payload_axis_tready({ip_tx_eth_payload_axis_tready, arp_tx_eth_payload_axis_tready}),
|
||||
.s_eth_payload_axis_tlast({ip_tx_eth_payload_axis_tlast, arp_tx_eth_payload_axis_tlast}),
|
||||
.s_eth_payload_axis_tid(0),
|
||||
.s_eth_payload_axis_tdest(0),
|
||||
.s_eth_payload_axis_tuser({ip_tx_eth_payload_axis_tuser, arp_tx_eth_payload_axis_tuser}),
|
||||
// Ethernet frame output
|
||||
.m_eth_hdr_valid(m_eth_hdr_valid),
|
||||
.m_eth_hdr_ready(m_eth_hdr_ready),
|
||||
.m_eth_dest_mac(m_eth_dest_mac),
|
||||
.m_eth_src_mac(m_eth_src_mac),
|
||||
.m_eth_type(m_eth_type),
|
||||
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
|
||||
.m_eth_payload_axis_tkeep(),
|
||||
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
|
||||
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
|
||||
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
|
||||
.m_eth_payload_axis_tid(),
|
||||
.m_eth_payload_axis_tdest(),
|
||||
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser)
|
||||
);
|
||||
|
||||
/*
|
||||
* IP module
|
||||
*/
|
||||
ip
|
||||
ip_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// Ethernet frame input
|
||||
.s_eth_hdr_valid(ip_rx_eth_hdr_valid),
|
||||
.s_eth_hdr_ready(ip_rx_eth_hdr_ready),
|
||||
.s_eth_dest_mac(ip_rx_eth_dest_mac),
|
||||
.s_eth_src_mac(ip_rx_eth_src_mac),
|
||||
.s_eth_type(ip_rx_eth_type),
|
||||
.s_eth_payload_axis_tdata(ip_rx_eth_payload_axis_tdata),
|
||||
.s_eth_payload_axis_tvalid(ip_rx_eth_payload_axis_tvalid),
|
||||
.s_eth_payload_axis_tready(ip_rx_eth_payload_axis_tready),
|
||||
.s_eth_payload_axis_tlast(ip_rx_eth_payload_axis_tlast),
|
||||
.s_eth_payload_axis_tuser(ip_rx_eth_payload_axis_tuser),
|
||||
// Ethernet frame output
|
||||
.m_eth_hdr_valid(ip_tx_eth_hdr_valid),
|
||||
.m_eth_hdr_ready(ip_tx_eth_hdr_ready),
|
||||
.m_eth_dest_mac(ip_tx_eth_dest_mac),
|
||||
.m_eth_src_mac(ip_tx_eth_src_mac),
|
||||
.m_eth_type(ip_tx_eth_type),
|
||||
.m_eth_payload_axis_tdata(ip_tx_eth_payload_axis_tdata),
|
||||
.m_eth_payload_axis_tvalid(ip_tx_eth_payload_axis_tvalid),
|
||||
.m_eth_payload_axis_tready(ip_tx_eth_payload_axis_tready),
|
||||
.m_eth_payload_axis_tlast(ip_tx_eth_payload_axis_tlast),
|
||||
.m_eth_payload_axis_tuser(ip_tx_eth_payload_axis_tuser),
|
||||
// IP frame output
|
||||
.m_ip_hdr_valid(m_ip_hdr_valid),
|
||||
.m_ip_hdr_ready(m_ip_hdr_ready),
|
||||
.m_ip_eth_dest_mac(m_ip_eth_dest_mac),
|
||||
.m_ip_eth_src_mac(m_ip_eth_src_mac),
|
||||
.m_ip_eth_type(m_ip_eth_type),
|
||||
.m_ip_version(m_ip_version),
|
||||
.m_ip_ihl(m_ip_ihl),
|
||||
.m_ip_dscp(m_ip_dscp),
|
||||
.m_ip_ecn(m_ip_ecn),
|
||||
.m_ip_length(m_ip_length),
|
||||
.m_ip_identification(m_ip_identification),
|
||||
.m_ip_flags(m_ip_flags),
|
||||
.m_ip_fragment_offset(m_ip_fragment_offset),
|
||||
.m_ip_ttl(m_ip_ttl),
|
||||
.m_ip_protocol(m_ip_protocol),
|
||||
.m_ip_header_checksum(m_ip_header_checksum),
|
||||
.m_ip_source_ip(m_ip_source_ip),
|
||||
.m_ip_dest_ip(m_ip_dest_ip),
|
||||
.m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
|
||||
.m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
|
||||
.m_ip_payload_axis_tready(m_ip_payload_axis_tready),
|
||||
.m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
|
||||
.m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
|
||||
// IP frame input
|
||||
.s_ip_hdr_valid(s_ip_hdr_valid),
|
||||
.s_ip_hdr_ready(s_ip_hdr_ready),
|
||||
.s_ip_dscp(s_ip_dscp),
|
||||
.s_ip_ecn(s_ip_ecn),
|
||||
.s_ip_length(s_ip_length),
|
||||
.s_ip_ttl(s_ip_ttl),
|
||||
.s_ip_protocol(s_ip_protocol),
|
||||
.s_ip_source_ip(s_ip_source_ip),
|
||||
.s_ip_dest_ip(s_ip_dest_ip),
|
||||
.s_ip_payload_axis_tdata(s_ip_payload_axis_tdata),
|
||||
.s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid),
|
||||
.s_ip_payload_axis_tready(s_ip_payload_axis_tready),
|
||||
.s_ip_payload_axis_tlast(s_ip_payload_axis_tlast),
|
||||
.s_ip_payload_axis_tuser(s_ip_payload_axis_tuser),
|
||||
// ARP requests
|
||||
.arp_request_valid(arp_request_valid),
|
||||
.arp_request_ready(arp_request_ready),
|
||||
.arp_request_ip(arp_request_ip),
|
||||
.arp_response_valid(arp_response_valid),
|
||||
.arp_response_ready(arp_response_ready),
|
||||
.arp_response_error(arp_response_error),
|
||||
.arp_response_mac(arp_response_mac),
|
||||
// Status
|
||||
.rx_busy(rx_busy),
|
||||
.tx_busy(tx_busy),
|
||||
.rx_error_header_early_termination(rx_error_header_early_termination),
|
||||
.rx_error_payload_early_termination(rx_error_payload_early_termination),
|
||||
.rx_error_invalid_header(rx_error_invalid_header),
|
||||
.rx_error_invalid_checksum(rx_error_invalid_checksum),
|
||||
.tx_error_payload_early_termination(tx_error_payload_early_termination),
|
||||
.tx_error_arp_failed(tx_error_arp_failed),
|
||||
// Configuration
|
||||
.local_mac(local_mac),
|
||||
.local_ip(local_ip)
|
||||
);
|
||||
|
||||
/*
|
||||
* ARP module
|
||||
*/
|
||||
arp #(
|
||||
.CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH),
|
||||
.REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT),
|
||||
.REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL),
|
||||
.REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT)
|
||||
)
|
||||
arp_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// Ethernet frame input
|
||||
.s_eth_hdr_valid(arp_rx_eth_hdr_valid),
|
||||
.s_eth_hdr_ready(arp_rx_eth_hdr_ready),
|
||||
.s_eth_dest_mac(arp_rx_eth_dest_mac),
|
||||
.s_eth_src_mac(arp_rx_eth_src_mac),
|
||||
.s_eth_type(arp_rx_eth_type),
|
||||
.s_eth_payload_axis_tdata(arp_rx_eth_payload_axis_tdata),
|
||||
.s_eth_payload_axis_tvalid(arp_rx_eth_payload_axis_tvalid),
|
||||
.s_eth_payload_axis_tready(arp_rx_eth_payload_axis_tready),
|
||||
.s_eth_payload_axis_tlast(arp_rx_eth_payload_axis_tlast),
|
||||
.s_eth_payload_axis_tuser(arp_rx_eth_payload_axis_tuser),
|
||||
// Ethernet frame output
|
||||
.m_eth_hdr_valid(arp_tx_eth_hdr_valid),
|
||||
.m_eth_hdr_ready(arp_tx_eth_hdr_ready),
|
||||
.m_eth_dest_mac(arp_tx_eth_dest_mac),
|
||||
.m_eth_src_mac(arp_tx_eth_src_mac),
|
||||
.m_eth_type(arp_tx_eth_type),
|
||||
.m_eth_payload_axis_tdata(arp_tx_eth_payload_axis_tdata),
|
||||
.m_eth_payload_axis_tvalid(arp_tx_eth_payload_axis_tvalid),
|
||||
.m_eth_payload_axis_tready(arp_tx_eth_payload_axis_tready),
|
||||
.m_eth_payload_axis_tlast(arp_tx_eth_payload_axis_tlast),
|
||||
.m_eth_payload_axis_tuser(arp_tx_eth_payload_axis_tuser),
|
||||
// ARP requests
|
||||
.arp_request_valid(arp_request_valid),
|
||||
.arp_request_ready(arp_request_ready),
|
||||
.arp_request_ip(arp_request_ip),
|
||||
.arp_response_valid(arp_response_valid),
|
||||
.arp_response_ready(arp_response_ready),
|
||||
.arp_response_error(arp_response_error),
|
||||
.arp_response_mac(arp_response_mac),
|
||||
// Configuration
|
||||
.local_mac(local_mac),
|
||||
.local_ip(local_ip),
|
||||
.gateway_ip(gateway_ip),
|
||||
.subnet_mask(subnet_mask),
|
||||
.clear_cache(clear_arp_cache)
|
||||
);
|
||||
|
||||
endmodule
|
575
fpga/src/ip_eth_rx.sv
Normal file
575
fpga/src/ip_eth_rx.sv
Normal file
|
@ -0,0 +1,575 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* IP ethernet frame receiver (Ethernet frame in, IP frame out)
|
||||
*/
|
||||
module ip_eth_rx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame input
|
||||
*/
|
||||
input wire s_eth_hdr_valid,
|
||||
output wire s_eth_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [7:0] s_eth_payload_axis_tdata,
|
||||
input wire s_eth_payload_axis_tvalid,
|
||||
output wire s_eth_payload_axis_tready,
|
||||
input wire s_eth_payload_axis_tlast,
|
||||
input wire s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP frame output
|
||||
*/
|
||||
output wire m_ip_hdr_valid,
|
||||
input wire m_ip_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [7:0] m_ip_payload_axis_tdata,
|
||||
output wire m_ip_payload_axis_tvalid,
|
||||
input wire m_ip_payload_axis_tready,
|
||||
output wire m_ip_payload_axis_tlast,
|
||||
output wire m_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy,
|
||||
output wire error_header_early_termination,
|
||||
output wire error_payload_early_termination,
|
||||
output wire error_invalid_header,
|
||||
output wire error_invalid_checksum
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
IP Frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype (0x0800) 2 octets
|
||||
Version (4) 4 bits
|
||||
IHL (5-15) 4 bits
|
||||
DSCP (0) 6 bits
|
||||
ECN (0) 2 bits
|
||||
length 2 octets
|
||||
identification (0?) 2 octets
|
||||
flags (010) 3 bits
|
||||
fragment offset (0) 13 bits
|
||||
time to live (64?) 1 octet
|
||||
protocol 1 octet
|
||||
header checksum 2 octets
|
||||
source IP 4 octets
|
||||
destination IP 4 octets
|
||||
options (IHL-5)*4 octets
|
||||
payload length octets
|
||||
|
||||
This module receives an Ethernet frame with header fields in parallel and
|
||||
payload on an AXI stream interface, decodes and strips the IP header fields,
|
||||
then produces the header fields in parallel along with the IP payload in a
|
||||
separate AXI stream.
|
||||
|
||||
*/
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_READ_HEADER = 3'd1,
|
||||
STATE_READ_PAYLOAD = 3'd2,
|
||||
STATE_READ_PAYLOAD_LAST = 3'd3,
|
||||
STATE_WAIT_LAST = 3'd4;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_eth_hdr;
|
||||
reg store_ip_version_ihl;
|
||||
reg store_ip_dscp_ecn;
|
||||
reg store_ip_length_0;
|
||||
reg store_ip_length_1;
|
||||
reg store_ip_identification_0;
|
||||
reg store_ip_identification_1;
|
||||
reg store_ip_flags_fragment_offset_0;
|
||||
reg store_ip_flags_fragment_offset_1;
|
||||
reg store_ip_ttl;
|
||||
reg store_ip_protocol;
|
||||
reg store_ip_header_checksum_0;
|
||||
reg store_ip_header_checksum_1;
|
||||
reg store_ip_source_ip_0;
|
||||
reg store_ip_source_ip_1;
|
||||
reg store_ip_source_ip_2;
|
||||
reg store_ip_source_ip_3;
|
||||
reg store_ip_dest_ip_0;
|
||||
reg store_ip_dest_ip_1;
|
||||
reg store_ip_dest_ip_2;
|
||||
reg store_ip_dest_ip_3;
|
||||
reg store_last_word;
|
||||
|
||||
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
|
||||
|
||||
reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next;
|
||||
|
||||
reg [7:0] last_word_data_reg = 8'd0;
|
||||
|
||||
reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next;
|
||||
reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next;
|
||||
|
||||
reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
reg [3:0] m_ip_version_reg = 4'd0;
|
||||
reg [3:0] m_ip_ihl_reg = 4'd0;
|
||||
reg [5:0] m_ip_dscp_reg = 6'd0;
|
||||
reg [1:0] m_ip_ecn_reg = 2'd0;
|
||||
reg [15:0] m_ip_length_reg = 16'd0;
|
||||
reg [15:0] m_ip_identification_reg = 16'd0;
|
||||
reg [2:0] m_ip_flags_reg = 3'd0;
|
||||
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
|
||||
reg [7:0] m_ip_ttl_reg = 8'd0;
|
||||
reg [7:0] m_ip_protocol_reg = 8'd0;
|
||||
reg [15:0] m_ip_header_checksum_reg = 16'd0;
|
||||
reg [31:0] m_ip_source_ip_reg = 32'd0;
|
||||
reg [31:0] m_ip_dest_ip_reg = 32'd0;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
|
||||
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
|
||||
reg error_invalid_header_reg = 1'b0, error_invalid_header_next;
|
||||
reg error_invalid_checksum_reg = 1'b0, error_invalid_checksum_next;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] m_ip_payload_axis_tdata_int;
|
||||
reg m_ip_payload_axis_tvalid_int;
|
||||
reg m_ip_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_ip_payload_axis_tlast_int;
|
||||
reg m_ip_payload_axis_tuser_int;
|
||||
wire m_ip_payload_axis_tready_int_early;
|
||||
|
||||
assign s_eth_hdr_ready = s_eth_hdr_ready_reg;
|
||||
assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg;
|
||||
|
||||
assign m_ip_hdr_valid = m_ip_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
assign m_ip_version = m_ip_version_reg;
|
||||
assign m_ip_ihl = m_ip_ihl_reg;
|
||||
assign m_ip_dscp = m_ip_dscp_reg;
|
||||
assign m_ip_ecn = m_ip_ecn_reg;
|
||||
assign m_ip_length = m_ip_length_reg;
|
||||
assign m_ip_identification = m_ip_identification_reg;
|
||||
assign m_ip_flags = m_ip_flags_reg;
|
||||
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
|
||||
assign m_ip_ttl = m_ip_ttl_reg;
|
||||
assign m_ip_protocol = m_ip_protocol_reg;
|
||||
assign m_ip_header_checksum = m_ip_header_checksum_reg;
|
||||
assign m_ip_source_ip = m_ip_source_ip_reg;
|
||||
assign m_ip_dest_ip = m_ip_dest_ip_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
assign error_header_early_termination = error_header_early_termination_reg;
|
||||
assign error_payload_early_termination = error_payload_early_termination_reg;
|
||||
assign error_invalid_header = error_invalid_header_reg;
|
||||
assign error_invalid_checksum = error_invalid_checksum_reg;
|
||||
|
||||
function [15:0] add1c16b;
|
||||
input [15:0] a, b;
|
||||
reg [16:0] t;
|
||||
begin
|
||||
t = a+b;
|
||||
add1c16b = t[15:0] + t[16];
|
||||
end
|
||||
endfunction
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_eth_hdr_ready_next = 1'b0;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
|
||||
store_eth_hdr = 1'b0;
|
||||
store_ip_version_ihl = 1'b0;
|
||||
store_ip_dscp_ecn = 1'b0;
|
||||
store_ip_length_0 = 1'b0;
|
||||
store_ip_length_1 = 1'b0;
|
||||
store_ip_identification_0 = 1'b0;
|
||||
store_ip_identification_1 = 1'b0;
|
||||
store_ip_flags_fragment_offset_0 = 1'b0;
|
||||
store_ip_flags_fragment_offset_1 = 1'b0;
|
||||
store_ip_ttl = 1'b0;
|
||||
store_ip_protocol = 1'b0;
|
||||
store_ip_header_checksum_0 = 1'b0;
|
||||
store_ip_header_checksum_1 = 1'b0;
|
||||
store_ip_source_ip_0 = 1'b0;
|
||||
store_ip_source_ip_1 = 1'b0;
|
||||
store_ip_source_ip_2 = 1'b0;
|
||||
store_ip_source_ip_3 = 1'b0;
|
||||
store_ip_dest_ip_0 = 1'b0;
|
||||
store_ip_dest_ip_1 = 1'b0;
|
||||
store_ip_dest_ip_2 = 1'b0;
|
||||
store_ip_dest_ip_3 = 1'b0;
|
||||
|
||||
store_last_word = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
hdr_sum_next = hdr_sum_reg;
|
||||
|
||||
m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready;
|
||||
|
||||
error_header_early_termination_next = 1'b0;
|
||||
error_payload_early_termination_next = 1'b0;
|
||||
error_invalid_header_next = 1'b0;
|
||||
error_invalid_checksum_next = 1'b0;
|
||||
|
||||
m_ip_payload_axis_tdata_int = 8'd0;
|
||||
m_ip_payload_axis_tvalid_int = 1'b0;
|
||||
m_ip_payload_axis_tlast_int = 1'b0;
|
||||
m_ip_payload_axis_tuser_int = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for header
|
||||
frame_ptr_next = 16'd0;
|
||||
hdr_sum_next = 16'd0;
|
||||
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
|
||||
if (s_eth_hdr_ready && s_eth_hdr_valid) begin
|
||||
s_eth_hdr_ready_next = 1'b0;
|
||||
s_eth_payload_axis_tready_next = 1'b1;
|
||||
store_eth_hdr = 1'b1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_READ_HEADER: begin
|
||||
// read header
|
||||
s_eth_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
|
||||
// word transfer in - store it
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
|
||||
if (frame_ptr_reg[0]) begin
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, {8'd0, s_eth_payload_axis_tdata});
|
||||
end else begin
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, {s_eth_payload_axis_tdata, 8'd0});
|
||||
end
|
||||
|
||||
case (frame_ptr_reg)
|
||||
8'h00: store_ip_version_ihl = 1'b1;
|
||||
8'h01: store_ip_dscp_ecn = 1'b1;
|
||||
8'h02: store_ip_length_1 = 1'b1;
|
||||
8'h03: store_ip_length_0 = 1'b1;
|
||||
8'h04: store_ip_identification_1 = 1'b1;
|
||||
8'h05: store_ip_identification_0 = 1'b1;
|
||||
8'h06: store_ip_flags_fragment_offset_1 = 1'b1;
|
||||
8'h07: store_ip_flags_fragment_offset_0 = 1'b1;
|
||||
8'h08: store_ip_ttl = 1'b1;
|
||||
8'h09: store_ip_protocol = 1'b1;
|
||||
8'h0A: store_ip_header_checksum_1 = 1'b1;
|
||||
8'h0B: store_ip_header_checksum_0 = 1'b1;
|
||||
8'h0C: store_ip_source_ip_3 = 1'b1;
|
||||
8'h0D: store_ip_source_ip_2 = 1'b1;
|
||||
8'h0E: store_ip_source_ip_1 = 1'b1;
|
||||
8'h0F: store_ip_source_ip_0 = 1'b1;
|
||||
8'h10: store_ip_dest_ip_3 = 1'b1;
|
||||
8'h11: store_ip_dest_ip_2 = 1'b1;
|
||||
8'h12: store_ip_dest_ip_1 = 1'b1;
|
||||
8'h13: begin
|
||||
store_ip_dest_ip_0 = 1'b1;
|
||||
if (m_ip_version_reg != 4'd4 || m_ip_ihl_reg != 4'd5) begin
|
||||
error_invalid_header_next = 1'b1;
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end else if (hdr_sum_next != 16'hffff) begin
|
||||
error_invalid_checksum_next = 1'b1;
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end else begin
|
||||
m_ip_hdr_valid_next = 1'b1;
|
||||
s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
|
||||
if (s_eth_payload_axis_tlast) begin
|
||||
error_header_early_termination_next = 1'b1;
|
||||
m_ip_hdr_valid_next = 1'b0;
|
||||
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
|
||||
end else begin
|
||||
state_next = STATE_READ_HEADER;
|
||||
end
|
||||
end
|
||||
STATE_READ_PAYLOAD: begin
|
||||
// read payload
|
||||
s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
|
||||
|
||||
m_ip_payload_axis_tdata_int = s_eth_payload_axis_tdata;
|
||||
m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid;
|
||||
m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast;
|
||||
m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser;
|
||||
|
||||
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
|
||||
// word transfer through
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
if (s_eth_payload_axis_tlast) begin
|
||||
if (frame_ptr_next != m_ip_length_reg) begin
|
||||
// end of frame, but length does not match
|
||||
m_ip_payload_axis_tuser_int = 1'b1;
|
||||
error_payload_early_termination_next = 1'b1;
|
||||
end
|
||||
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
if (frame_ptr_next == m_ip_length_reg) begin
|
||||
store_last_word = 1'b1;
|
||||
m_ip_payload_axis_tvalid_int = 1'b0;
|
||||
state_next = STATE_READ_PAYLOAD_LAST;
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
end
|
||||
STATE_READ_PAYLOAD_LAST: begin
|
||||
// read and discard until end of frame
|
||||
s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
|
||||
|
||||
m_ip_payload_axis_tdata_int = last_word_data_reg;
|
||||
m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid && s_eth_payload_axis_tlast;
|
||||
m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast;
|
||||
m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser;
|
||||
|
||||
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
|
||||
if (s_eth_payload_axis_tlast) begin
|
||||
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD_LAST;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_LAST: begin
|
||||
// read and discard until end of frame
|
||||
s_eth_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
|
||||
if (s_eth_payload_axis_tlast) begin
|
||||
s_eth_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
s_eth_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 16'd0;
|
||||
hdr_sum_reg <= 16'd0;
|
||||
s_eth_hdr_ready_reg <= 1'b0;
|
||||
s_eth_payload_axis_tready_reg <= 1'b0;
|
||||
m_ip_hdr_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
error_header_early_termination_reg <= 1'b0;
|
||||
error_payload_early_termination_reg <= 1'b0;
|
||||
error_invalid_header_reg <= 1'b0;
|
||||
error_invalid_checksum_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
hdr_sum_reg <= hdr_sum_next;
|
||||
|
||||
s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
|
||||
s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next;
|
||||
|
||||
m_ip_hdr_valid_reg <= m_ip_hdr_valid_next;
|
||||
|
||||
error_header_early_termination_reg <= error_header_early_termination_next;
|
||||
error_payload_early_termination_reg <= error_payload_early_termination_next;
|
||||
error_invalid_header_reg <= error_invalid_header_next;
|
||||
error_invalid_checksum_reg <= error_invalid_checksum_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_eth_hdr) begin
|
||||
m_eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
m_eth_src_mac_reg <= s_eth_src_mac;
|
||||
m_eth_type_reg <= s_eth_type;
|
||||
end
|
||||
|
||||
if (store_last_word) begin
|
||||
last_word_data_reg <= m_ip_payload_axis_tdata_int;
|
||||
end
|
||||
|
||||
if (store_ip_version_ihl) {m_ip_version_reg, m_ip_ihl_reg} <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_dscp_ecn) {m_ip_dscp_reg, m_ip_ecn_reg} <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_length_0) m_ip_length_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_length_1) m_ip_length_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_identification_0) m_ip_identification_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_identification_1) m_ip_identification_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_flags_fragment_offset_0) m_ip_fragment_offset_reg[ 7:0] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_flags_fragment_offset_1) {m_ip_flags_reg, m_ip_fragment_offset_reg[12:8]} <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_ttl) m_ip_ttl_reg <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_protocol) m_ip_protocol_reg <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_header_checksum_0) m_ip_header_checksum_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_header_checksum_1) m_ip_header_checksum_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_source_ip_0) m_ip_source_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_source_ip_1) m_ip_source_ip_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_source_ip_2) m_ip_source_ip_reg[23:16] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_source_ip_3) m_ip_source_ip_reg[31:24] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_dest_ip_0) m_ip_dest_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_dest_ip_1) m_ip_dest_ip_reg[15: 8] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_dest_ip_2) m_ip_dest_ip_reg[23:16] <= s_eth_payload_axis_tdata;
|
||||
if (store_ip_dest_ip_3) m_ip_dest_ip_reg[31:24] <= s_eth_payload_axis_tdata;
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0;
|
||||
reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next;
|
||||
reg m_ip_payload_axis_tlast_reg = 1'b0;
|
||||
reg m_ip_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0;
|
||||
reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next;
|
||||
reg temp_m_ip_payload_axis_tlast_reg = 1'b0;
|
||||
reg temp_m_ip_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_ip_payload_int_to_output;
|
||||
reg store_ip_payload_int_to_temp;
|
||||
reg store_ip_payload_axis_temp_to_output;
|
||||
|
||||
assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg;
|
||||
assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg;
|
||||
assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg;
|
||||
assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg;
|
||||
temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
|
||||
|
||||
store_ip_payload_int_to_output = 1'b0;
|
||||
store_ip_payload_int_to_temp = 1'b0;
|
||||
store_ip_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_ip_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
|
||||
store_ip_payload_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
|
||||
store_ip_payload_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_ip_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
|
||||
temp_m_ip_payload_axis_tvalid_next = 1'b0;
|
||||
store_ip_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_ip_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_ip_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next;
|
||||
m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early;
|
||||
temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_ip_payload_int_to_output) begin
|
||||
m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
|
||||
m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
|
||||
m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
|
||||
end else if (store_ip_payload_axis_temp_to_output) begin
|
||||
m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg;
|
||||
m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg;
|
||||
m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_ip_payload_int_to_temp) begin
|
||||
temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
|
||||
temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
|
||||
temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
494
fpga/src/ip_eth_tx.sv
Normal file
494
fpga/src/ip_eth_tx.sv
Normal file
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* IP ethernet frame transmitter (IP frame in, Ethernet frame out)
|
||||
*/
|
||||
module ip_eth_tx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* IP frame input
|
||||
*/
|
||||
input wire s_ip_hdr_valid,
|
||||
output wire s_ip_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_length,
|
||||
input wire [15:0] s_ip_identification,
|
||||
input wire [2:0] s_ip_flags,
|
||||
input wire [12:0] s_ip_fragment_offset,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [7:0] s_ip_protocol,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [7:0] s_ip_payload_axis_tdata,
|
||||
input wire s_ip_payload_axis_tvalid,
|
||||
output wire s_ip_payload_axis_tready,
|
||||
input wire s_ip_payload_axis_tlast,
|
||||
input wire s_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [7:0] m_eth_payload_axis_tdata,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire m_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy,
|
||||
output wire error_payload_early_termination
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
IP Frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype (0x0800) 2 octets
|
||||
Version (4) 4 bits
|
||||
IHL (5-15) 4 bits
|
||||
DSCP (0) 6 bits
|
||||
ECN (0) 2 bits
|
||||
length 2 octets
|
||||
identification (0?) 2 octets
|
||||
flags (010) 3 bits
|
||||
fragment offset (0) 13 bits
|
||||
time to live (64?) 1 octet
|
||||
protocol 1 octet
|
||||
header checksum 2 octets
|
||||
source IP 4 octets
|
||||
destination IP 4 octets
|
||||
options (IHL-5)*4 octets
|
||||
payload length octets
|
||||
|
||||
This module receives an IP frame with header fields in parallel along with the
|
||||
payload in an AXI stream, combines the header with the payload, passes through
|
||||
the Ethernet headers, and transmits the complete Ethernet payload on an AXI
|
||||
interface.
|
||||
|
||||
*/
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_WRITE_HEADER = 3'd1,
|
||||
STATE_WRITE_PAYLOAD = 3'd2,
|
||||
STATE_WRITE_PAYLOAD_LAST = 3'd3,
|
||||
STATE_WAIT_LAST = 3'd4;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_ip_hdr;
|
||||
reg store_last_word;
|
||||
|
||||
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
|
||||
|
||||
reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next;
|
||||
|
||||
reg [7:0] last_word_data_reg = 8'd0;
|
||||
|
||||
reg [5:0] ip_dscp_reg = 6'd0;
|
||||
reg [1:0] ip_ecn_reg = 2'd0;
|
||||
reg [15:0] ip_length_reg = 16'd0;
|
||||
reg [15:0] ip_identification_reg = 16'd0;
|
||||
reg [2:0] ip_flags_reg = 3'd0;
|
||||
reg [12:0] ip_fragment_offset_reg = 13'd0;
|
||||
reg [7:0] ip_ttl_reg = 8'd0;
|
||||
reg [7:0] ip_protocol_reg = 8'd0;
|
||||
reg [31:0] ip_source_ip_reg = 32'd0;
|
||||
reg [31:0] ip_dest_ip_reg = 32'd0;
|
||||
|
||||
reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next;
|
||||
reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next;
|
||||
|
||||
reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] m_eth_payload_axis_tdata_int;
|
||||
reg m_eth_payload_axis_tvalid_int;
|
||||
reg m_eth_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_eth_payload_axis_tlast_int;
|
||||
reg m_eth_payload_axis_tuser_int;
|
||||
wire m_eth_payload_axis_tready_int_early;
|
||||
|
||||
assign s_ip_hdr_ready = s_ip_hdr_ready_reg;
|
||||
assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg;
|
||||
|
||||
assign m_eth_hdr_valid = m_eth_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
assign error_payload_early_termination = error_payload_early_termination_reg;
|
||||
|
||||
function [15:0] add1c16b;
|
||||
input [15:0] a, b;
|
||||
reg [16:0] t;
|
||||
begin
|
||||
t = a+b;
|
||||
add1c16b = t[15:0] + t[16];
|
||||
end
|
||||
endfunction
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_ip_hdr_ready_next = 1'b0;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
|
||||
store_ip_hdr = 1'b0;
|
||||
|
||||
store_last_word = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
hdr_sum_next = hdr_sum_reg;
|
||||
|
||||
m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
|
||||
|
||||
error_payload_early_termination_next = 1'b0;
|
||||
|
||||
m_eth_payload_axis_tdata_int = 8'd0;
|
||||
m_eth_payload_axis_tvalid_int = 1'b0;
|
||||
m_eth_payload_axis_tlast_int = 1'b0;
|
||||
m_eth_payload_axis_tuser_int = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for data
|
||||
frame_ptr_next = 16'd0;
|
||||
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
|
||||
|
||||
if (s_ip_hdr_ready && s_ip_hdr_valid) begin
|
||||
store_ip_hdr = 1'b1;
|
||||
s_ip_hdr_ready_next = 1'b0;
|
||||
m_eth_hdr_valid_next = 1'b1;
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
m_eth_payload_axis_tvalid_int = 1'b1;
|
||||
m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl
|
||||
frame_ptr_next = 16'd1;
|
||||
end
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_HEADER: begin
|
||||
// write header
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
m_eth_payload_axis_tvalid_int = 1;
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
case (frame_ptr_reg)
|
||||
8'h00: begin
|
||||
m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl
|
||||
end
|
||||
8'h01: begin
|
||||
m_eth_payload_axis_tdata_int = {ip_dscp_reg, ip_ecn_reg};
|
||||
hdr_sum_next = {4'd4, 4'd5, ip_dscp_reg, ip_ecn_reg};
|
||||
end
|
||||
8'h02: begin
|
||||
m_eth_payload_axis_tdata_int = ip_length_reg[15: 8];
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, ip_length_reg);
|
||||
end
|
||||
8'h03: begin
|
||||
m_eth_payload_axis_tdata_int = ip_length_reg[ 7: 0];
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, ip_identification_reg);
|
||||
end
|
||||
8'h04: begin
|
||||
m_eth_payload_axis_tdata_int = ip_identification_reg[15: 8];
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, {ip_flags_reg, ip_fragment_offset_reg});
|
||||
end
|
||||
8'h05: begin
|
||||
m_eth_payload_axis_tdata_int = ip_identification_reg[ 7: 0];
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, {ip_ttl_reg, ip_protocol_reg});
|
||||
end
|
||||
8'h06: begin
|
||||
m_eth_payload_axis_tdata_int = {ip_flags_reg, ip_fragment_offset_reg[12:8]};
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[31:16]);
|
||||
end
|
||||
8'h07: begin
|
||||
m_eth_payload_axis_tdata_int = ip_fragment_offset_reg[ 7: 0];
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[15:0]);
|
||||
end
|
||||
8'h08: begin
|
||||
m_eth_payload_axis_tdata_int = ip_ttl_reg;
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[31:16]);
|
||||
end
|
||||
8'h09: begin
|
||||
m_eth_payload_axis_tdata_int = ip_protocol_reg;
|
||||
hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[15:0]);
|
||||
end
|
||||
8'h0A: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[15: 8];
|
||||
8'h0B: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[ 7: 0];
|
||||
8'h0C: m_eth_payload_axis_tdata_int = ip_source_ip_reg[31:24];
|
||||
8'h0D: m_eth_payload_axis_tdata_int = ip_source_ip_reg[23:16];
|
||||
8'h0E: m_eth_payload_axis_tdata_int = ip_source_ip_reg[15: 8];
|
||||
8'h0F: m_eth_payload_axis_tdata_int = ip_source_ip_reg[ 7: 0];
|
||||
8'h10: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[31:24];
|
||||
8'h11: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[23:16];
|
||||
8'h12: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[15: 8];
|
||||
8'h13: begin
|
||||
m_eth_payload_axis_tdata_int = ip_dest_ip_reg[ 7: 0];
|
||||
s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early;
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
endcase
|
||||
end else begin
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_PAYLOAD: begin
|
||||
// write payload
|
||||
s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early;
|
||||
|
||||
m_eth_payload_axis_tdata_int = s_ip_payload_axis_tdata;
|
||||
m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid;
|
||||
m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast;
|
||||
m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser;
|
||||
|
||||
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
// word transfer through
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
if (s_ip_payload_axis_tlast) begin
|
||||
if (frame_ptr_next != ip_length_reg) begin
|
||||
// end of frame, but length does not match
|
||||
m_eth_payload_axis_tuser_int = 1'b1;
|
||||
error_payload_early_termination_next = 1'b1;
|
||||
end
|
||||
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
if (frame_ptr_next == ip_length_reg) begin
|
||||
store_last_word = 1'b1;
|
||||
m_eth_payload_axis_tvalid_int = 1'b0;
|
||||
state_next = STATE_WRITE_PAYLOAD_LAST;
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_PAYLOAD_LAST: begin
|
||||
// read and discard until end of frame
|
||||
s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early;
|
||||
|
||||
m_eth_payload_axis_tdata_int = last_word_data_reg;
|
||||
m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast;
|
||||
m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast;
|
||||
m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser;
|
||||
|
||||
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
if (s_ip_payload_axis_tlast) begin
|
||||
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD_LAST;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_LAST: begin
|
||||
// read and discard until end of frame
|
||||
s_ip_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
if (s_ip_payload_axis_tlast) begin
|
||||
s_ip_hdr_ready_next = !m_eth_hdr_valid_next;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 16'd0;
|
||||
hdr_sum_reg <= 16'd0;
|
||||
s_ip_hdr_ready_reg <= 1'b0;
|
||||
s_ip_payload_axis_tready_reg <= 1'b0;
|
||||
m_eth_hdr_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
error_payload_early_termination_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
hdr_sum_reg <= hdr_sum_next;
|
||||
|
||||
s_ip_hdr_ready_reg <= s_ip_hdr_ready_next;
|
||||
s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next;
|
||||
|
||||
m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
|
||||
error_payload_early_termination_reg <= error_payload_early_termination_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_ip_hdr) begin
|
||||
m_eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
m_eth_src_mac_reg <= s_eth_src_mac;
|
||||
m_eth_type_reg <= s_eth_type;
|
||||
ip_dscp_reg <= s_ip_dscp;
|
||||
ip_ecn_reg <= s_ip_ecn;
|
||||
ip_length_reg <= s_ip_length;
|
||||
ip_identification_reg <= s_ip_identification;
|
||||
ip_flags_reg <= s_ip_flags;
|
||||
ip_fragment_offset_reg <= s_ip_fragment_offset;
|
||||
ip_ttl_reg <= s_ip_ttl;
|
||||
ip_protocol_reg <= s_ip_protocol;
|
||||
ip_source_ip_reg <= s_ip_source_ip;
|
||||
ip_dest_ip_reg <= s_ip_dest_ip;
|
||||
end
|
||||
|
||||
if (store_last_word) begin
|
||||
last_word_data_reg <= m_eth_payload_axis_tdata_int;
|
||||
end
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0;
|
||||
reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next;
|
||||
reg m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg m_eth_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0;
|
||||
reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next;
|
||||
reg temp_m_eth_payload_axis_tlast_reg = 1'b0;
|
||||
reg temp_m_eth_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_eth_payload_int_to_output;
|
||||
reg store_eth_payload_int_to_temp;
|
||||
reg store_eth_payload_axis_temp_to_output;
|
||||
|
||||
assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg;
|
||||
assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg;
|
||||
assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg;
|
||||
assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
|
||||
store_eth_payload_int_to_output = 1'b0;
|
||||
store_eth_payload_int_to_temp = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_eth_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_eth_payload_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int;
|
||||
store_eth_payload_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_eth_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg;
|
||||
temp_m_eth_payload_axis_tvalid_next = 1'b0;
|
||||
store_eth_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_eth_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next;
|
||||
m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early;
|
||||
temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_eth_payload_int_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end else if (store_eth_payload_axis_temp_to_output) begin
|
||||
m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg;
|
||||
m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg;
|
||||
m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_eth_payload_int_to_temp) begin
|
||||
temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int;
|
||||
temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int;
|
||||
temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
142
fpga/src/oddr.sv
Normal file
142
fpga/src/oddr.sv
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2016-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Generic ODDR module
|
||||
*/
|
||||
module oddr #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// IODDR style ("IODDR", "IODDR2")
|
||||
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
|
||||
// Use IODDR2 for Spartan-6
|
||||
parameter IODDR_STYLE = "IODDR2",
|
||||
// Width of register in bits
|
||||
parameter WIDTH = 1
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
|
||||
input wire [WIDTH-1:0] d1,
|
||||
input wire [WIDTH-1:0] d2,
|
||||
|
||||
output wire [WIDTH-1:0] q
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
Provides a consistent output DDR flip flop across multiple FPGA families
|
||||
_____ _____ _____ _____
|
||||
clk ____/ \_____/ \_____/ \_____/ \_____
|
||||
_ ___________ ___________ ___________ ___________ __
|
||||
d1 _X____D0_____X____D2_____X____D4_____X____D6_____X__
|
||||
_ ___________ ___________ ___________ ___________ __
|
||||
d2 _X____D1_____X____D3_____X____D5_____X____D7_____X__
|
||||
_____ _____ _____ _____ _____ _____ _____ _____ ____
|
||||
d _____X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7_
|
||||
|
||||
*/
|
||||
|
||||
genvar n;
|
||||
|
||||
generate
|
||||
|
||||
if (TARGET == "XILINX") begin
|
||||
for (n = 0; n < WIDTH; n = n + 1) begin : oddr
|
||||
if (IODDR_STYLE == "IODDR") begin
|
||||
ODDR #(
|
||||
.DDR_CLK_EDGE("SAME_EDGE"),
|
||||
.SRTYPE("ASYNC")
|
||||
)
|
||||
oddr_inst (
|
||||
.Q(q[n]),
|
||||
.C(clk),
|
||||
.CE(1'b1),
|
||||
.D1(d1[n]),
|
||||
.D2(d2[n]),
|
||||
.R(1'b0),
|
||||
.S(1'b0)
|
||||
);
|
||||
end else if (IODDR_STYLE == "IODDR2") begin
|
||||
ODDR2 #(
|
||||
.DDR_ALIGNMENT("C0"),
|
||||
.SRTYPE("ASYNC")
|
||||
)
|
||||
oddr_inst (
|
||||
.Q(q[n]),
|
||||
.C0(clk),
|
||||
.C1(~clk),
|
||||
.CE(1'b1),
|
||||
.D0(d1[n]),
|
||||
.D1(d2[n]),
|
||||
.R(1'b0),
|
||||
.S(1'b0)
|
||||
);
|
||||
end
|
||||
end
|
||||
end else if (TARGET == "ALTERA") begin
|
||||
altddio_out #(
|
||||
.WIDTH(WIDTH),
|
||||
.POWER_UP_HIGH("OFF"),
|
||||
.OE_REG("UNUSED")
|
||||
)
|
||||
altddio_out_inst (
|
||||
.aset(1'b0),
|
||||
.datain_h(d1),
|
||||
.datain_l(d2),
|
||||
.outclocken(1'b1),
|
||||
.outclock(clk),
|
||||
.aclr(1'b0),
|
||||
.dataout(q)
|
||||
);
|
||||
end else begin
|
||||
reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}};
|
||||
reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}};
|
||||
|
||||
reg [WIDTH-1:0] q_reg = {WIDTH{1'b0}};
|
||||
|
||||
always @(posedge clk) begin
|
||||
d_reg_1 <= d1;
|
||||
d_reg_2 <= d2;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
q_reg <= d1;
|
||||
end
|
||||
|
||||
always @(negedge clk) begin
|
||||
q_reg <= d_reg_2;
|
||||
end
|
||||
|
||||
assign q = q_reg;
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
endmodule
|
94
fpga/src/priority_encoder.sv
Normal file
94
fpga/src/priority_encoder.sv
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Priority encoder module
|
||||
*/
|
||||
module priority_encoder #
|
||||
(
|
||||
parameter WIDTH = 4,
|
||||
// LSB priority: "LOW", "HIGH"
|
||||
parameter LSB_PRIORITY = "LOW"
|
||||
)
|
||||
(
|
||||
input wire [WIDTH-1:0] input_unencoded,
|
||||
output wire output_valid,
|
||||
output wire [$clog2(WIDTH)-1:0] output_encoded,
|
||||
output wire [WIDTH-1:0] output_unencoded
|
||||
);
|
||||
|
||||
// power-of-two width
|
||||
parameter W1 = 2**$clog2(WIDTH);
|
||||
parameter W2 = W1/2;
|
||||
|
||||
generate
|
||||
if (WIDTH == 2) begin
|
||||
// two inputs - just an OR gate
|
||||
assign output_valid = |input_unencoded;
|
||||
if (LSB_PRIORITY == "LOW") begin
|
||||
assign output_encoded = input_unencoded[1];
|
||||
end else begin
|
||||
assign output_encoded = ~input_unencoded[0];
|
||||
end
|
||||
end else begin
|
||||
// more than two inputs - split into two parts and recurse
|
||||
// also pad input to correct power-of-two width
|
||||
wire [$clog2(W2)-1:0] out1, out2;
|
||||
wire valid1, valid2;
|
||||
priority_encoder #(
|
||||
.WIDTH(W2),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
priority_encoder_inst1 (
|
||||
.input_unencoded(input_unencoded[W2-1:0]),
|
||||
.output_valid(valid1),
|
||||
.output_encoded(out1)
|
||||
);
|
||||
priority_encoder #(
|
||||
.WIDTH(W2),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
priority_encoder_inst2 (
|
||||
.input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}),
|
||||
.output_valid(valid2),
|
||||
.output_encoded(out2)
|
||||
);
|
||||
// multiplexer to select part
|
||||
assign output_valid = valid1 | valid2;
|
||||
if (LSB_PRIORITY == "LOW") begin
|
||||
assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1};
|
||||
end else begin
|
||||
assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2};
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// unencoded output
|
||||
assign output_unencoded = 1 << output_encoded;
|
||||
|
||||
endmodule
|
153
fpga/src/rgmii_arbiter.sv
Normal file
153
fpga/src/rgmii_arbiter.sv
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Arbiter module
|
||||
*/
|
||||
module rgmii_arbiter #
|
||||
(
|
||||
parameter PORTS = 4,
|
||||
// arbitration type: "PRIORITY" or "ROUND_ROBIN"
|
||||
parameter TYPE = "PRIORITY",
|
||||
// block type: "NONE", "REQUEST", "ACKNOWLEDGE"
|
||||
parameter BLOCK = "NONE",
|
||||
// LSB priority: "LOW", "HIGH"
|
||||
parameter LSB_PRIORITY = "LOW"
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire [PORTS-1:0] request,
|
||||
input wire [PORTS-1:0] acknowledge,
|
||||
|
||||
output wire [PORTS-1:0] grant,
|
||||
output wire grant_valid,
|
||||
output wire [$clog2(PORTS)-1:0] grant_encoded
|
||||
);
|
||||
|
||||
reg [PORTS-1:0] grant_reg = 0, grant_next;
|
||||
reg grant_valid_reg = 0, grant_valid_next;
|
||||
reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next;
|
||||
|
||||
assign grant_valid = grant_valid_reg;
|
||||
assign grant = grant_reg;
|
||||
assign grant_encoded = grant_encoded_reg;
|
||||
|
||||
wire request_valid;
|
||||
wire [$clog2(PORTS)-1:0] request_index;
|
||||
wire [PORTS-1:0] request_mask;
|
||||
|
||||
priority_encoder #(
|
||||
.WIDTH(PORTS),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
priority_encoder_inst (
|
||||
.input_unencoded(request),
|
||||
.output_valid(request_valid),
|
||||
.output_encoded(request_index),
|
||||
.output_unencoded(request_mask)
|
||||
);
|
||||
|
||||
reg [PORTS-1:0] mask_reg = 0, mask_next;
|
||||
|
||||
wire masked_request_valid;
|
||||
wire [$clog2(PORTS)-1:0] masked_request_index;
|
||||
wire [PORTS-1:0] masked_request_mask;
|
||||
|
||||
priority_encoder #(
|
||||
.WIDTH(PORTS),
|
||||
.LSB_PRIORITY(LSB_PRIORITY)
|
||||
)
|
||||
priority_encoder_masked (
|
||||
.input_unencoded(request & mask_reg),
|
||||
.output_valid(masked_request_valid),
|
||||
.output_encoded(masked_request_index),
|
||||
.output_unencoded(masked_request_mask)
|
||||
);
|
||||
|
||||
always @* begin
|
||||
grant_next = 0;
|
||||
grant_valid_next = 0;
|
||||
grant_encoded_next = 0;
|
||||
mask_next = mask_reg;
|
||||
|
||||
if (BLOCK == "REQUEST" && grant_reg & request) begin
|
||||
// granted request still asserted; hold it
|
||||
grant_valid_next = grant_valid_reg;
|
||||
grant_next = grant_reg;
|
||||
grant_encoded_next = grant_encoded_reg;
|
||||
end else if (BLOCK == "ACKNOWLEDGE" && grant_valid && !(grant_reg & acknowledge)) begin
|
||||
// granted request not yet acknowledged; hold it
|
||||
grant_valid_next = grant_valid_reg;
|
||||
grant_next = grant_reg;
|
||||
grant_encoded_next = grant_encoded_reg;
|
||||
end else if (request_valid) begin
|
||||
if (TYPE == "PRIORITY") begin
|
||||
grant_valid_next = 1;
|
||||
grant_next = request_mask;
|
||||
grant_encoded_next = request_index;
|
||||
end else if (TYPE == "ROUND_ROBIN") begin
|
||||
if (masked_request_valid) begin
|
||||
grant_valid_next = 1;
|
||||
grant_next = masked_request_mask;
|
||||
grant_encoded_next = masked_request_index;
|
||||
if (LSB_PRIORITY == "LOW") begin
|
||||
mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index);
|
||||
end else begin
|
||||
mask_next = {PORTS{1'b1}} << (masked_request_index + 1);
|
||||
end
|
||||
end else begin
|
||||
grant_valid_next = 1;
|
||||
grant_next = request_mask;
|
||||
grant_encoded_next = request_index;
|
||||
if (LSB_PRIORITY == "LOW") begin
|
||||
mask_next = {PORTS{1'b1}} >> (PORTS - request_index);
|
||||
end else begin
|
||||
mask_next = {PORTS{1'b1}} << (request_index + 1);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
grant_reg <= 0;
|
||||
grant_valid_reg <= 0;
|
||||
grant_encoded_reg <= 0;
|
||||
mask_reg <= 0;
|
||||
end else begin
|
||||
grant_reg <= grant_next;
|
||||
grant_valid_reg <= grant_valid_next;
|
||||
grant_encoded_reg <= grant_encoded_next;
|
||||
mask_reg <= mask_next;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
132
fpga/src/rgmii_core.sv
Normal file
132
fpga/src/rgmii_core.sv
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* FPGA core logic
|
||||
*/
|
||||
module rgmii_core #
|
||||
(
|
||||
parameter TARGET = "XILINX"
|
||||
)
|
||||
(
|
||||
/*
|
||||
* Clock: 125MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
input wire clk,
|
||||
input wire clk90,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
input wire phy_rx_clk,
|
||||
input wire [3:0] phy_rxd,
|
||||
input wire phy_rx_ctl,
|
||||
output wire phy_tx_clk,
|
||||
output wire [3:0] phy_txd,
|
||||
output wire phy_tx_ctl,
|
||||
output wire phy_reset_n,
|
||||
input wire phy_int_n,
|
||||
input wire phy_pme_n,
|
||||
output wire mac_gmii_tx_en,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
|
||||
input tx_axis_tvalid,
|
||||
input tx_axis_tlast,
|
||||
input [7:0] tx_axis_tdata,
|
||||
output tx_axis_tready,
|
||||
input tx_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
|
||||
output wire [7:0] rx_axis_tdata,
|
||||
output wire rx_axis_tvalid,
|
||||
output wire rx_axis_tlast,
|
||||
output wire rx_axis_tuser
|
||||
);
|
||||
|
||||
assign phy_reset_n = !rst;
|
||||
|
||||
eth_mac_1g_rgmii_fifo #(
|
||||
.TARGET(TARGET),
|
||||
.IODDR_STYLE("IODDR"),
|
||||
.CLOCK_INPUT_STYLE("BUFR"),
|
||||
.USE_CLK90("TRUE"),
|
||||
.ENABLE_PADDING(1),
|
||||
.MIN_FRAME_LENGTH(64),
|
||||
.TX_FIFO_ADDR_WIDTH(12),
|
||||
.TX_FRAME_FIFO(1),
|
||||
.RX_FIFO_ADDR_WIDTH(12),
|
||||
.RX_FRAME_FIFO(1)
|
||||
)
|
||||
eth_mac_inst (
|
||||
.gtx_clk(clk),
|
||||
.gtx_clk90(clk90),
|
||||
.gtx_rst(rst),
|
||||
.logic_clk(clk),
|
||||
.logic_rst(rst),
|
||||
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser),
|
||||
|
||||
.rgmii_rx_clk(phy_rx_clk),
|
||||
.rgmii_rxd(phy_rxd),
|
||||
.rgmii_rx_ctl(phy_rx_ctl),
|
||||
.rgmii_tx_clk(phy_tx_clk),
|
||||
.rgmii_txd(phy_txd),
|
||||
.rgmii_tx_ctl(phy_tx_ctl),
|
||||
.mac_gmii_tx_en(mac_gmii_tx_en),
|
||||
|
||||
.tx_fifo_overflow(),
|
||||
.tx_fifo_bad_frame(),
|
||||
.tx_fifo_good_frame(),
|
||||
.rx_error_bad_frame(),
|
||||
.rx_error_bad_fcs(),
|
||||
.rx_fifo_overflow(),
|
||||
.rx_fifo_bad_frame(),
|
||||
.rx_fifo_good_frame(),
|
||||
.speed(),
|
||||
|
||||
.ifg_delay(12)
|
||||
);
|
||||
|
||||
endmodule
|
442
fpga/src/rgmii_lfsr.sv
Normal file
442
fpga/src/rgmii_lfsr.sv
Normal file
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2016-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Parametrizable combinatorial parallel LFSR/CRC
|
||||
*/
|
||||
module rgmii_lfsr #
|
||||
(
|
||||
// width of LFSR
|
||||
parameter LFSR_WIDTH = 31,
|
||||
// LFSR polynomial
|
||||
parameter LFSR_POLY = 31'h10000001,
|
||||
// LFSR configuration: "GALOIS", "FIBONACCI"
|
||||
parameter LFSR_CONFIG = "FIBONACCI",
|
||||
// LFSR feed forward enable
|
||||
parameter LFSR_FEED_FORWARD = 0,
|
||||
// bit-reverse input and output
|
||||
parameter REVERSE = 0,
|
||||
// width of data input
|
||||
parameter DATA_WIDTH = 8,
|
||||
// implementation style: "AUTO", "LOOP", "REDUCTION"
|
||||
parameter STYLE = "AUTO"
|
||||
)
|
||||
(
|
||||
input wire [DATA_WIDTH-1:0] data_in,
|
||||
input wire [LFSR_WIDTH-1:0] state_in,
|
||||
output wire [DATA_WIDTH-1:0] data_out,
|
||||
output wire [LFSR_WIDTH-1:0] state_out
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
Fully parametrizable combinatorial parallel LFSR/CRC module. Implements an unrolled LFSR
|
||||
next state computation, shifting DATA_WIDTH bits per pass through the module. Input data
|
||||
is XORed with LFSR feedback path, tie data_in to zero if this is not required.
|
||||
|
||||
Works in two parts: statically computes a set of bit masks, then uses these bit masks to
|
||||
select bits for XORing to compute the next state.
|
||||
|
||||
Ports:
|
||||
|
||||
data_in
|
||||
|
||||
Data bits to be shifted through the LFSR (DATA_WIDTH bits)
|
||||
|
||||
state_in
|
||||
|
||||
LFSR/CRC current state input (LFSR_WIDTH bits)
|
||||
|
||||
data_out
|
||||
|
||||
Data bits shifted out of LFSR (DATA_WIDTH bits)
|
||||
|
||||
state_out
|
||||
|
||||
LFSR/CRC next state output (LFSR_WIDTH bits)
|
||||
|
||||
Parameters:
|
||||
|
||||
LFSR_WIDTH
|
||||
|
||||
Specify width of LFSR/CRC register
|
||||
|
||||
LFSR_POLY
|
||||
|
||||
Specify the LFSR/CRC polynomial in hex format. For example, the polynomial
|
||||
|
||||
x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
|
||||
|
||||
would be represented as
|
||||
|
||||
32'h04c11db7
|
||||
|
||||
Note that the largest term (x^32) is suppressed. This term is generated automatically based
|
||||
on LFSR_WIDTH.
|
||||
|
||||
LFSR_CONFIG
|
||||
|
||||
Specify the LFSR configuration, either Fibonacci or Galois. Fibonacci is generally used
|
||||
for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators,
|
||||
scramblers, and descrambers, while Galois is generally used for cyclic redundancy check
|
||||
generators and checkers.
|
||||
|
||||
Fibonacci style (example for 64b66b scrambler, 0x8000000001)
|
||||
|
||||
DIN (LSB first)
|
||||
|
|
||||
V
|
||||
(+)<---------------------------(+)<-----------------------------.
|
||||
| ^ |
|
||||
| .----. .----. .----. | .----. .----. .----. |
|
||||
+->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--'
|
||||
| '----' '----' '----' '----' '----' '----'
|
||||
V
|
||||
DOUT
|
||||
|
||||
Galois style (example for CRC16, 0x8005)
|
||||
|
||||
,-------------------+-------------------------+----------(+)<-- DIN (MSB first)
|
||||
| | | ^
|
||||
| .----. .----. V .----. .----. V .----. |
|
||||
`->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |--+---> DOUT
|
||||
'----' '----' '----' '----' '----'
|
||||
|
||||
LFSR_FEED_FORWARD
|
||||
|
||||
Generate feed forward instead of feed back LFSR. Enable this for PRBS checking and self-
|
||||
synchronous descrambling.
|
||||
|
||||
Fibonacci feed-forward style (example for 64b66b descrambler, 0x8000000001)
|
||||
|
||||
DIN (LSB first)
|
||||
|
|
||||
| .----. .----. .----. .----. .----. .----.
|
||||
+->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--.
|
||||
| '----' '----' '----' | '----' '----' '----' |
|
||||
| V |
|
||||
(+)<---------------------------(+)------------------------------'
|
||||
|
|
||||
V
|
||||
DOUT
|
||||
|
||||
Galois feed-forward style
|
||||
|
||||
,-------------------+-------------------------+------------+--- DIN (MSB first)
|
||||
| | | |
|
||||
| .----. .----. V .----. .----. V .----. V
|
||||
`->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |->(+)-> DOUT
|
||||
'----' '----' '----' '----' '----'
|
||||
|
||||
REVERSE
|
||||
|
||||
Bit-reverse LFSR input and output. Shifts MSB first by default, set REVERSE for LSB first.
|
||||
|
||||
DATA_WIDTH
|
||||
|
||||
Specify width of input and output data bus. The module will perform one shift per input
|
||||
data bit, so if the input data bus is not required tie data_in to zero and set DATA_WIDTH
|
||||
to the required number of shifts per clock cycle.
|
||||
|
||||
STYLE
|
||||
|
||||
Specify implementation style. Can be "AUTO", "LOOP", or "REDUCTION". When "AUTO"
|
||||
is selected, implemenation will be "LOOP" or "REDUCTION" based on synthesis translate
|
||||
directives. "REDUCTION" and "LOOP" are functionally identical, however they simulate
|
||||
and synthesize differently. "REDUCTION" is implemented with a loop over a Verilog
|
||||
reduction operator. "LOOP" is implemented as a doubly-nested loop with no reduction
|
||||
operator. "REDUCTION" is very fast for simulation in iverilog and synthesizes well in
|
||||
Quartus but synthesizes poorly in ISE, likely due to large inferred XOR gates causing
|
||||
problems with the optimizer. "LOOP" synthesizes will in both ISE and Quartus. "AUTO"
|
||||
will default to "REDUCTION" when simulating and "LOOP" for synthesizers that obey
|
||||
synthesis translate directives.
|
||||
|
||||
Settings for common LFSR/CRC implementations:
|
||||
|
||||
Name Configuration Length Polynomial Initial value Notes
|
||||
CRC16-IBM Galois, bit-reverse 16 16'h8005 16'hffff
|
||||
CRC16-CCITT Galois 16 16'h1021 16'h1d0f
|
||||
CRC32 Galois, bit-reverse 32 32'h04c11db7 32'hffffffff Ethernet FCS; invert final output
|
||||
PRBS6 Fibonacci 6 6'h21 any
|
||||
PRBS7 Fibonacci 7 7'h41 any
|
||||
PRBS9 Fibonacci 9 9'h021 any ITU V.52
|
||||
PRBS10 Fibonacci 10 10'h081 any ITU
|
||||
PRBS11 Fibonacci 11 11'h201 any ITU O.152
|
||||
PRBS15 Fibonacci, inverted 15 15'h4001 any ITU O.152
|
||||
PRBS17 Fibonacci 17 17'h04001 any
|
||||
PRBS20 Fibonacci 20 20'h00009 any ITU V.57
|
||||
PRBS23 Fibonacci, inverted 23 23'h040001 any ITU O.151
|
||||
PRBS29 Fibonacci, inverted 29 29'h08000001 any
|
||||
PRBS31 Fibonacci, inverted 31 31'h10000001 any
|
||||
64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet
|
||||
128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3
|
||||
|
||||
*/
|
||||
|
||||
reg [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0];
|
||||
reg [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0];
|
||||
reg [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0];
|
||||
reg [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0];
|
||||
|
||||
reg [LFSR_WIDTH-1:0] state_val = 0;
|
||||
reg [DATA_WIDTH-1:0] data_val = 0;
|
||||
|
||||
integer i, j, k;
|
||||
|
||||
initial begin
|
||||
// init bit masks
|
||||
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
|
||||
lfsr_mask_state[i] = {LFSR_WIDTH{1'b0}};
|
||||
lfsr_mask_state[i][i] = 1'b1;
|
||||
lfsr_mask_data[i] = {DATA_WIDTH{1'b0}};
|
||||
end
|
||||
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
|
||||
output_mask_state[i] = {LFSR_WIDTH{1'b0}};
|
||||
if (i < LFSR_WIDTH) begin
|
||||
output_mask_state[i][i] = 1'b1;
|
||||
end
|
||||
output_mask_data[i] = {DATA_WIDTH{1'b0}};
|
||||
end
|
||||
|
||||
// simulate shift register
|
||||
if (LFSR_CONFIG == "FIBONACCI") begin
|
||||
// Fibonacci configuration
|
||||
for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin
|
||||
// determine shift in value
|
||||
// current value in last FF, XOR with input data bit (MSB first)
|
||||
state_val = lfsr_mask_state[LFSR_WIDTH-1];
|
||||
data_val = lfsr_mask_data[LFSR_WIDTH-1];
|
||||
data_val = data_val ^ (1 << i);
|
||||
|
||||
// add XOR inputs from correct indicies
|
||||
for (j = 1; j < LFSR_WIDTH; j = j + 1) begin
|
||||
if (LFSR_POLY & (1 << j)) begin
|
||||
state_val = lfsr_mask_state[j-1] ^ state_val;
|
||||
data_val = lfsr_mask_data[j-1] ^ data_val;
|
||||
end
|
||||
end
|
||||
|
||||
// shift
|
||||
for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin
|
||||
lfsr_mask_state[j] = lfsr_mask_state[j-1];
|
||||
lfsr_mask_data[j] = lfsr_mask_data[j-1];
|
||||
end
|
||||
for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin
|
||||
output_mask_state[j] = output_mask_state[j-1];
|
||||
output_mask_data[j] = output_mask_data[j-1];
|
||||
end
|
||||
output_mask_state[0] = state_val;
|
||||
output_mask_data[0] = data_val;
|
||||
if (LFSR_FEED_FORWARD) begin
|
||||
// only shift in new input data
|
||||
state_val = {LFSR_WIDTH{1'b0}};
|
||||
data_val = 1 << i;
|
||||
end
|
||||
lfsr_mask_state[0] = state_val;
|
||||
lfsr_mask_data[0] = data_val;
|
||||
end
|
||||
end else if (LFSR_CONFIG == "GALOIS") begin
|
||||
// Galois configuration
|
||||
for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin
|
||||
// determine shift in value
|
||||
// current value in last FF, XOR with input data bit (MSB first)
|
||||
state_val = lfsr_mask_state[LFSR_WIDTH-1];
|
||||
data_val = lfsr_mask_data[LFSR_WIDTH-1];
|
||||
data_val = data_val ^ (1 << i);
|
||||
|
||||
// shift
|
||||
for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin
|
||||
lfsr_mask_state[j] = lfsr_mask_state[j-1];
|
||||
lfsr_mask_data[j] = lfsr_mask_data[j-1];
|
||||
end
|
||||
for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin
|
||||
output_mask_state[j] = output_mask_state[j-1];
|
||||
output_mask_data[j] = output_mask_data[j-1];
|
||||
end
|
||||
output_mask_state[0] = state_val;
|
||||
output_mask_data[0] = data_val;
|
||||
if (LFSR_FEED_FORWARD) begin
|
||||
// only shift in new input data
|
||||
state_val = {LFSR_WIDTH{1'b0}};
|
||||
data_val = 1 << i;
|
||||
end
|
||||
lfsr_mask_state[0] = state_val;
|
||||
lfsr_mask_data[0] = data_val;
|
||||
|
||||
// add XOR inputs at correct indicies
|
||||
for (j = 1; j < LFSR_WIDTH; j = j + 1) begin
|
||||
if (LFSR_POLY & (1 << j)) begin
|
||||
lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val;
|
||||
lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val;
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
$error("Error: unknown configuration setting!");
|
||||
$finish;
|
||||
end
|
||||
|
||||
// reverse bits if selected
|
||||
if (REVERSE) begin
|
||||
// reverse order
|
||||
for (i = 0; i < LFSR_WIDTH/2; i = i + 1) begin
|
||||
state_val = lfsr_mask_state[i];
|
||||
data_val = lfsr_mask_data[i];
|
||||
lfsr_mask_state[i] = lfsr_mask_state[LFSR_WIDTH-i-1];
|
||||
lfsr_mask_data[i] = lfsr_mask_data[LFSR_WIDTH-i-1];
|
||||
lfsr_mask_state[LFSR_WIDTH-i-1] = state_val;
|
||||
lfsr_mask_data[LFSR_WIDTH-i-1] = data_val;
|
||||
end
|
||||
for (i = 0; i < DATA_WIDTH/2; i = i + 1) begin
|
||||
state_val = output_mask_state[i];
|
||||
data_val = output_mask_data[i];
|
||||
output_mask_state[i] = output_mask_state[DATA_WIDTH-i-1];
|
||||
output_mask_data[i] = output_mask_data[DATA_WIDTH-i-1];
|
||||
output_mask_state[DATA_WIDTH-i-1] = state_val;
|
||||
output_mask_data[DATA_WIDTH-i-1] = data_val;
|
||||
end
|
||||
// reverse bits
|
||||
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
|
||||
state_val = 0;
|
||||
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
|
||||
state_val[j] = lfsr_mask_state[i][LFSR_WIDTH-j-1];
|
||||
end
|
||||
lfsr_mask_state[i] = state_val;
|
||||
|
||||
data_val = 0;
|
||||
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
|
||||
data_val[j] = lfsr_mask_data[i][DATA_WIDTH-j-1];
|
||||
end
|
||||
lfsr_mask_data[i] = data_val;
|
||||
end
|
||||
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
|
||||
state_val = 0;
|
||||
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
|
||||
state_val[j] = output_mask_state[i][LFSR_WIDTH-j-1];
|
||||
end
|
||||
output_mask_state[i] = state_val;
|
||||
|
||||
data_val = 0;
|
||||
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
|
||||
data_val[j] = output_mask_data[i][DATA_WIDTH-j-1];
|
||||
end
|
||||
output_mask_data[i] = data_val;
|
||||
end
|
||||
end
|
||||
|
||||
// for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
|
||||
// $display("%b %b", lfsr_mask_state[i], lfsr_mask_data[i]);
|
||||
// end
|
||||
end
|
||||
|
||||
// synthesis translate_off
|
||||
`define SIMULATION
|
||||
// synthesis translate_on
|
||||
|
||||
`ifdef SIMULATION
|
||||
// "AUTO" style is "REDUCTION" for faster simulation
|
||||
parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE;
|
||||
`else
|
||||
// "AUTO" style is "LOOP" for better synthesis result
|
||||
parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE;
|
||||
`endif
|
||||
|
||||
genvar n;
|
||||
|
||||
generate
|
||||
|
||||
if (STYLE_INT == "REDUCTION") begin
|
||||
|
||||
// use Verilog reduction operator
|
||||
// fast in iverilog
|
||||
// significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer)
|
||||
// slightly smaller than generated code with Quartus
|
||||
// --> better for simulation
|
||||
|
||||
for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : loop1
|
||||
assign state_out[n] = ^{(state_in & lfsr_mask_state[n]), (data_in & lfsr_mask_data[n])};
|
||||
end
|
||||
for (n = 0; n < DATA_WIDTH; n = n + 1) begin : loop2
|
||||
assign data_out[n] = ^{(state_in & output_mask_state[n]), (data_in & output_mask_data[n])};
|
||||
end
|
||||
|
||||
end else if (STYLE_INT == "LOOP") begin
|
||||
|
||||
// use nested loops
|
||||
// very slow in iverilog
|
||||
// slightly smaller than generated code with ISE
|
||||
// same size as generated code with Quartus
|
||||
// --> better for synthesis
|
||||
|
||||
reg [LFSR_WIDTH-1:0] state_out_reg = 0;
|
||||
reg [DATA_WIDTH-1:0] data_out_reg = 0;
|
||||
|
||||
assign state_out = state_out_reg;
|
||||
assign data_out = data_out_reg;
|
||||
|
||||
always @* begin
|
||||
for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
|
||||
state_out_reg[i] = 0;
|
||||
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
|
||||
if (lfsr_mask_state[i][j]) begin
|
||||
state_out_reg[i] = state_out_reg[i] ^ state_in[j];
|
||||
end
|
||||
end
|
||||
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
|
||||
if (lfsr_mask_data[i][j]) begin
|
||||
state_out_reg[i] = state_out_reg[i] ^ data_in[j];
|
||||
end
|
||||
end
|
||||
end
|
||||
for (i = 0; i < DATA_WIDTH; i = i + 1) begin
|
||||
data_out_reg[i] = 0;
|
||||
for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
|
||||
if (output_mask_state[i][j]) begin
|
||||
data_out_reg[i] = data_out_reg[i] ^ state_in[j];
|
||||
end
|
||||
end
|
||||
for (j = 0; j < DATA_WIDTH; j = j + 1) begin
|
||||
if (output_mask_data[i][j]) begin
|
||||
data_out_reg[i] = data_out_reg[i] ^ data_in[j];
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end else begin
|
||||
|
||||
initial begin
|
||||
$error("Error: unknown style setting!");
|
||||
$finish;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
endmodule
|
262
fpga/src/rgmii_phy_if.sv
Normal file
262
fpga/src/rgmii_phy_if.sv
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* RGMII PHY interface
|
||||
*/
|
||||
module rgmii_phy_if #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// IODDR style ("IODDR", "IODDR2")
|
||||
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
|
||||
// Use IODDR2 for Spartan-6
|
||||
parameter IODDR_STYLE = "IODDR2",
|
||||
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
|
||||
// Use BUFR for Virtex-5, Virtex-6, 7-series
|
||||
// Use BUFG for Ultrascale
|
||||
// Use BUFIO2 for Spartan-6
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2",
|
||||
// Use 90 degree clock for RGMII transmit ("TRUE", "FALSE")
|
||||
parameter USE_CLK90 = "TRUE"
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire clk90,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* GMII interface to MAC
|
||||
*/
|
||||
output wire mac_gmii_rx_clk,
|
||||
output wire mac_gmii_rx_rst,
|
||||
output wire [7:0] mac_gmii_rxd,
|
||||
output wire mac_gmii_rx_dv,
|
||||
output wire mac_gmii_rx_er,
|
||||
output wire mac_gmii_tx_clk,
|
||||
output wire mac_gmii_tx_rst,
|
||||
output wire mac_gmii_tx_clk_en,
|
||||
input wire [7:0] mac_gmii_txd,
|
||||
input wire mac_gmii_tx_en,
|
||||
input wire mac_gmii_tx_er,
|
||||
|
||||
/*
|
||||
* RGMII interface to PHY
|
||||
*/
|
||||
input wire phy_rgmii_rx_clk,
|
||||
input wire [3:0] phy_rgmii_rxd,
|
||||
input wire phy_rgmii_rx_ctl,
|
||||
output wire phy_rgmii_tx_clk,
|
||||
output wire [3:0] phy_rgmii_txd,
|
||||
output wire phy_rgmii_tx_ctl,
|
||||
|
||||
/*
|
||||
* Control
|
||||
*/
|
||||
input wire [1:0] speed
|
||||
);
|
||||
|
||||
// receive
|
||||
|
||||
wire rgmii_rx_ctl_1;
|
||||
wire rgmii_rx_ctl_2;
|
||||
|
||||
ssio_ddr_in #
|
||||
(
|
||||
.TARGET(TARGET),
|
||||
.CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
|
||||
.IODDR_STYLE(IODDR_STYLE),
|
||||
.WIDTH(5)
|
||||
)
|
||||
rx_ssio_ddr_inst (
|
||||
.input_clk(phy_rgmii_rx_clk),
|
||||
.input_d({phy_rgmii_rxd, phy_rgmii_rx_ctl}),
|
||||
.output_clk(mac_gmii_rx_clk),
|
||||
.output_q1({mac_gmii_rxd[3:0], rgmii_rx_ctl_1}),
|
||||
.output_q2({mac_gmii_rxd[7:4], rgmii_rx_ctl_2})
|
||||
);
|
||||
|
||||
assign mac_gmii_rx_dv = rgmii_rx_ctl_1;
|
||||
assign mac_gmii_rx_er = rgmii_rx_ctl_1 ^ rgmii_rx_ctl_2;
|
||||
|
||||
// transmit
|
||||
|
||||
reg rgmii_tx_clk_1 = 1'b1;
|
||||
reg rgmii_tx_clk_2 = 1'b0;
|
||||
reg rgmii_tx_clk_rise = 1'b1;
|
||||
reg rgmii_tx_clk_fall = 1'b1;
|
||||
|
||||
reg [5:0] count_reg = 6'd0, count_next;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
rgmii_tx_clk_1 <= 1'b1;
|
||||
rgmii_tx_clk_2 <= 1'b0;
|
||||
rgmii_tx_clk_rise <= 1'b1;
|
||||
rgmii_tx_clk_fall <= 1'b1;
|
||||
count_reg <= 0;
|
||||
end else begin
|
||||
rgmii_tx_clk_1 <= rgmii_tx_clk_2;
|
||||
|
||||
if (speed == 2'b00) begin
|
||||
// 10M
|
||||
count_reg <= count_reg + 1;
|
||||
rgmii_tx_clk_rise <= 1'b0;
|
||||
rgmii_tx_clk_fall <= 1'b0;
|
||||
if (count_reg == 24) begin
|
||||
rgmii_tx_clk_1 <= 1'b1;
|
||||
rgmii_tx_clk_2 <= 1'b1;
|
||||
rgmii_tx_clk_rise <= 1'b1;
|
||||
end else if (count_reg >= 49) begin
|
||||
rgmii_tx_clk_1 <= 1'b0;
|
||||
rgmii_tx_clk_2 <= 1'b0;
|
||||
rgmii_tx_clk_fall <= 1'b1;
|
||||
count_reg <= 0;
|
||||
end
|
||||
end else if (speed == 2'b01) begin
|
||||
// 100M
|
||||
count_reg <= count_reg + 1;
|
||||
rgmii_tx_clk_rise <= 1'b0;
|
||||
rgmii_tx_clk_fall <= 1'b0;
|
||||
if (count_reg == 2) begin
|
||||
rgmii_tx_clk_1 <= 1'b1;
|
||||
rgmii_tx_clk_2 <= 1'b1;
|
||||
rgmii_tx_clk_rise <= 1'b1;
|
||||
end else if (count_reg >= 4) begin
|
||||
rgmii_tx_clk_2 <= 1'b0;
|
||||
rgmii_tx_clk_fall <= 1'b1;
|
||||
count_reg <= 0;
|
||||
end
|
||||
end else begin
|
||||
// 1000M
|
||||
rgmii_tx_clk_1 <= 1'b1;
|
||||
rgmii_tx_clk_2 <= 1'b0;
|
||||
rgmii_tx_clk_rise <= 1'b1;
|
||||
rgmii_tx_clk_fall <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
reg [3:0] rgmii_txd_1;
|
||||
reg [3:0] rgmii_txd_2;
|
||||
reg rgmii_tx_ctl_1;
|
||||
reg rgmii_tx_ctl_2;
|
||||
|
||||
reg gmii_clk_en;
|
||||
|
||||
always @* begin
|
||||
if (speed == 2'b00) begin
|
||||
// 10M
|
||||
rgmii_txd_1 = mac_gmii_txd[3:0];
|
||||
rgmii_txd_2 = mac_gmii_txd[3:0];
|
||||
if (rgmii_tx_clk_2) begin
|
||||
rgmii_tx_ctl_1 = mac_gmii_tx_en;
|
||||
rgmii_tx_ctl_2 = mac_gmii_tx_en;
|
||||
end else begin
|
||||
rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er;
|
||||
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
|
||||
end
|
||||
gmii_clk_en = rgmii_tx_clk_fall;
|
||||
end else if (speed == 2'b01) begin
|
||||
// 100M
|
||||
rgmii_txd_1 = mac_gmii_txd[3:0];
|
||||
rgmii_txd_2 = mac_gmii_txd[3:0];
|
||||
if (rgmii_tx_clk_2) begin
|
||||
rgmii_tx_ctl_1 = mac_gmii_tx_en;
|
||||
rgmii_tx_ctl_2 = mac_gmii_tx_en;
|
||||
end else begin
|
||||
rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er;
|
||||
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
|
||||
end
|
||||
gmii_clk_en = rgmii_tx_clk_fall;
|
||||
end else begin
|
||||
// 1000M
|
||||
rgmii_txd_1 = mac_gmii_txd[3:0];
|
||||
rgmii_txd_2 = mac_gmii_txd[7:4];
|
||||
rgmii_tx_ctl_1 = mac_gmii_tx_en;
|
||||
rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er;
|
||||
gmii_clk_en = 1;
|
||||
end
|
||||
end
|
||||
|
||||
wire phy_rgmii_tx_clk_new;
|
||||
wire [3:0] phy_rgmii_txd_new;
|
||||
wire phy_rgmii_tx_ctl_new;
|
||||
|
||||
oddr #(
|
||||
.TARGET(TARGET),
|
||||
.IODDR_STYLE(IODDR_STYLE),
|
||||
.WIDTH(1)
|
||||
)
|
||||
clk_oddr_inst (
|
||||
.clk(USE_CLK90 == "TRUE" ? clk90 : clk),
|
||||
.d1(rgmii_tx_clk_1),
|
||||
.d2(rgmii_tx_clk_2),
|
||||
.q(phy_rgmii_tx_clk)
|
||||
);
|
||||
|
||||
oddr #(
|
||||
.TARGET(TARGET),
|
||||
.IODDR_STYLE(IODDR_STYLE),
|
||||
.WIDTH(5)
|
||||
)
|
||||
data_oddr_inst (
|
||||
.clk(clk),
|
||||
.d1({rgmii_txd_1, rgmii_tx_ctl_1}),
|
||||
.d2({rgmii_txd_2, rgmii_tx_ctl_2}),
|
||||
.q({phy_rgmii_txd, phy_rgmii_tx_ctl})
|
||||
);
|
||||
|
||||
assign mac_gmii_tx_clk = clk;
|
||||
|
||||
assign mac_gmii_tx_clk_en = gmii_clk_en;
|
||||
|
||||
// reset sync
|
||||
reg [3:0] tx_rst_reg = 4'hf;
|
||||
assign mac_gmii_tx_rst = tx_rst_reg[0];
|
||||
|
||||
always @(posedge mac_gmii_tx_clk or posedge rst) begin
|
||||
if (rst) begin
|
||||
tx_rst_reg <= 4'hf;
|
||||
end else begin
|
||||
tx_rst_reg <= {1'b0, tx_rst_reg[3:1]};
|
||||
end
|
||||
end
|
||||
|
||||
reg [3:0] rx_rst_reg = 4'hf;
|
||||
assign mac_gmii_rx_rst = rx_rst_reg[0];
|
||||
|
||||
always @(posedge mac_gmii_rx_clk or posedge rst) begin
|
||||
if (rst) begin
|
||||
rx_rst_reg <= 4'hf;
|
||||
end else begin
|
||||
rx_rst_reg <= {1'b0, rx_rst_reg[3:1]};
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
216
fpga/src/rgmii_soc.sv
Normal file
216
fpga/src/rgmii_soc.sv
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
based on fpga.v
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* RGMII top-level module
|
||||
*/
|
||||
|
||||
module rgmii_soc (
|
||||
// Internal 125 MHz clock
|
||||
input clk_int,
|
||||
input rst_int,
|
||||
input clk90_int,
|
||||
input clk_200_int,
|
||||
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
input wire phy_rx_clk,
|
||||
input wire [3:0] phy_rxd,
|
||||
input wire phy_rx_ctl,
|
||||
output wire phy_tx_clk,
|
||||
output wire [3:0] phy_txd,
|
||||
output wire phy_tx_ctl,
|
||||
output wire phy_reset_n,
|
||||
input wire phy_int_n,
|
||||
input wire phy_pme_n,
|
||||
output wire mac_gmii_tx_en,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input tx_axis_tvalid,
|
||||
input tx_axis_tlast,
|
||||
input [7:0] tx_axis_tdata,
|
||||
output tx_axis_tready,
|
||||
input tx_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] rx_axis_tdata,
|
||||
output wire rx_axis_tvalid,
|
||||
output wire rx_axis_tlast,
|
||||
output rx_axis_tuser
|
||||
|
||||
);
|
||||
|
||||
// IODELAY elements for RGMII interface to PHY
|
||||
wire [3:0] phy_rxd_delay;
|
||||
wire phy_rx_ctl_delay;
|
||||
|
||||
IDELAYCTRL
|
||||
idelayctrl_inst
|
||||
(
|
||||
.REFCLK(clk_200_int),
|
||||
.RST(rst_int),
|
||||
.RDY()
|
||||
);
|
||||
|
||||
IDELAYE2 #(
|
||||
.IDELAY_TYPE("FIXED")
|
||||
)
|
||||
phy_rxd_idelay_0
|
||||
(
|
||||
.IDATAIN(phy_rxd[0]),
|
||||
.DATAOUT(phy_rxd_delay[0]),
|
||||
.DATAIN(1'b0),
|
||||
.C(1'b0),
|
||||
.CE(1'b0),
|
||||
.INC(1'b0),
|
||||
.CINVCTRL(1'b0),
|
||||
.CNTVALUEIN(5'd0),
|
||||
.CNTVALUEOUT(),
|
||||
.LD(1'b0),
|
||||
.LDPIPEEN(1'b0),
|
||||
.REGRST(1'b0)
|
||||
);
|
||||
|
||||
IDELAYE2 #(
|
||||
.IDELAY_TYPE("FIXED")
|
||||
)
|
||||
phy_rxd_idelay_1
|
||||
(
|
||||
.IDATAIN(phy_rxd[1]),
|
||||
.DATAOUT(phy_rxd_delay[1]),
|
||||
.DATAIN(1'b0),
|
||||
.C(1'b0),
|
||||
.CE(1'b0),
|
||||
.INC(1'b0),
|
||||
.CINVCTRL(1'b0),
|
||||
.CNTVALUEIN(5'd0),
|
||||
.CNTVALUEOUT(),
|
||||
.LD(1'b0),
|
||||
.LDPIPEEN(1'b0),
|
||||
.REGRST(1'b0)
|
||||
);
|
||||
|
||||
IDELAYE2 #(
|
||||
.IDELAY_TYPE("FIXED")
|
||||
)
|
||||
phy_rxd_idelay_2
|
||||
(
|
||||
.IDATAIN(phy_rxd[2]),
|
||||
.DATAOUT(phy_rxd_delay[2]),
|
||||
.DATAIN(1'b0),
|
||||
.C(1'b0),
|
||||
.CE(1'b0),
|
||||
.INC(1'b0),
|
||||
.CINVCTRL(1'b0),
|
||||
.CNTVALUEIN(5'd0),
|
||||
.CNTVALUEOUT(),
|
||||
.LD(1'b0),
|
||||
.LDPIPEEN(1'b0),
|
||||
.REGRST(1'b0)
|
||||
);
|
||||
|
||||
IDELAYE2 #(
|
||||
.IDELAY_TYPE("FIXED")
|
||||
)
|
||||
phy_rxd_idelay_3
|
||||
(
|
||||
.IDATAIN(phy_rxd[3]),
|
||||
.DATAOUT(phy_rxd_delay[3]),
|
||||
.DATAIN(1'b0),
|
||||
.C(1'b0),
|
||||
.CE(1'b0),
|
||||
.INC(1'b0),
|
||||
.CINVCTRL(1'b0),
|
||||
.CNTVALUEIN(5'd0),
|
||||
.CNTVALUEOUT(),
|
||||
.LD(1'b0),
|
||||
.LDPIPEEN(1'b0),
|
||||
.REGRST(1'b0)
|
||||
);
|
||||
|
||||
IDELAYE2 #(
|
||||
.IDELAY_VALUE(0),
|
||||
.IDELAY_TYPE("FIXED")
|
||||
)
|
||||
phy_rx_ctl_idelay
|
||||
(
|
||||
.IDATAIN(phy_rx_ctl),
|
||||
.DATAOUT(phy_rx_ctl_delay),
|
||||
.DATAIN(1'b0),
|
||||
.C(1'b0),
|
||||
.CE(1'b0),
|
||||
.INC(1'b0),
|
||||
.CINVCTRL(1'b0),
|
||||
.CNTVALUEIN(5'd0),
|
||||
.CNTVALUEOUT(),
|
||||
.LD(1'b0),
|
||||
.LDPIPEEN(1'b0),
|
||||
.REGRST(1'b0)
|
||||
);
|
||||
|
||||
rgmii_core
|
||||
core_inst (
|
||||
/*
|
||||
* Clock: 125MHz
|
||||
* Synchronous reset
|
||||
*/
|
||||
.clk(clk_int),
|
||||
.clk90(clk90_int),
|
||||
.rst(rst_int),
|
||||
/*
|
||||
* Ethernet: 1000BASE-T RGMII
|
||||
*/
|
||||
.phy_rx_clk(phy_rx_clk),
|
||||
.phy_rxd(phy_rxd_delay),
|
||||
.phy_rx_ctl(phy_rx_ctl_delay),
|
||||
.phy_tx_clk(phy_tx_clk),
|
||||
.phy_txd(phy_txd),
|
||||
.phy_tx_ctl(phy_tx_ctl),
|
||||
.phy_reset_n(phy_reset_n),
|
||||
.phy_int_n(phy_int_n),
|
||||
.phy_pme_n(phy_pme_n),
|
||||
.mac_gmii_tx_en(mac_gmii_tx_en),
|
||||
.tx_axis_tdata(tx_axis_tdata),
|
||||
.tx_axis_tvalid(tx_axis_tvalid),
|
||||
.tx_axis_tready(tx_axis_tready),
|
||||
.tx_axis_tlast(tx_axis_tlast),
|
||||
.tx_axis_tuser(tx_axis_tuser),
|
||||
.rx_axis_tdata(rx_axis_tdata),
|
||||
.rx_axis_tvalid(rx_axis_tvalid),
|
||||
.rx_axis_tlast(rx_axis_tlast),
|
||||
.rx_axis_tuser(rx_axis_tuser)
|
||||
);
|
||||
|
||||
endmodule
|
171
fpga/src/ssio_ddr_in.sv
Normal file
171
fpga/src/ssio_ddr_in.sv
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2016-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Generic source synchronous DDR input
|
||||
*/
|
||||
module ssio_ddr_in #
|
||||
(
|
||||
// target ("SIM", "GENERIC", "XILINX", "ALTERA")
|
||||
parameter TARGET = "GENERIC",
|
||||
// IODDR style ("IODDR", "IODDR2")
|
||||
// Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale
|
||||
// Use IODDR2 for Spartan-6
|
||||
parameter IODDR_STYLE = "IODDR2",
|
||||
// Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2")
|
||||
// Use BUFR for Virtex-5, Virtex-6, 7-series
|
||||
// Use BUFG for Ultrascale
|
||||
// Use BUFIO2 for Spartan-6
|
||||
parameter CLOCK_INPUT_STYLE = "BUFIO2",
|
||||
// Width of register in bits
|
||||
parameter WIDTH = 1
|
||||
)
|
||||
(
|
||||
input wire input_clk,
|
||||
|
||||
input wire [WIDTH-1:0] input_d,
|
||||
|
||||
output wire output_clk,
|
||||
|
||||
output wire [WIDTH-1:0] output_q1,
|
||||
output wire [WIDTH-1:0] output_q2
|
||||
);
|
||||
|
||||
wire clk_int;
|
||||
wire clk_io;
|
||||
|
||||
generate
|
||||
|
||||
if (TARGET == "XILINX") begin
|
||||
|
||||
// use Xilinx clocking primitives
|
||||
|
||||
if (CLOCK_INPUT_STYLE == "BUFG") begin
|
||||
|
||||
// buffer RX clock
|
||||
BUFG
|
||||
clk_bufg (
|
||||
.I(input_clk),
|
||||
.O(clk_int)
|
||||
);
|
||||
|
||||
// pass through RX clock to logic and input buffers
|
||||
assign clk_io = clk_int;
|
||||
assign output_clk = clk_int;
|
||||
|
||||
end else if (CLOCK_INPUT_STYLE == "BUFR") begin
|
||||
|
||||
assign clk_int = input_clk;
|
||||
|
||||
// pass through RX clock to input buffers
|
||||
BUFIO
|
||||
clk_bufio (
|
||||
.I(clk_int),
|
||||
.O(clk_io)
|
||||
);
|
||||
|
||||
// pass through RX clock to logic
|
||||
BUFR #(
|
||||
.BUFR_DIVIDE("BYPASS")
|
||||
)
|
||||
clk_bufr (
|
||||
.I(clk_int),
|
||||
.O(output_clk),
|
||||
.CE(1'b1),
|
||||
.CLR(1'b0)
|
||||
);
|
||||
|
||||
end else if (CLOCK_INPUT_STYLE == "BUFIO") begin
|
||||
|
||||
assign clk_int = input_clk;
|
||||
|
||||
// pass through RX clock to input buffers
|
||||
BUFIO
|
||||
clk_bufio (
|
||||
.I(clk_int),
|
||||
.O(clk_io)
|
||||
);
|
||||
|
||||
// pass through RX clock to MAC
|
||||
BUFG
|
||||
clk_bufg (
|
||||
.I(clk_int),
|
||||
.O(output_clk)
|
||||
);
|
||||
|
||||
end else if (CLOCK_INPUT_STYLE == "BUFIO2") begin
|
||||
|
||||
// pass through RX clock to input buffers
|
||||
BUFIO2 #(
|
||||
.DIVIDE(1),
|
||||
.DIVIDE_BYPASS("TRUE"),
|
||||
.I_INVERT("FALSE"),
|
||||
.USE_DOUBLER("FALSE")
|
||||
)
|
||||
clk_bufio (
|
||||
.I(input_clk),
|
||||
.DIVCLK(clk_int),
|
||||
.IOCLK(clk_io),
|
||||
.SERDESSTROBE()
|
||||
);
|
||||
|
||||
// pass through RX clock to MAC
|
||||
BUFG
|
||||
clk_bufg (
|
||||
.I(clk_int),
|
||||
.O(output_clk)
|
||||
);
|
||||
|
||||
end
|
||||
|
||||
end else begin
|
||||
|
||||
// pass through RX clock to input buffers
|
||||
assign clk_io = input_clk;
|
||||
|
||||
// pass through RX clock to logic
|
||||
assign clk_int = input_clk;
|
||||
assign output_clk = clk_int;
|
||||
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
iddr #(
|
||||
.TARGET(TARGET),
|
||||
.IODDR_STYLE(IODDR_STYLE),
|
||||
.WIDTH(WIDTH)
|
||||
)
|
||||
data_iddr_inst (
|
||||
.clk(clk_io),
|
||||
.d(input_d),
|
||||
.q1(output_q1),
|
||||
.q2(output_q2)
|
||||
);
|
||||
|
||||
endmodule
|
52
fpga/src/sync_reset.sv
Normal file
52
fpga/src/sync_reset.sv
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog-2001
|
||||
|
||||
`timescale 1 ns / 1 ps
|
||||
|
||||
/*
|
||||
* Synchronizes an active-high asynchronous reset signal to a given clock by
|
||||
* using a pipeline of N registers.
|
||||
*/
|
||||
module sync_reset #(
|
||||
parameter N=2 // depth of synchronizer
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
output wire sync_reset_out
|
||||
);
|
||||
|
||||
reg [N-1:0] sync_reg = {N{1'b1}};
|
||||
|
||||
assign sync_reset_out = sync_reg[N-1];
|
||||
|
||||
always @(posedge clk or posedge rst) begin
|
||||
if (rst)
|
||||
sync_reg <= {N{1'b1}};
|
||||
else
|
||||
sync_reg <= {sync_reg[N-2:0], 1'b0};
|
||||
end
|
||||
|
||||
endmodule
|
413
fpga/src/udp.sv
Normal file
413
fpga/src/udp.sv
Normal file
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* UDP block, IP interface
|
||||
*/
|
||||
module udp #
|
||||
(
|
||||
parameter CHECKSUM_GEN_ENABLE = 1,
|
||||
parameter CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11,
|
||||
parameter CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* IP frame input
|
||||
*/
|
||||
input wire s_ip_hdr_valid,
|
||||
output wire s_ip_hdr_ready,
|
||||
input wire [47:0] s_ip_eth_dest_mac,
|
||||
input wire [47:0] s_ip_eth_src_mac,
|
||||
input wire [15:0] s_ip_eth_type,
|
||||
input wire [3:0] s_ip_version,
|
||||
input wire [3:0] s_ip_ihl,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_length,
|
||||
input wire [15:0] s_ip_identification,
|
||||
input wire [2:0] s_ip_flags,
|
||||
input wire [12:0] s_ip_fragment_offset,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [7:0] s_ip_protocol,
|
||||
input wire [15:0] s_ip_header_checksum,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [7:0] s_ip_payload_axis_tdata,
|
||||
input wire s_ip_payload_axis_tvalid,
|
||||
output wire s_ip_payload_axis_tready,
|
||||
input wire s_ip_payload_axis_tlast,
|
||||
input wire s_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP frame output
|
||||
*/
|
||||
output wire m_ip_hdr_valid,
|
||||
input wire m_ip_hdr_ready,
|
||||
output wire [47:0] m_ip_eth_dest_mac,
|
||||
output wire [47:0] m_ip_eth_src_mac,
|
||||
output wire [15:0] m_ip_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [7:0] m_ip_payload_axis_tdata,
|
||||
output wire m_ip_payload_axis_tvalid,
|
||||
input wire m_ip_payload_axis_tready,
|
||||
output wire m_ip_payload_axis_tlast,
|
||||
output wire m_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* UDP frame input
|
||||
*/
|
||||
input wire s_udp_hdr_valid,
|
||||
output wire s_udp_hdr_ready,
|
||||
input wire [47:0] s_udp_eth_dest_mac,
|
||||
input wire [47:0] s_udp_eth_src_mac,
|
||||
input wire [15:0] s_udp_eth_type,
|
||||
input wire [3:0] s_udp_ip_version,
|
||||
input wire [3:0] s_udp_ip_ihl,
|
||||
input wire [5:0] s_udp_ip_dscp,
|
||||
input wire [1:0] s_udp_ip_ecn,
|
||||
input wire [15:0] s_udp_ip_identification,
|
||||
input wire [2:0] s_udp_ip_flags,
|
||||
input wire [12:0] s_udp_ip_fragment_offset,
|
||||
input wire [7:0] s_udp_ip_ttl,
|
||||
input wire [15:0] s_udp_ip_header_checksum,
|
||||
input wire [31:0] s_udp_ip_source_ip,
|
||||
input wire [31:0] s_udp_ip_dest_ip,
|
||||
input wire [15:0] s_udp_source_port,
|
||||
input wire [15:0] s_udp_dest_port,
|
||||
input wire [15:0] s_udp_length,
|
||||
input wire [15:0] s_udp_checksum,
|
||||
input wire [7:0] s_udp_payload_axis_tdata,
|
||||
input wire s_udp_payload_axis_tvalid,
|
||||
output wire s_udp_payload_axis_tready,
|
||||
input wire s_udp_payload_axis_tlast,
|
||||
input wire s_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* UDP frame output
|
||||
*/
|
||||
output wire m_udp_hdr_valid,
|
||||
input wire m_udp_hdr_ready,
|
||||
output wire [47:0] m_udp_eth_dest_mac,
|
||||
output wire [47:0] m_udp_eth_src_mac,
|
||||
output wire [15:0] m_udp_eth_type,
|
||||
output wire [3:0] m_udp_ip_version,
|
||||
output wire [3:0] m_udp_ip_ihl,
|
||||
output wire [5:0] m_udp_ip_dscp,
|
||||
output wire [1:0] m_udp_ip_ecn,
|
||||
output wire [15:0] m_udp_ip_length,
|
||||
output wire [15:0] m_udp_ip_identification,
|
||||
output wire [2:0] m_udp_ip_flags,
|
||||
output wire [12:0] m_udp_ip_fragment_offset,
|
||||
output wire [7:0] m_udp_ip_ttl,
|
||||
output wire [7:0] m_udp_ip_protocol,
|
||||
output wire [15:0] m_udp_ip_header_checksum,
|
||||
output wire [31:0] m_udp_ip_source_ip,
|
||||
output wire [31:0] m_udp_ip_dest_ip,
|
||||
output wire [15:0] m_udp_source_port,
|
||||
output wire [15:0] m_udp_dest_port,
|
||||
output wire [15:0] m_udp_length,
|
||||
output wire [15:0] m_udp_checksum,
|
||||
output wire [7:0] m_udp_payload_axis_tdata,
|
||||
output wire m_udp_payload_axis_tvalid,
|
||||
input wire m_udp_payload_axis_tready,
|
||||
output wire m_udp_payload_axis_tlast,
|
||||
output wire m_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire rx_busy,
|
||||
output wire tx_busy,
|
||||
output wire rx_error_header_early_termination,
|
||||
output wire rx_error_payload_early_termination,
|
||||
output wire tx_error_payload_early_termination
|
||||
);
|
||||
|
||||
wire tx_udp_hdr_valid;
|
||||
wire tx_udp_hdr_ready;
|
||||
wire [47:0] tx_udp_eth_dest_mac;
|
||||
wire [47:0] tx_udp_eth_src_mac;
|
||||
wire [15:0] tx_udp_eth_type;
|
||||
wire [3:0] tx_udp_ip_version;
|
||||
wire [3:0] tx_udp_ip_ihl;
|
||||
wire [5:0] tx_udp_ip_dscp;
|
||||
wire [1:0] tx_udp_ip_ecn;
|
||||
wire [15:0] tx_udp_ip_identification;
|
||||
wire [2:0] tx_udp_ip_flags;
|
||||
wire [12:0] tx_udp_ip_fragment_offset;
|
||||
wire [7:0] tx_udp_ip_ttl;
|
||||
wire [15:0] tx_udp_ip_header_checksum;
|
||||
wire [31:0] tx_udp_ip_source_ip;
|
||||
wire [31:0] tx_udp_ip_dest_ip;
|
||||
wire [15:0] tx_udp_source_port;
|
||||
wire [15:0] tx_udp_dest_port;
|
||||
wire [15:0] tx_udp_length;
|
||||
wire [15:0] tx_udp_checksum;
|
||||
wire [7:0] tx_udp_payload_axis_tdata;
|
||||
wire tx_udp_payload_axis_tvalid;
|
||||
wire tx_udp_payload_axis_tready;
|
||||
wire tx_udp_payload_axis_tlast;
|
||||
wire tx_udp_payload_axis_tuser;
|
||||
|
||||
udp_ip_rx
|
||||
udp_ip_rx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// IP frame input
|
||||
.s_ip_hdr_valid(s_ip_hdr_valid),
|
||||
.s_ip_hdr_ready(s_ip_hdr_ready),
|
||||
.s_eth_dest_mac(s_ip_eth_dest_mac),
|
||||
.s_eth_src_mac(s_ip_eth_src_mac),
|
||||
.s_eth_type(s_ip_eth_type),
|
||||
.s_ip_version(s_ip_version),
|
||||
.s_ip_ihl(s_ip_ihl),
|
||||
.s_ip_dscp(s_ip_dscp),
|
||||
.s_ip_ecn(s_ip_ecn),
|
||||
.s_ip_length(s_ip_length),
|
||||
.s_ip_identification(s_ip_identification),
|
||||
.s_ip_flags(s_ip_flags),
|
||||
.s_ip_fragment_offset(s_ip_fragment_offset),
|
||||
.s_ip_ttl(s_ip_ttl),
|
||||
.s_ip_protocol(s_ip_protocol),
|
||||
.s_ip_header_checksum(s_ip_header_checksum),
|
||||
.s_ip_source_ip(s_ip_source_ip),
|
||||
.s_ip_dest_ip(s_ip_dest_ip),
|
||||
.s_ip_payload_axis_tdata(s_ip_payload_axis_tdata),
|
||||
.s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid),
|
||||
.s_ip_payload_axis_tready(s_ip_payload_axis_tready),
|
||||
.s_ip_payload_axis_tlast(s_ip_payload_axis_tlast),
|
||||
.s_ip_payload_axis_tuser(s_ip_payload_axis_tuser),
|
||||
// UDP frame output
|
||||
.m_udp_hdr_valid(m_udp_hdr_valid),
|
||||
.m_udp_hdr_ready(m_udp_hdr_ready),
|
||||
.m_eth_dest_mac(m_udp_eth_dest_mac),
|
||||
.m_eth_src_mac(m_udp_eth_src_mac),
|
||||
.m_eth_type(m_udp_eth_type),
|
||||
.m_ip_version(m_udp_ip_version),
|
||||
.m_ip_ihl(m_udp_ip_ihl),
|
||||
.m_ip_dscp(m_udp_ip_dscp),
|
||||
.m_ip_ecn(m_udp_ip_ecn),
|
||||
.m_ip_length(m_udp_ip_length),
|
||||
.m_ip_identification(m_udp_ip_identification),
|
||||
.m_ip_flags(m_udp_ip_flags),
|
||||
.m_ip_fragment_offset(m_udp_ip_fragment_offset),
|
||||
.m_ip_ttl(m_udp_ip_ttl),
|
||||
.m_ip_protocol(m_udp_ip_protocol),
|
||||
.m_ip_header_checksum(m_udp_ip_header_checksum),
|
||||
.m_ip_source_ip(m_udp_ip_source_ip),
|
||||
.m_ip_dest_ip(m_udp_ip_dest_ip),
|
||||
.m_udp_source_port(m_udp_source_port),
|
||||
.m_udp_dest_port(m_udp_dest_port),
|
||||
.m_udp_length(m_udp_length),
|
||||
.m_udp_checksum(m_udp_checksum),
|
||||
.m_udp_payload_axis_tdata(m_udp_payload_axis_tdata),
|
||||
.m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid),
|
||||
.m_udp_payload_axis_tready(m_udp_payload_axis_tready),
|
||||
.m_udp_payload_axis_tlast(m_udp_payload_axis_tlast),
|
||||
.m_udp_payload_axis_tuser(m_udp_payload_axis_tuser),
|
||||
// Status signals
|
||||
.busy(rx_busy),
|
||||
.error_header_early_termination(rx_error_header_early_termination),
|
||||
.error_payload_early_termination(rx_error_payload_early_termination)
|
||||
);
|
||||
|
||||
generate
|
||||
|
||||
if (CHECKSUM_GEN_ENABLE) begin
|
||||
|
||||
udp_checksum_gen #(
|
||||
.PAYLOAD_FIFO_ADDR_WIDTH(CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH),
|
||||
.HEADER_FIFO_ADDR_WIDTH(CHECKSUM_HEADER_FIFO_ADDR_WIDTH)
|
||||
)
|
||||
udp_checksum_gen_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// UDP frame input
|
||||
.s_udp_hdr_valid(s_udp_hdr_valid),
|
||||
.s_udp_hdr_ready(s_udp_hdr_ready),
|
||||
.s_eth_dest_mac(s_udp_eth_dest_mac),
|
||||
.s_eth_src_mac(s_udp_eth_src_mac),
|
||||
.s_eth_type(s_udp_eth_type),
|
||||
.s_ip_version(s_udp_ip_version),
|
||||
.s_ip_ihl(s_udp_ip_ihl),
|
||||
.s_ip_dscp(s_udp_ip_dscp),
|
||||
.s_ip_ecn(s_udp_ip_ecn),
|
||||
.s_ip_identification(s_udp_ip_identification),
|
||||
.s_ip_flags(s_udp_ip_flags),
|
||||
.s_ip_fragment_offset(s_udp_ip_fragment_offset),
|
||||
.s_ip_ttl(s_udp_ip_ttl),
|
||||
.s_ip_header_checksum(s_udp_ip_header_checksum),
|
||||
.s_ip_source_ip(s_udp_ip_source_ip),
|
||||
.s_ip_dest_ip(s_udp_ip_dest_ip),
|
||||
.s_udp_source_port(s_udp_source_port),
|
||||
.s_udp_dest_port(s_udp_dest_port),
|
||||
.s_udp_payload_axis_tdata(s_udp_payload_axis_tdata),
|
||||
.s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid),
|
||||
.s_udp_payload_axis_tready(s_udp_payload_axis_tready),
|
||||
.s_udp_payload_axis_tlast(s_udp_payload_axis_tlast),
|
||||
.s_udp_payload_axis_tuser(s_udp_payload_axis_tuser),
|
||||
// UDP frame output
|
||||
.m_udp_hdr_valid(tx_udp_hdr_valid),
|
||||
.m_udp_hdr_ready(tx_udp_hdr_ready),
|
||||
.m_eth_dest_mac(tx_udp_eth_dest_mac),
|
||||
.m_eth_src_mac(tx_udp_eth_src_mac),
|
||||
.m_eth_type(tx_udp_eth_type),
|
||||
.m_ip_version(tx_udp_ip_version),
|
||||
.m_ip_ihl(tx_udp_ip_ihl),
|
||||
.m_ip_dscp(tx_udp_ip_dscp),
|
||||
.m_ip_ecn(tx_udp_ip_ecn),
|
||||
.m_ip_length(),
|
||||
.m_ip_identification(tx_udp_ip_identification),
|
||||
.m_ip_flags(tx_udp_ip_flags),
|
||||
.m_ip_fragment_offset(tx_udp_ip_fragment_offset),
|
||||
.m_ip_ttl(tx_udp_ip_ttl),
|
||||
.m_ip_protocol(),
|
||||
.m_ip_header_checksum(tx_udp_ip_header_checksum),
|
||||
.m_ip_source_ip(tx_udp_ip_source_ip),
|
||||
.m_ip_dest_ip(tx_udp_ip_dest_ip),
|
||||
.m_udp_source_port(tx_udp_source_port),
|
||||
.m_udp_dest_port(tx_udp_dest_port),
|
||||
.m_udp_length(tx_udp_length),
|
||||
.m_udp_checksum(tx_udp_checksum),
|
||||
.m_udp_payload_axis_tdata(tx_udp_payload_axis_tdata),
|
||||
.m_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid),
|
||||
.m_udp_payload_axis_tready(tx_udp_payload_axis_tready),
|
||||
.m_udp_payload_axis_tlast(tx_udp_payload_axis_tlast),
|
||||
.m_udp_payload_axis_tuser(tx_udp_payload_axis_tuser),
|
||||
// Status signals
|
||||
.busy()
|
||||
);
|
||||
|
||||
end else begin
|
||||
|
||||
assign tx_udp_hdr_valid = s_udp_hdr_valid;
|
||||
assign s_udp_hdr_ready = tx_udp_hdr_ready;
|
||||
assign tx_udp_eth_dest_mac = s_udp_eth_dest_mac;
|
||||
assign tx_udp_eth_src_mac = s_udp_eth_src_mac;
|
||||
assign tx_udp_eth_type = s_udp_eth_type;
|
||||
assign tx_udp_ip_version = s_udp_ip_version;
|
||||
assign tx_udp_ip_ihl = s_udp_ip_ihl;
|
||||
assign tx_udp_ip_dscp = s_udp_ip_dscp;
|
||||
assign tx_udp_ip_ecn = s_udp_ip_ecn;
|
||||
assign tx_udp_ip_identification = s_udp_ip_identification;
|
||||
assign tx_udp_ip_flags = s_udp_ip_flags;
|
||||
assign tx_udp_ip_fragment_offset = s_udp_ip_fragment_offset;
|
||||
assign tx_udp_ip_ttl = s_udp_ip_ttl;
|
||||
assign tx_udp_ip_header_checksum = s_udp_ip_header_checksum;
|
||||
assign tx_udp_ip_source_ip = s_udp_ip_source_ip;
|
||||
assign tx_udp_ip_dest_ip = s_udp_ip_dest_ip;
|
||||
assign tx_udp_source_port = s_udp_source_port;
|
||||
assign tx_udp_dest_port = s_udp_dest_port;
|
||||
assign tx_udp_length = s_udp_length;
|
||||
assign tx_udp_checksum = s_udp_checksum;
|
||||
assign tx_udp_payload_axis_tdata = s_udp_payload_axis_tdata;
|
||||
assign tx_udp_payload_axis_tvalid = s_udp_payload_axis_tvalid;
|
||||
assign s_udp_payload_axis_tready = tx_udp_payload_axis_tready;
|
||||
assign tx_udp_payload_axis_tlast = s_udp_payload_axis_tlast;
|
||||
assign tx_udp_payload_axis_tuser = s_udp_payload_axis_tuser;
|
||||
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
udp_ip_tx
|
||||
udp_ip_tx_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// UDP frame input
|
||||
.s_udp_hdr_valid(tx_udp_hdr_valid),
|
||||
.s_udp_hdr_ready(tx_udp_hdr_ready),
|
||||
.s_eth_dest_mac(tx_udp_eth_dest_mac),
|
||||
.s_eth_src_mac(tx_udp_eth_src_mac),
|
||||
.s_eth_type(tx_udp_eth_type),
|
||||
.s_ip_version(tx_udp_ip_version),
|
||||
.s_ip_ihl(tx_udp_ip_ihl),
|
||||
.s_ip_dscp(tx_udp_ip_dscp),
|
||||
.s_ip_ecn(tx_udp_ip_ecn),
|
||||
.s_ip_identification(tx_udp_ip_identification),
|
||||
.s_ip_flags(tx_udp_ip_flags),
|
||||
.s_ip_fragment_offset(tx_udp_ip_fragment_offset),
|
||||
.s_ip_ttl(tx_udp_ip_ttl),
|
||||
.s_ip_protocol(8'h11),
|
||||
.s_ip_header_checksum(tx_udp_ip_header_checksum),
|
||||
.s_ip_source_ip(tx_udp_ip_source_ip),
|
||||
.s_ip_dest_ip(tx_udp_ip_dest_ip),
|
||||
.s_udp_source_port(tx_udp_source_port),
|
||||
.s_udp_dest_port(tx_udp_dest_port),
|
||||
.s_udp_length(tx_udp_length),
|
||||
.s_udp_checksum(tx_udp_checksum),
|
||||
.s_udp_payload_axis_tdata(tx_udp_payload_axis_tdata),
|
||||
.s_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid),
|
||||
.s_udp_payload_axis_tready(tx_udp_payload_axis_tready),
|
||||
.s_udp_payload_axis_tlast(tx_udp_payload_axis_tlast),
|
||||
.s_udp_payload_axis_tuser(tx_udp_payload_axis_tuser),
|
||||
// IP frame output
|
||||
.m_ip_hdr_valid(m_ip_hdr_valid),
|
||||
.m_ip_hdr_ready(m_ip_hdr_ready),
|
||||
.m_eth_dest_mac(m_ip_eth_dest_mac),
|
||||
.m_eth_src_mac(m_ip_eth_src_mac),
|
||||
.m_eth_type(m_ip_eth_type),
|
||||
.m_ip_version(m_ip_version),
|
||||
.m_ip_ihl(m_ip_ihl),
|
||||
.m_ip_dscp(m_ip_dscp),
|
||||
.m_ip_ecn(m_ip_ecn),
|
||||
.m_ip_length(m_ip_length),
|
||||
.m_ip_identification(m_ip_identification),
|
||||
.m_ip_flags(m_ip_flags),
|
||||
.m_ip_fragment_offset(m_ip_fragment_offset),
|
||||
.m_ip_ttl(m_ip_ttl),
|
||||
.m_ip_protocol(m_ip_protocol),
|
||||
.m_ip_header_checksum(m_ip_header_checksum),
|
||||
.m_ip_source_ip(m_ip_source_ip),
|
||||
.m_ip_dest_ip(m_ip_dest_ip),
|
||||
.m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
|
||||
.m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
|
||||
.m_ip_payload_axis_tready(m_ip_payload_axis_tready),
|
||||
.m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
|
||||
.m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
|
||||
// Status signals
|
||||
.busy(tx_busy),
|
||||
.error_payload_early_termination(tx_error_payload_early_termination)
|
||||
);
|
||||
|
||||
endmodule
|
555
fpga/src/udp_checksum_gen.sv
Normal file
555
fpga/src/udp_checksum_gen.sv
Normal file
|
@ -0,0 +1,555 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2016-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* UDP checksum calculation module
|
||||
*/
|
||||
module udp_checksum_gen #
|
||||
(
|
||||
parameter PAYLOAD_FIFO_ADDR_WIDTH = 11,
|
||||
parameter HEADER_FIFO_ADDR_WIDTH = 3
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* UDP frame input
|
||||
*/
|
||||
input wire s_udp_hdr_valid,
|
||||
output wire s_udp_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [3:0] s_ip_version,
|
||||
input wire [3:0] s_ip_ihl,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_identification,
|
||||
input wire [2:0] s_ip_flags,
|
||||
input wire [12:0] s_ip_fragment_offset,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [15:0] s_ip_header_checksum,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [15:0] s_udp_source_port,
|
||||
input wire [15:0] s_udp_dest_port,
|
||||
input wire [7:0] s_udp_payload_axis_tdata,
|
||||
input wire s_udp_payload_axis_tvalid,
|
||||
output wire s_udp_payload_axis_tready,
|
||||
input wire s_udp_payload_axis_tlast,
|
||||
input wire s_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* UDP frame output
|
||||
*/
|
||||
output wire m_udp_hdr_valid,
|
||||
input wire m_udp_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [15:0] m_udp_source_port,
|
||||
output wire [15:0] m_udp_dest_port,
|
||||
output wire [15:0] m_udp_length,
|
||||
output wire [15:0] m_udp_checksum,
|
||||
output wire [7:0] m_udp_payload_axis_tdata,
|
||||
output wire m_udp_payload_axis_tvalid,
|
||||
input wire m_udp_payload_axis_tready,
|
||||
output wire m_udp_payload_axis_tlast,
|
||||
output wire m_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
UDP Frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype (0x0800) 2 octets
|
||||
Version (4) 4 bits
|
||||
IHL (5-15) 4 bits
|
||||
DSCP (0) 6 bits
|
||||
ECN (0) 2 bits
|
||||
length 2 octets
|
||||
identification (0?) 2 octets
|
||||
flags (010) 3 bits
|
||||
fragment offset (0) 13 bits
|
||||
time to live (64?) 1 octet
|
||||
protocol 1 octet
|
||||
header checksum 2 octets
|
||||
source IP 4 octets
|
||||
destination IP 4 octets
|
||||
options (IHL-5)*4 octets
|
||||
|
||||
source port 2 octets
|
||||
desination port 2 octets
|
||||
length 2 octets
|
||||
checksum 2 octets
|
||||
|
||||
payload length octets
|
||||
|
||||
This module receives a UDP frame with header fields in parallel and payload on
|
||||
an AXI stream interface, calculates the length and checksum, then produces the
|
||||
header fields in parallel along with the UDP payload in a separate AXI stream.
|
||||
|
||||
*/
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_SUM_HEADER_1 = 3'd1,
|
||||
STATE_SUM_HEADER_2 = 3'd2,
|
||||
STATE_SUM_HEADER_3 = 3'd3,
|
||||
STATE_SUM_PAYLOAD = 3'd4,
|
||||
STATE_FINISH_SUM = 3'd5;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_udp_hdr;
|
||||
reg shift_payload_in;
|
||||
reg [31:0] checksum_part;
|
||||
|
||||
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
|
||||
|
||||
reg [31:0] checksum_reg = 32'd0, checksum_next;
|
||||
|
||||
reg [47:0] eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] eth_type_reg = 16'd0;
|
||||
reg [3:0] ip_version_reg = 4'd0;
|
||||
reg [3:0] ip_ihl_reg = 4'd0;
|
||||
reg [5:0] ip_dscp_reg = 6'd0;
|
||||
reg [1:0] ip_ecn_reg = 2'd0;
|
||||
reg [15:0] ip_identification_reg = 16'd0;
|
||||
reg [2:0] ip_flags_reg = 3'd0;
|
||||
reg [12:0] ip_fragment_offset_reg = 13'd0;
|
||||
reg [7:0] ip_ttl_reg = 8'd0;
|
||||
reg [15:0] ip_header_checksum_reg = 16'd0;
|
||||
reg [31:0] ip_source_ip_reg = 32'd0;
|
||||
reg [31:0] ip_dest_ip_reg = 32'd0;
|
||||
reg [15:0] udp_source_port_reg = 16'd0;
|
||||
reg [15:0] udp_dest_port_reg = 16'd0;
|
||||
|
||||
reg hdr_valid_reg = 0, hdr_valid_next;
|
||||
|
||||
reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next;
|
||||
reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
|
||||
/*
|
||||
* UDP Payload FIFO
|
||||
*/
|
||||
wire [7:0] s_udp_payload_fifo_tdata;
|
||||
wire s_udp_payload_fifo_tvalid;
|
||||
wire s_udp_payload_fifo_tready;
|
||||
wire s_udp_payload_fifo_tlast;
|
||||
wire s_udp_payload_fifo_tuser;
|
||||
|
||||
wire [7:0] m_udp_payload_fifo_tdata;
|
||||
wire m_udp_payload_fifo_tvalid;
|
||||
wire m_udp_payload_fifo_tready;
|
||||
wire m_udp_payload_fifo_tlast;
|
||||
wire m_udp_payload_fifo_tuser;
|
||||
|
||||
axis_fifo #(
|
||||
.ADDR_WIDTH(PAYLOAD_FIFO_ADDR_WIDTH),
|
||||
.DATA_WIDTH(8),
|
||||
.KEEP_ENABLE(0),
|
||||
.LAST_ENABLE(1),
|
||||
.ID_ENABLE(0),
|
||||
.DEST_ENABLE(0),
|
||||
.USER_ENABLE(1),
|
||||
.USER_WIDTH(1),
|
||||
.FRAME_FIFO(0)
|
||||
)
|
||||
payload_fifo (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// AXI input
|
||||
.s_axis_tdata(s_udp_payload_fifo_tdata),
|
||||
.s_axis_tkeep(0),
|
||||
.s_axis_tvalid(s_udp_payload_fifo_tvalid),
|
||||
.s_axis_tready(s_udp_payload_fifo_tready),
|
||||
.s_axis_tlast(s_udp_payload_fifo_tlast),
|
||||
.s_axis_tid(0),
|
||||
.s_axis_tdest(0),
|
||||
.s_axis_tuser(s_udp_payload_fifo_tuser),
|
||||
// AXI output
|
||||
.m_axis_tdata(m_udp_payload_fifo_tdata),
|
||||
.m_axis_tkeep(),
|
||||
.m_axis_tvalid(m_udp_payload_fifo_tvalid),
|
||||
.m_axis_tready(m_udp_payload_fifo_tready),
|
||||
.m_axis_tlast(m_udp_payload_fifo_tlast),
|
||||
.m_axis_tid(),
|
||||
.m_axis_tdest(),
|
||||
.m_axis_tuser(m_udp_payload_fifo_tuser),
|
||||
// Status
|
||||
.status_overflow(),
|
||||
.status_bad_frame(),
|
||||
.status_good_frame()
|
||||
);
|
||||
|
||||
assign s_udp_payload_fifo_tdata = s_udp_payload_axis_tdata;
|
||||
assign s_udp_payload_fifo_tvalid = s_udp_payload_axis_tvalid && shift_payload_in;
|
||||
assign s_udp_payload_axis_tready = s_udp_payload_fifo_tready && shift_payload_in;
|
||||
assign s_udp_payload_fifo_tlast = s_udp_payload_axis_tlast;
|
||||
assign s_udp_payload_fifo_tuser = s_udp_payload_axis_tuser;
|
||||
|
||||
assign m_udp_payload_axis_tdata = m_udp_payload_fifo_tdata;
|
||||
assign m_udp_payload_axis_tvalid = m_udp_payload_fifo_tvalid;
|
||||
assign m_udp_payload_fifo_tready = m_udp_payload_axis_tready;
|
||||
assign m_udp_payload_axis_tlast = m_udp_payload_fifo_tlast;
|
||||
assign m_udp_payload_axis_tuser = m_udp_payload_fifo_tuser;
|
||||
|
||||
/*
|
||||
* UDP Header FIFO
|
||||
*/
|
||||
reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_wr_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_wr_ptr_next;
|
||||
reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_rd_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_rd_ptr_next;
|
||||
|
||||
reg [47:0] eth_dest_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [47:0] eth_src_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [15:0] eth_type_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [3:0] ip_version_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [3:0] ip_ihl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [5:0] ip_dscp_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [1:0] ip_ecn_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [15:0] ip_identification_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [2:0] ip_flags_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [12:0] ip_fragment_offset_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [7:0] ip_ttl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [15:0] ip_header_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [31:0] ip_source_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [31:0] ip_dest_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [15:0] udp_source_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [15:0] udp_dest_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [15:0] udp_length_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
reg [15:0] udp_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0];
|
||||
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
reg [3:0] m_ip_version_reg = 4'd0;
|
||||
reg [3:0] m_ip_ihl_reg = 4'd0;
|
||||
reg [5:0] m_ip_dscp_reg = 6'd0;
|
||||
reg [1:0] m_ip_ecn_reg = 2'd0;
|
||||
reg [15:0] m_ip_identification_reg = 16'd0;
|
||||
reg [2:0] m_ip_flags_reg = 3'd0;
|
||||
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
|
||||
reg [7:0] m_ip_ttl_reg = 8'd0;
|
||||
reg [15:0] m_ip_header_checksum_reg = 16'd0;
|
||||
reg [31:0] m_ip_source_ip_reg = 32'd0;
|
||||
reg [31:0] m_ip_dest_ip_reg = 32'd0;
|
||||
reg [15:0] m_udp_source_port_reg = 16'd0;
|
||||
reg [15:0] m_udp_dest_port_reg = 16'd0;
|
||||
reg [15:0] m_udp_length_reg = 16'd0;
|
||||
reg [15:0] m_udp_checksum_reg = 16'd0;
|
||||
|
||||
reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next;
|
||||
|
||||
// full when first MSB different but rest same
|
||||
wire header_fifo_full = ((header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH] != header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH]) &&
|
||||
(header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0] == header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]));
|
||||
// empty when pointers match exactly
|
||||
wire header_fifo_empty = header_fifo_wr_ptr_reg == header_fifo_rd_ptr_reg;
|
||||
|
||||
// control signals
|
||||
reg header_fifo_write;
|
||||
reg header_fifo_read;
|
||||
|
||||
wire header_fifo_ready = !header_fifo_full;
|
||||
|
||||
assign m_udp_hdr_valid = m_udp_hdr_valid_reg;
|
||||
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
assign m_ip_version = m_ip_version_reg;
|
||||
assign m_ip_ihl = m_ip_ihl_reg;
|
||||
assign m_ip_dscp = m_ip_dscp_reg;
|
||||
assign m_ip_ecn = m_ip_ecn_reg;
|
||||
assign m_ip_length = m_udp_length_reg + 16'd20;
|
||||
assign m_ip_identification = m_ip_identification_reg;
|
||||
assign m_ip_flags = m_ip_flags_reg;
|
||||
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
|
||||
assign m_ip_ttl = m_ip_ttl_reg;
|
||||
assign m_ip_protocol = 8'h11;
|
||||
assign m_ip_header_checksum = m_ip_header_checksum_reg;
|
||||
assign m_ip_source_ip = m_ip_source_ip_reg;
|
||||
assign m_ip_dest_ip = m_ip_dest_ip_reg;
|
||||
assign m_udp_source_port = m_udp_source_port_reg;
|
||||
assign m_udp_dest_port = m_udp_dest_port_reg;
|
||||
assign m_udp_length = m_udp_length_reg;
|
||||
assign m_udp_checksum = m_udp_checksum_reg;
|
||||
|
||||
// Write logic
|
||||
always @* begin
|
||||
header_fifo_write = 1'b0;
|
||||
|
||||
header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg;
|
||||
|
||||
if (hdr_valid_reg) begin
|
||||
// input data valid
|
||||
if (~header_fifo_full) begin
|
||||
// not full, perform write
|
||||
header_fifo_write = 1'b1;
|
||||
header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
header_fifo_wr_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}};
|
||||
end else begin
|
||||
header_fifo_wr_ptr_reg <= header_fifo_wr_ptr_next;
|
||||
end
|
||||
|
||||
if (header_fifo_write) begin
|
||||
eth_dest_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_dest_mac_reg;
|
||||
eth_src_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_src_mac_reg;
|
||||
eth_type_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_type_reg;
|
||||
ip_version_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_version_reg;
|
||||
ip_ihl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ihl_reg;
|
||||
ip_dscp_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dscp_reg;
|
||||
ip_ecn_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ecn_reg;
|
||||
ip_identification_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_identification_reg;
|
||||
ip_flags_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_flags_reg;
|
||||
ip_fragment_offset_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_fragment_offset_reg;
|
||||
ip_ttl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ttl_reg;
|
||||
ip_header_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_header_checksum_reg;
|
||||
ip_source_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_source_ip_reg;
|
||||
ip_dest_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dest_ip_reg;
|
||||
udp_source_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_source_port_reg;
|
||||
udp_dest_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_dest_port_reg;
|
||||
udp_length_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= frame_ptr_reg;
|
||||
udp_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= checksum_reg[15:0];
|
||||
end
|
||||
end
|
||||
|
||||
// Read logic
|
||||
always @* begin
|
||||
header_fifo_read = 1'b0;
|
||||
|
||||
header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg;
|
||||
|
||||
m_udp_hdr_valid_next = m_udp_hdr_valid_reg;
|
||||
|
||||
if (m_udp_hdr_ready || !m_udp_hdr_valid) begin
|
||||
// output data not valid OR currently being transferred
|
||||
if (!header_fifo_empty) begin
|
||||
// not empty, perform read
|
||||
header_fifo_read = 1'b1;
|
||||
m_udp_hdr_valid_next = 1'b1;
|
||||
header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg + 1;
|
||||
end else begin
|
||||
// empty, invalidate
|
||||
m_udp_hdr_valid_next = 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
header_fifo_rd_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}};
|
||||
m_udp_hdr_valid_reg <= 1'b0;
|
||||
end else begin
|
||||
header_fifo_rd_ptr_reg <= header_fifo_rd_ptr_next;
|
||||
m_udp_hdr_valid_reg <= m_udp_hdr_valid_next;
|
||||
end
|
||||
|
||||
if (header_fifo_read) begin
|
||||
m_eth_dest_mac_reg <= eth_dest_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_eth_src_mac_reg <= eth_src_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_eth_type_reg <= eth_type_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_version_reg <= ip_version_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_ihl_reg <= ip_ihl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_dscp_reg <= ip_dscp_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_ecn_reg <= ip_ecn_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_identification_reg <= ip_identification_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_flags_reg <= ip_flags_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_fragment_offset_reg <= ip_fragment_offset_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_ttl_reg <= ip_ttl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_header_checksum_reg <= ip_header_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_source_ip_reg <= ip_source_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_ip_dest_ip_reg <= ip_dest_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_udp_source_port_reg <= udp_source_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_udp_dest_port_reg <= udp_dest_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_udp_length_reg <= udp_length_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
m_udp_checksum_reg <= udp_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]];
|
||||
end
|
||||
end
|
||||
|
||||
assign s_udp_hdr_ready = s_udp_hdr_ready_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_udp_hdr_ready_next = 1'b0;
|
||||
s_udp_payload_axis_tready_next = 1'b0;
|
||||
|
||||
store_udp_hdr = 1'b0;
|
||||
shift_payload_in = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
checksum_next = checksum_reg;
|
||||
|
||||
hdr_valid_next = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state
|
||||
s_udp_hdr_ready_next = header_fifo_ready;
|
||||
|
||||
if (s_udp_hdr_ready && s_udp_hdr_valid) begin
|
||||
store_udp_hdr = 1'b1;
|
||||
frame_ptr_next = 0;
|
||||
// 16'h0011 = zero padded type field
|
||||
// 16'h0010 = header length times two
|
||||
checksum_next = 16'h0011 + 16'h0010;
|
||||
s_udp_hdr_ready_next = 1'b0;
|
||||
state_next = STATE_SUM_HEADER_1;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_SUM_HEADER_1: begin
|
||||
// sum pseudo header and header
|
||||
checksum_next = checksum_reg + ip_source_ip_reg[31:16] + ip_source_ip_reg[15:0];
|
||||
state_next = STATE_SUM_HEADER_2;
|
||||
end
|
||||
STATE_SUM_HEADER_2: begin
|
||||
// sum pseudo header and header
|
||||
checksum_next = checksum_reg + ip_dest_ip_reg[31:16] + ip_dest_ip_reg[15:0];
|
||||
state_next = STATE_SUM_HEADER_3;
|
||||
end
|
||||
STATE_SUM_HEADER_3: begin
|
||||
// sum pseudo header and header
|
||||
checksum_next = checksum_reg + udp_source_port_reg + udp_dest_port_reg;
|
||||
frame_ptr_next = 8;
|
||||
state_next = STATE_SUM_PAYLOAD;
|
||||
end
|
||||
STATE_SUM_PAYLOAD: begin
|
||||
// sum payload
|
||||
shift_payload_in = 1'b1;
|
||||
|
||||
if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin
|
||||
// checksum computation for payload - alternately store and accumulate
|
||||
// add 2 for length calculation (two length fields in pseudo header)
|
||||
if (frame_ptr_reg[0]) begin
|
||||
checksum_next = checksum_reg + {8'h00, s_udp_payload_axis_tdata} + 2;
|
||||
end else begin
|
||||
checksum_next = checksum_reg + {s_udp_payload_axis_tdata, 8'h00} + 2;
|
||||
end
|
||||
|
||||
frame_ptr_next = frame_ptr_reg + 1;
|
||||
|
||||
if (s_udp_payload_axis_tlast) begin
|
||||
state_next = STATE_FINISH_SUM;
|
||||
end else begin
|
||||
state_next = STATE_SUM_PAYLOAD;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_SUM_PAYLOAD;
|
||||
end
|
||||
end
|
||||
STATE_FINISH_SUM: begin
|
||||
// add MSW (twice!) for proper ones complement sum
|
||||
checksum_part = checksum_reg[15:0] + checksum_reg[31:16];
|
||||
checksum_next = ~(checksum_part[15:0] + checksum_part[16]);
|
||||
hdr_valid_next = 1;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
s_udp_hdr_ready_reg <= 1'b0;
|
||||
s_udp_payload_axis_tready_reg <= 1'b0;
|
||||
hdr_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
s_udp_hdr_ready_reg <= s_udp_hdr_ready_next;
|
||||
s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next;
|
||||
|
||||
hdr_valid_reg <= hdr_valid_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
checksum_reg <= checksum_next;
|
||||
|
||||
// datapath
|
||||
if (store_udp_hdr) begin
|
||||
eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
eth_src_mac_reg <= s_eth_src_mac;
|
||||
eth_type_reg <= s_eth_type;
|
||||
ip_version_reg <= s_ip_version;
|
||||
ip_ihl_reg <= s_ip_ihl;
|
||||
ip_dscp_reg <= s_ip_dscp;
|
||||
ip_ecn_reg <= s_ip_ecn;
|
||||
ip_identification_reg <= s_ip_identification;
|
||||
ip_flags_reg <= s_ip_flags;
|
||||
ip_fragment_offset_reg <= s_ip_fragment_offset;
|
||||
ip_ttl_reg <= s_ip_ttl;
|
||||
ip_header_checksum_reg <= s_ip_header_checksum;
|
||||
ip_source_ip_reg <= s_ip_source_ip;
|
||||
ip_dest_ip_reg <= s_ip_dest_ip;
|
||||
udp_source_port_reg <= s_udp_source_port;
|
||||
udp_dest_port_reg <= s_udp_dest_port;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
637
fpga/src/udp_complete.sv
Normal file
637
fpga/src/udp_complete.sv
Normal file
|
@ -0,0 +1,637 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* IPv4 and ARP block with UDP support, ethernet frame interface
|
||||
*/
|
||||
module udp_complete #(
|
||||
parameter ARP_CACHE_ADDR_WIDTH = 9,
|
||||
parameter ARP_REQUEST_RETRY_COUNT = 4,
|
||||
parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2,
|
||||
parameter ARP_REQUEST_TIMEOUT = 125000000*30,
|
||||
parameter UDP_CHECKSUM_GEN_ENABLE = 1,
|
||||
parameter UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH = 11,
|
||||
parameter UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH = 3
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* Ethernet frame input
|
||||
*/
|
||||
input wire s_eth_hdr_valid,
|
||||
output wire s_eth_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [7:0] s_eth_payload_axis_tdata,
|
||||
input wire s_eth_payload_axis_tvalid,
|
||||
output wire s_eth_payload_axis_tready,
|
||||
input wire s_eth_payload_axis_tlast,
|
||||
input wire s_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Ethernet frame output
|
||||
*/
|
||||
output wire m_eth_hdr_valid,
|
||||
input wire m_eth_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [7:0] m_eth_payload_axis_tdata,
|
||||
output wire m_eth_payload_axis_tvalid,
|
||||
input wire m_eth_payload_axis_tready,
|
||||
output wire m_eth_payload_axis_tlast,
|
||||
output wire m_eth_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP input
|
||||
*/
|
||||
input wire s_ip_hdr_valid,
|
||||
output wire s_ip_hdr_ready,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_length,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [7:0] s_ip_protocol,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [7:0] s_ip_payload_axis_tdata,
|
||||
input wire s_ip_payload_axis_tvalid,
|
||||
output wire s_ip_payload_axis_tready,
|
||||
input wire s_ip_payload_axis_tlast,
|
||||
input wire s_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP output
|
||||
*/
|
||||
output wire m_ip_hdr_valid,
|
||||
input wire m_ip_hdr_ready,
|
||||
output wire [47:0] m_ip_eth_dest_mac,
|
||||
output wire [47:0] m_ip_eth_src_mac,
|
||||
output wire [15:0] m_ip_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [7:0] m_ip_payload_axis_tdata,
|
||||
output wire m_ip_payload_axis_tvalid,
|
||||
input wire m_ip_payload_axis_tready,
|
||||
output wire m_ip_payload_axis_tlast,
|
||||
output wire m_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* UDP input
|
||||
*/
|
||||
input wire s_udp_hdr_valid,
|
||||
output wire s_udp_hdr_ready,
|
||||
input wire [5:0] s_udp_ip_dscp,
|
||||
input wire [1:0] s_udp_ip_ecn,
|
||||
input wire [7:0] s_udp_ip_ttl,
|
||||
input wire [31:0] s_udp_ip_source_ip,
|
||||
input wire [31:0] s_udp_ip_dest_ip,
|
||||
input wire [15:0] s_udp_source_port,
|
||||
input wire [15:0] s_udp_dest_port,
|
||||
input wire [15:0] s_udp_length,
|
||||
input wire [15:0] s_udp_checksum,
|
||||
input wire [7:0] s_udp_payload_axis_tdata,
|
||||
input wire s_udp_payload_axis_tvalid,
|
||||
output wire s_udp_payload_axis_tready,
|
||||
input wire s_udp_payload_axis_tlast,
|
||||
input wire s_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* UDP output
|
||||
*/
|
||||
output wire m_udp_hdr_valid,
|
||||
input wire m_udp_hdr_ready,
|
||||
output wire [47:0] m_udp_eth_dest_mac,
|
||||
output wire [47:0] m_udp_eth_src_mac,
|
||||
output wire [15:0] m_udp_eth_type,
|
||||
output wire [3:0] m_udp_ip_version,
|
||||
output wire [3:0] m_udp_ip_ihl,
|
||||
output wire [5:0] m_udp_ip_dscp,
|
||||
output wire [1:0] m_udp_ip_ecn,
|
||||
output wire [15:0] m_udp_ip_length,
|
||||
output wire [15:0] m_udp_ip_identification,
|
||||
output wire [2:0] m_udp_ip_flags,
|
||||
output wire [12:0] m_udp_ip_fragment_offset,
|
||||
output wire [7:0] m_udp_ip_ttl,
|
||||
output wire [7:0] m_udp_ip_protocol,
|
||||
output wire [15:0] m_udp_ip_header_checksum,
|
||||
output wire [31:0] m_udp_ip_source_ip,
|
||||
output wire [31:0] m_udp_ip_dest_ip,
|
||||
output wire [15:0] m_udp_source_port,
|
||||
output wire [15:0] m_udp_dest_port,
|
||||
output wire [15:0] m_udp_length,
|
||||
output wire [15:0] m_udp_checksum,
|
||||
output wire [7:0] m_udp_payload_axis_tdata,
|
||||
output wire m_udp_payload_axis_tvalid,
|
||||
input wire m_udp_payload_axis_tready,
|
||||
output wire m_udp_payload_axis_tlast,
|
||||
output wire m_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire ip_rx_busy,
|
||||
output wire ip_tx_busy,
|
||||
output wire udp_rx_busy,
|
||||
output wire udp_tx_busy,
|
||||
output wire ip_rx_error_header_early_termination,
|
||||
output wire ip_rx_error_payload_early_termination,
|
||||
output wire ip_rx_error_invalid_header,
|
||||
output wire ip_rx_error_invalid_checksum,
|
||||
output wire ip_tx_error_payload_early_termination,
|
||||
output wire ip_tx_error_arp_failed,
|
||||
output wire udp_rx_error_header_early_termination,
|
||||
output wire udp_rx_error_payload_early_termination,
|
||||
output wire udp_tx_error_payload_early_termination,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire [47:0] local_mac,
|
||||
input wire [31:0] local_ip,
|
||||
input wire [31:0] gateway_ip,
|
||||
input wire [31:0] subnet_mask,
|
||||
input wire clear_arp_cache
|
||||
);
|
||||
|
||||
wire ip_rx_ip_hdr_valid;
|
||||
wire ip_rx_ip_hdr_ready;
|
||||
wire [47:0] ip_rx_ip_eth_dest_mac;
|
||||
wire [47:0] ip_rx_ip_eth_src_mac;
|
||||
wire [15:0] ip_rx_ip_eth_type;
|
||||
wire [3:0] ip_rx_ip_version;
|
||||
wire [3:0] ip_rx_ip_ihl;
|
||||
wire [5:0] ip_rx_ip_dscp;
|
||||
wire [1:0] ip_rx_ip_ecn;
|
||||
wire [15:0] ip_rx_ip_length;
|
||||
wire [15:0] ip_rx_ip_identification;
|
||||
wire [2:0] ip_rx_ip_flags;
|
||||
wire [12:0] ip_rx_ip_fragment_offset;
|
||||
wire [7:0] ip_rx_ip_ttl;
|
||||
wire [7:0] ip_rx_ip_protocol;
|
||||
wire [15:0] ip_rx_ip_header_checksum;
|
||||
wire [31:0] ip_rx_ip_source_ip;
|
||||
wire [31:0] ip_rx_ip_dest_ip;
|
||||
wire [7:0] ip_rx_ip_payload_axis_tdata;
|
||||
wire ip_rx_ip_payload_axis_tvalid;
|
||||
wire ip_rx_ip_payload_axis_tlast;
|
||||
wire ip_rx_ip_payload_axis_tuser;
|
||||
wire ip_rx_ip_payload_axis_tready;
|
||||
|
||||
wire ip_tx_ip_hdr_valid;
|
||||
wire ip_tx_ip_hdr_ready;
|
||||
wire [5:0] ip_tx_ip_dscp;
|
||||
wire [1:0] ip_tx_ip_ecn;
|
||||
wire [15:0] ip_tx_ip_length;
|
||||
wire [7:0] ip_tx_ip_ttl;
|
||||
wire [7:0] ip_tx_ip_protocol;
|
||||
wire [31:0] ip_tx_ip_source_ip;
|
||||
wire [31:0] ip_tx_ip_dest_ip;
|
||||
wire [7:0] ip_tx_ip_payload_axis_tdata;
|
||||
wire ip_tx_ip_payload_axis_tvalid;
|
||||
wire ip_tx_ip_payload_axis_tlast;
|
||||
wire ip_tx_ip_payload_axis_tuser;
|
||||
wire ip_tx_ip_payload_axis_tready;
|
||||
|
||||
wire udp_rx_ip_hdr_valid;
|
||||
wire udp_rx_ip_hdr_ready;
|
||||
wire [47:0] udp_rx_ip_eth_dest_mac;
|
||||
wire [47:0] udp_rx_ip_eth_src_mac;
|
||||
wire [15:0] udp_rx_ip_eth_type;
|
||||
wire [3:0] udp_rx_ip_version;
|
||||
wire [3:0] udp_rx_ip_ihl;
|
||||
wire [5:0] udp_rx_ip_dscp;
|
||||
wire [1:0] udp_rx_ip_ecn;
|
||||
wire [15:0] udp_rx_ip_length;
|
||||
wire [15:0] udp_rx_ip_identification;
|
||||
wire [2:0] udp_rx_ip_flags;
|
||||
wire [12:0] udp_rx_ip_fragment_offset;
|
||||
wire [7:0] udp_rx_ip_ttl;
|
||||
wire [7:0] udp_rx_ip_protocol;
|
||||
wire [15:0] udp_rx_ip_header_checksum;
|
||||
wire [31:0] udp_rx_ip_source_ip;
|
||||
wire [31:0] udp_rx_ip_dest_ip;
|
||||
wire [7:0] udp_rx_ip_payload_axis_tdata;
|
||||
wire udp_rx_ip_payload_axis_tvalid;
|
||||
wire udp_rx_ip_payload_axis_tlast;
|
||||
wire udp_rx_ip_payload_axis_tuser;
|
||||
wire udp_rx_ip_payload_axis_tready;
|
||||
|
||||
wire udp_tx_ip_hdr_valid;
|
||||
wire udp_tx_ip_hdr_ready;
|
||||
wire [5:0] udp_tx_ip_dscp;
|
||||
wire [1:0] udp_tx_ip_ecn;
|
||||
wire [15:0] udp_tx_ip_length;
|
||||
wire [7:0] udp_tx_ip_ttl;
|
||||
wire [7:0] udp_tx_ip_protocol;
|
||||
wire [31:0] udp_tx_ip_source_ip;
|
||||
wire [31:0] udp_tx_ip_dest_ip;
|
||||
wire [7:0] udp_tx_ip_payload_axis_tdata;
|
||||
wire udp_tx_ip_payload_axis_tvalid;
|
||||
wire udp_tx_ip_payload_axis_tlast;
|
||||
wire udp_tx_ip_payload_axis_tuser;
|
||||
wire udp_tx_ip_payload_axis_tready;
|
||||
|
||||
/*
|
||||
* Input classifier (ip_protocol)
|
||||
*/
|
||||
wire s_select_udp = (ip_rx_ip_protocol == 8'h11);
|
||||
wire s_select_ip = !s_select_udp;
|
||||
|
||||
reg s_select_udp_reg = 1'b0;
|
||||
reg s_select_ip_reg = 1'b0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
s_select_udp_reg <= 1'b0;
|
||||
s_select_ip_reg <= 1'b0;
|
||||
end else begin
|
||||
if (ip_rx_ip_payload_axis_tvalid) begin
|
||||
if ((!s_select_udp_reg && !s_select_ip_reg) ||
|
||||
(ip_rx_ip_payload_axis_tvalid && ip_rx_ip_payload_axis_tready && ip_rx_ip_payload_axis_tlast)) begin
|
||||
s_select_udp_reg <= s_select_udp;
|
||||
s_select_ip_reg <= s_select_ip;
|
||||
end
|
||||
end else begin
|
||||
s_select_udp_reg <= 1'b0;
|
||||
s_select_ip_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// IP frame to UDP module
|
||||
assign udp_rx_ip_hdr_valid = s_select_udp && ip_rx_ip_hdr_valid;
|
||||
assign udp_rx_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac;
|
||||
assign udp_rx_ip_eth_src_mac = ip_rx_ip_eth_src_mac;
|
||||
assign udp_rx_ip_eth_type = ip_rx_ip_eth_type;
|
||||
assign udp_rx_ip_version = ip_rx_ip_version;
|
||||
assign udp_rx_ip_ihl = ip_rx_ip_ihl;
|
||||
assign udp_rx_ip_dscp = ip_rx_ip_dscp;
|
||||
assign udp_rx_ip_ecn = ip_rx_ip_ecn;
|
||||
assign udp_rx_ip_length = ip_rx_ip_length;
|
||||
assign udp_rx_ip_identification = ip_rx_ip_identification;
|
||||
assign udp_rx_ip_flags = ip_rx_ip_flags;
|
||||
assign udp_rx_ip_fragment_offset = ip_rx_ip_fragment_offset;
|
||||
assign udp_rx_ip_ttl = ip_rx_ip_ttl;
|
||||
assign udp_rx_ip_protocol = 8'h11;
|
||||
assign udp_rx_ip_header_checksum = ip_rx_ip_header_checksum;
|
||||
assign udp_rx_ip_source_ip = ip_rx_ip_source_ip;
|
||||
assign udp_rx_ip_dest_ip = ip_rx_ip_dest_ip;
|
||||
assign udp_rx_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata;
|
||||
assign udp_rx_ip_payload_axis_tvalid = s_select_udp_reg && ip_rx_ip_payload_axis_tvalid;
|
||||
assign udp_rx_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast;
|
||||
assign udp_rx_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser;
|
||||
|
||||
// External IP frame output
|
||||
assign m_ip_hdr_valid = s_select_ip && ip_rx_ip_hdr_valid;
|
||||
assign m_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac;
|
||||
assign m_ip_eth_src_mac = ip_rx_ip_eth_src_mac;
|
||||
assign m_ip_eth_type = ip_rx_ip_eth_type;
|
||||
assign m_ip_version = ip_rx_ip_version;
|
||||
assign m_ip_ihl = ip_rx_ip_ihl;
|
||||
assign m_ip_dscp = ip_rx_ip_dscp;
|
||||
assign m_ip_ecn = ip_rx_ip_ecn;
|
||||
assign m_ip_length = ip_rx_ip_length;
|
||||
assign m_ip_identification = ip_rx_ip_identification;
|
||||
assign m_ip_flags = ip_rx_ip_flags;
|
||||
assign m_ip_fragment_offset = ip_rx_ip_fragment_offset;
|
||||
assign m_ip_ttl = ip_rx_ip_ttl;
|
||||
assign m_ip_protocol = ip_rx_ip_protocol;
|
||||
assign m_ip_header_checksum = ip_rx_ip_header_checksum;
|
||||
assign m_ip_source_ip = ip_rx_ip_source_ip;
|
||||
assign m_ip_dest_ip = ip_rx_ip_dest_ip;
|
||||
assign m_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata;
|
||||
assign m_ip_payload_axis_tvalid = s_select_ip_reg && ip_rx_ip_payload_axis_tvalid;
|
||||
assign m_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast;
|
||||
assign m_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser;
|
||||
|
||||
assign ip_rx_ip_hdr_ready = (s_select_udp && udp_rx_ip_hdr_ready) ||
|
||||
(s_select_ip && m_ip_hdr_ready);
|
||||
|
||||
assign ip_rx_ip_payload_axis_tready = (s_select_udp_reg && udp_rx_ip_payload_axis_tready) ||
|
||||
(s_select_ip_reg && m_ip_payload_axis_tready);
|
||||
|
||||
/*
|
||||
* Output arbiter
|
||||
*/
|
||||
ip_arb_mux #(
|
||||
.S_COUNT(2),
|
||||
.DATA_WIDTH(8),
|
||||
.KEEP_ENABLE(0),
|
||||
.ID_ENABLE(0),
|
||||
.DEST_ENABLE(0),
|
||||
.USER_ENABLE(1),
|
||||
.USER_WIDTH(1),
|
||||
.ARB_TYPE("PRIORITY"),
|
||||
.LSB_PRIORITY("HIGH")
|
||||
)
|
||||
ip_arb_mux_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// IP frame inputs
|
||||
.s_ip_hdr_valid({s_ip_hdr_valid, udp_tx_ip_hdr_valid}),
|
||||
.s_ip_hdr_ready({s_ip_hdr_ready, udp_tx_ip_hdr_ready}),
|
||||
.s_eth_dest_mac(0),
|
||||
.s_eth_src_mac(0),
|
||||
.s_eth_type(0),
|
||||
.s_ip_version(0),
|
||||
.s_ip_ihl(0),
|
||||
.s_ip_dscp({s_ip_dscp, udp_tx_ip_dscp}),
|
||||
.s_ip_ecn({s_ip_ecn, udp_tx_ip_ecn}),
|
||||
.s_ip_length({s_ip_length, udp_tx_ip_length}),
|
||||
.s_ip_identification(0),
|
||||
.s_ip_flags(0),
|
||||
.s_ip_fragment_offset(0),
|
||||
.s_ip_ttl({s_ip_ttl, udp_tx_ip_ttl}),
|
||||
.s_ip_protocol({s_ip_protocol, udp_tx_ip_protocol}),
|
||||
.s_ip_header_checksum(0),
|
||||
.s_ip_source_ip({s_ip_source_ip, udp_tx_ip_source_ip}),
|
||||
.s_ip_dest_ip({s_ip_dest_ip, udp_tx_ip_dest_ip}),
|
||||
.s_ip_payload_axis_tdata({s_ip_payload_axis_tdata, udp_tx_ip_payload_axis_tdata}),
|
||||
.s_ip_payload_axis_tkeep(0),
|
||||
.s_ip_payload_axis_tvalid({s_ip_payload_axis_tvalid, udp_tx_ip_payload_axis_tvalid}),
|
||||
.s_ip_payload_axis_tready({s_ip_payload_axis_tready, udp_tx_ip_payload_axis_tready}),
|
||||
.s_ip_payload_axis_tlast({s_ip_payload_axis_tlast, udp_tx_ip_payload_axis_tlast}),
|
||||
.s_ip_payload_axis_tid(0),
|
||||
.s_ip_payload_axis_tdest(0),
|
||||
.s_ip_payload_axis_tuser({s_ip_payload_axis_tuser, udp_tx_ip_payload_axis_tuser}),
|
||||
// IP frame output
|
||||
.m_ip_hdr_valid(ip_tx_ip_hdr_valid),
|
||||
.m_ip_hdr_ready(ip_tx_ip_hdr_ready),
|
||||
.m_eth_dest_mac(),
|
||||
.m_eth_src_mac(),
|
||||
.m_eth_type(),
|
||||
.m_ip_version(),
|
||||
.m_ip_ihl(),
|
||||
.m_ip_dscp(ip_tx_ip_dscp),
|
||||
.m_ip_ecn(ip_tx_ip_ecn),
|
||||
.m_ip_length(ip_tx_ip_length),
|
||||
.m_ip_identification(),
|
||||
.m_ip_flags(),
|
||||
.m_ip_fragment_offset(),
|
||||
.m_ip_ttl(ip_tx_ip_ttl),
|
||||
.m_ip_protocol(ip_tx_ip_protocol),
|
||||
.m_ip_header_checksum(),
|
||||
.m_ip_source_ip(ip_tx_ip_source_ip),
|
||||
.m_ip_dest_ip(ip_tx_ip_dest_ip),
|
||||
.m_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata),
|
||||
.m_ip_payload_axis_tkeep(),
|
||||
.m_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid),
|
||||
.m_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready),
|
||||
.m_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast),
|
||||
.m_ip_payload_axis_tid(),
|
||||
.m_ip_payload_axis_tdest(),
|
||||
.m_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser)
|
||||
);
|
||||
|
||||
/*
|
||||
* IP stack
|
||||
*/
|
||||
ip_complete #(
|
||||
.ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH),
|
||||
.ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT),
|
||||
.ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL),
|
||||
.ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT)
|
||||
)
|
||||
ip_complete_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// Ethernet frame input
|
||||
.s_eth_hdr_valid(s_eth_hdr_valid),
|
||||
.s_eth_hdr_ready(s_eth_hdr_ready),
|
||||
.s_eth_dest_mac(s_eth_dest_mac),
|
||||
.s_eth_src_mac(s_eth_src_mac),
|
||||
.s_eth_type(s_eth_type),
|
||||
.s_eth_payload_axis_tdata(s_eth_payload_axis_tdata),
|
||||
.s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid),
|
||||
.s_eth_payload_axis_tready(s_eth_payload_axis_tready),
|
||||
.s_eth_payload_axis_tlast(s_eth_payload_axis_tlast),
|
||||
.s_eth_payload_axis_tuser(s_eth_payload_axis_tuser),
|
||||
// Ethernet frame output
|
||||
.m_eth_hdr_valid(m_eth_hdr_valid),
|
||||
.m_eth_hdr_ready(m_eth_hdr_ready),
|
||||
.m_eth_dest_mac(m_eth_dest_mac),
|
||||
.m_eth_src_mac(m_eth_src_mac),
|
||||
.m_eth_type(m_eth_type),
|
||||
.m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
|
||||
.m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
|
||||
.m_eth_payload_axis_tready(m_eth_payload_axis_tready),
|
||||
.m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
|
||||
.m_eth_payload_axis_tuser(m_eth_payload_axis_tuser),
|
||||
// IP frame input
|
||||
.s_ip_hdr_valid(ip_tx_ip_hdr_valid),
|
||||
.s_ip_hdr_ready(ip_tx_ip_hdr_ready),
|
||||
.s_ip_dscp(ip_tx_ip_dscp),
|
||||
.s_ip_ecn(ip_tx_ip_ecn),
|
||||
.s_ip_length(ip_tx_ip_length),
|
||||
.s_ip_ttl(ip_tx_ip_ttl),
|
||||
.s_ip_protocol(ip_tx_ip_protocol),
|
||||
.s_ip_source_ip(ip_tx_ip_source_ip),
|
||||
.s_ip_dest_ip(ip_tx_ip_dest_ip),
|
||||
.s_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata),
|
||||
.s_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid),
|
||||
.s_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready),
|
||||
.s_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast),
|
||||
.s_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser),
|
||||
// IP frame output
|
||||
.m_ip_hdr_valid(ip_rx_ip_hdr_valid),
|
||||
.m_ip_hdr_ready(ip_rx_ip_hdr_ready),
|
||||
.m_ip_eth_dest_mac(ip_rx_ip_eth_dest_mac),
|
||||
.m_ip_eth_src_mac(ip_rx_ip_eth_src_mac),
|
||||
.m_ip_eth_type(ip_rx_ip_eth_type),
|
||||
.m_ip_version(ip_rx_ip_version),
|
||||
.m_ip_ihl(ip_rx_ip_ihl),
|
||||
.m_ip_dscp(ip_rx_ip_dscp),
|
||||
.m_ip_ecn(ip_rx_ip_ecn),
|
||||
.m_ip_length(ip_rx_ip_length),
|
||||
.m_ip_identification(ip_rx_ip_identification),
|
||||
.m_ip_flags(ip_rx_ip_flags),
|
||||
.m_ip_fragment_offset(ip_rx_ip_fragment_offset),
|
||||
.m_ip_ttl(ip_rx_ip_ttl),
|
||||
.m_ip_protocol(ip_rx_ip_protocol),
|
||||
.m_ip_header_checksum(ip_rx_ip_header_checksum),
|
||||
.m_ip_source_ip(ip_rx_ip_source_ip),
|
||||
.m_ip_dest_ip(ip_rx_ip_dest_ip),
|
||||
.m_ip_payload_axis_tdata(ip_rx_ip_payload_axis_tdata),
|
||||
.m_ip_payload_axis_tvalid(ip_rx_ip_payload_axis_tvalid),
|
||||
.m_ip_payload_axis_tready(ip_rx_ip_payload_axis_tready),
|
||||
.m_ip_payload_axis_tlast(ip_rx_ip_payload_axis_tlast),
|
||||
.m_ip_payload_axis_tuser(ip_rx_ip_payload_axis_tuser),
|
||||
// Status
|
||||
.rx_busy(ip_rx_busy),
|
||||
.tx_busy(ip_tx_busy),
|
||||
.rx_error_header_early_termination(ip_rx_error_header_early_termination),
|
||||
.rx_error_payload_early_termination(ip_rx_error_payload_early_termination),
|
||||
.rx_error_invalid_header(ip_rx_error_invalid_header),
|
||||
.rx_error_invalid_checksum(ip_rx_error_invalid_checksum),
|
||||
.tx_error_payload_early_termination(ip_tx_error_payload_early_termination),
|
||||
.tx_error_arp_failed(ip_tx_error_arp_failed),
|
||||
// Configuration
|
||||
.local_mac(local_mac),
|
||||
.local_ip(local_ip),
|
||||
.gateway_ip(gateway_ip),
|
||||
.subnet_mask(subnet_mask),
|
||||
.clear_arp_cache(clear_arp_cache)
|
||||
);
|
||||
|
||||
/*
|
||||
* UDP interface
|
||||
*/
|
||||
udp #(
|
||||
.CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE),
|
||||
.CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH(UDP_CHECKSUM_PAYLOAD_FIFO_ADDR_WIDTH),
|
||||
.CHECKSUM_HEADER_FIFO_ADDR_WIDTH(UDP_CHECKSUM_HEADER_FIFO_ADDR_WIDTH)
|
||||
)
|
||||
udp_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// IP frame input
|
||||
.s_ip_hdr_valid(udp_rx_ip_hdr_valid),
|
||||
.s_ip_hdr_ready(udp_rx_ip_hdr_ready),
|
||||
.s_ip_eth_dest_mac(udp_rx_ip_eth_dest_mac),
|
||||
.s_ip_eth_src_mac(udp_rx_ip_eth_src_mac),
|
||||
.s_ip_eth_type(udp_rx_ip_eth_type),
|
||||
.s_ip_version(udp_rx_ip_version),
|
||||
.s_ip_ihl(udp_rx_ip_ihl),
|
||||
.s_ip_dscp(udp_rx_ip_dscp),
|
||||
.s_ip_ecn(udp_rx_ip_ecn),
|
||||
.s_ip_length(udp_rx_ip_length),
|
||||
.s_ip_identification(udp_rx_ip_identification),
|
||||
.s_ip_flags(udp_rx_ip_flags),
|
||||
.s_ip_fragment_offset(udp_rx_ip_fragment_offset),
|
||||
.s_ip_ttl(udp_rx_ip_ttl),
|
||||
.s_ip_protocol(udp_rx_ip_protocol),
|
||||
.s_ip_header_checksum(udp_rx_ip_header_checksum),
|
||||
.s_ip_source_ip(udp_rx_ip_source_ip),
|
||||
.s_ip_dest_ip(udp_rx_ip_dest_ip),
|
||||
.s_ip_payload_axis_tdata(udp_rx_ip_payload_axis_tdata),
|
||||
.s_ip_payload_axis_tvalid(udp_rx_ip_payload_axis_tvalid),
|
||||
.s_ip_payload_axis_tready(udp_rx_ip_payload_axis_tready),
|
||||
.s_ip_payload_axis_tlast(udp_rx_ip_payload_axis_tlast),
|
||||
.s_ip_payload_axis_tuser(udp_rx_ip_payload_axis_tuser),
|
||||
// IP frame output
|
||||
.m_ip_hdr_valid(udp_tx_ip_hdr_valid),
|
||||
.m_ip_hdr_ready(udp_tx_ip_hdr_ready),
|
||||
.m_ip_eth_dest_mac(),
|
||||
.m_ip_eth_src_mac(),
|
||||
.m_ip_eth_type(),
|
||||
.m_ip_version(),
|
||||
.m_ip_ihl(),
|
||||
.m_ip_dscp(udp_tx_ip_dscp),
|
||||
.m_ip_ecn(udp_tx_ip_ecn),
|
||||
.m_ip_length(udp_tx_ip_length),
|
||||
.m_ip_identification(),
|
||||
.m_ip_flags(),
|
||||
.m_ip_fragment_offset(),
|
||||
.m_ip_ttl(udp_tx_ip_ttl),
|
||||
.m_ip_protocol(udp_tx_ip_protocol),
|
||||
.m_ip_header_checksum(),
|
||||
.m_ip_source_ip(udp_tx_ip_source_ip),
|
||||
.m_ip_dest_ip(udp_tx_ip_dest_ip),
|
||||
.m_ip_payload_axis_tdata(udp_tx_ip_payload_axis_tdata),
|
||||
.m_ip_payload_axis_tvalid(udp_tx_ip_payload_axis_tvalid),
|
||||
.m_ip_payload_axis_tready(udp_tx_ip_payload_axis_tready),
|
||||
.m_ip_payload_axis_tlast(udp_tx_ip_payload_axis_tlast),
|
||||
.m_ip_payload_axis_tuser(udp_tx_ip_payload_axis_tuser),
|
||||
// UDP frame input
|
||||
.s_udp_hdr_valid(s_udp_hdr_valid),
|
||||
.s_udp_hdr_ready(s_udp_hdr_ready),
|
||||
.s_udp_eth_dest_mac(48'd0),
|
||||
.s_udp_eth_src_mac(48'd0),
|
||||
.s_udp_eth_type(16'd0),
|
||||
.s_udp_ip_version(4'd0),
|
||||
.s_udp_ip_ihl(4'd0),
|
||||
.s_udp_ip_dscp(s_udp_ip_dscp),
|
||||
.s_udp_ip_ecn(s_udp_ip_ecn),
|
||||
.s_udp_ip_identification(16'd0),
|
||||
.s_udp_ip_flags(3'd0),
|
||||
.s_udp_ip_fragment_offset(13'd0),
|
||||
.s_udp_ip_ttl(s_udp_ip_ttl),
|
||||
.s_udp_ip_header_checksum(16'd0),
|
||||
.s_udp_ip_source_ip(s_udp_ip_source_ip),
|
||||
.s_udp_ip_dest_ip(s_udp_ip_dest_ip),
|
||||
.s_udp_source_port(s_udp_source_port),
|
||||
.s_udp_dest_port(s_udp_dest_port),
|
||||
.s_udp_length(s_udp_length),
|
||||
.s_udp_checksum(s_udp_checksum),
|
||||
.s_udp_payload_axis_tdata(s_udp_payload_axis_tdata),
|
||||
.s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid),
|
||||
.s_udp_payload_axis_tready(s_udp_payload_axis_tready),
|
||||
.s_udp_payload_axis_tlast(s_udp_payload_axis_tlast),
|
||||
.s_udp_payload_axis_tuser(s_udp_payload_axis_tuser),
|
||||
// UDP frame output
|
||||
.m_udp_hdr_valid(m_udp_hdr_valid),
|
||||
.m_udp_hdr_ready(m_udp_hdr_ready),
|
||||
.m_udp_eth_dest_mac(m_udp_eth_dest_mac),
|
||||
.m_udp_eth_src_mac(m_udp_eth_src_mac),
|
||||
.m_udp_eth_type(m_udp_eth_type),
|
||||
.m_udp_ip_version(m_udp_ip_version),
|
||||
.m_udp_ip_ihl(m_udp_ip_ihl),
|
||||
.m_udp_ip_dscp(m_udp_ip_dscp),
|
||||
.m_udp_ip_ecn(m_udp_ip_ecn),
|
||||
.m_udp_ip_length(m_udp_ip_length),
|
||||
.m_udp_ip_identification(m_udp_ip_identification),
|
||||
.m_udp_ip_flags(m_udp_ip_flags),
|
||||
.m_udp_ip_fragment_offset(m_udp_ip_fragment_offset),
|
||||
.m_udp_ip_ttl(m_udp_ip_ttl),
|
||||
.m_udp_ip_protocol(m_udp_ip_protocol),
|
||||
.m_udp_ip_header_checksum(m_udp_ip_header_checksum),
|
||||
.m_udp_ip_source_ip(m_udp_ip_source_ip),
|
||||
.m_udp_ip_dest_ip(m_udp_ip_dest_ip),
|
||||
.m_udp_source_port(m_udp_source_port),
|
||||
.m_udp_dest_port(m_udp_dest_port),
|
||||
.m_udp_length(m_udp_length),
|
||||
.m_udp_checksum(m_udp_checksum),
|
||||
.m_udp_payload_axis_tdata(m_udp_payload_axis_tdata),
|
||||
.m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid),
|
||||
.m_udp_payload_axis_tready(m_udp_payload_axis_tready),
|
||||
.m_udp_payload_axis_tlast(m_udp_payload_axis_tlast),
|
||||
.m_udp_payload_axis_tuser(m_udp_payload_axis_tuser),
|
||||
// Status
|
||||
.rx_busy(udp_rx_busy),
|
||||
.tx_busy(udp_tx_busy),
|
||||
.rx_error_header_early_termination(udp_rx_error_header_early_termination),
|
||||
.rx_error_payload_early_termination(udp_rx_error_payload_early_termination),
|
||||
.tx_error_payload_early_termination(udp_tx_error_payload_early_termination)
|
||||
);
|
||||
|
||||
endmodule
|
528
fpga/src/udp_ip_rx.sv
Normal file
528
fpga/src/udp_ip_rx.sv
Normal file
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* UDP ethernet frame receiver (IP frame in, UDP frame out)
|
||||
*/
|
||||
module udp_ip_rx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* IP frame input
|
||||
*/
|
||||
input wire s_ip_hdr_valid,
|
||||
output wire s_ip_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [3:0] s_ip_version,
|
||||
input wire [3:0] s_ip_ihl,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_length,
|
||||
input wire [15:0] s_ip_identification,
|
||||
input wire [2:0] s_ip_flags,
|
||||
input wire [12:0] s_ip_fragment_offset,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [7:0] s_ip_protocol,
|
||||
input wire [15:0] s_ip_header_checksum,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [7:0] s_ip_payload_axis_tdata,
|
||||
input wire s_ip_payload_axis_tvalid,
|
||||
output wire s_ip_payload_axis_tready,
|
||||
input wire s_ip_payload_axis_tlast,
|
||||
input wire s_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* UDP frame output
|
||||
*/
|
||||
output wire m_udp_hdr_valid,
|
||||
input wire m_udp_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [15:0] m_udp_source_port,
|
||||
output wire [15:0] m_udp_dest_port,
|
||||
output wire [15:0] m_udp_length,
|
||||
output wire [15:0] m_udp_checksum,
|
||||
output wire [7:0] m_udp_payload_axis_tdata,
|
||||
output wire m_udp_payload_axis_tvalid,
|
||||
input wire m_udp_payload_axis_tready,
|
||||
output wire m_udp_payload_axis_tlast,
|
||||
output wire m_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy,
|
||||
output wire error_header_early_termination,
|
||||
output wire error_payload_early_termination
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
UDP Frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype (0x0800) 2 octets
|
||||
Version (4) 4 bits
|
||||
IHL (5-15) 4 bits
|
||||
DSCP (0) 6 bits
|
||||
ECN (0) 2 bits
|
||||
length 2 octets
|
||||
identification (0?) 2 octets
|
||||
flags (010) 3 bits
|
||||
fragment offset (0) 13 bits
|
||||
time to live (64?) 1 octet
|
||||
protocol 1 octet
|
||||
header checksum 2 octets
|
||||
source IP 4 octets
|
||||
destination IP 4 octets
|
||||
options (IHL-5)*4 octets
|
||||
|
||||
source port 2 octets
|
||||
desination port 2 octets
|
||||
length 2 octets
|
||||
checksum 2 octets
|
||||
|
||||
payload length octets
|
||||
|
||||
This module receives an IP frame with header fields in parallel and payload on
|
||||
an AXI stream interface, decodes and strips the UDP header fields, then
|
||||
produces the header fields in parallel along with the UDP payload in a
|
||||
separate AXI stream.
|
||||
|
||||
*/
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_READ_HEADER = 3'd1,
|
||||
STATE_READ_PAYLOAD = 3'd2,
|
||||
STATE_READ_PAYLOAD_LAST = 3'd3,
|
||||
STATE_WAIT_LAST = 3'd4;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_ip_hdr;
|
||||
reg store_udp_source_port_0;
|
||||
reg store_udp_source_port_1;
|
||||
reg store_udp_dest_port_0;
|
||||
reg store_udp_dest_port_1;
|
||||
reg store_udp_length_0;
|
||||
reg store_udp_length_1;
|
||||
reg store_udp_checksum_0;
|
||||
reg store_udp_checksum_1;
|
||||
reg store_last_word;
|
||||
|
||||
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
|
||||
|
||||
reg [7:0] last_word_data_reg = 8'd0;
|
||||
|
||||
reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
reg [3:0] m_ip_version_reg = 4'd0;
|
||||
reg [3:0] m_ip_ihl_reg = 4'd0;
|
||||
reg [5:0] m_ip_dscp_reg = 6'd0;
|
||||
reg [1:0] m_ip_ecn_reg = 2'd0;
|
||||
reg [15:0] m_ip_length_reg = 16'd0;
|
||||
reg [15:0] m_ip_identification_reg = 16'd0;
|
||||
reg [2:0] m_ip_flags_reg = 3'd0;
|
||||
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
|
||||
reg [7:0] m_ip_ttl_reg = 8'd0;
|
||||
reg [7:0] m_ip_protocol_reg = 8'd0;
|
||||
reg [15:0] m_ip_header_checksum_reg = 16'd0;
|
||||
reg [31:0] m_ip_source_ip_reg = 32'd0;
|
||||
reg [31:0] m_ip_dest_ip_reg = 32'd0;
|
||||
reg [15:0] m_udp_source_port_reg = 16'd0;
|
||||
reg [15:0] m_udp_dest_port_reg = 16'd0;
|
||||
reg [15:0] m_udp_length_reg = 16'd0;
|
||||
reg [15:0] m_udp_checksum_reg = 16'd0;
|
||||
|
||||
reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next;
|
||||
reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next;
|
||||
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] m_udp_payload_axis_tdata_int;
|
||||
reg m_udp_payload_axis_tvalid_int;
|
||||
reg m_udp_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_udp_payload_axis_tlast_int;
|
||||
reg m_udp_payload_axis_tuser_int;
|
||||
wire m_udp_payload_axis_tready_int_early;
|
||||
|
||||
assign s_ip_hdr_ready = s_ip_hdr_ready_reg;
|
||||
assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg;
|
||||
|
||||
assign m_udp_hdr_valid = m_udp_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
assign m_ip_version = m_ip_version_reg;
|
||||
assign m_ip_ihl = m_ip_ihl_reg;
|
||||
assign m_ip_dscp = m_ip_dscp_reg;
|
||||
assign m_ip_ecn = m_ip_ecn_reg;
|
||||
assign m_ip_length = m_ip_length_reg;
|
||||
assign m_ip_identification = m_ip_identification_reg;
|
||||
assign m_ip_flags = m_ip_flags_reg;
|
||||
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
|
||||
assign m_ip_ttl = m_ip_ttl_reg;
|
||||
assign m_ip_protocol = m_ip_protocol_reg;
|
||||
assign m_ip_header_checksum = m_ip_header_checksum_reg;
|
||||
assign m_ip_source_ip = m_ip_source_ip_reg;
|
||||
assign m_ip_dest_ip = m_ip_dest_ip_reg;
|
||||
assign m_udp_source_port = m_udp_source_port_reg;
|
||||
assign m_udp_dest_port = m_udp_dest_port_reg;
|
||||
assign m_udp_length = m_udp_length_reg;
|
||||
assign m_udp_checksum = m_udp_checksum_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
assign error_header_early_termination = error_header_early_termination_reg;
|
||||
assign error_payload_early_termination = error_payload_early_termination_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_ip_hdr_ready_next = 1'b0;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
|
||||
store_ip_hdr = 1'b0;
|
||||
store_udp_source_port_0 = 1'b0;
|
||||
store_udp_source_port_1 = 1'b0;
|
||||
store_udp_dest_port_0 = 1'b0;
|
||||
store_udp_dest_port_1 = 1'b0;
|
||||
store_udp_length_0 = 1'b0;
|
||||
store_udp_length_1 = 1'b0;
|
||||
store_udp_checksum_0 = 1'b0;
|
||||
store_udp_checksum_1 = 1'b0;
|
||||
|
||||
store_last_word = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready;
|
||||
|
||||
error_header_early_termination_next = 1'b0;
|
||||
error_payload_early_termination_next = 1'b0;
|
||||
|
||||
m_udp_payload_axis_tdata_int = 8'd0;
|
||||
m_udp_payload_axis_tvalid_int = 1'b0;
|
||||
m_udp_payload_axis_tlast_int = 1'b0;
|
||||
m_udp_payload_axis_tuser_int = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for header
|
||||
frame_ptr_next = 16'd0;
|
||||
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
|
||||
|
||||
if (s_ip_hdr_ready && s_ip_hdr_valid) begin
|
||||
s_ip_hdr_ready_next = 1'b0;
|
||||
s_ip_payload_axis_tready_next = 1'b1;
|
||||
store_ip_hdr = 1'b1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_READ_HEADER: begin
|
||||
// read header state
|
||||
s_ip_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
// word transfer in - store it
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
state_next = STATE_READ_HEADER;
|
||||
|
||||
case (frame_ptr_reg)
|
||||
8'h00: store_udp_source_port_1 = 1'b1;
|
||||
8'h01: store_udp_source_port_0 = 1'b1;
|
||||
8'h02: store_udp_dest_port_1 = 1'b1;
|
||||
8'h03: store_udp_dest_port_0 = 1'b1;
|
||||
8'h04: store_udp_length_1 = 1'b1;
|
||||
8'h05: store_udp_length_0 = 1'b1;
|
||||
8'h06: store_udp_checksum_1 = 1'b1;
|
||||
8'h07: begin
|
||||
store_udp_checksum_0 = 1'b1;
|
||||
m_udp_hdr_valid_next = 1'b1;
|
||||
s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early;
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (s_ip_payload_axis_tlast) begin
|
||||
error_header_early_termination_next = 1'b1;
|
||||
m_udp_hdr_valid_next = 1'b0;
|
||||
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
|
||||
end else begin
|
||||
state_next = STATE_READ_HEADER;
|
||||
end
|
||||
end
|
||||
STATE_READ_PAYLOAD: begin
|
||||
// read payload
|
||||
s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early;
|
||||
|
||||
m_udp_payload_axis_tdata_int = s_ip_payload_axis_tdata;
|
||||
m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid;
|
||||
m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast;
|
||||
m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser;
|
||||
|
||||
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
// word transfer through
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
if (s_ip_payload_axis_tlast) begin
|
||||
if (frame_ptr_next != m_udp_length_reg) begin
|
||||
// end of frame, but length does not match
|
||||
m_udp_payload_axis_tuser_int = 1'b1;
|
||||
error_payload_early_termination_next = 1'b1;
|
||||
end
|
||||
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
if (frame_ptr_next == m_udp_length_reg) begin
|
||||
store_last_word = 1'b1;
|
||||
m_udp_payload_axis_tvalid_int = 1'b0;
|
||||
state_next = STATE_READ_PAYLOAD_LAST;
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD;
|
||||
end
|
||||
end
|
||||
STATE_READ_PAYLOAD_LAST: begin
|
||||
// read and discard until end of frame
|
||||
s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early;
|
||||
|
||||
m_udp_payload_axis_tdata_int = last_word_data_reg;
|
||||
m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast;
|
||||
m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast;
|
||||
m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser;
|
||||
|
||||
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
if (s_ip_payload_axis_tlast) begin
|
||||
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_READ_PAYLOAD_LAST;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_LAST: begin
|
||||
// wait for end of frame; read and discard
|
||||
s_ip_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
|
||||
if (s_ip_payload_axis_tlast) begin
|
||||
s_ip_hdr_ready_next = !m_udp_hdr_valid_next;
|
||||
s_ip_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 16'd0;
|
||||
s_ip_hdr_ready_reg <= 1'b0;
|
||||
s_ip_payload_axis_tready_reg <= 1'b0;
|
||||
m_udp_hdr_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
error_header_early_termination_reg <= 1'b0;
|
||||
error_payload_early_termination_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
s_ip_hdr_ready_reg <= s_ip_hdr_ready_next;
|
||||
s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next;
|
||||
|
||||
m_udp_hdr_valid_reg <= m_udp_hdr_valid_next;
|
||||
|
||||
error_header_early_termination_reg <= error_header_early_termination_next;
|
||||
error_payload_early_termination_reg <= error_payload_early_termination_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_ip_hdr) begin
|
||||
m_eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
m_eth_src_mac_reg <= s_eth_src_mac;
|
||||
m_eth_type_reg <= s_eth_type;
|
||||
m_ip_version_reg <= s_ip_version;
|
||||
m_ip_ihl_reg <= s_ip_ihl;
|
||||
m_ip_dscp_reg <= s_ip_dscp;
|
||||
m_ip_ecn_reg <= s_ip_ecn;
|
||||
m_ip_length_reg <= s_ip_length;
|
||||
m_ip_identification_reg <= s_ip_identification;
|
||||
m_ip_flags_reg <= s_ip_flags;
|
||||
m_ip_fragment_offset_reg <= s_ip_fragment_offset;
|
||||
m_ip_ttl_reg <= s_ip_ttl;
|
||||
m_ip_protocol_reg <= s_ip_protocol;
|
||||
m_ip_header_checksum_reg <= s_ip_header_checksum;
|
||||
m_ip_source_ip_reg <= s_ip_source_ip;
|
||||
m_ip_dest_ip_reg <= s_ip_dest_ip;
|
||||
end
|
||||
|
||||
if (store_last_word) begin
|
||||
last_word_data_reg <= m_udp_payload_axis_tdata_int;
|
||||
end
|
||||
|
||||
if (store_udp_source_port_0) m_udp_source_port_reg[ 7: 0] <= s_ip_payload_axis_tdata;
|
||||
if (store_udp_source_port_1) m_udp_source_port_reg[15: 8] <= s_ip_payload_axis_tdata;
|
||||
if (store_udp_dest_port_0) m_udp_dest_port_reg[ 7: 0] <= s_ip_payload_axis_tdata;
|
||||
if (store_udp_dest_port_1) m_udp_dest_port_reg[15: 8] <= s_ip_payload_axis_tdata;
|
||||
if (store_udp_length_0) m_udp_length_reg[ 7: 0] <= s_ip_payload_axis_tdata;
|
||||
if (store_udp_length_1) m_udp_length_reg[15: 8] <= s_ip_payload_axis_tdata;
|
||||
if (store_udp_checksum_0) m_udp_checksum_reg[ 7: 0] <= s_ip_payload_axis_tdata;
|
||||
if (store_udp_checksum_1) m_udp_checksum_reg[15: 8] <= s_ip_payload_axis_tdata;
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] m_udp_payload_axis_tdata_reg = 8'd0;
|
||||
reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next;
|
||||
reg m_udp_payload_axis_tlast_reg = 1'b0;
|
||||
reg m_udp_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_m_udp_payload_axis_tdata_reg = 8'd0;
|
||||
reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next;
|
||||
reg temp_m_udp_payload_axis_tlast_reg = 1'b0;
|
||||
reg temp_m_udp_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_udp_payload_int_to_output;
|
||||
reg store_udp_payload_int_to_temp;
|
||||
reg store_udp_payload_axis_temp_to_output;
|
||||
|
||||
assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg;
|
||||
assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg;
|
||||
assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg;
|
||||
assign m_udp_payload_axis_tuser = m_udp_payload_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg;
|
||||
temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg;
|
||||
|
||||
store_udp_payload_int_to_output = 1'b0;
|
||||
store_udp_payload_int_to_temp = 1'b0;
|
||||
store_udp_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_udp_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int;
|
||||
store_udp_payload_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int;
|
||||
store_udp_payload_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_udp_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg;
|
||||
temp_m_udp_payload_axis_tvalid_next = 1'b0;
|
||||
store_udp_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_udp_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_udp_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_udp_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next;
|
||||
m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early;
|
||||
temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_udp_payload_int_to_output) begin
|
||||
m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int;
|
||||
m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int;
|
||||
m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int;
|
||||
end else if (store_udp_payload_axis_temp_to_output) begin
|
||||
m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg;
|
||||
m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg;
|
||||
m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_udp_payload_int_to_temp) begin
|
||||
temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int;
|
||||
temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int;
|
||||
temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
489
fpga/src/udp_ip_tx.sv
Normal file
489
fpga/src/udp_ip_tx.sv
Normal file
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2014-2018 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* UDP ethernet frame transmitter (UDP frame in, IP frame out)
|
||||
*/
|
||||
module udp_ip_tx
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* UDP frame input
|
||||
*/
|
||||
input wire s_udp_hdr_valid,
|
||||
output wire s_udp_hdr_ready,
|
||||
input wire [47:0] s_eth_dest_mac,
|
||||
input wire [47:0] s_eth_src_mac,
|
||||
input wire [15:0] s_eth_type,
|
||||
input wire [3:0] s_ip_version,
|
||||
input wire [3:0] s_ip_ihl,
|
||||
input wire [5:0] s_ip_dscp,
|
||||
input wire [1:0] s_ip_ecn,
|
||||
input wire [15:0] s_ip_identification,
|
||||
input wire [2:0] s_ip_flags,
|
||||
input wire [12:0] s_ip_fragment_offset,
|
||||
input wire [7:0] s_ip_ttl,
|
||||
input wire [7:0] s_ip_protocol,
|
||||
input wire [15:0] s_ip_header_checksum,
|
||||
input wire [31:0] s_ip_source_ip,
|
||||
input wire [31:0] s_ip_dest_ip,
|
||||
input wire [15:0] s_udp_source_port,
|
||||
input wire [15:0] s_udp_dest_port,
|
||||
input wire [15:0] s_udp_length,
|
||||
input wire [15:0] s_udp_checksum,
|
||||
input wire [7:0] s_udp_payload_axis_tdata,
|
||||
input wire s_udp_payload_axis_tvalid,
|
||||
output wire s_udp_payload_axis_tready,
|
||||
input wire s_udp_payload_axis_tlast,
|
||||
input wire s_udp_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* IP frame output
|
||||
*/
|
||||
output wire m_ip_hdr_valid,
|
||||
input wire m_ip_hdr_ready,
|
||||
output wire [47:0] m_eth_dest_mac,
|
||||
output wire [47:0] m_eth_src_mac,
|
||||
output wire [15:0] m_eth_type,
|
||||
output wire [3:0] m_ip_version,
|
||||
output wire [3:0] m_ip_ihl,
|
||||
output wire [5:0] m_ip_dscp,
|
||||
output wire [1:0] m_ip_ecn,
|
||||
output wire [15:0] m_ip_length,
|
||||
output wire [15:0] m_ip_identification,
|
||||
output wire [2:0] m_ip_flags,
|
||||
output wire [12:0] m_ip_fragment_offset,
|
||||
output wire [7:0] m_ip_ttl,
|
||||
output wire [7:0] m_ip_protocol,
|
||||
output wire [15:0] m_ip_header_checksum,
|
||||
output wire [31:0] m_ip_source_ip,
|
||||
output wire [31:0] m_ip_dest_ip,
|
||||
output wire [7:0] m_ip_payload_axis_tdata,
|
||||
output wire m_ip_payload_axis_tvalid,
|
||||
input wire m_ip_payload_axis_tready,
|
||||
output wire m_ip_payload_axis_tlast,
|
||||
output wire m_ip_payload_axis_tuser,
|
||||
|
||||
/*
|
||||
* Status signals
|
||||
*/
|
||||
output wire busy,
|
||||
output wire error_payload_early_termination
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
UDP Frame
|
||||
|
||||
Field Length
|
||||
Destination MAC address 6 octets
|
||||
Source MAC address 6 octets
|
||||
Ethertype (0x0800) 2 octets
|
||||
Version (4) 4 bits
|
||||
IHL (5-15) 4 bits
|
||||
DSCP (0) 6 bits
|
||||
ECN (0) 2 bits
|
||||
length 2 octets
|
||||
identification (0?) 2 octets
|
||||
flags (010) 3 bits
|
||||
fragment offset (0) 13 bits
|
||||
time to live (64?) 1 octet
|
||||
protocol 1 octet
|
||||
header checksum 2 octets
|
||||
source IP 4 octets
|
||||
destination IP 4 octets
|
||||
options (IHL-5)*4 octets
|
||||
|
||||
source port 2 octets
|
||||
desination port 2 octets
|
||||
length 2 octets
|
||||
checksum 2 octets
|
||||
|
||||
payload length octets
|
||||
|
||||
This module receives a UDP frame with header fields in parallel along with the
|
||||
payload in an AXI stream, combines the header with the payload, passes through
|
||||
the IP headers, and transmits the complete IP payload on an AXI interface.
|
||||
|
||||
*/
|
||||
|
||||
localparam [2:0]
|
||||
STATE_IDLE = 3'd0,
|
||||
STATE_WRITE_HEADER = 3'd1,
|
||||
STATE_WRITE_PAYLOAD = 3'd2,
|
||||
STATE_WRITE_PAYLOAD_LAST = 3'd3,
|
||||
STATE_WAIT_LAST = 3'd4;
|
||||
|
||||
reg [2:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
// datapath control signals
|
||||
reg store_udp_hdr;
|
||||
reg store_last_word;
|
||||
|
||||
reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next;
|
||||
|
||||
reg [7:0] last_word_data_reg = 8'd0;
|
||||
|
||||
reg [15:0] udp_source_port_reg = 16'd0;
|
||||
reg [15:0] udp_dest_port_reg = 16'd0;
|
||||
reg [15:0] udp_length_reg = 16'd0;
|
||||
reg [15:0] udp_checksum_reg = 16'd0;
|
||||
|
||||
reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next;
|
||||
reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next;
|
||||
|
||||
reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next;
|
||||
reg [47:0] m_eth_dest_mac_reg = 48'd0;
|
||||
reg [47:0] m_eth_src_mac_reg = 48'd0;
|
||||
reg [15:0] m_eth_type_reg = 16'd0;
|
||||
reg [3:0] m_ip_version_reg = 4'd0;
|
||||
reg [3:0] m_ip_ihl_reg = 4'd0;
|
||||
reg [5:0] m_ip_dscp_reg = 6'd0;
|
||||
reg [1:0] m_ip_ecn_reg = 2'd0;
|
||||
reg [15:0] m_ip_length_reg = 16'd0;
|
||||
reg [15:0] m_ip_identification_reg = 16'd0;
|
||||
reg [2:0] m_ip_flags_reg = 3'd0;
|
||||
reg [12:0] m_ip_fragment_offset_reg = 13'd0;
|
||||
reg [7:0] m_ip_ttl_reg = 8'd0;
|
||||
reg [7:0] m_ip_protocol_reg = 8'd0;
|
||||
reg [15:0] m_ip_header_checksum_reg = 16'd0;
|
||||
reg [31:0] m_ip_source_ip_reg = 32'd0;
|
||||
reg [31:0] m_ip_dest_ip_reg = 32'd0;
|
||||
|
||||
reg busy_reg = 1'b0;
|
||||
reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] m_ip_payload_axis_tdata_int;
|
||||
reg m_ip_payload_axis_tvalid_int;
|
||||
reg m_ip_payload_axis_tready_int_reg = 1'b0;
|
||||
reg m_ip_payload_axis_tlast_int;
|
||||
reg m_ip_payload_axis_tuser_int;
|
||||
wire m_ip_payload_axis_tready_int_early;
|
||||
|
||||
assign s_udp_hdr_ready = s_udp_hdr_ready_reg;
|
||||
assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg;
|
||||
|
||||
assign m_ip_hdr_valid = m_ip_hdr_valid_reg;
|
||||
assign m_eth_dest_mac = m_eth_dest_mac_reg;
|
||||
assign m_eth_src_mac = m_eth_src_mac_reg;
|
||||
assign m_eth_type = m_eth_type_reg;
|
||||
assign m_ip_version = m_ip_version_reg;
|
||||
assign m_ip_ihl = m_ip_ihl_reg;
|
||||
assign m_ip_dscp = m_ip_dscp_reg;
|
||||
assign m_ip_ecn = m_ip_ecn_reg;
|
||||
assign m_ip_length = m_ip_length_reg;
|
||||
assign m_ip_identification = m_ip_identification_reg;
|
||||
assign m_ip_flags = m_ip_flags_reg;
|
||||
assign m_ip_fragment_offset = m_ip_fragment_offset_reg;
|
||||
assign m_ip_ttl = m_ip_ttl_reg;
|
||||
assign m_ip_protocol = m_ip_protocol_reg;
|
||||
assign m_ip_header_checksum = m_ip_header_checksum_reg;
|
||||
assign m_ip_source_ip = m_ip_source_ip_reg;
|
||||
assign m_ip_dest_ip = m_ip_dest_ip_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
assign error_payload_early_termination = error_payload_early_termination_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
s_udp_hdr_ready_next = 1'b0;
|
||||
s_udp_payload_axis_tready_next = 1'b0;
|
||||
|
||||
store_udp_hdr = 1'b0;
|
||||
|
||||
store_last_word = 1'b0;
|
||||
|
||||
frame_ptr_next = frame_ptr_reg;
|
||||
|
||||
m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready;
|
||||
|
||||
error_payload_early_termination_next = 1'b0;
|
||||
|
||||
m_ip_payload_axis_tdata_int = 8'd0;
|
||||
m_ip_payload_axis_tvalid_int = 1'b0;
|
||||
m_ip_payload_axis_tlast_int = 1'b0;
|
||||
m_ip_payload_axis_tuser_int = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state - wait for data
|
||||
frame_ptr_next = 16'd0;
|
||||
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
|
||||
if (s_udp_hdr_ready && s_udp_hdr_valid) begin
|
||||
store_udp_hdr = 1'b1;
|
||||
s_udp_hdr_ready_next = 1'b0;
|
||||
m_ip_hdr_valid_next = 1'b1;
|
||||
if (m_ip_payload_axis_tready_int_reg) begin
|
||||
m_ip_payload_axis_tvalid_int = 1'b1;
|
||||
m_ip_payload_axis_tdata_int = s_udp_source_port[15: 8];
|
||||
frame_ptr_next = 1'b1;
|
||||
end
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_HEADER: begin
|
||||
// write header state
|
||||
if (m_ip_payload_axis_tready_int_reg) begin
|
||||
// word transfer out
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
m_ip_payload_axis_tvalid_int = 1'b1;
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
case (frame_ptr_reg)
|
||||
8'h00: m_ip_payload_axis_tdata_int = udp_source_port_reg[15: 8];
|
||||
8'h01: m_ip_payload_axis_tdata_int = udp_source_port_reg[ 7: 0];
|
||||
8'h02: m_ip_payload_axis_tdata_int = udp_dest_port_reg[15: 8];
|
||||
8'h03: m_ip_payload_axis_tdata_int = udp_dest_port_reg[ 7: 0];
|
||||
8'h04: m_ip_payload_axis_tdata_int = udp_length_reg[15: 8];
|
||||
8'h05: m_ip_payload_axis_tdata_int = udp_length_reg[ 7: 0];
|
||||
8'h06: m_ip_payload_axis_tdata_int = udp_checksum_reg[15: 8];
|
||||
8'h07: begin
|
||||
m_ip_payload_axis_tdata_int = udp_checksum_reg[ 7: 0];
|
||||
s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
endcase
|
||||
end else begin
|
||||
state_next = STATE_WRITE_HEADER;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_PAYLOAD: begin
|
||||
// write payload
|
||||
s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
|
||||
|
||||
m_ip_payload_axis_tdata_int = s_udp_payload_axis_tdata;
|
||||
m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid;
|
||||
m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast;
|
||||
m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser;
|
||||
|
||||
if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin
|
||||
// word transfer through
|
||||
frame_ptr_next = frame_ptr_reg + 16'd1;
|
||||
if (s_udp_payload_axis_tlast) begin
|
||||
if (frame_ptr_next != udp_length_reg) begin
|
||||
// end of frame, but length does not match
|
||||
m_ip_payload_axis_tuser_int = 1'b1;
|
||||
error_payload_early_termination_next = 1'b1;
|
||||
end
|
||||
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
s_udp_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
if (frame_ptr_next == udp_length_reg) begin
|
||||
store_last_word = 1'b1;
|
||||
m_ip_payload_axis_tvalid_int = 1'b0;
|
||||
state_next = STATE_WRITE_PAYLOAD_LAST;
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD;
|
||||
end
|
||||
end
|
||||
STATE_WRITE_PAYLOAD_LAST: begin
|
||||
// read and discard until end of frame
|
||||
s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early;
|
||||
|
||||
m_ip_payload_axis_tdata_int = last_word_data_reg;
|
||||
m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid && s_udp_payload_axis_tlast;
|
||||
m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast;
|
||||
m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser;
|
||||
|
||||
if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin
|
||||
if (s_udp_payload_axis_tlast) begin
|
||||
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
s_udp_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WRITE_PAYLOAD_LAST;
|
||||
end
|
||||
end
|
||||
STATE_WAIT_LAST: begin
|
||||
// wait for end of frame; read and discard
|
||||
s_udp_payload_axis_tready_next = 1'b1;
|
||||
|
||||
if (s_udp_payload_axis_tvalid) begin
|
||||
if (s_udp_payload_axis_tlast) begin
|
||||
s_udp_hdr_ready_next = !m_ip_hdr_valid_next;
|
||||
s_udp_payload_axis_tready_next = 1'b0;
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_WAIT_LAST;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
frame_ptr_reg <= 16'd0;
|
||||
s_udp_hdr_ready_reg <= 1'b0;
|
||||
s_udp_payload_axis_tready_reg <= 1'b0;
|
||||
m_ip_hdr_valid_reg <= 1'b0;
|
||||
busy_reg <= 1'b0;
|
||||
error_payload_early_termination_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
|
||||
frame_ptr_reg <= frame_ptr_next;
|
||||
|
||||
s_udp_hdr_ready_reg <= s_udp_hdr_ready_next;
|
||||
s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next;
|
||||
|
||||
m_ip_hdr_valid_reg <= m_ip_hdr_valid_next;
|
||||
|
||||
busy_reg <= state_next != STATE_IDLE;
|
||||
|
||||
error_payload_early_termination_reg <= error_payload_early_termination_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_udp_hdr) begin
|
||||
m_eth_dest_mac_reg <= s_eth_dest_mac;
|
||||
m_eth_src_mac_reg <= s_eth_src_mac;
|
||||
m_eth_type_reg <= s_eth_type;
|
||||
m_ip_version_reg <= s_ip_version;
|
||||
m_ip_ihl_reg <= s_ip_ihl;
|
||||
m_ip_dscp_reg <= s_ip_dscp;
|
||||
m_ip_ecn_reg <= s_ip_ecn;
|
||||
m_ip_length_reg <= s_udp_length + 20;
|
||||
m_ip_identification_reg <= s_ip_identification;
|
||||
m_ip_flags_reg <= s_ip_flags;
|
||||
m_ip_fragment_offset_reg <= s_ip_fragment_offset;
|
||||
m_ip_ttl_reg <= s_ip_ttl;
|
||||
m_ip_protocol_reg <= s_ip_protocol;
|
||||
m_ip_header_checksum_reg <= s_ip_header_checksum;
|
||||
m_ip_source_ip_reg <= s_ip_source_ip;
|
||||
m_ip_dest_ip_reg <= s_ip_dest_ip;
|
||||
udp_source_port_reg <= s_udp_source_port;
|
||||
udp_dest_port_reg <= s_udp_dest_port;
|
||||
udp_length_reg <= s_udp_length;
|
||||
udp_checksum_reg <= s_udp_checksum;
|
||||
end
|
||||
|
||||
if (store_last_word) begin
|
||||
last_word_data_reg <= m_ip_payload_axis_tdata_int;
|
||||
end
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0;
|
||||
reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next;
|
||||
reg m_ip_payload_axis_tlast_reg = 1'b0;
|
||||
reg m_ip_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0;
|
||||
reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next;
|
||||
reg temp_m_ip_payload_axis_tlast_reg = 1'b0;
|
||||
reg temp_m_ip_payload_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_ip_payload_int_to_output;
|
||||
reg store_ip_payload_int_to_temp;
|
||||
reg store_ip_payload_axis_temp_to_output;
|
||||
|
||||
assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg;
|
||||
assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg;
|
||||
assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg;
|
||||
assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg;
|
||||
temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
|
||||
|
||||
store_ip_payload_int_to_output = 1'b0;
|
||||
store_ip_payload_int_to_temp = 1'b0;
|
||||
store_ip_payload_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (m_ip_payload_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
|
||||
store_ip_payload_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int;
|
||||
store_ip_payload_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (m_ip_payload_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg;
|
||||
temp_m_ip_payload_axis_tvalid_next = 1'b0;
|
||||
store_ip_payload_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
m_ip_payload_axis_tvalid_reg <= 1'b0;
|
||||
m_ip_payload_axis_tready_int_reg <= 1'b0;
|
||||
temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next;
|
||||
m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early;
|
||||
temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_ip_payload_int_to_output) begin
|
||||
m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
|
||||
m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
|
||||
m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
|
||||
end else if (store_ip_payload_axis_temp_to_output) begin
|
||||
m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg;
|
||||
m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg;
|
||||
m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_ip_payload_int_to_temp) begin
|
||||
temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int;
|
||||
temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int;
|
||||
temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -8,10 +8,20 @@ set_property board_part $boardName [current_project]
|
|||
|
||||
create_ip -name clk_wiz -vendor xilinx.com -library ip -module_name $ipName
|
||||
|
||||
set_property -dict [list CONFIG.PRIM_IN_FREQ {200.000} CONFIG.CLKOUT2_USED {true} CONFIG.CLKOUT3_USED {true} CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {50} CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {25} CONFIG.CLKIN1_JITTER_PS {50.0} CONFIG.MMCM_DIVCLK_DIVIDE {1} CONFIG.MMCM_CLKFBOUT_MULT_F {5.000} CONFIG.MMCM_CLKIN1_PERIOD {5.000} CONFIG.MMCM_CLKIN2_PERIOD {10.0} CONFIG.MMCM_CLKOUT0_DIVIDE_F {20.000} CONFIG.MMCM_CLKOUT1_DIVIDE {40} CONFIG.MMCM_CLKOUT2_DIVIDE {10} CONFIG.NUM_OUT_CLKS {3} CONFIG.CLKOUT1_JITTER {129.198} CONFIG.CLKOUT1_PHASE_ERROR {89.971} CONFIG.CLKOUT2_JITTER {148.629} CONFIG.CLKOUT2_PHASE_ERROR {89.971} CONFIG.CLKOUT3_JITTER {112.316} CONFIG.CLKOUT3_PHASE_ERROR {89.971}] [get_ips $ipName]
|
||||
set_property -dict [list CONFIG.PRIM_IN_FREQ {200.000} \
|
||||
CONFIG.NUM_OUT_CLKS {4} \
|
||||
CONFIG.CLKOUT2_USED {true} \
|
||||
CONFIG.CLKOUT3_USED {true} \
|
||||
CONFIG.CLKOUT4_USED {true} \
|
||||
CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {50} \
|
||||
CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {125} \
|
||||
CONFIG.CLKOUT3_REQUESTED_OUT_FREQ {125} \
|
||||
CONFIG.CLKOUT3_REQUESTED_PHASE {90.000} \
|
||||
CONFIG.CLKIN1_JITTER_PS {50.0} \
|
||||
] [get_ips $ipName]
|
||||
|
||||
generate_target {instantiation_template} [get_files ./$ipName.srcs/sources_1/ip/$ipName/$ipName.xci]
|
||||
generate_target all [get_files ./$ipName.srcs/sources_1/ip/$ipName/$ipName.xci]
|
||||
create_ip_run [get_files -of_objects [get_fileset sources_1] ./$ipName.srcs/sources_1/ip/$ipName/$ipName.xci]
|
||||
launch_run -jobs 8 ${ipName}_synth_1
|
||||
wait_on_run ${ipName}_synth_1
|
||||
wait_on_run ${ipName}_synth_1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue