Add files via upload
diff --git a/verilog/rtl/arbiter.v b/verilog/rtl/arbiter.v
new file mode 100644
index 0000000..ba98afb
--- /dev/null
+++ b/verilog/rtl/arbiter.v
@@ -0,0 +1,156 @@
+/*
+
+Copyright (c) 2014-2021 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 arbiter #
+(
+ parameter PORTS = 4,
+ // select round robin arbitration
+ parameter ARB_TYPE_ROUND_ROBIN = 0,
+ // blocking arbiter enable
+ parameter ARB_BLOCK = 0,
+ // block on acknowledge assert when nonzero, request deassert when 0
+ parameter ARB_BLOCK_ACK = 1,
+ // LSB priority selection
+ parameter ARB_LSB_HIGH_PRIORITY = 0
+)
+(
+ 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, grant_next;
+reg grant_valid_reg, grant_valid_next;
+reg [$clog2(PORTS)-1:0] grant_encoded_reg, 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_HIGH_PRIORITY(ARB_LSB_HIGH_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, 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_HIGH_PRIORITY(ARB_LSB_HIGH_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 (ARB_BLOCK && !ARB_BLOCK_ACK && 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 (ARB_BLOCK && ARB_BLOCK_ACK && 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 (ARB_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 (ARB_LSB_HIGH_PRIORITY) begin
+ mask_next = {PORTS{1'b1}} << (masked_request_index + 1);
+ end else begin
+ mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index);
+ end
+ end else begin
+ grant_valid_next = 1;
+ grant_next = request_mask;
+ grant_encoded_next = request_index;
+ if (ARB_LSB_HIGH_PRIORITY) begin
+ mask_next = {PORTS{1'b1}} << (request_index + 1);
+ end else begin
+ mask_next = {PORTS{1'b1}} >> (PORTS - request_index);
+ end
+ end
+ end else begin
+ grant_valid_next = 1;
+ grant_next = request_mask;
+ grant_encoded_next = request_index;
+ end
+ end
+end
+
+always @(posedge clk) begin
+ if (rst) begin
+ grant_reg <= 0;
+ grant_valid_reg <= 0;
+ grant_encoded_reg <= 0;
+ mask_reg <= 0;
+ end else begin
+ grant_reg <= grant_next;
+ grant_valid_reg <= grant_valid_next;
+ grant_encoded_reg <= grant_encoded_next;
+ mask_reg <= mask_next;
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/arp.v b/verilog/rtl/arp.v
new file mode 100644
index 0000000..2aa1d06
--- /dev/null
+++ b/verilog/rtl/arp.v
@@ -0,0 +1,450 @@
+/*
+
+Copyright (c) 2014-2020 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 #
+(
+ // Width of AXI stream interfaces in bits
+ parameter DATA_WIDTH = 8,
+ // Propagate tkeep signal
+ // If disabled, tkeep assumed to be 1'b1
+ parameter KEEP_ENABLE = (DATA_WIDTH>8),
+ // tkeep signal width (words per cycle)
+ parameter KEEP_WIDTH = (DATA_WIDTH/8),
+ // Log2 of ARP cache size
+ parameter CACHE_ADDR_WIDTH = 9,
+ // ARP request retry count
+ parameter REQUEST_RETRY_COUNT = 4,
+ // ARP request retry interval (in cycles)
+ parameter REQUEST_RETRY_INTERVAL = 125000000*2,
+ // ARP request timeout (in cycles)
+ 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 [DATA_WIDTH-1:0] s_eth_payload_axis_tdata,
+ input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep,
+ 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 [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 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 #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .KEEP_ENABLE(KEEP_ENABLE),
+ .KEEP_WIDTH(KEEP_WIDTH)
+)
+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_tkeep(s_eth_payload_axis_tkeep),
+ .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, outgoing_frame_valid_next;
+wire outgoing_frame_ready;
+reg [47:0] outgoing_eth_dest_mac_reg, outgoing_eth_dest_mac_next;
+reg [15:0] outgoing_arp_oper_reg, outgoing_arp_oper_next;
+reg [47:0] outgoing_arp_tha_reg, outgoing_arp_tha_next;
+reg [31:0] outgoing_arp_tpa_reg, outgoing_arp_tpa_next;
+
+arp_eth_tx #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .KEEP_ENABLE(KEEP_ENABLE),
+ .KEEP_WIDTH(KEEP_WIDTH)
+)
+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_tkeep(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_tuser(m_eth_payload_axis_tuser),
+ // Status signals
+ .busy()
+);
+
+reg cache_query_request_valid_reg, cache_query_request_valid_next;
+reg [31:0] cache_query_request_ip_reg, 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, cache_write_request_valid_next;
+reg [31:0] cache_write_request_ip_reg, cache_write_request_ip_next;
+reg [47:0] cache_write_request_mac_reg, 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, arp_request_operation_next;
+
+reg arp_request_ready_reg, arp_request_ready_next;
+reg [31:0] arp_request_ip_reg, arp_request_ip_next;
+
+reg arp_response_valid_reg, arp_response_valid_next;
+reg arp_response_error_reg, arp_response_error_next;
+reg [47:0] arp_response_mac_reg, arp_response_mac_next;
+
+reg [5:0] arp_request_retry_cnt_reg, arp_request_retry_cnt_next;
+reg [35:0] arp_request_timer_reg, 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_REQUEST) 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_query_request_ip_reg <= 32'd0;
+ cache_write_request_valid_reg <= 1'b0;
+ cache_write_request_ip_reg <= 32'd0;
+ cache_write_request_mac_reg <= 48'd0;
+ outgoing_eth_dest_mac_reg <= 48'd0;
+ outgoing_arp_oper_reg <= 16'd0;
+ outgoing_arp_tha_reg <= 48'd0;
+ outgoing_arp_tpa_reg <= 32'd0;
+ arp_request_operation_reg <= 1'b0;
+ arp_request_ready_reg <= 1'b0;
+ arp_request_ip_reg <= 32'd0;
+ arp_response_valid_reg <= 1'b0;
+ arp_response_error_reg <= 1'b0;
+ arp_response_mac_reg <= 48'd0;
+ arp_request_retry_cnt_reg <= 6'd0;
+ arp_request_timer_reg <= 36'd0;
+ 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;
+ 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
+end
+
+endmodule
+
diff --git a/verilog/rtl/arp_cache.v b/verilog/rtl/arp_cache.v
new file mode 100644
index 0000000..b9192ff
--- /dev/null
+++ b/verilog/rtl/arp_cache.v
@@ -0,0 +1,250 @@
+/*
+
+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;
+reg store_query;
+reg store_write;
+
+reg query_ip_valid_reg, query_ip_valid_next;
+reg [31:0] query_ip_reg;
+reg write_ip_valid_reg, write_ip_valid_next;
+reg [31:0] write_ip_reg;
+reg [47:0] write_mac_reg;
+reg clear_cache_reg, clear_cache_next;
+
+reg [CACHE_ADDR_WIDTH-1:0] wr_ptr_reg, wr_ptr_next;
+reg [CACHE_ADDR_WIDTH-1:0] rd_ptr_reg, 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, query_request_ready_next;
+
+reg query_response_valid_reg, query_response_valid_next;
+reg query_response_error_reg, query_response_error_next;
+reg [47:0] query_response_mac_reg;
+
+reg write_request_ready_reg, 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;
+
+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)
+);
+
+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)
+);
+
+integer i;
+
+initial begin
+ for (i = 0; i < 2**CACHE_ADDR_WIDTH; i = i + 1) begin
+ valid_mem[i] = 1'b0;
+ ip_addr_mem[i] = 32'd0;
+ mac_addr_mem[i] = 48'd0;
+ end
+end
+
+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;
+
+ clear_cache_next = clear_cache_reg | clear_cache;
+
+ query_ip_valid_next = query_ip_valid_reg;
+
+ query_request_ready_next = (~query_ip_valid_reg || ~query_request_valid || query_response_ready) && !clear_cache_next;
+
+ 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 = !clear_cache_next;
+
+ 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
+
+ if (clear_cache) begin
+ clear_cache_next = 1'b1;
+ wr_ptr_next = 0;
+ end else if (clear_cache_reg) begin
+ wr_ptr_next = wr_ptr_reg + 1;
+ clear_cache_next = wr_ptr_next != 0;
+ mem_write = 1;
+ 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;
+ clear_cache_reg <= 1'b1;
+ wr_ptr_reg <= 0;
+ query_ip_reg <= 0;
+ write_ip_reg <= 0;
+ write_mac_reg <= 0;
+ rd_ptr_reg <= {CACHE_ADDR_WIDTH{1'b0}};
+ query_response_error_reg <= 0;
+ query_response_mac_reg <= 0;
+ 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;
+ clear_cache_reg <= clear_cache_next;
+ wr_ptr_reg <= wr_ptr_next;
+
+ 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
+
+ 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] <= !clear_cache_reg;
+ ip_addr_mem[wr_ptr_reg] <= write_ip_reg;
+ mac_addr_mem[wr_ptr_reg] <= write_mac_reg;
+ end
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/arp_eth_rx.v b/verilog/rtl/arp_eth_rx.v
new file mode 100644
index 0000000..901f4cc
--- /dev/null
+++ b/verilog/rtl/arp_eth_rx.v
@@ -0,0 +1,337 @@
+/*
+
+Copyright (c) 2014-2020 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 #
+(
+ // Width of AXI stream interfaces in bits
+ parameter DATA_WIDTH = 8,
+ // Propagate tkeep signal
+ // If disabled, tkeep assumed to be 1'b1
+ parameter KEEP_ENABLE = (DATA_WIDTH>8),
+ // tkeep signal width (words per cycle)
+ parameter KEEP_WIDTH = (DATA_WIDTH/8)
+)
+(
+ 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 [DATA_WIDTH-1:0] s_eth_payload_axis_tdata,
+ input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep,
+ 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
+);
+
+parameter CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH;
+
+parameter PTR_WIDTH = $clog2(CYCLE_COUNT);
+
+parameter OFFSET = 28 % KEEP_WIDTH;
+
+// bus width assertions
+initial begin
+ if (KEEP_WIDTH * 8 != DATA_WIDTH) begin
+ $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)");
+ $finish;
+ end
+end
+
+/*
+
+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.
+
+*/
+
+// datapath control signals
+reg store_eth_hdr;
+
+reg read_eth_header_reg, read_eth_header_next;
+reg read_arp_header_reg, read_arp_header_next;
+reg [PTR_WIDTH-1:0] ptr_reg, ptr_next;
+
+reg s_eth_hdr_ready_reg, s_eth_hdr_ready_next;
+reg s_eth_payload_axis_tready_reg, s_eth_payload_axis_tready_next;
+
+reg m_frame_valid_reg, m_frame_valid_next;
+reg [47:0] m_eth_dest_mac_reg;
+reg [47:0] m_eth_src_mac_reg;
+reg [15:0] m_eth_type_reg;
+reg [15:0] m_arp_htype_reg, m_arp_htype_next;
+reg [15:0] m_arp_ptype_reg, m_arp_ptype_next;
+reg [7:0] m_arp_hlen_reg, m_arp_hlen_next;
+reg [7:0] m_arp_plen_reg, m_arp_plen_next;
+reg [15:0] m_arp_oper_reg, m_arp_oper_next;
+reg [47:0] m_arp_sha_reg, m_arp_sha_next;
+reg [31:0] m_arp_spa_reg, m_arp_spa_next;
+reg [47:0] m_arp_tha_reg, m_arp_tha_next;
+reg [31:0] m_arp_tpa_reg, m_arp_tpa_next;
+
+reg busy_reg;
+reg error_header_early_termination_reg, error_header_early_termination_next;
+reg error_invalid_header_reg, 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
+ read_eth_header_next = read_eth_header_reg;
+ read_arp_header_next = read_arp_header_reg;
+ ptr_next = ptr_reg;
+
+ s_eth_hdr_ready_next = 1'b0;
+ s_eth_payload_axis_tready_next = 1'b0;
+
+ store_eth_hdr = 1'b0;
+
+ m_frame_valid_next = m_frame_valid_reg && !m_frame_ready;
+
+ m_arp_htype_next = m_arp_htype_reg;
+ m_arp_ptype_next = m_arp_ptype_reg;
+ m_arp_hlen_next = m_arp_hlen_reg;
+ m_arp_plen_next = m_arp_plen_reg;
+ m_arp_oper_next = m_arp_oper_reg;
+ m_arp_sha_next = m_arp_sha_reg;
+ m_arp_spa_next = m_arp_spa_reg;
+ m_arp_tha_next = m_arp_tha_reg;
+ m_arp_tpa_next = m_arp_tpa_reg;
+
+ error_header_early_termination_next = 1'b0;
+ error_invalid_header_next = 1'b0;
+
+ if (s_eth_hdr_ready && s_eth_hdr_valid) begin
+ if (read_eth_header_reg) begin
+ store_eth_hdr = 1'b1;
+ ptr_next = 0;
+ read_eth_header_next = 1'b0;
+ read_arp_header_next = 1'b1;
+ end
+ end
+
+ if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
+ if (read_arp_header_reg) begin
+ // word transfer in - store it
+ ptr_next = ptr_reg + 1;
+
+ `define _HEADER_FIELD_(offset, field) \
+ if (ptr_reg == offset/KEEP_WIDTH && (!KEEP_ENABLE || s_eth_payload_axis_tkeep[offset%KEEP_WIDTH])) begin \
+ field = s_eth_payload_axis_tdata[(offset%KEEP_WIDTH)*8 +: 8]; \
+ end
+
+ `_HEADER_FIELD_(0, m_arp_htype_next[1*8 +: 8])
+ `_HEADER_FIELD_(1, m_arp_htype_next[0*8 +: 8])
+ `_HEADER_FIELD_(2, m_arp_ptype_next[1*8 +: 8])
+ `_HEADER_FIELD_(3, m_arp_ptype_next[0*8 +: 8])
+ `_HEADER_FIELD_(4, m_arp_hlen_next[0*8 +: 8])
+ `_HEADER_FIELD_(5, m_arp_plen_next[0*8 +: 8])
+ `_HEADER_FIELD_(6, m_arp_oper_next[1*8 +: 8])
+ `_HEADER_FIELD_(7, m_arp_oper_next[0*8 +: 8])
+ `_HEADER_FIELD_(8, m_arp_sha_next[5*8 +: 8])
+ `_HEADER_FIELD_(9, m_arp_sha_next[4*8 +: 8])
+ `_HEADER_FIELD_(10, m_arp_sha_next[3*8 +: 8])
+ `_HEADER_FIELD_(11, m_arp_sha_next[2*8 +: 8])
+ `_HEADER_FIELD_(12, m_arp_sha_next[1*8 +: 8])
+ `_HEADER_FIELD_(13, m_arp_sha_next[0*8 +: 8])
+ `_HEADER_FIELD_(14, m_arp_spa_next[3*8 +: 8])
+ `_HEADER_FIELD_(15, m_arp_spa_next[2*8 +: 8])
+ `_HEADER_FIELD_(16, m_arp_spa_next[1*8 +: 8])
+ `_HEADER_FIELD_(17, m_arp_spa_next[0*8 +: 8])
+ `_HEADER_FIELD_(18, m_arp_tha_next[5*8 +: 8])
+ `_HEADER_FIELD_(19, m_arp_tha_next[4*8 +: 8])
+ `_HEADER_FIELD_(20, m_arp_tha_next[3*8 +: 8])
+ `_HEADER_FIELD_(21, m_arp_tha_next[2*8 +: 8])
+ `_HEADER_FIELD_(22, m_arp_tha_next[1*8 +: 8])
+ `_HEADER_FIELD_(23, m_arp_tha_next[0*8 +: 8])
+ `_HEADER_FIELD_(24, m_arp_tpa_next[3*8 +: 8])
+ `_HEADER_FIELD_(25, m_arp_tpa_next[2*8 +: 8])
+ `_HEADER_FIELD_(26, m_arp_tpa_next[1*8 +: 8])
+ `_HEADER_FIELD_(27, m_arp_tpa_next[0*8 +: 8])
+
+ if (ptr_reg == 27/KEEP_WIDTH && (!KEEP_ENABLE || s_eth_payload_axis_tkeep[27%KEEP_WIDTH])) begin
+ read_arp_header_next = 1'b0;
+ end
+
+ `undef _HEADER_FIELD_
+ end
+
+ if (s_eth_payload_axis_tlast) begin
+ if (read_arp_header_next) begin
+ // don't have the whole header
+ error_header_early_termination_next = 1'b1;
+ end else if (m_arp_hlen_next != 4'd6 || m_arp_plen_next != 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
+
+ ptr_next = 1'b0;
+ read_eth_header_next = 1'b1;
+ read_arp_header_next = 1'b0;
+ end
+ end
+
+ if (read_eth_header_next) begin
+ s_eth_hdr_ready_next = !m_frame_valid_next;
+ end else begin
+ s_eth_payload_axis_tready_next = 1'b1;
+ end
+end
+
+always @(posedge clk) begin
+ read_eth_header_reg <= read_eth_header_next;
+ read_arp_header_reg <= read_arp_header_next;
+ ptr_reg <= ptr_next;
+
+ s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
+ s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next;
+
+ m_frame_valid_reg <= m_frame_valid_next;
+
+ m_arp_htype_reg <= m_arp_htype_next;
+ m_arp_ptype_reg <= m_arp_ptype_next;
+ m_arp_hlen_reg <= m_arp_hlen_next;
+ m_arp_plen_reg <= m_arp_plen_next;
+ m_arp_oper_reg <= m_arp_oper_next;
+ m_arp_sha_reg <= m_arp_sha_next;
+ m_arp_spa_reg <= m_arp_spa_next;
+ m_arp_tha_reg <= m_arp_tha_next;
+ m_arp_tpa_reg <= m_arp_tpa_next;
+
+ error_header_early_termination_reg <= error_header_early_termination_next;
+ error_invalid_header_reg <= error_invalid_header_next;
+
+ busy_reg <= read_arp_header_next;
+
+ // 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 (rst) begin
+ read_eth_header_reg <= 1'b1;
+ read_arp_header_reg <= 1'b0;
+ ptr_reg <= 0;
+ s_eth_hdr_ready_reg <= 1'b0;
+ s_eth_payload_axis_tready_reg <= 1'b0;
+ m_frame_valid_reg <= 1'b0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ m_arp_htype_reg <= 16'd0;
+ m_arp_ptype_reg <= 16'd0;
+ m_arp_hlen_reg <= 8'd0;
+ m_arp_plen_reg <= 8'd0;
+ m_arp_oper_reg <= 16'd0;
+ m_arp_sha_reg <= 48'd0;
+ m_arp_spa_reg <= 32'd0;
+ m_arp_tha_reg <= 48'd0;
+ m_arp_tpa_reg <= 32'd0;
+ busy_reg <= 1'b0;
+ error_header_early_termination_reg <= 1'b0;
+ error_invalid_header_reg <= 1'b0;
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/arp_eth_tx.v b/verilog/rtl/arp_eth_tx.v
new file mode 100644
index 0000000..9f593f0
--- /dev/null
+++ b/verilog/rtl/arp_eth_tx.v
@@ -0,0 +1,379 @@
+/*
+
+Copyright (c) 2014-2020 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 #
+(
+ // Width of AXI stream interfaces in bits
+ parameter DATA_WIDTH = 8,
+ // Propagate tkeep signal
+ // If disabled, tkeep assumed to be 1'b1
+ parameter KEEP_ENABLE = (DATA_WIDTH>8),
+ // tkeep signal width (words per cycle)
+ parameter KEEP_WIDTH = (DATA_WIDTH/8)
+)
+(
+ 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 [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 m_eth_payload_axis_tuser,
+
+ /*
+ * Status signals
+ */
+ output wire busy
+);
+
+parameter CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH;
+
+parameter PTR_WIDTH = $clog2(CYCLE_COUNT);
+
+parameter OFFSET = 28 % KEEP_WIDTH;
+
+// bus width assertions
+initial begin
+ if (KEEP_WIDTH * 8 != DATA_WIDTH) begin
+ $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)");
+ $finish;
+ end
+end
+
+/*
+
+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.
+
+*/
+
+// datapath control signals
+reg store_frame;
+
+reg send_arp_header_reg, send_arp_header_next;
+reg [PTR_WIDTH-1:0] ptr_reg, ptr_next;
+
+reg [15:0] arp_htype_reg;
+reg [15:0] arp_ptype_reg;
+reg [15:0] arp_oper_reg;
+reg [47:0] arp_sha_reg;
+reg [31:0] arp_spa_reg;
+reg [47:0] arp_tha_reg;
+reg [31:0] arp_tpa_reg;
+
+reg s_frame_ready_reg, s_frame_ready_next;
+
+reg m_eth_hdr_valid_reg, m_eth_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg;
+reg [47:0] m_eth_src_mac_reg;
+reg [15:0] m_eth_type_reg;
+
+reg busy_reg;
+
+// 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;
+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
+ send_arp_header_next = send_arp_header_reg;
+ ptr_next = ptr_reg;
+
+ s_frame_ready_next = 1'b0;
+
+ store_frame = 1'b0;
+
+ m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready;
+
+ m_eth_payload_axis_tdata_int = {DATA_WIDTH{1'b0}};
+ m_eth_payload_axis_tkeep_int = {KEEP_WIDTH{1'b0}};
+ m_eth_payload_axis_tvalid_int = 1'b0;
+ m_eth_payload_axis_tlast_int = 1'b0;
+ m_eth_payload_axis_tuser_int = 1'b0;
+
+ if (s_frame_ready && s_frame_valid) begin
+ store_frame = 1'b1;
+ m_eth_hdr_valid_next = 1'b1;
+ ptr_next = 0;
+ send_arp_header_next = 1'b1;
+ end
+
+ if (m_eth_payload_axis_tready_int_reg) begin
+ if (send_arp_header_reg) begin
+ ptr_next = ptr_reg + 1;
+
+ m_eth_payload_axis_tdata_int = {DATA_WIDTH{1'b0}};
+ m_eth_payload_axis_tkeep_int = {KEEP_WIDTH{1'b0}};
+ m_eth_payload_axis_tvalid_int = 1'b1;
+ m_eth_payload_axis_tlast_int = 1'b0;
+ m_eth_payload_axis_tuser_int = 1'b0;
+
+ `define _HEADER_FIELD_(offset, field) \
+ if (ptr_reg == offset/KEEP_WIDTH) begin \
+ m_eth_payload_axis_tdata_int[(offset%KEEP_WIDTH)*8 +: 8] = field; \
+ m_eth_payload_axis_tkeep_int[offset%KEEP_WIDTH] = 1'b1; \
+ end
+
+ `_HEADER_FIELD_(0, arp_htype_reg[1*8 +: 8])
+ `_HEADER_FIELD_(1, arp_htype_reg[0*8 +: 8])
+ `_HEADER_FIELD_(2, arp_ptype_reg[1*8 +: 8])
+ `_HEADER_FIELD_(3, arp_ptype_reg[0*8 +: 8])
+ `_HEADER_FIELD_(4, 8'd6)
+ `_HEADER_FIELD_(5, 8'd4)
+ `_HEADER_FIELD_(6, arp_oper_reg[1*8 +: 8])
+ `_HEADER_FIELD_(7, arp_oper_reg[0*8 +: 8])
+ `_HEADER_FIELD_(8, arp_sha_reg[5*8 +: 8])
+ `_HEADER_FIELD_(9, arp_sha_reg[4*8 +: 8])
+ `_HEADER_FIELD_(10, arp_sha_reg[3*8 +: 8])
+ `_HEADER_FIELD_(11, arp_sha_reg[2*8 +: 8])
+ `_HEADER_FIELD_(12, arp_sha_reg[1*8 +: 8])
+ `_HEADER_FIELD_(13, arp_sha_reg[0*8 +: 8])
+ `_HEADER_FIELD_(14, arp_spa_reg[3*8 +: 8])
+ `_HEADER_FIELD_(15, arp_spa_reg[2*8 +: 8])
+ `_HEADER_FIELD_(16, arp_spa_reg[1*8 +: 8])
+ `_HEADER_FIELD_(17, arp_spa_reg[0*8 +: 8])
+ `_HEADER_FIELD_(18, arp_tha_reg[5*8 +: 8])
+ `_HEADER_FIELD_(19, arp_tha_reg[4*8 +: 8])
+ `_HEADER_FIELD_(20, arp_tha_reg[3*8 +: 8])
+ `_HEADER_FIELD_(21, arp_tha_reg[2*8 +: 8])
+ `_HEADER_FIELD_(22, arp_tha_reg[1*8 +: 8])
+ `_HEADER_FIELD_(23, arp_tha_reg[0*8 +: 8])
+ `_HEADER_FIELD_(24, arp_tpa_reg[3*8 +: 8])
+ `_HEADER_FIELD_(25, arp_tpa_reg[2*8 +: 8])
+ `_HEADER_FIELD_(26, arp_tpa_reg[1*8 +: 8])
+ `_HEADER_FIELD_(27, arp_tpa_reg[0*8 +: 8])
+
+ if (ptr_reg == 27/KEEP_WIDTH) begin
+ m_eth_payload_axis_tlast_int = 1'b1;
+ send_arp_header_next = 1'b0;
+ end
+
+ `undef _HEADER_FIELD_
+ end
+ end
+
+ s_frame_ready_next = !m_eth_hdr_valid_next && !send_arp_header_next;
+end
+
+always @(posedge clk) begin
+ send_arp_header_reg <= send_arp_header_next;
+ ptr_reg <= ptr_next;
+
+ s_frame_ready_reg <= s_frame_ready_next;
+
+ m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
+
+ busy_reg <= send_arp_header_next;
+
+ 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
+
+ if (rst) begin
+ send_arp_header_reg <= 1'b0;
+ ptr_reg <= 0;
+ arp_htype_reg <= 16'd0;
+ arp_ptype_reg <= 16'd0;
+ arp_oper_reg <= 16'd0;
+ arp_sha_reg <= 48'd0;
+ arp_spa_reg <= 32'd0;
+ arp_tha_reg <= 48'd0;
+ arp_tpa_reg <= 32'd0;
+ s_frame_ready_reg <= 1'b0;
+ m_eth_hdr_valid_reg <= 1'b0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ busy_reg <= 1'b0;
+ end
+end
+
+// output datapath logic
+reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg;
+reg m_eth_payload_axis_tvalid_reg, m_eth_payload_axis_tvalid_next;
+reg m_eth_payload_axis_tlast_reg;
+reg m_eth_payload_axis_tuser_reg;
+
+reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg;
+reg temp_m_eth_payload_axis_tvalid_reg, temp_m_eth_payload_axis_tvalid_next;
+reg temp_m_eth_payload_axis_tlast_reg;
+reg temp_m_eth_payload_axis_tuser_reg;
+
+// 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_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_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_tready_int_reg <= 1'b0;
+ m_eth_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ m_eth_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ m_eth_payload_axis_tvalid_reg <= 1'b0;
+ m_eth_payload_axis_tlast_reg <= 1'b0;
+ m_eth_payload_axis_tuser_reg <= 1'b0;
+ temp_m_eth_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_eth_payload_axis_tlast_reg <= 1'b0;
+ temp_m_eth_payload_axis_tuser_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;
+
+ // 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_tkeep_reg <= m_eth_payload_axis_tkeep_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_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_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_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_tuser_reg <= m_eth_payload_axis_tuser_int;
+ end
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/axis_async_fifo.v b/verilog/rtl/axis_async_fifo.v
new file mode 100644
index 0000000..d64231c
--- /dev/null
+++ b/verilog/rtl/axis_async_fifo.v
@@ -0,0 +1,469 @@
+/*
+
+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 #
+(
+ // FIFO depth in words
+ // KEEP_WIDTH words per cycle if KEEP_ENABLE set
+ // Rounded up to nearest power of 2 cycles
+ parameter DEPTH = 4096,
+ // Width of AXI stream interfaces in bits
+ parameter DATA_WIDTH = 8,
+ // Propagate tkeep signal
+ // If disabled, tkeep assumed to be 1'b1
+ parameter KEEP_ENABLE = (DATA_WIDTH>8),
+ // tkeep signal width (words per cycle)
+ parameter KEEP_WIDTH = (DATA_WIDTH/8),
+ // Propagate tlast signal
+ parameter LAST_ENABLE = 1,
+ // Propagate tid signal
+ parameter ID_ENABLE = 0,
+ // tid signal width
+ parameter ID_WIDTH = 8,
+ // Propagate tdest signal
+ parameter DEST_ENABLE = 0,
+ // tdest signal width
+ parameter DEST_WIDTH = 8,
+ // Propagate tuser signal
+ parameter USER_ENABLE = 1,
+ // tuser signal width
+ parameter USER_WIDTH = 1,
+ // number of output pipeline registers
+ parameter PIPELINE_OUTPUT = 2,
+ // Frame FIFO mode - operate on frames instead of cycles
+ // When set, m_axis_tvalid will not be deasserted within a frame
+ // Requires LAST_ENABLE set
+ parameter FRAME_FIFO = 0,
+ // tuser value for bad frame marker
+ parameter USER_BAD_FRAME_VALUE = 1'b1,
+ // tuser mask for bad frame marker
+ parameter USER_BAD_FRAME_MASK = 1'b1,
+ // Drop frames marked bad
+ // Requires FRAME_FIFO set
+ parameter DROP_BAD_FRAME = 0,
+ // Drop incoming frames when full
+ // When set, s_axis_tready is always asserted
+ // Requires FRAME_FIFO set
+ 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
+);
+
+parameter ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH);
+
+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;
+reg [ADDR_WIDTH:0] wr_ptr_cur_reg;
+reg [ADDR_WIDTH:0] wr_ptr_gray_reg;
+reg [ADDR_WIDTH:0] wr_ptr_sync_gray_reg;
+reg [ADDR_WIDTH:0] wr_ptr_cur_gray_reg;
+reg [ADDR_WIDTH:0] rd_ptr_reg;
+reg [ADDR_WIDTH:0] rd_ptr_gray_reg;
+
+reg [ADDR_WIDTH:0] wr_ptr_temp;
+reg [ADDR_WIDTH:0] rd_ptr_temp;
+
+(* SHREG_EXTRACT = "NO" *)
+reg [ADDR_WIDTH:0] wr_ptr_gray_sync1_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg [ADDR_WIDTH:0] wr_ptr_gray_sync2_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg [ADDR_WIDTH:0] rd_ptr_gray_sync1_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg [ADDR_WIDTH:0] rd_ptr_gray_sync2_reg;
+
+reg wr_ptr_update_valid_reg;
+reg wr_ptr_update_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg wr_ptr_update_sync1_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg wr_ptr_update_sync2_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg wr_ptr_update_sync3_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg wr_ptr_update_ack_sync1_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg wr_ptr_update_ack_sync2_reg;
+
+(* SHREG_EXTRACT = "NO" *)
+reg s_rst_sync1_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg s_rst_sync2_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg s_rst_sync3_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg m_rst_sync1_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg m_rst_sync2_reg;
+(* SHREG_EXTRACT = "NO" *)
+reg m_rst_sync3_reg;
+
+reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0];
+reg [WIDTH-1:0] mem_read_data_reg;
+
+wire [WIDTH-1:0] s_axis;
+
+(* SHREG_EXTRACT = "NO" *)
+reg [WIDTH-1:0] m_axis_pipe_reg[PIPELINE_OUTPUT-1:0];
+(* SHREG_EXTRACT = "NO" *)
+reg [PIPELINE_OUTPUT-1:0] m_axis_tvalid_pipe_reg;
+
+// 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 == (rd_ptr_gray_sync2_reg ^ {2'b11, {ADDR_WIDTH-1{1'b0}}});
+wire full_cur = wr_ptr_cur_gray_reg == (rd_ptr_gray_sync2_reg ^ {2'b11, {ADDR_WIDTH-1{1'b0}}});
+// empty when pointers match exactly
+wire empty = rd_ptr_gray_reg == (FRAME_FIFO ? wr_ptr_gray_sync1_reg : wr_ptr_gray_sync2_reg);
+// overflow within packet
+wire full_wr = wr_ptr_reg == (wr_ptr_cur_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}});
+
+// control signals
+reg write;
+reg read;
+reg store_output;
+
+reg drop_frame_reg;
+reg overflow_reg;
+reg bad_frame_reg;
+reg good_frame_reg;
+
+reg overflow_sync1_reg;
+reg overflow_sync2_reg;
+reg overflow_sync3_reg;
+reg overflow_sync4_reg;
+reg bad_frame_sync1_reg;
+reg bad_frame_sync2_reg;
+reg bad_frame_sync3_reg;
+reg bad_frame_sync4_reg;
+reg good_frame_sync1_reg;
+reg good_frame_sync2_reg;
+reg good_frame_sync3_reg;
+reg good_frame_sync4_reg;
+
+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_pipe_reg[PIPELINE_OUTPUT-1];
+
+assign m_axis_tdata = m_axis_pipe_reg[PIPELINE_OUTPUT-1][DATA_WIDTH-1:0];
+assign m_axis_tkeep = KEEP_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}};
+assign m_axis_tlast = LAST_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][LAST_OFFSET] : 1'b1;
+assign m_axis_tid = ID_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}};
+assign m_axis_tdest = DEST_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}};
+assign m_axis_tuser = USER_ENABLE ? m_axis_pipe_reg[PIPELINE_OUTPUT-1][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 @(posedge s_clk) begin
+ overflow_reg <= 1'b0;
+ bad_frame_reg <= 1'b0;
+ good_frame_reg <= 1'b0;
+
+ if (FRAME_FIFO && wr_ptr_update_valid_reg) begin
+ // have updated pointer to sync
+ if (wr_ptr_update_reg == wr_ptr_update_ack_sync2_reg) begin
+ // no sync in progress; sync update
+ wr_ptr_update_valid_reg <= 1'b0;
+ wr_ptr_sync_gray_reg <= wr_ptr_gray_reg;
+ wr_ptr_update_reg <= !wr_ptr_update_ack_sync2_reg;
+ end
+ end
+
+ if (s_axis_tready && s_axis_tvalid) begin
+ // transfer in
+ if (!FRAME_FIFO) begin
+ // normal FIFO mode
+ mem[wr_ptr_reg[ADDR_WIDTH-1:0]] <= s_axis;
+ wr_ptr_temp = wr_ptr_reg + 1;
+ wr_ptr_reg <= wr_ptr_temp;
+ wr_ptr_gray_reg <= wr_ptr_temp ^ (wr_ptr_temp >> 1);
+ end else if (full_cur || full_wr || drop_frame_reg) begin
+ // full, packet overflow, or currently dropping frame
+ // drop frame
+ drop_frame_reg <= 1'b1;
+ if (s_axis_tlast) begin
+ // end of frame, reset write pointer
+ wr_ptr_temp = wr_ptr_reg;
+ wr_ptr_cur_reg <= wr_ptr_temp;
+ wr_ptr_cur_gray_reg <= wr_ptr_temp ^ (wr_ptr_temp >> 1);
+ drop_frame_reg <= 1'b0;
+ overflow_reg <= 1'b1;
+ end
+ end else begin
+ mem[wr_ptr_cur_reg[ADDR_WIDTH-1:0]] <= s_axis;
+ wr_ptr_temp = wr_ptr_cur_reg + 1;
+ wr_ptr_cur_reg <= wr_ptr_temp;
+ wr_ptr_cur_gray_reg <= wr_ptr_temp ^ (wr_ptr_temp >> 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_temp = wr_ptr_reg;
+ wr_ptr_cur_reg <= wr_ptr_temp;
+ wr_ptr_cur_gray_reg <= wr_ptr_temp ^ (wr_ptr_temp >> 1);
+ bad_frame_reg <= 1'b1;
+ end else begin
+ // good packet, update write pointer
+ wr_ptr_temp = wr_ptr_cur_reg + 1;
+ wr_ptr_reg <= wr_ptr_temp;
+ wr_ptr_gray_reg <= wr_ptr_temp ^ (wr_ptr_temp >> 1);
+
+ if (wr_ptr_update_reg == wr_ptr_update_ack_sync2_reg) begin
+ // no sync in progress; sync update
+ wr_ptr_update_valid_reg <= 1'b0;
+ wr_ptr_sync_gray_reg <= wr_ptr_temp ^ (wr_ptr_temp >> 1);
+ wr_ptr_update_reg <= !wr_ptr_update_ack_sync2_reg;
+ end else begin
+ // sync in progress; flag it for later
+ wr_ptr_update_valid_reg <= 1'b1;
+ end
+
+ good_frame_reg <= 1'b1;
+ end
+ end
+ end
+ end
+
+ 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_sync_gray_reg <= {ADDR_WIDTH+1{1'b0}};
+ wr_ptr_cur_gray_reg <= {ADDR_WIDTH+1{1'b0}};
+
+ wr_ptr_update_valid_reg <= 1'b0;
+ wr_ptr_update_reg <= 1'b0;
+
+ drop_frame_reg <= 1'b0;
+ overflow_reg <= 1'b0;
+ bad_frame_reg <= 1'b0;
+ good_frame_reg <= 1'b0;
+ end
+end
+
+// pointer synchronization
+always @(posedge s_clk) begin
+ rd_ptr_gray_sync1_reg <= rd_ptr_gray_reg;
+ rd_ptr_gray_sync2_reg <= rd_ptr_gray_sync1_reg;
+ wr_ptr_update_ack_sync1_reg <= wr_ptr_update_sync3_reg;
+ wr_ptr_update_ack_sync2_reg <= wr_ptr_update_ack_sync1_reg;
+
+ 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}};
+ wr_ptr_update_ack_sync1_reg <= 1'b0;
+ wr_ptr_update_ack_sync2_reg <= 1'b0;
+ end
+end
+
+always @(posedge m_clk) begin
+ if (!FRAME_FIFO) begin
+ wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg;
+ end else if (wr_ptr_update_sync2_reg ^ wr_ptr_update_sync3_reg) begin
+ wr_ptr_gray_sync1_reg <= wr_ptr_sync_gray_reg;
+ end
+ wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg;
+ wr_ptr_update_sync1_reg <= wr_ptr_update_reg;
+ wr_ptr_update_sync2_reg <= wr_ptr_update_sync1_reg;
+ wr_ptr_update_sync3_reg <= wr_ptr_update_sync2_reg;
+
+ 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}};
+ wr_ptr_update_sync1_reg <= 1'b0;
+ wr_ptr_update_sync2_reg <= 1'b0;
+ wr_ptr_update_sync3_reg <= 1'b0;
+ end
+end
+
+// status synchronization
+always @(posedge s_clk) 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;
+
+ if (s_rst_sync3_reg) begin
+ overflow_sync1_reg <= 1'b0;
+ bad_frame_sync1_reg <= 1'b0;
+ good_frame_sync1_reg <= 1'b0;
+ end
+end
+
+always @(posedge m_clk) 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;
+
+ if (m_rst_sync3_reg) begin
+ overflow_sync2_reg <= 1'b0;
+ overflow_sync3_reg <= 1'b0;
+ overflow_sync4_reg <= 1'b0;
+ bad_frame_sync2_reg <= 1'b0;
+ bad_frame_sync3_reg <= 1'b0;
+ bad_frame_sync4_reg <= 1'b0;
+ good_frame_sync2_reg <= 1'b0;
+ good_frame_sync3_reg <= 1'b0;
+ good_frame_sync4_reg <= 1'b0;
+ end
+end
+
+// Read logic
+integer j;
+
+always @(posedge m_clk) begin
+ if (m_axis_tready) begin
+ // output ready; invalidate stage
+ m_axis_tvalid_pipe_reg[PIPELINE_OUTPUT-1] <= 1'b0;
+ end
+
+ for (j = PIPELINE_OUTPUT-1; j > 0; j = j - 1) begin
+ if (m_axis_tready || ((~m_axis_tvalid_pipe_reg) >> j)) begin
+ // output ready or bubble in pipeline; transfer down pipeline
+ m_axis_tvalid_pipe_reg[j] <= m_axis_tvalid_pipe_reg[j-1];
+ m_axis_pipe_reg[j] <= m_axis_pipe_reg[j-1];
+ m_axis_tvalid_pipe_reg[j-1] <= 1'b0;
+ end
+ end
+
+ if (m_axis_tready || ~m_axis_tvalid_pipe_reg) begin
+ // output ready or bubble in pipeline; read new data from FIFO
+ m_axis_tvalid_pipe_reg[0] <= 1'b0;
+ m_axis_pipe_reg[0] <= mem[rd_ptr_reg[ADDR_WIDTH-1:0]];
+ if (!empty) begin
+ // not empty, increment pointer
+ m_axis_tvalid_pipe_reg[0] <= 1'b1;
+ rd_ptr_temp = rd_ptr_reg + 1;
+ rd_ptr_reg <= rd_ptr_temp;
+ rd_ptr_gray_reg <= rd_ptr_temp ^ (rd_ptr_temp >> 1);
+ end
+ end
+
+ if (m_rst_sync3_reg) begin
+ rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}};
+ rd_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}};
+ m_axis_tvalid_pipe_reg <= {PIPELINE_OUTPUT{1'b0}};
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/axis_async_fifo_adapter.v b/verilog/rtl/axis_async_fifo_adapter.v
new file mode 100644
index 0000000..c04e527
--- /dev/null
+++ b/verilog/rtl/axis_async_fifo_adapter.v
@@ -0,0 +1,351 @@
+/*
+
+Copyright (c) 2019 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 with width converter
+ */
+module axis_async_fifo_adapter #
+(
+ // FIFO depth in words
+ // KEEP_WIDTH words per cycle if KEEP_ENABLE set
+ // Rounded up to nearest power of 2 cycles
+ parameter DEPTH = 4096,
+ // Width of input AXI stream interface in bits
+ parameter S_DATA_WIDTH = 8,
+ // Propagate tkeep signal on input interface
+ // If disabled, tkeep assumed to be 1'b1
+ parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8),
+ // tkeep signal width (words per cycle) on input interface
+ parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8),
+ // Width of output AXI stream interface in bits
+ parameter M_DATA_WIDTH = 8,
+ // Propagate tkeep signal on output interface
+ // If disabled, tkeep assumed to be 1'b1
+ parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8),
+ // tkeep signal width (words per cycle) on output interface
+ parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8),
+ // Propagate tid signal
+ parameter ID_ENABLE = 0,
+ // tid signal width
+ parameter ID_WIDTH = 8,
+ // Propagate tdest signal
+ parameter DEST_ENABLE = 0,
+ // tdest signal width
+ parameter DEST_WIDTH = 8,
+ // Propagate tuser signal
+ parameter USER_ENABLE = 1,
+ // tuser signal width
+ parameter USER_WIDTH = 1,
+ // number of output pipeline registers
+ parameter PIPELINE_OUTPUT = 2,
+ // Frame FIFO mode - operate on frames instead of cycles
+ // When set, m_axis_tvalid will not be deasserted within a frame
+ // Requires LAST_ENABLE set
+ parameter FRAME_FIFO = 0,
+ // tuser value for bad frame marker
+ parameter USER_BAD_FRAME_VALUE = 1'b1,
+ // tuser mask for bad frame marker
+ parameter USER_BAD_FRAME_MASK = 1'b1,
+ // Drop frames marked bad
+ // Requires FRAME_FIFO set
+ parameter DROP_BAD_FRAME = 0,
+ // Drop incoming frames when full
+ // When set, s_axis_tready is always asserted
+ // Requires FRAME_FIFO set
+ parameter DROP_WHEN_FULL = 0
+)
+(
+ /*
+ * AXI input
+ */
+ input wire s_clk,
+ input wire s_rst,
+ input wire [S_DATA_WIDTH-1:0] s_axis_tdata,
+ input wire [S_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,
+ input wire m_rst,
+ output wire [M_DATA_WIDTH-1:0] m_axis_tdata,
+ output wire [M_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
+);
+
+// force keep width to 1 when disabled
+parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1;
+parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1;
+
+// bus word sizes (must be identical)
+parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT;
+parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT;
+// output bus is wider
+parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT;
+// total data and keep widths
+parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH;
+parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT;
+
+// bus width assertions
+initial begin
+ if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin
+ $error("Error: input data width not evenly divisble (instance %m)");
+ $finish;
+ end
+
+ if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin
+ $error("Error: output data width not evenly divisble (instance %m)");
+ $finish;
+ end
+
+ if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin
+ $error("Error: word size mismatch (instance %m)");
+ $finish;
+ end
+end
+
+wire [DATA_WIDTH-1:0] pre_fifo_axis_tdata;
+wire [KEEP_WIDTH-1:0] pre_fifo_axis_tkeep;
+wire pre_fifo_axis_tvalid;
+wire pre_fifo_axis_tready;
+wire pre_fifo_axis_tlast;
+wire [ID_WIDTH-1:0] pre_fifo_axis_tid;
+wire [DEST_WIDTH-1:0] pre_fifo_axis_tdest;
+wire [USER_WIDTH-1:0] pre_fifo_axis_tuser;
+
+wire [DATA_WIDTH-1:0] post_fifo_axis_tdata;
+wire [KEEP_WIDTH-1:0] post_fifo_axis_tkeep;
+wire post_fifo_axis_tvalid;
+wire post_fifo_axis_tready;
+wire post_fifo_axis_tlast;
+wire [ID_WIDTH-1:0] post_fifo_axis_tid;
+wire [DEST_WIDTH-1:0] post_fifo_axis_tdest;
+wire [USER_WIDTH-1:0] post_fifo_axis_tuser;
+
+generate
+
+if (M_KEEP_WIDTH == S_KEEP_WIDTH) begin
+
+ // same width, no adapter needed
+
+ assign pre_fifo_axis_tdata = s_axis_tdata;
+ assign pre_fifo_axis_tkeep = s_axis_tkeep;
+ assign pre_fifo_axis_tvalid = s_axis_tvalid;
+ assign s_axis_tready = pre_fifo_axis_tready;
+ assign pre_fifo_axis_tlast = s_axis_tlast;
+ assign pre_fifo_axis_tid = s_axis_tid;
+ assign pre_fifo_axis_tdest = s_axis_tdest;
+ assign pre_fifo_axis_tuser = s_axis_tuser;
+
+ assign m_axis_tdata = post_fifo_axis_tdata;
+ assign m_axis_tkeep = post_fifo_axis_tkeep;
+ assign m_axis_tvalid = post_fifo_axis_tvalid;
+ assign post_fifo_axis_tready = m_axis_tready;
+ assign m_axis_tlast = post_fifo_axis_tlast;
+ assign m_axis_tid = post_fifo_axis_tid;
+ assign m_axis_tdest = post_fifo_axis_tdest;
+ assign m_axis_tuser = post_fifo_axis_tuser;
+
+
+end else if (EXPAND_BUS) begin
+
+ // output wider, adapt width before FIFO
+
+ axis_adapter #(
+ .S_DATA_WIDTH(S_DATA_WIDTH),
+ .S_KEEP_ENABLE(S_KEEP_ENABLE),
+ .S_KEEP_WIDTH(S_KEEP_WIDTH),
+ .M_DATA_WIDTH(M_DATA_WIDTH),
+ .M_KEEP_ENABLE(M_KEEP_ENABLE),
+ .M_KEEP_WIDTH(M_KEEP_WIDTH),
+ .ID_ENABLE(ID_ENABLE),
+ .ID_WIDTH(ID_WIDTH),
+ .DEST_ENABLE(DEST_ENABLE),
+ .DEST_WIDTH(DEST_WIDTH),
+ .USER_ENABLE(USER_ENABLE),
+ .USER_WIDTH(USER_WIDTH)
+ )
+ adapter_inst (
+ .clk(s_clk),
+ .rst(s_rst),
+ // AXI input
+ .s_axis_tdata(s_axis_tdata),
+ .s_axis_tkeep(s_axis_tkeep),
+ .s_axis_tvalid(s_axis_tvalid),
+ .s_axis_tready(s_axis_tready),
+ .s_axis_tlast(s_axis_tlast),
+ .s_axis_tid(s_axis_tid),
+ .s_axis_tdest(s_axis_tdest),
+ .s_axis_tuser(s_axis_tuser),
+ // AXI output
+ .m_axis_tdata(pre_fifo_axis_tdata),
+ .m_axis_tkeep(pre_fifo_axis_tkeep),
+ .m_axis_tvalid(pre_fifo_axis_tvalid),
+ .m_axis_tready(pre_fifo_axis_tready),
+ .m_axis_tlast(pre_fifo_axis_tlast),
+ .m_axis_tid(pre_fifo_axis_tid),
+ .m_axis_tdest(pre_fifo_axis_tdest),
+ .m_axis_tuser(pre_fifo_axis_tuser)
+ );
+
+ assign m_axis_tdata = post_fifo_axis_tdata;
+ assign m_axis_tkeep = post_fifo_axis_tkeep;
+ assign m_axis_tvalid = post_fifo_axis_tvalid;
+ assign post_fifo_axis_tready = m_axis_tready;
+ assign m_axis_tlast = post_fifo_axis_tlast;
+ assign m_axis_tid = post_fifo_axis_tid;
+ assign m_axis_tdest = post_fifo_axis_tdest;
+ assign m_axis_tuser = post_fifo_axis_tuser;
+
+end else begin
+
+ // input wider, adapt width after FIFO
+
+ assign pre_fifo_axis_tdata = s_axis_tdata;
+ assign pre_fifo_axis_tkeep = s_axis_tkeep;
+ assign pre_fifo_axis_tvalid = s_axis_tvalid;
+ assign s_axis_tready = pre_fifo_axis_tready;
+ assign pre_fifo_axis_tlast = s_axis_tlast;
+ assign pre_fifo_axis_tid = s_axis_tid;
+ assign pre_fifo_axis_tdest = s_axis_tdest;
+ assign pre_fifo_axis_tuser = s_axis_tuser;
+
+ axis_adapter #(
+ .S_DATA_WIDTH(S_DATA_WIDTH),
+ .S_KEEP_ENABLE(S_KEEP_ENABLE),
+ .S_KEEP_WIDTH(S_KEEP_WIDTH),
+ .M_DATA_WIDTH(M_DATA_WIDTH),
+ .M_KEEP_ENABLE(M_KEEP_ENABLE),
+ .M_KEEP_WIDTH(M_KEEP_WIDTH),
+ .ID_ENABLE(ID_ENABLE),
+ .ID_WIDTH(ID_WIDTH),
+ .DEST_ENABLE(DEST_ENABLE),
+ .DEST_WIDTH(DEST_WIDTH),
+ .USER_ENABLE(USER_ENABLE),
+ .USER_WIDTH(USER_WIDTH)
+ )
+ adapter_inst (
+ .clk(m_clk),
+ .rst(m_rst),
+ // AXI input
+ .s_axis_tdata(post_fifo_axis_tdata),
+ .s_axis_tkeep(post_fifo_axis_tkeep),
+ .s_axis_tvalid(post_fifo_axis_tvalid),
+ .s_axis_tready(post_fifo_axis_tready),
+ .s_axis_tlast(post_fifo_axis_tlast),
+ .s_axis_tid(post_fifo_axis_tid),
+ .s_axis_tdest(post_fifo_axis_tdest),
+ .s_axis_tuser(post_fifo_axis_tuser),
+ // AXI output
+ .m_axis_tdata(m_axis_tdata),
+ .m_axis_tkeep(m_axis_tkeep),
+ .m_axis_tvalid(m_axis_tvalid),
+ .m_axis_tready(m_axis_tready),
+ .m_axis_tlast(m_axis_tlast),
+ .m_axis_tid(m_axis_tid),
+ .m_axis_tdest(m_axis_tdest),
+ .m_axis_tuser(m_axis_tuser)
+ );
+
+end
+
+endgenerate
+
+axis_async_fifo #(
+ .DEPTH(DEPTH),
+ .DATA_WIDTH(DATA_WIDTH),
+ .KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE),
+ .KEEP_WIDTH(KEEP_WIDTH),
+ .LAST_ENABLE(1),
+ .ID_ENABLE(ID_ENABLE),
+ .ID_WIDTH(ID_WIDTH),
+ .DEST_ENABLE(DEST_ENABLE),
+ .DEST_WIDTH(DEST_WIDTH),
+ .USER_ENABLE(USER_ENABLE),
+ .USER_WIDTH(USER_WIDTH),
+ .PIPELINE_OUTPUT(PIPELINE_OUTPUT),
+ .FRAME_FIFO(FRAME_FIFO),
+ .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE),
+ .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK),
+ .DROP_BAD_FRAME(DROP_BAD_FRAME),
+ .DROP_WHEN_FULL(DROP_WHEN_FULL)
+)
+fifo_inst (
+ // Common reset
+ .async_rst(s_rst | m_rst),
+ // AXI input
+ .s_clk(s_clk),
+ .s_axis_tdata(pre_fifo_axis_tdata),
+ .s_axis_tkeep(pre_fifo_axis_tkeep),
+ .s_axis_tvalid(pre_fifo_axis_tvalid),
+ .s_axis_tready(pre_fifo_axis_tready),
+ .s_axis_tlast(pre_fifo_axis_tlast),
+ .s_axis_tid(pre_fifo_axis_tid),
+ .s_axis_tdest(pre_fifo_axis_tdest),
+ .s_axis_tuser(pre_fifo_axis_tuser),
+ // AXI output
+ .m_clk(m_clk),
+ .m_axis_tdata(post_fifo_axis_tdata),
+ .m_axis_tkeep(post_fifo_axis_tkeep),
+ .m_axis_tvalid(post_fifo_axis_tvalid),
+ .m_axis_tready(post_fifo_axis_tready),
+ .m_axis_tlast(post_fifo_axis_tlast),
+ .m_axis_tid(post_fifo_axis_tid),
+ .m_axis_tdest(post_fifo_axis_tdest),
+ .m_axis_tuser(post_fifo_axis_tuser),
+ // Status
+ .s_status_overflow(s_status_overflow),
+ .s_status_bad_frame(s_status_bad_frame),
+ .s_status_good_frame(s_status_good_frame),
+ .m_status_overflow(m_status_overflow),
+ .m_status_bad_frame(m_status_bad_frame),
+ .m_status_good_frame(m_status_good_frame)
+);
+
+endmodule
diff --git a/verilog/rtl/axis_gmii_rx.v b/verilog/rtl/axis_gmii_rx.v
new file mode 100644
index 0000000..7d75b96
--- /dev/null
+++ b/verilog/rtl/axis_gmii_rx.v
@@ -0,0 +1,370 @@
+/*
+
+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 #
+(
+ parameter DATA_WIDTH = 8,
+ parameter PTP_TS_ENABLE = 0,
+ parameter PTP_TS_WIDTH = 96,
+ parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1
+)
+(
+ input wire clk,
+ input wire rst,
+
+ /*
+ * GMII input
+ */
+ input wire [DATA_WIDTH-1:0] gmii_rxd,
+ input wire gmii_rx_dv,
+ input wire gmii_rx_er,
+
+ /*
+ * AXI output
+ */
+ output wire [DATA_WIDTH-1:0] m_axis_tdata,
+ output wire m_axis_tvalid,
+ output wire m_axis_tlast,
+ output wire [USER_WIDTH-1:0] m_axis_tuser,
+
+ /*
+ * PTP
+ */
+ input wire [PTP_TS_WIDTH-1:0] ptp_ts,
+
+ /*
+ * Control
+ */
+ input wire clk_enable,
+ input wire mii_select,
+
+ /*
+ * Status
+ */
+ output wire start_packet,
+ output wire error_bad_frame,
+ output wire error_bad_fcs
+);
+
+// bus width assertions
+initial begin
+ if (DATA_WIDTH != 8) begin
+ $error("Error: Interface width must be 8");
+ $finish;
+ end
+end
+
+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_next;
+
+// datapath control signals
+reg reset_crc;
+reg update_crc;
+
+reg mii_odd;
+reg mii_locked;
+
+reg [DATA_WIDTH-1:0] gmii_rxd_d0;
+reg [DATA_WIDTH-1:0] gmii_rxd_d1;
+reg [DATA_WIDTH-1:0] gmii_rxd_d2;
+reg [DATA_WIDTH-1:0] gmii_rxd_d3;
+reg [DATA_WIDTH-1:0] gmii_rxd_d4;
+
+reg gmii_rx_dv_d0;
+reg gmii_rx_dv_d1;
+reg gmii_rx_dv_d2;
+reg gmii_rx_dv_d3;
+reg gmii_rx_dv_d4;
+
+reg gmii_rx_er_d0;
+reg gmii_rx_er_d1;
+reg gmii_rx_er_d2;
+reg gmii_rx_er_d3;
+reg gmii_rx_er_d4;
+
+reg [DATA_WIDTH-1:0] m_axis_tdata_reg, m_axis_tdata_next;
+reg m_axis_tvalid_reg, m_axis_tvalid_next;
+reg m_axis_tlast_reg, m_axis_tlast_next;
+reg m_axis_tuser_reg, m_axis_tuser_next;
+
+reg start_packet_reg, start_packet_next;
+reg error_bad_frame_reg, error_bad_frame_next;
+reg error_bad_fcs_reg, error_bad_fcs_next;
+
+reg [PTP_TS_WIDTH-1:0] ptp_ts_reg, ptp_ts_next;
+
+reg [31:0] crc_state;
+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 = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg;
+
+assign start_packet = start_packet_reg;
+assign error_bad_frame = error_bad_frame_reg;
+assign error_bad_fcs = error_bad_fcs_reg;
+
+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 = {DATA_WIDTH{1'b0}};
+ m_axis_tvalid_next = 1'b0;
+ m_axis_tlast_next = 1'b0;
+ m_axis_tuser_next = 1'b0;
+
+ start_packet_next = 1'b0;
+ error_bad_frame_next = 1'b0;
+ error_bad_fcs_next = 1'b0;
+
+ ptp_ts_next = ptp_ts_reg;
+
+ 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
+ ptp_ts_next = ptp_ts;
+ start_packet_next = 1'b1;
+ 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_tdata_reg <= {DATA_WIDTH{1'b0}};
+ m_axis_tvalid_reg <= 1'b0;
+ m_axis_tlast_reg <= 1'b0;
+ m_axis_tuser_reg <= 1'b0;
+
+ start_packet_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;
+
+ ptp_ts_reg <= 0;
+ end else begin
+ state_reg <= state_next;
+
+ m_axis_tvalid_reg <= m_axis_tvalid_next;
+
+ start_packet_reg <= start_packet_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
+
+ ptp_ts_reg <= ptp_ts_next;
+
+ m_axis_tdata_reg <= m_axis_tdata_next;
+ m_axis_tlast_reg <= m_axis_tlast_next;
+ m_axis_tuser_reg <= m_axis_tuser_next;
+ end
+
+
+ // delay input
+ if (rst) begin
+ gmii_rxd_d0 <= {DATA_WIDTH{1'b0}};
+ gmii_rxd_d1 <= {DATA_WIDTH{1'b0}};
+ gmii_rxd_d2 <= {DATA_WIDTH{1'b0}};
+ gmii_rxd_d3 <= {DATA_WIDTH{1'b0}};
+ gmii_rxd_d4 <= {DATA_WIDTH{1'b0}};
+
+ gmii_rx_er_d0 <= 1'b0;
+ gmii_rx_er_d1 <= 1'b0;
+ gmii_rx_er_d2 <= 1'b0;
+ gmii_rx_er_d3 <= 1'b0;
+ gmii_rx_er_d4 <= 1'b0;
+ end else if (clk_enable) begin
+ if (mii_select) begin
+ gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]};
+
+ if (mii_odd) begin
+ gmii_rxd_d1 <= gmii_rxd_d0;
+ gmii_rxd_d2 <= gmii_rxd_d1;
+ gmii_rxd_d3 <= gmii_rxd_d2;
+ gmii_rxd_d4 <= gmii_rxd_d3;
+
+ gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0;
+ gmii_rx_er_d1 <= gmii_rx_er_d0;
+ gmii_rx_er_d2 <= gmii_rx_er_d1;
+ gmii_rx_er_d3 <= gmii_rx_er_d2;
+ gmii_rx_er_d4 <= gmii_rx_er_d3;
+ end else begin
+ gmii_rx_er_d0 <= gmii_rx_er;
+ end
+ end else begin
+ gmii_rxd_d0 <= gmii_rxd;
+ gmii_rxd_d1 <= gmii_rxd_d0;
+ gmii_rxd_d2 <= gmii_rxd_d1;
+ gmii_rxd_d3 <= gmii_rxd_d2;
+ gmii_rxd_d4 <= gmii_rxd_d3;
+
+ gmii_rx_er_d0 <= gmii_rx_er;
+ gmii_rx_er_d1 <= gmii_rx_er_d0;
+ gmii_rx_er_d2 <= gmii_rx_er_d1;
+ gmii_rx_er_d3 <= gmii_rx_er_d2;
+ gmii_rx_er_d4 <= gmii_rx_er_d3;
+ end
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/axis_gmii_tx.v b/verilog/rtl/axis_gmii_tx.v
new file mode 100644
index 0000000..18e11e0
--- /dev/null
+++ b/verilog/rtl/axis_gmii_tx.v
@@ -0,0 +1,459 @@
+/*
+
+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 DATA_WIDTH = 8,
+ parameter ENABLE_PADDING = 1,
+ parameter MIN_FRAME_LENGTH = 64,
+ parameter PTP_TS_ENABLE = 0,
+ parameter PTP_TS_WIDTH = 96,
+ parameter PTP_TAG_ENABLE = PTP_TS_ENABLE,
+ parameter PTP_TAG_WIDTH = 16,
+ parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1
+)
+(
+ input wire clk,
+ input wire rst,
+
+ /*
+ * AXI input
+ */
+ input wire [DATA_WIDTH-1:0] s_axis_tdata,
+ input wire s_axis_tvalid,
+ output wire s_axis_tready,
+ input wire s_axis_tlast,
+ input wire [USER_WIDTH-1:0] s_axis_tuser,
+
+ /*
+ * GMII output
+ */
+ output wire [DATA_WIDTH-1:0] gmii_txd,
+ output wire gmii_tx_en,
+ output wire gmii_tx_er,
+
+ /*
+ * PTP
+ */
+ input wire [PTP_TS_WIDTH-1:0] ptp_ts,
+ output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts,
+ output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag,
+ output wire m_axis_ptp_ts_valid,
+
+ /*
+ * Control
+ */
+ input wire clk_enable,
+ input wire mii_select,
+
+ /*
+ * Configuration
+ */
+ input wire [7:0] ifg_delay,
+
+ /*
+ * Status
+ */
+ output wire start_packet,
+ output wire error_underflow
+);
+
+// bus width assertions
+initial begin
+ if (DATA_WIDTH != 8) begin
+ $error("Error: Interface width must be 8");
+ $finish;
+ end
+end
+
+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_next;
+
+// datapath control signals
+reg reset_crc;
+reg update_crc;
+
+reg [7:0] s_tdata_reg, s_tdata_next;
+
+reg mii_odd_reg, mii_odd_next;
+reg [3:0] mii_msn_reg, mii_msn_next;
+
+reg [15:0] frame_ptr_reg, frame_ptr_next;
+
+reg [7:0] gmii_txd_reg, gmii_txd_next;
+reg gmii_tx_en_reg, gmii_tx_en_next;
+reg gmii_tx_er_reg, gmii_tx_er_next;
+
+reg s_axis_tready_reg, s_axis_tready_next;
+
+reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg, m_axis_ptp_ts_next;
+reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg, m_axis_ptp_ts_tag_next;
+reg m_axis_ptp_ts_valid_reg, m_axis_ptp_ts_valid_next;
+
+reg start_packet_reg, start_packet_next;
+reg error_underflow_reg, error_underflow_next;
+
+reg [31:0] crc_state;
+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;
+
+assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0;
+assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0;
+assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0;
+
+assign start_packet = start_packet_reg;
+assign error_underflow = error_underflow_reg;
+
+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;
+
+ m_axis_ptp_ts_next = m_axis_ptp_ts_reg;
+ m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg;
+ m_axis_ptp_ts_valid_next = 1'b0;
+
+ gmii_txd_next = {DATA_WIDTH{1'b0}};
+ gmii_tx_en_next = 1'b0;
+ gmii_tx_er_next = 1'b0;
+
+ start_packet_next = 1'b0;
+ error_underflow_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;
+ m_axis_ptp_ts_next = ptp_ts;
+ m_axis_ptp_ts_tag_next = s_axis_tuser >> 1;
+ m_axis_ptp_ts_valid_next = 1'b1;
+ start_packet_next = 1'b1;
+ 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[0]) 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;
+ error_underflow_next = 1'b1;
+ 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;
+
+
+ m_axis_ptp_ts_valid_reg <= 1'b0;
+
+ gmii_tx_en_reg <= 1'b0;
+ gmii_tx_er_reg <= 1'b0;
+
+ start_packet_reg <= 1'b0;
+ error_underflow_reg <= 1'b0;
+
+ m_axis_ptp_ts_reg <= 0;
+ m_axis_ptp_ts_tag_reg <= 0;
+
+ mii_odd_reg <= 1'b0;
+ mii_msn_reg <= 4'b0;
+
+ s_tdata_reg <= 8'd0;
+
+ gmii_txd_reg <= 8'd0;
+
+ 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;
+
+ m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next;
+
+ gmii_tx_en_reg <= gmii_tx_en_next;
+ gmii_tx_er_reg <= gmii_tx_er_next;
+
+ start_packet_reg <= start_packet_next;
+ error_underflow_reg <= error_underflow_next;
+
+ m_axis_ptp_ts_reg <= m_axis_ptp_ts_next;
+ m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next;
+
+ mii_odd_reg <= mii_odd_next;
+ mii_msn_reg <= mii_msn_next;
+
+ s_tdata_reg <= s_tdata_next;
+
+ gmii_txd_reg <= gmii_txd_next;
+ // datapath
+ if (reset_crc) begin
+ crc_state <= 32'hFFFFFFFF;
+ end else if (update_crc) begin
+ crc_state <= crc_next;
+ end
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/eth_arb_mux.v b/verilog/rtl/eth_arb_mux.v
new file mode 100644
index 0000000..a418c76
--- /dev/null
+++ b/verilog/rtl/eth_arb_mux.v
@@ -0,0 +1,327 @@
+/*
+
+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,
+ // select round robin arbitration
+ parameter ARB_TYPE_ROUND_ROBIN = 0,
+ // LSB priority selection
+ parameter ARB_LSB_HIGH_PRIORITY = 1
+)
+(
+ 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, frame_next;
+
+reg [S_COUNT-1:0] s_eth_hdr_ready_reg, s_eth_hdr_ready_next;
+
+reg m_eth_hdr_valid_reg, m_eth_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg, m_eth_dest_mac_next;
+reg [47:0] m_eth_src_mac_reg, m_eth_src_mac_next;
+reg [15:0] m_eth_type_reg, 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;
+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_reg;
+
+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
+arbiter #(
+ .PORTS(S_COUNT),
+ .ARB_TYPE_ROUND_ROBIN(ARB_TYPE_ROUND_ROBIN),
+ .ARB_BLOCK(1),
+ .ARB_BLOCK_ACK(1),
+ .ARB_LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
+)
+arb_inst (
+ .clk(clk),
+ .rst(rst),
+ .request(request),
+ .acknowledge(acknowledge),
+ .grant(grant),
+ .grant_valid(grant_valid),
+ .grant_encoded(grant_encoded)
+);
+
+assign request = s_eth_hdr_valid & ~grant;
+assign acknowledge = grant & s_eth_payload_axis_tvalid & s_eth_payload_axis_tready & s_eth_payload_axis_tlast;
+
+always @* begin
+ frame_next = frame_reg;
+
+ s_eth_hdr_ready_next = {S_COUNT{1'b0}};
+
+ 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;
+ end
+ end
+
+ if (!frame_reg && grant_valid && (m_eth_hdr_ready || !m_eth_hdr_valid)) begin
+ // start of frame
+ frame_next = 1'b1;
+
+ s_eth_hdr_ready_next = grant;
+
+ 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
+ frame_reg <= frame_next;
+
+ s_eth_hdr_ready_reg <= s_eth_hdr_ready_next;
+
+ m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
+ 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;
+
+ if (rst) begin
+ frame_reg <= 1'b0;
+ s_eth_hdr_ready_reg <= {S_COUNT{1'b0}};
+ m_eth_hdr_valid_reg <= 1'b0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ end
+end
+
+// output datapath logic
+reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg;
+reg m_eth_payload_axis_tvalid_reg, m_eth_payload_axis_tvalid_next;
+reg m_eth_payload_axis_tlast_reg;
+reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg;
+reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg;
+reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg;
+
+reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg;
+reg temp_m_eth_payload_axis_tvalid_reg, temp_m_eth_payload_axis_tvalid_next;
+reg temp_m_eth_payload_axis_tlast_reg;
+reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg;
+reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg;
+reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg;
+
+// 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_tready_int_reg <= 1'b0;
+ m_eth_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ m_eth_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ m_eth_payload_axis_tvalid_reg <= 1'b0;
+ m_eth_payload_axis_tlast_reg <= 1'b0;
+ m_eth_payload_axis_tid_reg <= {ID_WIDTH{1'b0}};
+ m_eth_payload_axis_tdest_reg <= {DEST_WIDTH{1'b0}};
+ m_eth_payload_axis_tuser_reg <= {USER_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_eth_payload_axis_tlast_reg <= 1'b0;
+ temp_m_eth_payload_axis_tid_reg <= {ID_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tdest_reg <= {DEST_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tuser_reg <= {USER_WIDTH{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;
+
+ // 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
+end
+
+endmodule
+
diff --git a/verilog/rtl/eth_axis_rx.v b/verilog/rtl/eth_axis_rx.v
new file mode 100644
index 0000000..aa9150e
--- /dev/null
+++ b/verilog/rtl/eth_axis_rx.v
@@ -0,0 +1,416 @@
+/*
+
+Copyright (c) 2014-2020 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 #
+(
+ // Width of AXI stream interfaces in bits
+ parameter DATA_WIDTH = 8,
+ // Propagate tkeep signal
+ // If disabled, tkeep assumed to be 1'b1
+ parameter KEEP_ENABLE = (DATA_WIDTH>8),
+ // tkeep signal width (words per cycle)
+ parameter KEEP_WIDTH = (DATA_WIDTH/8)
+)
+(
+ 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 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 [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 m_eth_payload_axis_tuser,
+
+ /*
+ * Status signals
+ */
+ output wire busy,
+ output wire error_header_early_termination
+);
+
+parameter CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH;
+
+parameter PTR_WIDTH = $clog2(CYCLE_COUNT);
+
+parameter OFFSET = 14 % KEEP_WIDTH;
+
+// bus width assertions
+initial begin
+ if (KEEP_WIDTH * 8 != DATA_WIDTH) begin
+ $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)");
+ $finish;
+ end
+end
+
+/*
+
+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.
+
+*/
+
+reg read_eth_header_reg, read_eth_header_next;
+reg read_eth_payload_reg, read_eth_payload_next;
+reg [PTR_WIDTH-1:0] ptr_reg, ptr_next;
+
+reg flush_save;
+reg transfer_in_save;
+
+reg s_axis_tready_reg, s_axis_tready_next;
+
+reg m_eth_hdr_valid_reg, m_eth_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg, m_eth_dest_mac_next;
+reg [47:0] m_eth_src_mac_reg, m_eth_src_mac_next;
+reg [15:0] m_eth_type_reg, m_eth_type_next;
+
+reg busy_reg;
+reg error_header_early_termination_reg, error_header_early_termination_next;
+
+reg [DATA_WIDTH-1:0] save_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] save_axis_tkeep_reg;
+reg save_axis_tlast_reg;
+reg save_axis_tuser_reg;
+
+reg [DATA_WIDTH-1:0] shift_axis_tdata;
+reg [KEEP_WIDTH-1:0] shift_axis_tkeep;
+reg shift_axis_tvalid;
+reg shift_axis_tlast;
+reg shift_axis_tuser;
+reg shift_axis_input_tready;
+reg shift_axis_extra_cycle_reg;
+
+// 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;
+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
+ if (OFFSET == 0) begin
+ // passthrough if no overlap
+ shift_axis_tdata = s_axis_tdata;
+ shift_axis_tkeep = s_axis_tkeep;
+ shift_axis_tvalid = s_axis_tvalid;
+ shift_axis_tlast = s_axis_tlast;
+ shift_axis_tuser = s_axis_tuser;
+ shift_axis_input_tready = 1'b1;
+ end else if (shift_axis_extra_cycle_reg) begin
+ shift_axis_tdata = {s_axis_tdata, save_axis_tdata_reg} >> (OFFSET*8);
+ shift_axis_tkeep = {{KEEP_WIDTH{1'b0}}, save_axis_tkeep_reg} >> OFFSET;
+ shift_axis_tvalid = 1'b1;
+ shift_axis_tlast = save_axis_tlast_reg;
+ shift_axis_tuser = save_axis_tuser_reg;
+ shift_axis_input_tready = flush_save;
+ end else begin
+ shift_axis_tdata = {s_axis_tdata, save_axis_tdata_reg} >> (OFFSET*8);
+ shift_axis_tkeep = {s_axis_tkeep, save_axis_tkeep_reg} >> OFFSET;
+ shift_axis_tvalid = s_axis_tvalid;
+ shift_axis_tlast = (s_axis_tlast && ((s_axis_tkeep & ({KEEP_WIDTH{1'b1}} << OFFSET)) == 0));
+ shift_axis_tuser = (s_axis_tuser && ((s_axis_tkeep & ({KEEP_WIDTH{1'b1}} << OFFSET)) == 0));
+ shift_axis_input_tready = !(s_axis_tlast && s_axis_tready && s_axis_tvalid);
+ end
+end
+
+always @* begin
+ read_eth_header_next = read_eth_header_reg;
+ read_eth_payload_next = read_eth_payload_reg;
+ ptr_next = ptr_reg;
+
+ s_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_axis_input_tready && (!m_eth_hdr_valid || m_eth_hdr_ready);
+
+ flush_save = 1'b0;
+ transfer_in_save = 1'b0;
+
+ 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;
+
+ error_header_early_termination_next = 1'b0;
+
+ m_eth_payload_axis_tdata_int = shift_axis_tdata;
+ m_eth_payload_axis_tkeep_int = shift_axis_tkeep;
+ m_eth_payload_axis_tvalid_int = 1'b0;
+ m_eth_payload_axis_tlast_int = shift_axis_tlast;
+ m_eth_payload_axis_tuser_int = shift_axis_tuser;
+
+ if ((s_axis_tready && s_axis_tvalid) || (m_eth_payload_axis_tready_int_reg && shift_axis_extra_cycle_reg)) begin
+ transfer_in_save = 1'b1;
+
+ if (read_eth_header_reg) begin
+ // word transfer in - store it
+ ptr_next = ptr_reg + 1;
+
+ `define _HEADER_FIELD_(offset, field) \
+ if (ptr_reg == offset/KEEP_WIDTH && (!KEEP_ENABLE || s_axis_tkeep[offset%KEEP_WIDTH])) begin \
+ field = s_axis_tdata[(offset%KEEP_WIDTH)*8 +: 8]; \
+ end
+
+ `_HEADER_FIELD_(0, m_eth_dest_mac_next[5*8 +: 8])
+ `_HEADER_FIELD_(1, m_eth_dest_mac_next[4*8 +: 8])
+ `_HEADER_FIELD_(2, m_eth_dest_mac_next[3*8 +: 8])
+ `_HEADER_FIELD_(3, m_eth_dest_mac_next[2*8 +: 8])
+ `_HEADER_FIELD_(4, m_eth_dest_mac_next[1*8 +: 8])
+ `_HEADER_FIELD_(5, m_eth_dest_mac_next[0*8 +: 8])
+ `_HEADER_FIELD_(6, m_eth_src_mac_next[5*8 +: 8])
+ `_HEADER_FIELD_(7, m_eth_src_mac_next[4*8 +: 8])
+ `_HEADER_FIELD_(8, m_eth_src_mac_next[3*8 +: 8])
+ `_HEADER_FIELD_(9, m_eth_src_mac_next[2*8 +: 8])
+ `_HEADER_FIELD_(10, m_eth_src_mac_next[1*8 +: 8])
+ `_HEADER_FIELD_(11, m_eth_src_mac_next[0*8 +: 8])
+ `_HEADER_FIELD_(12, m_eth_type_next[1*8 +: 8])
+ `_HEADER_FIELD_(13, m_eth_type_next[0*8 +: 8])
+
+ if (ptr_reg == 13/KEEP_WIDTH && (!KEEP_ENABLE || s_axis_tkeep[13%KEEP_WIDTH])) begin
+ if (!shift_axis_tlast) begin
+ m_eth_hdr_valid_next = 1'b1;
+ read_eth_header_next = 1'b0;
+ read_eth_payload_next = 1'b1;
+ end
+ end
+
+ `undef _HEADER_FIELD_
+ end
+
+ if (read_eth_payload_reg) begin
+ // transfer payload
+ m_eth_payload_axis_tdata_int = shift_axis_tdata;
+ m_eth_payload_axis_tkeep_int = shift_axis_tkeep;
+ m_eth_payload_axis_tvalid_int = 1'b1;
+ m_eth_payload_axis_tlast_int = shift_axis_tlast;
+ m_eth_payload_axis_tuser_int = shift_axis_tuser;
+ end
+
+ if (shift_axis_tlast) begin
+ if (read_eth_header_next) begin
+ // don't have the whole header
+ error_header_early_termination_next = 1'b1;
+ end
+
+ flush_save = 1'b1;
+ ptr_next = 1'b0;
+ read_eth_header_next = 1'b1;
+ read_eth_payload_next = 1'b0;
+ end
+ end
+end
+
+always @(posedge clk) begin
+ read_eth_header_reg <= read_eth_header_next;
+ read_eth_payload_reg <= read_eth_payload_next;
+ ptr_reg <= ptr_next;
+
+ s_axis_tready_reg <= s_axis_tready_next;
+
+ m_eth_hdr_valid_reg <= m_eth_hdr_valid_next;
+ 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;
+
+ error_header_early_termination_reg <= error_header_early_termination_next;
+
+ busy_reg <= (read_eth_payload_next || ptr_next != 0);
+
+ if (transfer_in_save) begin
+ save_axis_tdata_reg <= s_axis_tdata;
+ save_axis_tkeep_reg <= s_axis_tkeep;
+ save_axis_tuser_reg <= s_axis_tuser;
+ end
+
+ if (flush_save) begin
+ save_axis_tlast_reg <= 1'b0;
+ shift_axis_extra_cycle_reg <= 1'b0;
+ end else if (transfer_in_save) begin
+ save_axis_tlast_reg <= s_axis_tlast;
+ shift_axis_extra_cycle_reg <= OFFSET ? s_axis_tlast && ((s_axis_tkeep & ({KEEP_WIDTH{1'b1}} << OFFSET)) != 0) : 1'b0;
+ end
+
+ if (rst) begin
+ read_eth_header_reg <= 1'b1;
+ read_eth_payload_reg <= 1'b0;
+ ptr_reg <= 0;
+ s_axis_tready_reg <= 1'b0;
+ m_eth_hdr_valid_reg <= 1'b0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ save_axis_tlast_reg <= 1'b0;
+ shift_axis_extra_cycle_reg <= 1'b0;
+ busy_reg <= 1'b0;
+ error_header_early_termination_reg <= 1'b0;
+ save_axis_tdata_reg <= 64'd0;
+ save_axis_tkeep_reg <= 8'd0;
+ save_axis_tlast_reg <= 1'b0;
+ save_axis_tuser_reg <= 1'b0;
+ end
+end
+
+// output datapath logic
+reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg;
+reg m_eth_payload_axis_tvalid_reg, m_eth_payload_axis_tvalid_next;
+reg m_eth_payload_axis_tlast_reg;
+reg m_eth_payload_axis_tuser_reg;
+
+reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg;
+reg temp_m_eth_payload_axis_tvalid_reg, temp_m_eth_payload_axis_tvalid_next;
+reg temp_m_eth_payload_axis_tlast_reg;
+reg temp_m_eth_payload_axis_tuser_reg;
+
+// 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_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_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_tdata_reg <= {DATA_WIDTH{1'b0}};
+ m_eth_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ m_eth_payload_axis_tvalid_reg <= 1'b0;
+ m_eth_payload_axis_tlast_reg <= 1'b0;
+ m_eth_payload_axis_tuser_reg <= 1'b0;
+
+ m_eth_payload_axis_tready_int_reg <= 1'b0;
+
+ temp_m_eth_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_eth_payload_axis_tlast_reg <= 1'b0;
+ temp_m_eth_payload_axis_tuser_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;
+
+ // 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_tkeep_reg <= m_eth_payload_axis_tkeep_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_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_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_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_tuser_reg <= m_eth_payload_axis_tuser_int;
+ end
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/eth_axis_tx.v b/verilog/rtl/eth_axis_tx.v
new file mode 100644
index 0000000..8aaa588
--- /dev/null
+++ b/verilog/rtl/eth_axis_tx.v
@@ -0,0 +1,419 @@
+/*
+
+Copyright (c) 2014-2020 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 #
+(
+ // Width of AXI stream interfaces in bits
+ parameter DATA_WIDTH = 8,
+ // Propagate tkeep signal
+ // If disabled, tkeep assumed to be 1'b1
+ parameter KEEP_ENABLE = (DATA_WIDTH>8),
+ // tkeep signal width (words per cycle)
+ parameter KEEP_WIDTH = (DATA_WIDTH/8)
+)
+(
+ 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 [DATA_WIDTH-1:0] s_eth_payload_axis_tdata,
+ input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep,
+ 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 [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 m_axis_tuser,
+
+ /*
+ * Status signals
+ */
+ output wire busy
+);
+
+parameter CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH;
+
+parameter PTR_WIDTH = $clog2(CYCLE_COUNT);
+
+parameter OFFSET = 14 % KEEP_WIDTH;
+
+// bus width assertions
+initial begin
+ if (KEEP_WIDTH * 8 != DATA_WIDTH) begin
+ $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)");
+ $finish;
+ end
+end
+
+/*
+
+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.
+
+*/
+
+// datapath control signals
+reg store_eth_hdr;
+
+reg send_eth_header_reg, send_eth_header_next;
+reg send_eth_payload_reg, send_eth_payload_next;
+reg [PTR_WIDTH-1:0] ptr_reg, ptr_next;
+
+reg flush_save;
+reg transfer_in_save;
+
+reg [47:0] eth_dest_mac_reg;
+reg [47:0] eth_src_mac_reg;
+reg [15:0] eth_type_reg;
+
+reg s_eth_hdr_ready_reg, s_eth_hdr_ready_next;
+reg s_eth_payload_axis_tready_reg, s_eth_payload_axis_tready_next;
+
+reg busy_reg;
+
+reg [DATA_WIDTH-1:0] save_eth_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] save_eth_payload_axis_tkeep_reg;
+reg save_eth_payload_axis_tlast_reg;
+reg save_eth_payload_axis_tuser_reg;
+
+reg [DATA_WIDTH-1:0] shift_eth_payload_axis_tdata;
+reg [KEEP_WIDTH-1:0] shift_eth_payload_axis_tkeep;
+reg shift_eth_payload_axis_tvalid;
+reg shift_eth_payload_axis_tlast;
+reg shift_eth_payload_axis_tuser;
+reg shift_eth_payload_axis_input_tready;
+reg shift_eth_payload_axis_extra_cycle_reg;
+
+// internal datapath
+reg [DATA_WIDTH-1:0] m_axis_tdata_int;
+reg [KEEP_WIDTH-1:0] m_axis_tkeep_int;
+reg m_axis_tvalid_int;
+reg m_axis_tready_int_reg;
+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
+ if (OFFSET == 0) begin
+ // passthrough if no overlap
+ shift_eth_payload_axis_tdata = s_eth_payload_axis_tdata;
+ shift_eth_payload_axis_tkeep = s_eth_payload_axis_tkeep;
+ shift_eth_payload_axis_tvalid = s_eth_payload_axis_tvalid;
+ shift_eth_payload_axis_tlast = s_eth_payload_axis_tlast;
+ shift_eth_payload_axis_tuser = s_eth_payload_axis_tuser;
+ shift_eth_payload_axis_input_tready = 1'b1;
+ end else if (shift_eth_payload_axis_extra_cycle_reg) begin
+ shift_eth_payload_axis_tdata = {s_eth_payload_axis_tdata, save_eth_payload_axis_tdata_reg} >> ((KEEP_WIDTH-OFFSET)*8);
+ shift_eth_payload_axis_tkeep = {{KEEP_WIDTH{1'b0}}, save_eth_payload_axis_tkeep_reg} >> (KEEP_WIDTH-OFFSET);
+ shift_eth_payload_axis_tvalid = 1'b1;
+ shift_eth_payload_axis_tlast = save_eth_payload_axis_tlast_reg;
+ shift_eth_payload_axis_tuser = save_eth_payload_axis_tuser_reg;
+ shift_eth_payload_axis_input_tready = flush_save;
+ end else begin
+ shift_eth_payload_axis_tdata = {s_eth_payload_axis_tdata, save_eth_payload_axis_tdata_reg} >> ((KEEP_WIDTH-OFFSET)*8);
+ shift_eth_payload_axis_tkeep = {s_eth_payload_axis_tkeep, save_eth_payload_axis_tkeep_reg} >> (KEEP_WIDTH-OFFSET);
+ shift_eth_payload_axis_tvalid = s_eth_payload_axis_tvalid;
+ shift_eth_payload_axis_tlast = (s_eth_payload_axis_tlast && ((s_eth_payload_axis_tkeep & ({KEEP_WIDTH{1'b1}} << (KEEP_WIDTH-OFFSET))) == 0));
+ shift_eth_payload_axis_tuser = (s_eth_payload_axis_tuser && ((s_eth_payload_axis_tkeep & ({KEEP_WIDTH{1'b1}} << (KEEP_WIDTH-OFFSET))) == 0));
+ shift_eth_payload_axis_input_tready = !(s_eth_payload_axis_tlast && s_eth_payload_axis_tready && s_eth_payload_axis_tvalid);
+ end
+end
+
+always @* begin
+ send_eth_header_next = send_eth_header_reg;
+ send_eth_payload_next = send_eth_payload_reg;
+ ptr_next = ptr_reg;
+
+ s_eth_hdr_ready_next = 1'b0;
+ s_eth_payload_axis_tready_next = 1'b0;
+
+ store_eth_hdr = 1'b0;
+
+ flush_save = 1'b0;
+ transfer_in_save = 1'b0;
+
+ m_axis_tdata_int = {DATA_WIDTH{1'b0}};
+ m_axis_tkeep_int = {KEEP_WIDTH{1'b0}};
+ m_axis_tvalid_int = 1'b0;
+ m_axis_tlast_int = 1'b0;
+ m_axis_tuser_int = 1'b0;
+
+ if (s_eth_hdr_ready && s_eth_hdr_valid) begin
+ store_eth_hdr = 1'b1;
+ ptr_next = 0;
+ send_eth_header_next = 1'b1;
+ send_eth_payload_next = (OFFSET != 0) && (CYCLE_COUNT == 1);
+ s_eth_payload_axis_tready_next = send_eth_payload_next && m_axis_tready_int_early;
+ end
+
+ if (send_eth_payload_reg) begin
+ s_eth_payload_axis_tready_next = m_axis_tready_int_early && shift_eth_payload_axis_input_tready;
+
+ if ((s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) || (m_axis_tready_int_reg && shift_eth_payload_axis_extra_cycle_reg)) begin
+ transfer_in_save = 1'b1;
+
+ m_axis_tdata_int = shift_eth_payload_axis_tdata;
+ m_axis_tkeep_int = shift_eth_payload_axis_tkeep;
+ m_axis_tvalid_int = 1'b1;
+ m_axis_tlast_int = shift_eth_payload_axis_tlast;
+ m_axis_tuser_int = shift_eth_payload_axis_tuser;
+
+ if (shift_eth_payload_axis_tlast) begin
+ flush_save = 1'b1;
+ s_eth_payload_axis_tready_next = 1'b0;
+ ptr_next = 0;
+ send_eth_payload_next = 1'b0;
+ end
+ end
+ end
+
+ if (m_axis_tready_int_reg && (!OFFSET || !send_eth_payload_reg || m_axis_tvalid_int)) begin
+ if (send_eth_header_reg) begin
+ ptr_next = ptr_reg + 1;
+
+ if ((OFFSET != 0) && (CYCLE_COUNT == 1 || ptr_next == CYCLE_COUNT-1) && !send_eth_payload_reg) begin
+ send_eth_payload_next = 1'b1;
+ s_eth_payload_axis_tready_next = m_axis_tready_int_early && shift_eth_payload_axis_input_tready;
+ end
+
+ m_axis_tvalid_int = 1'b1;
+
+ `define _HEADER_FIELD_(offset, field) \
+ if (ptr_reg == offset/KEEP_WIDTH) begin \
+ m_axis_tdata_int[(offset%KEEP_WIDTH)*8 +: 8] = field; \
+ m_axis_tkeep_int[offset%KEEP_WIDTH] = 1'b1; \
+ end
+
+ `_HEADER_FIELD_(0, eth_dest_mac_reg[5*8 +: 8])
+ `_HEADER_FIELD_(1, eth_dest_mac_reg[4*8 +: 8])
+ `_HEADER_FIELD_(2, eth_dest_mac_reg[3*8 +: 8])
+ `_HEADER_FIELD_(3, eth_dest_mac_reg[2*8 +: 8])
+ `_HEADER_FIELD_(4, eth_dest_mac_reg[1*8 +: 8])
+ `_HEADER_FIELD_(5, eth_dest_mac_reg[0*8 +: 8])
+ `_HEADER_FIELD_(6, eth_src_mac_reg[5*8 +: 8])
+ `_HEADER_FIELD_(7, eth_src_mac_reg[4*8 +: 8])
+ `_HEADER_FIELD_(8, eth_src_mac_reg[3*8 +: 8])
+ `_HEADER_FIELD_(9, eth_src_mac_reg[2*8 +: 8])
+ `_HEADER_FIELD_(10, eth_src_mac_reg[1*8 +: 8])
+ `_HEADER_FIELD_(11, eth_src_mac_reg[0*8 +: 8])
+ `_HEADER_FIELD_(12, eth_type_reg[1*8 +: 8])
+ `_HEADER_FIELD_(13, eth_type_reg[0*8 +: 8])
+
+ if (ptr_reg == 13/KEEP_WIDTH) begin
+ if (!send_eth_payload_reg) begin
+ s_eth_payload_axis_tready_next = m_axis_tready_int_early;
+ send_eth_payload_next = 1'b1;
+ end
+ send_eth_header_next = 1'b0;
+ end
+
+ `undef _HEADER_FIELD_
+ end
+ end
+
+ s_eth_hdr_ready_next = !(send_eth_header_next || send_eth_payload_next);
+end
+
+always @(posedge clk) begin
+ send_eth_header_reg <= send_eth_header_next;
+ send_eth_payload_reg <= send_eth_payload_next;
+ ptr_reg <= 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 <= send_eth_header_next || send_eth_payload_next;
+
+ 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
+
+ if (transfer_in_save) begin
+ save_eth_payload_axis_tdata_reg <= s_eth_payload_axis_tdata;
+ save_eth_payload_axis_tkeep_reg <= s_eth_payload_axis_tkeep;
+ save_eth_payload_axis_tuser_reg <= s_eth_payload_axis_tuser;
+ end
+
+ if (flush_save) begin
+ save_eth_payload_axis_tlast_reg <= 1'b0;
+ shift_eth_payload_axis_extra_cycle_reg <= 1'b0;
+ end else if (transfer_in_save) begin
+ save_eth_payload_axis_tlast_reg <= s_eth_payload_axis_tlast;
+ shift_eth_payload_axis_extra_cycle_reg <= OFFSET ? s_eth_payload_axis_tlast && ((s_eth_payload_axis_tkeep & ({KEEP_WIDTH{1'b1}} << (KEEP_WIDTH-OFFSET))) != 0) : 1'b0;
+ end
+
+ if (rst) begin
+ send_eth_header_reg <= 1'b0;
+ send_eth_payload_reg <= 1'b0;
+ ptr_reg <= 0;
+ s_eth_hdr_ready_reg <= 1'b0;
+ s_eth_payload_axis_tready_reg <= 1'b0;
+ busy_reg <= 1'b0;
+ eth_dest_mac_reg <= 48'd0;
+ eth_src_mac_reg <= 48'd0;
+ eth_type_reg <= 16'd0;
+ save_eth_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ save_eth_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ save_eth_payload_axis_tlast_reg <= 1'b0;
+ save_eth_payload_axis_tuser_reg <= 1'b0;
+ shift_eth_payload_axis_extra_cycle_reg <= 1'b0;
+ end
+end
+
+// output datapath logic
+reg [DATA_WIDTH-1:0] m_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg;
+reg m_axis_tvalid_reg, m_axis_tvalid_next;
+reg m_axis_tlast_reg;
+reg m_axis_tuser_reg;
+
+reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg;
+reg temp_m_axis_tvalid_reg, temp_m_axis_tvalid_next;
+reg temp_m_axis_tlast_reg;
+reg temp_m_axis_tuser_reg;
+
+// 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_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}};
+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_tready_int_reg <= 1'b0;
+
+ m_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ m_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ m_axis_tvalid_reg <= 1'b0;
+ m_axis_tlast_reg <= 1'b0;
+ m_axis_tuser_reg <= 1'b0;
+
+ temp_m_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ temp_m_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ temp_m_axis_tvalid_reg <= 1'b0;
+ temp_m_axis_tlast_reg <= 1'b0;
+ temp_m_axis_tuser_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;
+
+ // datapath
+ if (store_axis_int_to_output) begin
+ m_axis_tdata_reg <= m_axis_tdata_int;
+ m_axis_tkeep_reg <= m_axis_tkeep_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_tkeep_reg <= temp_m_axis_tkeep_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_tkeep_reg <= m_axis_tkeep_int;
+ temp_m_axis_tlast_reg <= m_axis_tlast_int;
+ temp_m_axis_tuser_reg <= m_axis_tuser_int;
+ end
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/eth_mac_1g.v b/verilog/rtl/eth_mac_1g.v
new file mode 100644
index 0000000..4f1075c
--- /dev/null
+++ b/verilog/rtl/eth_mac_1g.v
@@ -0,0 +1,167 @@
+/*
+
+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 DATA_WIDTH = 8,
+ parameter ENABLE_PADDING = 1,
+ parameter MIN_FRAME_LENGTH = 64,
+ parameter TX_PTP_TS_ENABLE = 0,
+ parameter TX_PTP_TS_WIDTH = 96,
+ parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE,
+ parameter TX_PTP_TAG_WIDTH = 16,
+ parameter RX_PTP_TS_ENABLE = 0,
+ parameter RX_PTP_TS_WIDTH = 96,
+ parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1,
+ parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1
+)
+(
+ input wire rx_clk,
+ input wire rx_rst,
+ input wire tx_clk,
+ input wire tx_rst,
+
+ /*
+ * AXI input
+ */
+ input wire [DATA_WIDTH-1:0] tx_axis_tdata,
+ input wire tx_axis_tvalid,
+ output wire tx_axis_tready,
+ input wire tx_axis_tlast,
+ input wire [TX_USER_WIDTH-1:0] tx_axis_tuser,
+
+ /*
+ * AXI output
+ */
+ output wire [DATA_WIDTH-1:0] rx_axis_tdata,
+ output wire rx_axis_tvalid,
+ output wire rx_axis_tlast,
+ output wire [RX_USER_WIDTH-1:0] rx_axis_tuser,
+
+ /*
+ * GMII interface
+ */
+ input wire [DATA_WIDTH-1:0] gmii_rxd,
+ input wire gmii_rx_dv,
+ input wire gmii_rx_er,
+ output wire [DATA_WIDTH-1:0] gmii_txd,
+ output wire gmii_tx_en,
+ output wire gmii_tx_er,
+
+ /*
+ * PTP
+ */
+ input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts,
+ input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts,
+ output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts,
+ output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag,
+ output wire tx_axis_ptp_ts_valid,
+
+ /*
+ * Control
+ */
+ input wire rx_clk_enable,
+ input wire tx_clk_enable,
+ input wire rx_mii_select,
+ input wire tx_mii_select,
+
+ /*
+ * Status
+ */
+ output wire tx_start_packet,
+ output wire tx_error_underflow,
+ output wire rx_start_packet,
+ output wire rx_error_bad_frame,
+ output wire rx_error_bad_fcs,
+
+ /*
+ * Configuration
+ */
+ input wire [7:0] ifg_delay
+);
+
+axis_gmii_rx #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .PTP_TS_ENABLE(RX_PTP_TS_ENABLE),
+ .PTP_TS_WIDTH(RX_PTP_TS_WIDTH),
+ .USER_WIDTH(RX_USER_WIDTH)
+)
+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),
+ .ptp_ts(rx_ptp_ts),
+ .clk_enable(rx_clk_enable),
+ .mii_select(rx_mii_select),
+ .start_packet(rx_start_packet),
+ .error_bad_frame(rx_error_bad_frame),
+ .error_bad_fcs(rx_error_bad_fcs)
+);
+
+axis_gmii_tx #(
+ .DATA_WIDTH(DATA_WIDTH),
+ .ENABLE_PADDING(ENABLE_PADDING),
+ .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH),
+ .PTP_TS_ENABLE(TX_PTP_TS_ENABLE),
+ .PTP_TS_WIDTH(TX_PTP_TS_WIDTH),
+ .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE),
+ .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH),
+ .USER_WIDTH(TX_USER_WIDTH)
+)
+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),
+ .ptp_ts(tx_ptp_ts),
+ .m_axis_ptp_ts(tx_axis_ptp_ts),
+ .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag),
+ .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid),
+ .clk_enable(tx_clk_enable),
+ .mii_select(tx_mii_select),
+ .ifg_delay(ifg_delay),
+ .start_packet(tx_start_packet),
+ .error_underflow(tx_error_underflow)
+);
+
+endmodule
diff --git a/verilog/rtl/eth_mac_mii.v b/verilog/rtl/eth_mac_mii.v
new file mode 100644
index 0000000..75c1f86
--- /dev/null
+++ b/verilog/rtl/eth_mac_mii.v
@@ -0,0 +1,166 @@
+/*
+
+Copyright (c) 2019 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
+
+/*
+ * 10M/100M Ethernet MAC with MII interface
+ */
+module eth_mac_mii #
+(
+ // target ("SIM", "GENERIC", "XILINX", "ALTERA")
+ parameter TARGET = "GENERIC",
+ // 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",
+ parameter ENABLE_PADDING = 1,
+ parameter MIN_FRAME_LENGTH = 64
+)
+(
+ input wire 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,
+
+ /*
+ * MII interface
+ */
+ input wire mii_rx_clk,
+ input wire [3:0] mii_rxd,
+ input wire mii_rx_dv,
+ input wire mii_rx_er,
+ input wire mii_tx_clk,
+ output wire [3:0] mii_txd,
+ output wire mii_tx_en,
+ output wire mii_tx_er,
+
+ /*
+ * Status
+ */
+ output wire tx_start_packet,
+ output wire tx_error_underflow,
+ output wire rx_start_packet,
+ output wire rx_error_bad_frame,
+ output wire rx_error_bad_fcs,
+
+ /*
+ * Configuration
+ */
+ input wire [7:0] ifg_delay
+);
+
+wire [3:0] mac_mii_rxd;
+wire mac_mii_rx_dv;
+wire mac_mii_rx_er;
+wire [3:0] mac_mii_txd;
+wire mac_mii_tx_en;
+wire mac_mii_tx_er;
+
+mii_phy_if #(
+ .TARGET(TARGET),
+ .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE)
+)
+mii_phy_if_inst (
+ .rst(rst),
+
+ .mac_mii_rx_clk(rx_clk),
+ .mac_mii_rx_rst(rx_rst),
+ .mac_mii_rxd(mac_mii_rxd),
+ .mac_mii_rx_dv(mac_mii_rx_dv),
+ .mac_mii_rx_er(mac_mii_rx_er),
+ .mac_mii_tx_clk(tx_clk),
+ .mac_mii_tx_rst(tx_rst),
+ .mac_mii_txd(mac_mii_txd),
+ .mac_mii_tx_en(mac_mii_tx_en),
+ .mac_mii_tx_er(mac_mii_tx_er),
+
+ .phy_mii_rx_clk(mii_rx_clk),
+ .phy_mii_rxd(mii_rxd),
+ .phy_mii_rx_dv(mii_rx_dv),
+ .phy_mii_rx_er(mii_rx_er),
+ .phy_mii_tx_clk(mii_tx_clk),
+ .phy_mii_txd(mii_txd),
+ .phy_mii_tx_en(mii_tx_en),
+ .phy_mii_tx_er(mii_tx_er)
+);
+
+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_mii_rxd),
+ .gmii_rx_dv(mac_mii_rx_dv),
+ .gmii_rx_er(mac_mii_rx_er),
+ .gmii_txd(mac_mii_txd),
+ .gmii_tx_en(mac_mii_tx_en),
+ .gmii_tx_er(mac_mii_tx_er),
+ .rx_clk_enable(1'b1),
+ .tx_clk_enable(1'b1),
+ .rx_mii_select(1'b1),
+ .tx_mii_select(1'b1),
+ .tx_start_packet(tx_start_packet),
+ .tx_error_underflow(tx_error_underflow),
+ .rx_start_packet(rx_start_packet),
+ .rx_error_bad_frame(rx_error_bad_frame),
+ .rx_error_bad_fcs(rx_error_bad_fcs),
+ .ifg_delay(ifg_delay)
+);
+
+endmodule
diff --git a/verilog/rtl/eth_mac_mii_fifo.v b/verilog/rtl/eth_mac_mii_fifo.v
new file mode 100644
index 0000000..95ef99a
--- /dev/null
+++ b/verilog/rtl/eth_mac_mii_fifo.v
@@ -0,0 +1,326 @@
+/*
+
+Copyright (c) 2019 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
+
+/*
+ * 10M/100M Ethernet MAC with MII interface and TX and RX FIFOs
+ */
+module eth_mac_mii_fifo #
+(
+ // target ("SIM", "GENERIC", "XILINX", "ALTERA")
+ parameter TARGET = "GENERIC",
+ // 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",
+ parameter AXIS_DATA_WIDTH = 8,
+ parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8),
+ parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8),
+ parameter ENABLE_PADDING = 1,
+ parameter MIN_FRAME_LENGTH = 64,
+ parameter TX_FIFO_DEPTH = 4096,
+ parameter TX_FIFO_PIPELINE_OUTPUT = 2,
+ parameter TX_FRAME_FIFO = 1,
+ parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO,
+ parameter TX_DROP_WHEN_FULL = 0,
+ parameter RX_FIFO_DEPTH = 4096,
+ parameter RX_FIFO_PIPELINE_OUTPUT = 2,
+ parameter RX_FRAME_FIFO = 1,
+ parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO,
+ parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO
+)
+(
+ input wire rst,
+ input wire logic_clk,
+ input wire logic_rst,
+
+ /*
+ * AXI input
+ */
+ input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata,
+ input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep,
+ input wire tx_axis_tvalid,
+ output wire tx_axis_tready,
+ input wire tx_axis_tlast,
+ input wire tx_axis_tuser,
+
+ /*
+ * AXI output
+ */
+ output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata,
+ output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep,
+ output wire rx_axis_tvalid,
+ input wire rx_axis_tready,
+ output wire rx_axis_tlast,
+ output wire rx_axis_tuser,
+
+ /*
+ * MII interface
+ */
+ input wire mii_rx_clk,
+ input wire [3:0] mii_rxd,
+ input wire mii_rx_dv,
+ input wire mii_rx_er,
+ input wire mii_tx_clk,
+ output wire [3:0] mii_txd,
+ output wire mii_tx_en,
+ output wire mii_tx_er,
+
+ /*
+ * Status
+ */
+ output wire tx_error_underflow,
+ 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,
+
+ /*
+ * Configuration
+ */
+ input wire [7:0] ifg_delay
+);
+
+wire tx_clk;
+wire rx_clk;
+wire tx_rst;
+wire rx_rst;
+
+wire [7:0] tx_fifo_axis_tdata;
+wire tx_fifo_axis_tvalid;
+wire tx_fifo_axis_tready;
+wire tx_fifo_axis_tlast;
+wire tx_fifo_axis_tuser;
+
+wire [7:0] rx_fifo_axis_tdata;
+wire rx_fifo_axis_tvalid;
+wire rx_fifo_axis_tlast;
+wire rx_fifo_axis_tuser;
+
+// synchronize MAC status signals into logic clock domain
+wire tx_error_underflow_int;
+
+reg [0:0] tx_sync_reg_1;
+reg [0:0] tx_sync_reg_2;
+reg [0:0] tx_sync_reg_3;
+reg [0:0] tx_sync_reg_4;
+
+assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0];
+
+always @(posedge tx_clk or posedge tx_rst) begin
+ if (tx_rst) begin
+ tx_sync_reg_1 <= 1'b0;
+ end else begin
+ tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int};
+ end
+end
+
+always @(posedge logic_clk or posedge logic_rst) begin
+ if (logic_rst) begin
+ tx_sync_reg_2 <= 1'b0;
+ tx_sync_reg_3 <= 1'b0;
+ tx_sync_reg_4 <= 1'b0;
+ end else begin
+ tx_sync_reg_2 <= tx_sync_reg_1;
+ tx_sync_reg_3 <= tx_sync_reg_2;
+ tx_sync_reg_4 <= tx_sync_reg_3;
+ end
+end
+
+wire rx_error_bad_frame_int;
+wire rx_error_bad_fcs_int;
+
+reg [1:0] rx_sync_reg_1;
+reg [1:0] rx_sync_reg_2;
+reg [1:0] rx_sync_reg_3;
+reg [1:0] rx_sync_reg_4;
+
+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_fcs_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
+
+eth_mac_mii #(
+ .TARGET(TARGET),
+ .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
+ .ENABLE_PADDING(ENABLE_PADDING),
+ .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH)
+)
+eth_mac_1g_mii_inst (
+ .rst(rst),
+ .tx_clk(tx_clk),
+ .tx_rst(tx_rst),
+ .rx_clk(rx_clk),
+ .rx_rst(rx_rst),
+ .tx_axis_tdata(tx_fifo_axis_tdata),
+ .tx_axis_tvalid(tx_fifo_axis_tvalid),
+ .tx_axis_tready(tx_fifo_axis_tready),
+ .tx_axis_tlast(tx_fifo_axis_tlast),
+ .tx_axis_tuser(tx_fifo_axis_tuser),
+ .rx_axis_tdata(rx_fifo_axis_tdata),
+ .rx_axis_tvalid(rx_fifo_axis_tvalid),
+ .rx_axis_tlast(rx_fifo_axis_tlast),
+ .rx_axis_tuser(rx_fifo_axis_tuser),
+ .mii_rx_clk(mii_rx_clk),
+ .mii_rxd(mii_rxd),
+ .mii_rx_dv(mii_rx_dv),
+ .mii_rx_er(mii_rx_er),
+ .mii_tx_clk(mii_tx_clk),
+ .mii_txd(mii_txd),
+ .mii_tx_en(mii_tx_en),
+ .mii_tx_er(mii_tx_er),
+ .tx_error_underflow(tx_error_underflow_int),
+ .rx_error_bad_frame(rx_error_bad_frame_int),
+ .rx_error_bad_fcs(rx_error_bad_fcs_int),
+ .ifg_delay(ifg_delay)
+);
+
+axis_async_fifo_adapter #(
+ .DEPTH(TX_FIFO_DEPTH),
+ .S_DATA_WIDTH(AXIS_DATA_WIDTH),
+ .S_KEEP_ENABLE(AXIS_KEEP_ENABLE),
+ .S_KEEP_WIDTH(AXIS_KEEP_WIDTH),
+ .M_DATA_WIDTH(8),
+ .M_KEEP_ENABLE(0),
+ .ID_ENABLE(0),
+ .DEST_ENABLE(0),
+ .USER_ENABLE(1),
+ .USER_WIDTH(1),
+ .PIPELINE_OUTPUT(TX_FIFO_PIPELINE_OUTPUT),
+ .FRAME_FIFO(TX_FRAME_FIFO),
+ .USER_BAD_FRAME_VALUE(1'b1),
+ .USER_BAD_FRAME_MASK(1'b1),
+ .DROP_BAD_FRAME(TX_DROP_BAD_FRAME),
+ .DROP_WHEN_FULL(TX_DROP_WHEN_FULL)
+)
+tx_fifo (
+ // AXI input
+ .s_clk(logic_clk),
+ .s_rst(logic_rst),
+ .s_axis_tdata(tx_axis_tdata),
+ .s_axis_tkeep(tx_axis_tkeep),
+ .s_axis_tvalid(tx_axis_tvalid),
+ .s_axis_tready(tx_axis_tready),
+ .s_axis_tlast(tx_axis_tlast),
+ .s_axis_tid(0),
+ .s_axis_tdest(0),
+ .s_axis_tuser(tx_axis_tuser),
+ // AXI output
+ .m_clk(tx_clk),
+ .m_rst(tx_rst),
+ .m_axis_tdata(tx_fifo_axis_tdata),
+ .m_axis_tkeep(),
+ .m_axis_tvalid(tx_fifo_axis_tvalid),
+ .m_axis_tready(tx_fifo_axis_tready),
+ .m_axis_tlast(tx_fifo_axis_tlast),
+ .m_axis_tid(),
+ .m_axis_tdest(),
+ .m_axis_tuser(tx_fifo_axis_tuser),
+ // Status
+ .s_status_overflow(tx_fifo_overflow),
+ .s_status_bad_frame(tx_fifo_bad_frame),
+ .s_status_good_frame(tx_fifo_good_frame),
+ .m_status_overflow(),
+ .m_status_bad_frame(),
+ .m_status_good_frame()
+);
+
+axis_async_fifo_adapter #(
+ .DEPTH(RX_FIFO_DEPTH),
+ .S_DATA_WIDTH(8),
+ .S_KEEP_ENABLE(0),
+ .M_DATA_WIDTH(AXIS_DATA_WIDTH),
+ .M_KEEP_ENABLE(AXIS_KEEP_ENABLE),
+ .M_KEEP_WIDTH(AXIS_KEEP_WIDTH),
+ .ID_ENABLE(0),
+ .DEST_ENABLE(0),
+ .USER_ENABLE(1),
+ .USER_WIDTH(1),
+ .PIPELINE_OUTPUT(RX_FIFO_PIPELINE_OUTPUT),
+ .FRAME_FIFO(RX_FRAME_FIFO),
+ .USER_BAD_FRAME_VALUE(1'b1),
+ .USER_BAD_FRAME_MASK(1'b1),
+ .DROP_BAD_FRAME(RX_DROP_BAD_FRAME),
+ .DROP_WHEN_FULL(RX_DROP_WHEN_FULL)
+)
+rx_fifo (
+ // AXI input
+ .s_clk(rx_clk),
+ .s_rst(rx_rst),
+ .s_axis_tdata(rx_fifo_axis_tdata),
+ .s_axis_tkeep(0),
+ .s_axis_tvalid(rx_fifo_axis_tvalid),
+ .s_axis_tready(),
+ .s_axis_tlast(rx_fifo_axis_tlast),
+ .s_axis_tid(0),
+ .s_axis_tdest(0),
+ .s_axis_tuser(rx_fifo_axis_tuser),
+ // AXI output
+ .m_clk(logic_clk),
+ .m_rst(logic_rst),
+ .m_axis_tdata(rx_axis_tdata),
+ .m_axis_tkeep(rx_axis_tkeep),
+ .m_axis_tvalid(rx_axis_tvalid),
+ .m_axis_tready(rx_axis_tready),
+ .m_axis_tlast(rx_axis_tlast),
+ .m_axis_tid(),
+ .m_axis_tdest(),
+ .m_axis_tuser(rx_axis_tuser),
+ // Status
+ .s_status_overflow(),
+ .s_status_bad_frame(),
+ .s_status_good_frame(),
+ .m_status_overflow(rx_fifo_overflow),
+ .m_status_bad_frame(rx_fifo_bad_frame),
+ .m_status_good_frame(rx_fifo_good_frame)
+);
+
+endmodule
+
diff --git a/verilog/rtl/fpga_core.v b/verilog/rtl/fpga_core.v
new file mode 100644
index 0000000..02c1634
--- /dev/null
+++ b/verilog/rtl/fpga_core.v
@@ -0,0 +1,492 @@
+/*
+
+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 fpga_core #
+(
+ parameter TARGET = "GENERIC",
+ parameter LOCAL_MAC = 48'h02_00_00_00_00_00,
+ parameter LOCAL_IP = {8'd192, 8'd168, 8'd1, 8'd128},
+ parameter GATEWAY_IP = {8'd192, 8'd168, 8'd1, 8'd1},
+ parameter SUBNET_MASK = {8'd255, 8'd255, 8'd255, 8'd0}
+)
+(
+ /*
+ * Clock: 125MHz
+ * Synchronous reset
+ */
+ input wire clk,
+ input wire rst,
+
+ /*
+ * Ethernet: 100BASE-T MII
+ */
+ input wire phy_rx_clk,
+ input wire [3:0] phy_rxd,
+ input wire phy_rx_dv,
+ input wire phy_rx_er,
+ input wire phy_tx_clk,
+ output wire [3:0] phy_txd,
+ output wire phy_tx_en,
+ output wire phy_reset_n,
+
+ /*
+ * AXIS Interface
+ */
+
+ output wire [7:0] rx_udp_payload_axis_tdata,
+ output wire rx_udp_payload_axis_tvalid,
+ input wire rx_udp_payload_axis_tready,
+ output wire rx_udp_payload_axis_tlast,
+ output wire rx_udp_payload_axis_tuser,
+
+ input wire [7:0] tx_udp_payload_axis_tdata,
+ input wire tx_udp_payload_axis_tvalid,
+ output wire tx_udp_payload_axis_tready,
+ input wire tx_udp_payload_axis_tlast,
+ input wire tx_udp_payload_axis_tuser
+);
+
+// AXI between MAC and Ethernet modules
+wire [7:0] rx_axis_tdata;
+wire rx_axis_tvalid;
+wire rx_axis_tready;
+wire rx_axis_tlast;
+wire rx_axis_tuser;
+
+wire [7:0] tx_axis_tdata;
+wire tx_axis_tvalid;
+wire tx_axis_tready;
+wire tx_axis_tlast;
+wire tx_axis_tuser;
+
+// Ethernet frame between Ethernet modules and UDP stack
+wire rx_eth_hdr_ready;
+wire rx_eth_hdr_valid;
+wire [47:0] rx_eth_dest_mac;
+wire [47:0] rx_eth_src_mac;
+wire [15:0] rx_eth_type;
+wire [7:0] rx_eth_payload_axis_tdata;
+wire rx_eth_payload_axis_tvalid;
+wire rx_eth_payload_axis_tready;
+wire rx_eth_payload_axis_tlast;
+wire rx_eth_payload_axis_tuser;
+
+wire tx_eth_hdr_ready;
+wire tx_eth_hdr_valid;
+wire [47:0] tx_eth_dest_mac;
+wire [47:0] tx_eth_src_mac;
+wire [15:0] tx_eth_type;
+wire [7:0] tx_eth_payload_axis_tdata;
+wire tx_eth_payload_axis_tvalid;
+wire tx_eth_payload_axis_tready;
+wire tx_eth_payload_axis_tlast;
+wire tx_eth_payload_axis_tuser;
+
+// IP frame connections
+wire rx_ip_hdr_valid;
+wire rx_ip_hdr_ready;
+wire [47:0] rx_ip_eth_dest_mac;
+wire [47:0] rx_ip_eth_src_mac;
+wire [15:0] rx_ip_eth_type;
+wire [3:0] rx_ip_version;
+wire [3:0] rx_ip_ihl;
+wire [5:0] rx_ip_dscp;
+wire [1:0] rx_ip_ecn;
+wire [15:0] rx_ip_length;
+wire [15:0] rx_ip_identification;
+wire [2:0] rx_ip_flags;
+wire [12:0] rx_ip_fragment_offset;
+wire [7:0] rx_ip_ttl;
+wire [7:0] rx_ip_protocol;
+wire [15:0] rx_ip_header_checksum;
+wire [31:0] rx_ip_source_ip;
+wire [31:0] rx_ip_dest_ip;
+wire [7:0] rx_ip_payload_axis_tdata;
+wire rx_ip_payload_axis_tvalid;
+wire rx_ip_payload_axis_tready;
+wire rx_ip_payload_axis_tlast;
+wire rx_ip_payload_axis_tuser;
+
+wire tx_ip_hdr_valid;
+wire tx_ip_hdr_ready;
+wire [5:0] tx_ip_dscp;
+wire [1:0] tx_ip_ecn;
+wire [15:0] tx_ip_length;
+wire [7:0] tx_ip_ttl;
+wire [7:0] tx_ip_protocol;
+wire [31:0] tx_ip_source_ip;
+wire [31:0] tx_ip_dest_ip;
+wire [7:0] tx_ip_payload_axis_tdata;
+wire tx_ip_payload_axis_tvalid;
+wire tx_ip_payload_axis_tready;
+wire tx_ip_payload_axis_tlast;
+wire tx_ip_payload_axis_tuser;
+
+// UDP frame connections
+wire rx_udp_hdr_valid;
+wire rx_udp_hdr_ready;
+wire [47:0] rx_udp_eth_dest_mac;
+wire [47:0] rx_udp_eth_src_mac;
+wire [15:0] rx_udp_eth_type;
+wire [3:0] rx_udp_ip_version;
+wire [3:0] rx_udp_ip_ihl;
+wire [5:0] rx_udp_ip_dscp;
+wire [1:0] rx_udp_ip_ecn;
+wire [15:0] rx_udp_ip_length;
+wire [15:0] rx_udp_ip_identification;
+wire [2:0] rx_udp_ip_flags;
+wire [12:0] rx_udp_ip_fragment_offset;
+wire [7:0] rx_udp_ip_ttl;
+wire [7:0] rx_udp_ip_protocol;
+wire [15:0] rx_udp_ip_header_checksum;
+wire [31:0] rx_udp_ip_source_ip;
+wire [31:0] rx_udp_ip_dest_ip;
+wire [15:0] rx_udp_source_port;
+wire [15:0] rx_udp_dest_port;
+wire [15:0] rx_udp_length;
+wire [15:0] rx_udp_checksum;
+
+wire tx_udp_hdr_valid;
+wire tx_udp_hdr_ready;
+wire [5:0] tx_udp_ip_dscp;
+wire [1:0] tx_udp_ip_ecn;
+wire [7:0] tx_udp_ip_ttl;
+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] rx_fifo_udp_payload_axis_tdata;
+wire rx_fifo_udp_payload_axis_tvalid;
+wire rx_fifo_udp_payload_axis_tready;
+wire rx_fifo_udp_payload_axis_tlast;
+wire rx_fifo_udp_payload_axis_tuser;
+
+wire [7:0] tx_fifo_udp_payload_axis_tdata;
+wire tx_fifo_udp_payload_axis_tvalid;
+wire tx_fifo_udp_payload_axis_tready;
+wire tx_fifo_udp_payload_axis_tlast;
+wire tx_fifo_udp_payload_axis_tuser;
+
+// IP ports not used
+assign rx_ip_hdr_ready = 1;
+assign rx_ip_payload_axis_tready = 1;
+
+assign tx_ip_hdr_valid = 0;
+assign tx_ip_dscp = 0;
+assign tx_ip_ecn = 0;
+assign tx_ip_length = 0;
+assign tx_ip_ttl = 0;
+assign tx_ip_protocol = 0;
+assign tx_ip_source_ip = 0;
+assign tx_ip_dest_ip = 0;
+assign tx_ip_payload_axis_tdata = 0;
+assign tx_ip_payload_axis_tvalid = 0;
+assign tx_ip_payload_axis_tlast = 0;
+assign tx_ip_payload_axis_tuser = 0;
+
+// Loop back UDP
+wire match_cond = rx_udp_dest_port == 1234;
+wire no_match = !match_cond;
+
+reg match_cond_reg;
+reg no_match_reg;
+
+always @(posedge clk) begin
+ if (rst) begin
+ match_cond_reg <= 0;
+ no_match_reg <= 0;
+ end else begin
+ if (rx_udp_payload_axis_tvalid) begin
+ if ((!match_cond_reg && !no_match_reg) ||
+ (rx_udp_payload_axis_tvalid && rx_udp_payload_axis_tready && rx_udp_payload_axis_tlast)) begin
+ match_cond_reg <= match_cond;
+ no_match_reg <= no_match;
+ end
+ end else begin
+ match_cond_reg <= 0;
+ no_match_reg <= 0;
+ end
+ end
+end
+
+assign tx_udp_hdr_valid = rx_udp_hdr_valid && match_cond;
+assign rx_udp_hdr_ready = (tx_eth_hdr_ready && match_cond) || no_match;
+assign tx_udp_ip_dscp = 0;
+assign tx_udp_ip_ecn = 0;
+assign tx_udp_ip_ttl = 64;
+assign tx_udp_ip_source_ip = LOCAL_IP;
+assign tx_udp_ip_dest_ip = rx_udp_ip_source_ip;
+assign tx_udp_source_port = rx_udp_dest_port;
+assign tx_udp_dest_port = rx_udp_source_port;
+assign tx_udp_length = rx_udp_length;
+assign tx_udp_checksum = 0;
+
+assign phy_reset_n = !rst;
+
+eth_mac_mii_fifo #(
+ .TARGET(TARGET),
+ .CLOCK_INPUT_STYLE("BUFR"),
+ .ENABLE_PADDING(1),
+ .MIN_FRAME_LENGTH(64),
+ .TX_FIFO_DEPTH(8),
+ .TX_FRAME_FIFO(0),
+ .RX_FIFO_DEPTH(8),
+ .RX_FRAME_FIFO(0)
+)
+eth_mac_inst (
+ .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_tready(rx_axis_tready),
+ .rx_axis_tlast(rx_axis_tlast),
+ .rx_axis_tuser(rx_axis_tuser),
+
+ .mii_rx_clk(phy_rx_clk),
+ .mii_rxd(phy_rxd),
+ .mii_rx_dv(phy_rx_dv),
+ .mii_rx_er(phy_rx_er),
+ .mii_tx_clk(phy_tx_clk),
+ .mii_txd(phy_txd),
+ .mii_tx_en(phy_tx_en),
+ .mii_tx_er(),
+
+ .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(),
+
+ .ifg_delay(12)
+);
+
+eth_axis_rx
+eth_axis_rx_inst (
+ .clk(clk),
+ .rst(rst),
+ // AXI input
+ .s_axis_tdata(rx_axis_tdata),
+ .s_axis_tvalid(rx_axis_tvalid),
+ .s_axis_tready(rx_axis_tready),
+ .s_axis_tlast(rx_axis_tlast),
+ .s_axis_tuser(rx_axis_tuser),
+ // Ethernet frame output
+ .m_eth_hdr_valid(rx_eth_hdr_valid),
+ .m_eth_hdr_ready(rx_eth_hdr_ready),
+ .m_eth_dest_mac(rx_eth_dest_mac),
+ .m_eth_src_mac(rx_eth_src_mac),
+ .m_eth_type(rx_eth_type),
+ .m_eth_payload_axis_tdata(rx_eth_payload_axis_tdata),
+ .m_eth_payload_axis_tvalid(rx_eth_payload_axis_tvalid),
+ .m_eth_payload_axis_tready(rx_eth_payload_axis_tready),
+ .m_eth_payload_axis_tlast(rx_eth_payload_axis_tlast),
+ .m_eth_payload_axis_tuser(rx_eth_payload_axis_tuser),
+ // Status signals
+ .busy(),
+ .error_header_early_termination()
+);
+
+eth_axis_tx
+eth_axis_tx_inst (
+ .clk(clk),
+ .rst(rst),
+ // Ethernet frame input
+ .s_eth_hdr_valid(tx_eth_hdr_valid),
+ .s_eth_hdr_ready(tx_eth_hdr_ready),
+ .s_eth_dest_mac(tx_eth_dest_mac),
+ .s_eth_src_mac(tx_eth_src_mac),
+ .s_eth_type(tx_eth_type),
+ .s_eth_payload_axis_tdata(tx_eth_payload_axis_tdata),
+ .s_eth_payload_axis_tvalid(tx_eth_payload_axis_tvalid),
+ .s_eth_payload_axis_tready(tx_eth_payload_axis_tready),
+ .s_eth_payload_axis_tlast(tx_eth_payload_axis_tlast),
+ .s_eth_payload_axis_tuser(tx_eth_payload_axis_tuser),
+ // AXI output
+ .m_axis_tdata(tx_axis_tdata),
+ .m_axis_tvalid(tx_axis_tvalid),
+ .m_axis_tready(tx_axis_tready),
+ .m_axis_tlast(tx_axis_tlast),
+ .m_axis_tuser(tx_axis_tuser),
+ // Status signals
+ .busy()
+);
+
+udp_complete #(
+ .UDP_CHECKSUM_GEN_ENABLE(0),
+ .ARP_CACHE_ADDR_WIDTH(4)
+)
+udp_complete_inst (
+ .clk(clk),
+ .rst(rst),
+ // Ethernet frame input
+ .s_eth_hdr_valid(rx_eth_hdr_valid),
+ .s_eth_hdr_ready(rx_eth_hdr_ready),
+ .s_eth_dest_mac(rx_eth_dest_mac),
+ .s_eth_src_mac(rx_eth_src_mac),
+ .s_eth_type(rx_eth_type),
+ .s_eth_payload_axis_tdata(rx_eth_payload_axis_tdata),
+ .s_eth_payload_axis_tvalid(rx_eth_payload_axis_tvalid),
+ .s_eth_payload_axis_tready(rx_eth_payload_axis_tready),
+ .s_eth_payload_axis_tlast(rx_eth_payload_axis_tlast),
+ .s_eth_payload_axis_tuser(rx_eth_payload_axis_tuser),
+ // Ethernet frame output
+ .m_eth_hdr_valid(tx_eth_hdr_valid),
+ .m_eth_hdr_ready(tx_eth_hdr_ready),
+ .m_eth_dest_mac(tx_eth_dest_mac),
+ .m_eth_src_mac(tx_eth_src_mac),
+ .m_eth_type(tx_eth_type),
+ .m_eth_payload_axis_tdata(tx_eth_payload_axis_tdata),
+ .m_eth_payload_axis_tvalid(tx_eth_payload_axis_tvalid),
+ .m_eth_payload_axis_tready(tx_eth_payload_axis_tready),
+ .m_eth_payload_axis_tlast(tx_eth_payload_axis_tlast),
+ .m_eth_payload_axis_tuser(tx_eth_payload_axis_tuser),
+ // IP frame input
+ .s_ip_hdr_valid(tx_ip_hdr_valid),
+ .s_ip_hdr_ready(tx_ip_hdr_ready),
+ .s_ip_dscp(tx_ip_dscp),
+ .s_ip_ecn(tx_ip_ecn),
+ .s_ip_length(tx_ip_length),
+ .s_ip_ttl(tx_ip_ttl),
+ .s_ip_protocol(tx_ip_protocol),
+ .s_ip_source_ip(tx_ip_source_ip),
+ .s_ip_dest_ip(tx_ip_dest_ip),
+ .s_ip_payload_axis_tdata(tx_ip_payload_axis_tdata),
+ .s_ip_payload_axis_tvalid(tx_ip_payload_axis_tvalid),
+ .s_ip_payload_axis_tready(tx_ip_payload_axis_tready),
+ .s_ip_payload_axis_tlast(tx_ip_payload_axis_tlast),
+ .s_ip_payload_axis_tuser(tx_ip_payload_axis_tuser),
+ // IP frame output
+ .m_ip_hdr_valid(rx_ip_hdr_valid),
+ .m_ip_hdr_ready(rx_ip_hdr_ready),
+ .m_ip_eth_dest_mac(rx_ip_eth_dest_mac),
+ .m_ip_eth_src_mac(rx_ip_eth_src_mac),
+ .m_ip_eth_type(rx_ip_eth_type),
+ .m_ip_version(rx_ip_version),
+ .m_ip_ihl(rx_ip_ihl),
+ .m_ip_dscp(rx_ip_dscp),
+ .m_ip_ecn(rx_ip_ecn),
+ .m_ip_length(rx_ip_length),
+ .m_ip_identification(rx_ip_identification),
+ .m_ip_flags(rx_ip_flags),
+ .m_ip_fragment_offset(rx_ip_fragment_offset),
+ .m_ip_ttl(rx_ip_ttl),
+ .m_ip_protocol(rx_ip_protocol),
+ .m_ip_header_checksum(rx_ip_header_checksum),
+ .m_ip_source_ip(rx_ip_source_ip),
+ .m_ip_dest_ip(rx_ip_dest_ip),
+ .m_ip_payload_axis_tdata(rx_ip_payload_axis_tdata),
+ .m_ip_payload_axis_tvalid(rx_ip_payload_axis_tvalid),
+ .m_ip_payload_axis_tready(rx_ip_payload_axis_tready),
+ .m_ip_payload_axis_tlast(rx_ip_payload_axis_tlast),
+ .m_ip_payload_axis_tuser(rx_ip_payload_axis_tuser),
+ // UDP frame input
+ .s_udp_hdr_valid(tx_udp_hdr_valid),
+ .s_udp_hdr_ready(tx_udp_hdr_ready),
+ .s_udp_ip_dscp(tx_udp_ip_dscp),
+ .s_udp_ip_ecn(tx_udp_ip_ecn),
+ .s_udp_ip_ttl(tx_udp_ip_ttl),
+ .s_udp_ip_source_ip(tx_udp_ip_source_ip),
+ .s_udp_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),
+ // UDP frame output
+ .m_udp_hdr_valid(rx_udp_hdr_valid),
+ .m_udp_hdr_ready(rx_udp_hdr_ready),
+ .m_udp_eth_dest_mac(rx_udp_eth_dest_mac),
+ .m_udp_eth_src_mac(rx_udp_eth_src_mac),
+ .m_udp_eth_type(rx_udp_eth_type),
+ .m_udp_ip_version(rx_udp_ip_version),
+ .m_udp_ip_ihl(rx_udp_ip_ihl),
+ .m_udp_ip_dscp(rx_udp_ip_dscp),
+ .m_udp_ip_ecn(rx_udp_ip_ecn),
+ .m_udp_ip_length(rx_udp_ip_length),
+ .m_udp_ip_identification(rx_udp_ip_identification),
+ .m_udp_ip_flags(rx_udp_ip_flags),
+ .m_udp_ip_fragment_offset(rx_udp_ip_fragment_offset),
+ .m_udp_ip_ttl(rx_udp_ip_ttl),
+ .m_udp_ip_protocol(rx_udp_ip_protocol),
+ .m_udp_ip_header_checksum(rx_udp_ip_header_checksum),
+ .m_udp_ip_source_ip(rx_udp_ip_source_ip),
+ .m_udp_ip_dest_ip(rx_udp_ip_dest_ip),
+ .m_udp_source_port(rx_udp_source_port),
+ .m_udp_dest_port(rx_udp_dest_port),
+ .m_udp_length(rx_udp_length),
+ .m_udp_checksum(rx_udp_checksum),
+ .m_udp_payload_axis_tdata(rx_udp_payload_axis_tdata),
+ .m_udp_payload_axis_tvalid(rx_udp_payload_axis_tvalid),
+ .m_udp_payload_axis_tready(rx_udp_payload_axis_tready),
+ .m_udp_payload_axis_tlast(rx_udp_payload_axis_tlast),
+ .m_udp_payload_axis_tuser(rx_udp_payload_axis_tuser),
+ // Status signals
+ .ip_rx_busy(),
+ .ip_tx_busy(),
+ .udp_rx_busy(),
+ .udp_tx_busy(),
+ .ip_rx_error_header_early_termination(),
+ .ip_rx_error_payload_early_termination(),
+ .ip_rx_error_invalid_header(),
+ .ip_rx_error_invalid_checksum(),
+ .ip_tx_error_payload_early_termination(),
+ .ip_tx_error_arp_failed(),
+ .udp_rx_error_header_early_termination(),
+ .udp_rx_error_payload_early_termination(),
+ .udp_tx_error_payload_early_termination(),
+ // Configuration
+ .local_mac(LOCAL_MAC),
+ .local_ip(LOCAL_IP),
+ .gateway_ip(GATEWAY_IP),
+ .subnet_mask(SUBNET_MASK),
+ .clear_arp_cache(0)
+);
+
+endmodule
+
diff --git a/verilog/rtl/ip.v b/verilog/rtl/ip.v
new file mode 100644
index 0000000..0f5a8b5
--- /dev/null
+++ b/verilog/rtl/ip.v
@@ -0,0 +1,342 @@
+/*
+
+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_next;
+
+reg outgoing_ip_hdr_valid_reg, outgoing_ip_hdr_valid_next;
+wire outgoing_ip_hdr_ready;
+reg [47:0] outgoing_eth_dest_mac_reg, 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, s_ip_hdr_ready_next;
+
+reg arp_request_valid_reg, arp_request_valid_next;
+
+reg arp_response_ready_reg, arp_response_ready_next;
+
+reg drop_packet_reg, 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_eth_dest_mac_reg <= 48'h000000000000;
+ 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;
+ outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next;
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/ip_arb_mux.v b/verilog/rtl/ip_arb_mux.v
new file mode 100644
index 0000000..0c705b7
--- /dev/null
+++ b/verilog/rtl/ip_arb_mux.v
@@ -0,0 +1,431 @@
+/*
+
+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,
+ // select round robin arbitration
+ parameter ARB_TYPE_ROUND_ROBIN = 0,
+ // LSB priority selection
+ parameter ARB_LSB_HIGH_PRIORITY = 1
+)
+(
+ 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, frame_next;
+
+reg [S_COUNT-1:0] s_ip_hdr_ready_reg, s_ip_hdr_ready_next;
+
+reg m_ip_hdr_valid_reg, m_ip_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg, m_eth_dest_mac_next;
+reg [47:0] m_eth_src_mac_reg, m_eth_src_mac_next;
+reg [15:0] m_eth_type_reg, m_eth_type_next;
+reg [3:0] m_ip_version_reg, m_ip_version_next;
+reg [3:0] m_ip_ihl_reg, m_ip_ihl_next;
+reg [5:0] m_ip_dscp_reg, m_ip_dscp_next;
+reg [1:0] m_ip_ecn_reg, m_ip_ecn_next;
+reg [15:0] m_ip_length_reg, m_ip_length_next;
+reg [15:0] m_ip_identification_reg, m_ip_identification_next;
+reg [2:0] m_ip_flags_reg, m_ip_flags_next;
+reg [12:0] m_ip_fragment_offset_reg, m_ip_fragment_offset_next;
+reg [7:0] m_ip_ttl_reg, m_ip_ttl_next;
+reg [7:0] m_ip_protocol_reg, m_ip_protocol_next;
+reg [15:0] m_ip_header_checksum_reg, m_ip_header_checksum_next;
+reg [31:0] m_ip_source_ip_reg, m_ip_source_ip_next;
+reg [31:0] m_ip_dest_ip_reg, 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;
+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_reg;
+
+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
+arbiter #(
+ .PORTS(S_COUNT),
+ .ARB_TYPE_ROUND_ROBIN(ARB_TYPE_ROUND_ROBIN),
+ .ARB_BLOCK(1),
+ .ARB_BLOCK_ACK(1),
+ .ARB_LSB_HIGH_PRIORITY(ARB_LSB_HIGH_PRIORITY)
+)
+arb_inst (
+ .clk(clk),
+ .rst(rst),
+ .request(request),
+ .acknowledge(acknowledge),
+ .grant(grant),
+ .grant_valid(grant_valid),
+ .grant_encoded(grant_encoded)
+);
+
+assign request = s_ip_hdr_valid & ~grant;
+assign acknowledge = grant & s_ip_payload_axis_tvalid & s_ip_payload_axis_tready & s_ip_payload_axis_tlast;
+
+always @* begin
+ frame_next = frame_reg;
+
+ s_ip_hdr_ready_next = {S_COUNT{1'b0}};
+
+ 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;
+ end
+ end
+
+ if (!frame_reg && grant_valid && (m_ip_hdr_ready || !m_ip_hdr_valid)) begin
+ // start of frame
+ frame_next = 1'b1;
+
+ s_ip_hdr_ready_next = grant;
+
+ 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
+ frame_reg <= frame_next;
+
+ s_ip_hdr_ready_reg <= s_ip_hdr_ready_next;
+
+ m_ip_hdr_valid_reg <= m_ip_hdr_valid_next;
+ 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;
+
+ if (rst) begin
+ frame_reg <= 1'b0;
+ s_ip_hdr_ready_reg <= {S_COUNT{1'b0}};
+ m_ip_hdr_valid_reg <= 1'b0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ m_ip_version_reg <= 4'd0;
+ m_ip_ihl_reg <= 4'd0;
+ m_ip_dscp_reg <= 6'd0;
+ m_ip_ecn_reg <= 2'd0;
+ m_ip_length_reg <= 16'd0;
+ m_ip_identification_reg <= 16'd0;
+ m_ip_flags_reg <= 3'd0;
+ m_ip_fragment_offset_reg <= 13'd0;
+ m_ip_ttl_reg <= 8'd0;
+ m_ip_protocol_reg <= 8'd0;
+ m_ip_header_checksum_reg <= 16'd0;
+ m_ip_source_ip_reg <= 32'd0;
+ m_ip_dest_ip_reg <= 32'd0;
+ end
+end
+
+// output datapath logic
+reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg;
+reg m_ip_payload_axis_tvalid_reg, m_ip_payload_axis_tvalid_next;
+reg m_ip_payload_axis_tlast_reg;
+reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg;
+reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg;
+reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg;
+
+reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg;
+reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg;
+reg temp_m_ip_payload_axis_tvalid_reg, temp_m_ip_payload_axis_tvalid_next;
+reg temp_m_ip_payload_axis_tlast_reg;
+reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg;
+reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg;
+reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg;
+
+// 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_tready_int_reg <= 1'b0;
+ m_ip_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ m_ip_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ m_ip_payload_axis_tvalid_reg <= 1'b0;
+ m_ip_payload_axis_tlast_reg <= 1'b0;
+ m_ip_payload_axis_tid_reg <= {ID_WIDTH{1'b0}};
+ m_ip_payload_axis_tdest_reg <= {DEST_WIDTH{1'b0}};
+ m_ip_payload_axis_tuser_reg <= {USER_WIDTH{1'b0}};
+ temp_m_ip_payload_axis_tdata_reg <= {DATA_WIDTH{1'b0}};
+ temp_m_ip_payload_axis_tkeep_reg <= {KEEP_WIDTH{1'b0}};
+ temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_ip_payload_axis_tlast_reg <= 1'b0;
+ temp_m_ip_payload_axis_tid_reg <= {ID_WIDTH{1'b0}};
+ temp_m_ip_payload_axis_tdest_reg <= {DEST_WIDTH{1'b0}};
+ temp_m_ip_payload_axis_tuser_reg <= {USER_WIDTH{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;
+
+ // 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
+end
+
+endmodule
+
diff --git a/verilog/rtl/ip_complete.v b/verilog/rtl/ip_complete.v
new file mode 100644
index 0000000..a3c978c
--- /dev/null
+++ b/verilog/rtl/ip_complete.v
@@ -0,0 +1,441 @@
+/*
+
+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;
+reg s_select_arp_reg;
+reg s_select_none_reg;
+
+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_ROUND_ROBIN(0),
+ .ARB_LSB_HIGH_PRIORITY(1)
+)
+eth_arb_mux_inst (
+ .clk(clk),
+ .rst(rst),
+ // Ethernet frame inputs
+ .s_eth_hdr_valid({ip_tx_eth_hdr_valid, arp_tx_eth_hdr_valid}),
+ .s_eth_hdr_ready({ip_tx_eth_hdr_ready, arp_tx_eth_hdr_ready}),
+ .s_eth_dest_mac({ip_tx_eth_dest_mac, arp_tx_eth_dest_mac}),
+ .s_eth_src_mac({ip_tx_eth_src_mac, arp_tx_eth_src_mac}),
+ .s_eth_type({ip_tx_eth_type, arp_tx_eth_type}),
+ .s_eth_payload_axis_tdata({ip_tx_eth_payload_axis_tdata, arp_tx_eth_payload_axis_tdata}),
+ .s_eth_payload_axis_tkeep(0),
+ .s_eth_payload_axis_tvalid({ip_tx_eth_payload_axis_tvalid, arp_tx_eth_payload_axis_tvalid}),
+ .s_eth_payload_axis_tready({ip_tx_eth_payload_axis_tready, arp_tx_eth_payload_axis_tready}),
+ .s_eth_payload_axis_tlast({ip_tx_eth_payload_axis_tlast, arp_tx_eth_payload_axis_tlast}),
+ .s_eth_payload_axis_tid(0),
+ .s_eth_payload_axis_tdest(0),
+ .s_eth_payload_axis_tuser({ip_tx_eth_payload_axis_tuser, arp_tx_eth_payload_axis_tuser}),
+ // Ethernet frame output
+ .m_eth_hdr_valid(m_eth_hdr_valid),
+ .m_eth_hdr_ready(m_eth_hdr_ready),
+ .m_eth_dest_mac(m_eth_dest_mac),
+ .m_eth_src_mac(m_eth_src_mac),
+ .m_eth_type(m_eth_type),
+ .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata),
+ .m_eth_payload_axis_tkeep(),
+ .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid),
+ .m_eth_payload_axis_tready(m_eth_payload_axis_tready),
+ .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast),
+ .m_eth_payload_axis_tid(),
+ .m_eth_payload_axis_tdest(),
+ .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser)
+);
+
+/*
+ * IP module
+ */
+ip
+ip_inst (
+ .clk(clk),
+ .rst(rst),
+ // Ethernet frame input
+ .s_eth_hdr_valid(ip_rx_eth_hdr_valid),
+ .s_eth_hdr_ready(ip_rx_eth_hdr_ready),
+ .s_eth_dest_mac(ip_rx_eth_dest_mac),
+ .s_eth_src_mac(ip_rx_eth_src_mac),
+ .s_eth_type(ip_rx_eth_type),
+ .s_eth_payload_axis_tdata(ip_rx_eth_payload_axis_tdata),
+ .s_eth_payload_axis_tvalid(ip_rx_eth_payload_axis_tvalid),
+ .s_eth_payload_axis_tready(ip_rx_eth_payload_axis_tready),
+ .s_eth_payload_axis_tlast(ip_rx_eth_payload_axis_tlast),
+ .s_eth_payload_axis_tuser(ip_rx_eth_payload_axis_tuser),
+ // Ethernet frame output
+ .m_eth_hdr_valid(ip_tx_eth_hdr_valid),
+ .m_eth_hdr_ready(ip_tx_eth_hdr_ready),
+ .m_eth_dest_mac(ip_tx_eth_dest_mac),
+ .m_eth_src_mac(ip_tx_eth_src_mac),
+ .m_eth_type(ip_tx_eth_type),
+ .m_eth_payload_axis_tdata(ip_tx_eth_payload_axis_tdata),
+ .m_eth_payload_axis_tvalid(ip_tx_eth_payload_axis_tvalid),
+ .m_eth_payload_axis_tready(ip_tx_eth_payload_axis_tready),
+ .m_eth_payload_axis_tlast(ip_tx_eth_payload_axis_tlast),
+ .m_eth_payload_axis_tuser(ip_tx_eth_payload_axis_tuser),
+ // IP frame output
+ .m_ip_hdr_valid(m_ip_hdr_valid),
+ .m_ip_hdr_ready(m_ip_hdr_ready),
+ .m_ip_eth_dest_mac(m_ip_eth_dest_mac),
+ .m_ip_eth_src_mac(m_ip_eth_src_mac),
+ .m_ip_eth_type(m_ip_eth_type),
+ .m_ip_version(m_ip_version),
+ .m_ip_ihl(m_ip_ihl),
+ .m_ip_dscp(m_ip_dscp),
+ .m_ip_ecn(m_ip_ecn),
+ .m_ip_length(m_ip_length),
+ .m_ip_identification(m_ip_identification),
+ .m_ip_flags(m_ip_flags),
+ .m_ip_fragment_offset(m_ip_fragment_offset),
+ .m_ip_ttl(m_ip_ttl),
+ .m_ip_protocol(m_ip_protocol),
+ .m_ip_header_checksum(m_ip_header_checksum),
+ .m_ip_source_ip(m_ip_source_ip),
+ .m_ip_dest_ip(m_ip_dest_ip),
+ .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
+ .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
+ .m_ip_payload_axis_tready(m_ip_payload_axis_tready),
+ .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
+ .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
+ // IP frame input
+ .s_ip_hdr_valid(s_ip_hdr_valid),
+ .s_ip_hdr_ready(s_ip_hdr_ready),
+ .s_ip_dscp(s_ip_dscp),
+ .s_ip_ecn(s_ip_ecn),
+ .s_ip_length(s_ip_length),
+ .s_ip_ttl(s_ip_ttl),
+ .s_ip_protocol(s_ip_protocol),
+ .s_ip_source_ip(s_ip_source_ip),
+ .s_ip_dest_ip(s_ip_dest_ip),
+ .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata),
+ .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid),
+ .s_ip_payload_axis_tready(s_ip_payload_axis_tready),
+ .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast),
+ .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser),
+ // ARP requests
+ .arp_request_valid(arp_request_valid),
+ .arp_request_ready(arp_request_ready),
+ .arp_request_ip(arp_request_ip),
+ .arp_response_valid(arp_response_valid),
+ .arp_response_ready(arp_response_ready),
+ .arp_response_error(arp_response_error),
+ .arp_response_mac(arp_response_mac),
+ // Status
+ .rx_busy(rx_busy),
+ .tx_busy(tx_busy),
+ .rx_error_header_early_termination(rx_error_header_early_termination),
+ .rx_error_payload_early_termination(rx_error_payload_early_termination),
+ .rx_error_invalid_header(rx_error_invalid_header),
+ .rx_error_invalid_checksum(rx_error_invalid_checksum),
+ .tx_error_payload_early_termination(tx_error_payload_early_termination),
+ .tx_error_arp_failed(tx_error_arp_failed),
+ // Configuration
+ .local_mac(local_mac),
+ .local_ip(local_ip)
+);
+
+/*
+ * ARP module
+ */
+arp #(
+ .CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH),
+ .REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT),
+ .REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL),
+ .REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT)
+)
+arp_inst (
+ .clk(clk),
+ .rst(rst),
+ // Ethernet frame input
+ .s_eth_hdr_valid(arp_rx_eth_hdr_valid),
+ .s_eth_hdr_ready(arp_rx_eth_hdr_ready),
+ .s_eth_dest_mac(arp_rx_eth_dest_mac),
+ .s_eth_src_mac(arp_rx_eth_src_mac),
+ .s_eth_type(arp_rx_eth_type),
+ .s_eth_payload_axis_tdata(arp_rx_eth_payload_axis_tdata),
+ .s_eth_payload_axis_tvalid(arp_rx_eth_payload_axis_tvalid),
+ .s_eth_payload_axis_tready(arp_rx_eth_payload_axis_tready),
+ .s_eth_payload_axis_tlast(arp_rx_eth_payload_axis_tlast),
+ .s_eth_payload_axis_tuser(arp_rx_eth_payload_axis_tuser),
+ // Ethernet frame output
+ .m_eth_hdr_valid(arp_tx_eth_hdr_valid),
+ .m_eth_hdr_ready(arp_tx_eth_hdr_ready),
+ .m_eth_dest_mac(arp_tx_eth_dest_mac),
+ .m_eth_src_mac(arp_tx_eth_src_mac),
+ .m_eth_type(arp_tx_eth_type),
+ .m_eth_payload_axis_tdata(arp_tx_eth_payload_axis_tdata),
+ .m_eth_payload_axis_tvalid(arp_tx_eth_payload_axis_tvalid),
+ .m_eth_payload_axis_tready(arp_tx_eth_payload_axis_tready),
+ .m_eth_payload_axis_tlast(arp_tx_eth_payload_axis_tlast),
+ .m_eth_payload_axis_tuser(arp_tx_eth_payload_axis_tuser),
+ // ARP requests
+ .arp_request_valid(arp_request_valid),
+ .arp_request_ready(arp_request_ready),
+ .arp_request_ip(arp_request_ip),
+ .arp_response_valid(arp_response_valid),
+ .arp_response_ready(arp_response_ready),
+ .arp_response_error(arp_response_error),
+ .arp_response_mac(arp_response_mac),
+ // Configuration
+ .local_mac(local_mac),
+ .local_ip(local_ip),
+ .gateway_ip(gateway_ip),
+ .subnet_mask(subnet_mask),
+ .clear_cache(clear_arp_cache)
+);
+
+endmodule
+
diff --git a/verilog/rtl/ip_eth_rx.v b/verilog/rtl/ip_eth_rx.v
new file mode 100644
index 0000000..82634e2
--- /dev/null
+++ b/verilog/rtl/ip_eth_rx.v
@@ -0,0 +1,604 @@
+/*
+
+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_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 [5:0] hdr_ptr_reg, hdr_ptr_next;
+reg [15:0] word_count_reg, word_count_next;
+
+reg [15:0] hdr_sum_reg, hdr_sum_next;
+
+reg [7:0] last_word_data_reg;
+
+reg s_eth_hdr_ready_reg, s_eth_hdr_ready_next;
+reg s_eth_payload_axis_tready_reg, s_eth_payload_axis_tready_next;
+
+reg m_ip_hdr_valid_reg, m_ip_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg;
+reg [47:0] m_eth_src_mac_reg;
+reg [15:0] m_eth_type_reg;
+reg [3:0] m_ip_version_reg;
+reg [3:0] m_ip_ihl_reg;
+reg [5:0] m_ip_dscp_reg;
+reg [1:0] m_ip_ecn_reg;
+reg [15:0] m_ip_length_reg;
+reg [15:0] m_ip_identification_reg;
+reg [2:0] m_ip_flags_reg;
+reg [12:0] m_ip_fragment_offset_reg;
+reg [7:0] m_ip_ttl_reg;
+reg [7:0] m_ip_protocol_reg;
+reg [15:0] m_ip_header_checksum_reg;
+reg [31:0] m_ip_source_ip_reg;
+reg [31:0] m_ip_dest_ip_reg;
+
+reg busy_reg;
+reg error_header_early_termination_reg, error_header_early_termination_next;
+reg error_payload_early_termination_reg, error_payload_early_termination_next;
+reg error_invalid_header_reg, error_invalid_header_next;
+reg error_invalid_checksum_reg, 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;
+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;
+
+ hdr_ptr_next = hdr_ptr_reg;
+ word_count_next = word_count_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
+ hdr_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;
+ word_count_next = m_ip_length_reg - 5*4;
+
+ if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin
+ // word transfer in - store it
+ hdr_ptr_next = hdr_ptr_reg + 6'd1;
+ state_next = STATE_READ_HEADER;
+
+ if (hdr_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 (hdr_ptr_reg)
+ 6'h00: store_ip_version_ihl = 1'b1;
+ 6'h01: store_ip_dscp_ecn = 1'b1;
+ 6'h02: store_ip_length_1 = 1'b1;
+ 6'h03: store_ip_length_0 = 1'b1;
+ 6'h04: store_ip_identification_1 = 1'b1;
+ 6'h05: store_ip_identification_0 = 1'b1;
+ 6'h06: store_ip_flags_fragment_offset_1 = 1'b1;
+ 6'h07: store_ip_flags_fragment_offset_0 = 1'b1;
+ 6'h08: store_ip_ttl = 1'b1;
+ 6'h09: store_ip_protocol = 1'b1;
+ 6'h0A: store_ip_header_checksum_1 = 1'b1;
+ 6'h0B: store_ip_header_checksum_0 = 1'b1;
+ 6'h0C: store_ip_source_ip_3 = 1'b1;
+ 6'h0D: store_ip_source_ip_2 = 1'b1;
+ 6'h0E: store_ip_source_ip_1 = 1'b1;
+ 6'h0F: store_ip_source_ip_0 = 1'b1;
+ 6'h10: store_ip_dest_ip_3 = 1'b1;
+ 6'h11: store_ip_dest_ip_2 = 1'b1;
+ 6'h12: store_ip_dest_ip_1 = 1'b1;
+ 6'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
+ word_count_next = word_count_reg - 16'd1;
+ if (s_eth_payload_axis_tlast) begin
+ if (word_count_reg > 16'd1) 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 (word_count_reg == 16'd1) 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;
+ 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;
+ hdr_ptr_reg <= 6'd0;
+ word_count_reg <= 16'd0;
+ hdr_sum_reg <= 16'd0;
+ last_word_data_reg <= 8'd0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ m_ip_version_reg <= 4'd0;
+ m_ip_ihl_reg <= 4'd0;
+ m_ip_dscp_reg <= 6'd0;
+ m_ip_ecn_reg <= 2'd0;
+ m_ip_length_reg <= 16'd0;
+ m_ip_identification_reg <= 16'd0;
+ m_ip_flags_reg <= 3'd0;
+ m_ip_fragment_offset_reg <= 13'd0;
+ m_ip_ttl_reg <= 8'd0;
+ m_ip_protocol_reg <= 8'd0;
+ m_ip_header_checksum_reg <= 16'd0;
+ m_ip_source_ip_reg <= 32'd0;
+ m_ip_dest_ip_reg <= 32'd0;
+ 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;
+
+ 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;
+
+ hdr_ptr_reg <= hdr_ptr_next;
+ word_count_reg <= word_count_next;
+
+ hdr_sum_reg <= hdr_sum_next;
+
+ // 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
+end
+
+// output datapath logic
+reg [7:0] m_ip_payload_axis_tdata_reg;
+reg m_ip_payload_axis_tvalid_reg, m_ip_payload_axis_tvalid_next;
+reg m_ip_payload_axis_tlast_reg;
+reg m_ip_payload_axis_tuser_reg;
+
+reg [7:0] temp_m_ip_payload_axis_tdata_reg;
+reg temp_m_ip_payload_axis_tvalid_reg, temp_m_ip_payload_axis_tvalid_next;
+reg temp_m_ip_payload_axis_tlast_reg;
+reg temp_m_ip_payload_axis_tuser_reg;
+
+// 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_tready_int_reg <= 1'b0;
+ m_ip_payload_axis_tdata_reg <= 8'd0;
+ m_ip_payload_axis_tvalid_reg <= 1'b0;
+ m_ip_payload_axis_tlast_reg <= 1'b0;
+ m_ip_payload_axis_tuser_reg <= 1'b0;
+ temp_m_ip_payload_axis_tdata_reg <= 8'd0;
+ temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_ip_payload_axis_tlast_reg <= 1'b0;
+ temp_m_ip_payload_axis_tuser_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;
+
+ // 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
+end
+
+endmodule
+
diff --git a/verilog/rtl/ip_eth_tx.v b/verilog/rtl/ip_eth_tx.v
new file mode 100644
index 0000000..ca9ca5c
--- /dev/null
+++ b/verilog/rtl/ip_eth_tx.v
@@ -0,0 +1,521 @@
+/*
+
+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_next;
+
+// datapath control signals
+reg store_ip_hdr;
+reg store_last_word;
+
+reg [5:0] hdr_ptr_reg, hdr_ptr_next;
+reg [15:0] word_count_reg, word_count_next;
+
+reg [15:0] hdr_sum_reg, hdr_sum_next;
+
+reg [7:0] last_word_data_reg;
+
+reg [5:0] ip_dscp_reg;
+reg [1:0] ip_ecn_reg;
+reg [15:0] ip_length_reg;
+reg [15:0] ip_identification_reg;
+reg [2:0] ip_flags_reg;
+reg [12:0] ip_fragment_offset_reg;
+reg [7:0] ip_ttl_reg;
+reg [7:0] ip_protocol_reg;
+reg [31:0] ip_source_ip_reg;
+reg [31:0] ip_dest_ip_reg;
+
+reg s_ip_hdr_ready_reg, s_ip_hdr_ready_next;
+reg s_ip_payload_axis_tready_reg, s_ip_payload_axis_tready_next;
+
+reg m_eth_hdr_valid_reg, m_eth_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg;
+reg [47:0] m_eth_src_mac_reg;
+reg [15:0] m_eth_type_reg;
+
+reg busy_reg;
+reg error_payload_early_termination_reg, 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;
+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;
+
+ hdr_ptr_next = hdr_ptr_reg;
+ word_count_next = word_count_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
+ hdr_ptr_next = 6'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
+ hdr_ptr_next = 6'd1;
+ end
+ state_next = STATE_WRITE_HEADER;
+ end else begin
+ state_next = STATE_IDLE;
+ end
+ end
+ STATE_WRITE_HEADER: begin
+ // write header
+ word_count_next = ip_length_reg - 5*4;
+
+ if (m_eth_payload_axis_tready_int_reg) begin
+ hdr_ptr_next = hdr_ptr_reg + 6'd1;
+ m_eth_payload_axis_tvalid_int = 1;
+ state_next = STATE_WRITE_HEADER;
+ case (hdr_ptr_reg)
+ 6'h00: begin
+ m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl
+ end
+ 6'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
+ 6'h02: begin
+ m_eth_payload_axis_tdata_int = ip_length_reg[15: 8];
+ hdr_sum_next = add1c16b(hdr_sum_reg, ip_length_reg);
+ end
+ 6'h03: begin
+ m_eth_payload_axis_tdata_int = ip_length_reg[ 7: 0];
+ hdr_sum_next = add1c16b(hdr_sum_reg, ip_identification_reg);
+ end
+ 6'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
+ 6'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
+ 6'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
+ 6'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
+ 6'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
+ 6'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
+ 6'h0A: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[15: 8];
+ 6'h0B: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[ 7: 0];
+ 6'h0C: m_eth_payload_axis_tdata_int = ip_source_ip_reg[31:24];
+ 6'h0D: m_eth_payload_axis_tdata_int = ip_source_ip_reg[23:16];
+ 6'h0E: m_eth_payload_axis_tdata_int = ip_source_ip_reg[15: 8];
+ 6'h0F: m_eth_payload_axis_tdata_int = ip_source_ip_reg[ 7: 0];
+ 6'h10: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[31:24];
+ 6'h11: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[23:16];
+ 6'h12: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[15: 8];
+ 6'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
+ word_count_next = word_count_reg - 6'd1;
+ if (s_ip_payload_axis_tlast) begin
+ if (word_count_reg != 16'd1) 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 (word_count_reg == 16'd1) 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;
+ 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;
+ hdr_ptr_reg <= 6'd0;
+ word_count_reg <= 16'd0;
+ hdr_sum_reg <= 16'd0;
+ last_word_data_reg <= 8'd0;
+ ip_dscp_reg <= 6'd0;
+ ip_ecn_reg <= 2'd0;
+ ip_length_reg <= 16'd0;
+ ip_identification_reg <= 16'd0;
+ ip_flags_reg <= 3'd0;
+ ip_fragment_offset_reg <= 13'd0;
+ ip_ttl_reg <= 8'd0;
+ ip_protocol_reg <= 8'd0;
+ ip_source_ip_reg <= 32'd0;
+ ip_dest_ip_reg <= 32'd0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ end else begin
+ state_reg <= state_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;
+
+ hdr_ptr_reg <= hdr_ptr_next;
+ word_count_reg <= word_count_next;
+
+ hdr_sum_reg <= hdr_sum_next;
+
+ // 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
+end
+
+// output datapath logic
+reg [7:0] m_eth_payload_axis_tdata_reg;
+reg m_eth_payload_axis_tvalid_reg, m_eth_payload_axis_tvalid_next;
+reg m_eth_payload_axis_tlast_reg;
+reg m_eth_payload_axis_tuser_reg;
+
+reg [7:0] temp_m_eth_payload_axis_tdata_reg;
+reg temp_m_eth_payload_axis_tvalid_reg, temp_m_eth_payload_axis_tvalid_next;
+reg temp_m_eth_payload_axis_tlast_reg;
+reg temp_m_eth_payload_axis_tuser_reg;
+
+// 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_tready_int_reg <= 1'b0;
+ m_eth_payload_axis_tdata_reg <= 8'd0;
+ m_eth_payload_axis_tvalid_reg <= 1'b0;
+ m_eth_payload_axis_tlast_reg <= 1'b0;
+ m_eth_payload_axis_tuser_reg <= 1'b0;
+ temp_m_eth_payload_axis_tdata_reg <= 8'd0;
+ temp_m_eth_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_eth_payload_axis_tlast_reg <= 1'b0;
+ temp_m_eth_payload_axis_tuser_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;
+
+ // 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
+end
+
+endmodule
+
diff --git a/verilog/rtl/lfsr.v b/verilog/rtl/lfsr.v
new file mode 100644
index 0000000..f340ab2
--- /dev/null
+++ b/verilog/rtl/lfsr.v
@@ -0,0 +1,383 @@
+/*
+
+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 lfsr #
+(
+ // width of LFSR
+ parameter LFSR_WIDTH = 32,
+ // 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
+
+*/
+
+wire [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0];
+wire [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0];
+wire [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0];
+wire [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0];
+
+wire [LFSR_WIDTH-1:0] state_val;
+wire [DATA_WIDTH-1:0] data_val;
+
+integer i, j;
+
+assign lfsr_mask_state[31] = 32'h00000082;
+assign lfsr_mask_state[30] = 32'h000000c3;
+assign lfsr_mask_state[29] = 32'h000000e3;
+assign lfsr_mask_state[28] = 32'h00000071;
+assign lfsr_mask_state[27] = 32'h000000ba;
+assign lfsr_mask_state[26] = 32'h000000df;
+assign lfsr_mask_state[25] = 32'h0000006f;
+assign lfsr_mask_state[24] = 32'h000000b5;
+assign lfsr_mask_state[23] = 32'h800000d8;
+assign lfsr_mask_state[22] = 32'h4000006c;
+assign lfsr_mask_state[21] = 32'h200000b4;
+assign lfsr_mask_state[20] = 32'h100000d8;
+assign lfsr_mask_state[19] = 32'h080000ee;
+assign lfsr_mask_state[18] = 32'h04000077;
+assign lfsr_mask_state[17] = 32'h0200003b;
+assign lfsr_mask_state[16] = 32'h0100001d;
+assign lfsr_mask_state[15] = 32'h0080008c;
+assign lfsr_mask_state[14] = 32'h00400046;
+assign lfsr_mask_state[13] = 32'h00200023;
+assign lfsr_mask_state[12] = 32'h00100011;
+assign lfsr_mask_state[11] = 32'h00080008;
+assign lfsr_mask_state[10] = 32'h00040004;
+assign lfsr_mask_state[9] = 32'h00020080;
+assign lfsr_mask_state[8] = 32'h000100c2;
+assign lfsr_mask_state[7] = 32'h00008061;
+assign lfsr_mask_state[6] = 32'h00004030;
+assign lfsr_mask_state[5] = 32'h0000209a;
+assign lfsr_mask_state[4] = 32'h0000104d;
+assign lfsr_mask_state[3] = 32'h00000826;
+assign lfsr_mask_state[2] = 32'h00000413;
+assign lfsr_mask_state[1] = 32'h00000209;
+assign lfsr_mask_state[0] = 32'h00000104;
+
+assign lfsr_mask_data[31] = 8'h82;
+assign lfsr_mask_data[30] = 8'hc3;
+assign lfsr_mask_data[29] = 8'he3;
+assign lfsr_mask_data[28] = 8'h71;
+assign lfsr_mask_data[27] = 8'hba;
+assign lfsr_mask_data[26] = 8'hdf;
+assign lfsr_mask_data[25] = 8'h6f;
+assign lfsr_mask_data[24] = 8'hb5;
+assign lfsr_mask_data[23] = 8'hd8;
+assign lfsr_mask_data[22] = 8'h6c;
+assign lfsr_mask_data[21] = 8'hb4;
+assign lfsr_mask_data[20] = 8'hd8;
+assign lfsr_mask_data[19] = 8'hee;
+assign lfsr_mask_data[18] = 8'h77;
+assign lfsr_mask_data[17] = 8'h3b;
+assign lfsr_mask_data[16] = 8'h1d;
+assign lfsr_mask_data[15] = 8'h8c;
+assign lfsr_mask_data[14] = 8'h46;
+assign lfsr_mask_data[13] = 8'h23;
+assign lfsr_mask_data[12] = 8'h11;
+assign lfsr_mask_data[11] = 8'h08;
+assign lfsr_mask_data[10] = 8'h04;
+assign lfsr_mask_data[9] = 8'h80;
+assign lfsr_mask_data[8] = 8'hc2;
+assign lfsr_mask_data[7] = 8'h61;
+assign lfsr_mask_data[6] = 8'h30;
+assign lfsr_mask_data[5] = 8'h9a;
+assign lfsr_mask_data[4] = 8'h4d;
+assign lfsr_mask_data[3] = 8'h26;
+assign lfsr_mask_data[2] = 8'h13;
+assign lfsr_mask_data[1] = 8'h09;
+assign lfsr_mask_data[0] = 8'h04;
+
+assign output_mask_state[7] = 32'h00000082;
+assign output_mask_state[6] = 32'h00000041;
+assign output_mask_state[5] = 32'h00000020;
+assign output_mask_state[4] = 32'h00000010;
+assign output_mask_state[3] = 32'h00000008;
+assign output_mask_state[2] = 32'h00000004;
+assign output_mask_state[1] = 32'h00000002;
+assign output_mask_state[0] = 32'h00000001;
+
+assign output_mask_data[7] = 8'h82;
+assign output_mask_data[6] = 8'h41;
+assign output_mask_data[5] = 8'h20;
+assign output_mask_data[4] = 8'h10;
+assign output_mask_data[3] = 8'h08;
+assign output_mask_data[2] = 8'h04;
+assign output_mask_data[1] = 8'h02;
+assign output_mask_data[0] = 8'h01;
+
+assign state_val = 32'h00000082;
+assign data_val = 8'h82;
+
+// synthesis translate_off
+`define SIMULATION
+// synthesis translate_on
+
+`ifdef SIMULATION
+// "AUTO" style is "REDUCTION" for faster simulation
+parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE;
+`else
+// "AUTO" style is "LOOP" for better synthesis result
+parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE;
+`endif
+
+genvar n;
+
+generate
+
+if (STYLE_INT == "REDUCTION") begin
+
+ // use Verilog reduction operator
+ // fast in iverilog
+ // significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer)
+ // slightly smaller than generated code with Quartus
+ // --> better for simulation
+
+ for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : loop1
+ assign state_out[n] = ^{(state_in & lfsr_mask_state[n]), (data_in & lfsr_mask_data[n])};
+ end
+ for (n = 0; n < DATA_WIDTH; n = n + 1) begin : loop2
+ assign data_out[n] = ^{(state_in & output_mask_state[n]), (data_in & output_mask_data[n])};
+ end
+
+end else if (STYLE_INT == "LOOP") begin
+
+ // use nested loops
+ // very slow in iverilog
+ // slightly smaller than generated code with ISE
+ // same size as generated code with Quartus
+ // --> better for synthesis
+
+ reg [LFSR_WIDTH-1:0] state_out_reg = 0;
+ reg [DATA_WIDTH-1:0] data_out_reg = 0;
+
+ assign state_out = state_out_reg;
+ assign data_out = data_out_reg;
+
+ always @* begin
+ for (i = 0; i < LFSR_WIDTH; i = i + 1) begin
+ state_out_reg[i] = 0;
+ for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
+ if (lfsr_mask_state[i][j]) begin
+ state_out_reg[i] = state_out_reg[i] ^ state_in[j];
+ end
+ end
+ for (j = 0; j < DATA_WIDTH; j = j + 1) begin
+ if (lfsr_mask_data[i][j]) begin
+ state_out_reg[i] = state_out_reg[i] ^ data_in[j];
+ end
+ end
+ end
+ for (i = 0; i < DATA_WIDTH; i = i + 1) begin
+ data_out_reg[i] = 0;
+ for (j = 0; j < LFSR_WIDTH; j = j + 1) begin
+ if (output_mask_state[i][j]) begin
+ data_out_reg[i] = data_out_reg[i] ^ state_in[j];
+ end
+ end
+ for (j = 0; j < DATA_WIDTH; j = j + 1) begin
+ if (output_mask_data[i][j]) begin
+ data_out_reg[i] = data_out_reg[i] ^ data_in[j];
+ end
+ end
+ end
+ end
+
+end else begin
+
+ initial begin
+ $error("Error: unknown style setting!");
+ $finish;
+ end
+
+end
+
+endgenerate
+
+endmodule
diff --git a/verilog/rtl/mii_phy_if.v b/verilog/rtl/mii_phy_if.v
new file mode 100644
index 0000000..22dff7b
--- /dev/null
+++ b/verilog/rtl/mii_phy_if.v
@@ -0,0 +1,144 @@
+/*
+
+Copyright (c) 2019 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
+
+/*
+ * MII PHY interface
+ */
+module mii_phy_if #
+(
+ // target ("SIM", "GENERIC", "XILINX", "ALTERA")
+ parameter TARGET = "GENERIC",
+ // 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"
+)
+(
+ input wire rst,
+
+ /*
+ * MII interface to MAC
+ */
+ output wire mac_mii_rx_clk,
+ output wire mac_mii_rx_rst,
+ output wire [3:0] mac_mii_rxd,
+ output wire mac_mii_rx_dv,
+ output wire mac_mii_rx_er,
+ output wire mac_mii_tx_clk,
+ output wire mac_mii_tx_rst,
+ input wire [3:0] mac_mii_txd,
+ input wire mac_mii_tx_en,
+ input wire mac_mii_tx_er,
+
+ /*
+ * MII interface to PHY
+ */
+ input wire phy_mii_rx_clk,
+ input wire [3:0] phy_mii_rxd,
+ input wire phy_mii_rx_dv,
+ input wire phy_mii_rx_er,
+ input wire phy_mii_tx_clk,
+ output wire [3:0] phy_mii_txd,
+ output wire phy_mii_tx_en,
+ output wire phy_mii_tx_er
+);
+
+ssio_sdr_in #
+(
+ .TARGET(TARGET),
+ .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE),
+ .WIDTH(6)
+)
+rx_ssio_sdr_inst (
+ .input_clk(phy_mii_rx_clk),
+ .input_d({phy_mii_rxd, phy_mii_rx_dv, phy_mii_rx_er}),
+ .output_clk(mac_mii_rx_clk),
+ .output_q({mac_mii_rxd, mac_mii_rx_dv, mac_mii_rx_er})
+);
+
+(* IOB = "TRUE" *)
+reg [3:0] phy_mii_txd_reg;
+(* IOB = "TRUE" *)
+reg phy_mii_tx_en_reg, phy_mii_tx_er_reg;
+
+assign phy_mii_txd = phy_mii_txd_reg;
+assign phy_mii_tx_en = phy_mii_tx_en_reg;
+assign phy_mii_tx_er = phy_mii_tx_er_reg;
+
+always @(posedge mac_mii_tx_clk) begin
+ if(rst) begin
+ phy_mii_txd_reg <= 4'd0;
+ phy_mii_tx_en_reg <= 1'b0;
+ phy_mii_tx_er_reg <= 1'b0;
+ end else begin
+ phy_mii_txd_reg <= mac_mii_txd;
+ phy_mii_tx_en_reg <= mac_mii_tx_en;
+ phy_mii_tx_er_reg <= mac_mii_tx_er;
+ end
+end
+
+generate
+
+if (TARGET == "XILINX") begin
+ BUFG
+ mii_bufg_inst (
+ .I(phy_mii_tx_clk),
+ .O(mac_mii_tx_clk)
+ );
+end else begin
+ assign mac_mii_tx_clk = phy_mii_tx_clk;
+end
+
+endgenerate
+
+// reset sync
+reg [3:0] tx_rst_reg;
+assign mac_mii_tx_rst = tx_rst_reg[0];
+
+always @(posedge mac_mii_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;
+assign mac_mii_rx_rst = rx_rst_reg[0];
+
+always @(posedge mac_mii_rx_clk or posedge rst) begin
+ if (rst) begin
+ rx_rst_reg <= 4'hf;
+ end else begin
+ rx_rst_reg <= {1'b0, rx_rst_reg[3:1]};
+ end
+end
+
+endmodule
+
diff --git a/verilog/rtl/priority_encoder.v b/verilog/rtl/priority_encoder.v
new file mode 100644
index 0000000..dd59fa4
--- /dev/null
+++ b/verilog/rtl/priority_encoder.v
@@ -0,0 +1,88 @@
+/*
+
+Copyright (c) 2014-2021 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 selection
+ parameter LSB_HIGH_PRIORITY = 0
+)
+(
+ 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
+);
+
+parameter LEVELS = WIDTH > 2 ? $clog2(WIDTH) : 1;
+parameter W = 2**LEVELS;
+
+// pad input to even power of two
+wire [W-1:0] input_padded = {{W-WIDTH{1'b0}}, input_unencoded};
+
+wire [W/2-1:0] stage_valid[LEVELS-1:0];
+wire [W/2-1:0] stage_enc[LEVELS-1:0];
+
+generate
+ genvar l, n;
+
+ // process input bits; generate valid bit and encoded bit for each pair
+ for (n = 0; n < W/2; n = n + 1) begin : loop_in
+ assign stage_valid[0][n] = |input_padded[n*2+1:n*2];
+ if (LSB_HIGH_PRIORITY) begin
+ // bit 0 is highest priority
+ assign stage_enc[0][n] = !input_padded[n*2+0];
+ end else begin
+ // bit 0 is lowest priority
+ assign stage_enc[0][n] = input_padded[n*2+1];
+ end
+ end
+
+ // compress down to single valid bit and encoded bus
+ for (l = 1; l < LEVELS; l = l + 1) begin : loop_levels
+ for (n = 0; n < W/(2*2**l); n = n + 1) begin : loop_compress
+ assign stage_valid[l][n] = |stage_valid[l-1][n*2+1:n*2];
+ if (LSB_HIGH_PRIORITY) begin
+ // bit 0 is highest priority
+ assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+0] ? {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]} : {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]};
+ end else begin
+ // bit 0 is lowest priority
+ assign stage_enc[l][(n+1)*(l+1)-1:n*(l+1)] = stage_valid[l-1][n*2+1] ? {1'b1, stage_enc[l-1][(n*2+2)*l-1:(n*2+1)*l]} : {1'b0, stage_enc[l-1][(n*2+1)*l-1:(n*2+0)*l]};
+ end
+ end
+ end
+endgenerate
+
+assign output_valid = stage_valid[LEVELS-1];
+assign output_encoded = stage_enc[LEVELS-1];
+assign output_unencoded = 1 << output_encoded;
+
+endmodule
diff --git a/verilog/rtl/ssio_sdr_in.v b/verilog/rtl/ssio_sdr_in.v
new file mode 100644
index 0000000..996ea91
--- /dev/null
+++ b/verilog/rtl/ssio_sdr_in.v
@@ -0,0 +1,164 @@
+/*
+
+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 SDR input
+ */
+module ssio_sdr_in #
+(
+ // target ("SIM", "GENERIC", "XILINX", "ALTERA")
+ parameter TARGET = "GENERIC",
+ // 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_q
+);
+
+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
+
+(* IOB = "TRUE" *)
+reg [WIDTH-1:0] output_q_reg;
+
+assign output_q = output_q_reg;
+
+always @(posedge clk_io) begin
+ output_q_reg <= input_d;
+end
+
+endmodule
+
diff --git a/verilog/rtl/udp.v b/verilog/rtl/udp.v
new file mode 100644
index 0000000..b38f42d
--- /dev/null
+++ b/verilog/rtl/udp.v
@@ -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
+
+/*
+ * UDP block, IP interface
+ */
+module udp #
+(
+ parameter CHECKSUM_GEN_ENABLE = 1,
+ parameter CHECKSUM_PAYLOAD_FIFO_DEPTH = 2048,
+ parameter CHECKSUM_HEADER_FIFO_DEPTH = 8
+)
+(
+ 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)
+);
+
+ 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;
+
+udp_ip_tx
+udp_ip_tx_inst (
+ .clk(clk),
+ .rst(rst),
+ // UDP frame input
+ .s_udp_hdr_valid(tx_udp_hdr_valid),
+ .s_udp_hdr_ready(tx_udp_hdr_ready),
+ .s_eth_dest_mac(tx_udp_eth_dest_mac),
+ .s_eth_src_mac(tx_udp_eth_src_mac),
+ .s_eth_type(tx_udp_eth_type),
+ .s_ip_version(tx_udp_ip_version),
+ .s_ip_ihl(tx_udp_ip_ihl),
+ .s_ip_dscp(tx_udp_ip_dscp),
+ .s_ip_ecn(tx_udp_ip_ecn),
+ .s_ip_identification(tx_udp_ip_identification),
+ .s_ip_flags(tx_udp_ip_flags),
+ .s_ip_fragment_offset(tx_udp_ip_fragment_offset),
+ .s_ip_ttl(tx_udp_ip_ttl),
+ .s_ip_protocol(8'h11),
+ .s_ip_header_checksum(tx_udp_ip_header_checksum),
+ .s_ip_source_ip(tx_udp_ip_source_ip),
+ .s_ip_dest_ip(tx_udp_ip_dest_ip),
+ .s_udp_source_port(tx_udp_source_port),
+ .s_udp_dest_port(tx_udp_dest_port),
+ .s_udp_length(tx_udp_length),
+ .s_udp_checksum(tx_udp_checksum),
+ .s_udp_payload_axis_tdata(tx_udp_payload_axis_tdata),
+ .s_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid),
+ .s_udp_payload_axis_tready(tx_udp_payload_axis_tready),
+ .s_udp_payload_axis_tlast(tx_udp_payload_axis_tlast),
+ .s_udp_payload_axis_tuser(tx_udp_payload_axis_tuser),
+ // IP frame output
+ .m_ip_hdr_valid(m_ip_hdr_valid),
+ .m_ip_hdr_ready(m_ip_hdr_ready),
+ .m_eth_dest_mac(m_ip_eth_dest_mac),
+ .m_eth_src_mac(m_ip_eth_src_mac),
+ .m_eth_type(m_ip_eth_type),
+ .m_ip_version(m_ip_version),
+ .m_ip_ihl(m_ip_ihl),
+ .m_ip_dscp(m_ip_dscp),
+ .m_ip_ecn(m_ip_ecn),
+ .m_ip_length(m_ip_length),
+ .m_ip_identification(m_ip_identification),
+ .m_ip_flags(m_ip_flags),
+ .m_ip_fragment_offset(m_ip_fragment_offset),
+ .m_ip_ttl(m_ip_ttl),
+ .m_ip_protocol(m_ip_protocol),
+ .m_ip_header_checksum(m_ip_header_checksum),
+ .m_ip_source_ip(m_ip_source_ip),
+ .m_ip_dest_ip(m_ip_dest_ip),
+ .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata),
+ .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid),
+ .m_ip_payload_axis_tready(m_ip_payload_axis_tready),
+ .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast),
+ .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser),
+ // Status signals
+ .busy(tx_busy),
+ .error_payload_early_termination(tx_error_payload_early_termination)
+);
+
+endmodule
diff --git a/verilog/rtl/udp_complete.v b/verilog/rtl/udp_complete.v
new file mode 100644
index 0000000..16a4893
--- /dev/null
+++ b/verilog/rtl/udp_complete.v
@@ -0,0 +1,638 @@
+/*
+
+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_DEPTH = 2048,
+ parameter UDP_CHECKSUM_HEADER_FIFO_DEPTH = 8
+)
+(
+ 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;
+reg s_select_ip_reg;
+
+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_ROUND_ROBIN(0),
+ .ARB_LSB_HIGH_PRIORITY(1)
+)
+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_DEPTH(UDP_CHECKSUM_PAYLOAD_FIFO_DEPTH),
+ .CHECKSUM_HEADER_FIFO_DEPTH(UDP_CHECKSUM_HEADER_FIFO_DEPTH)
+)
+udp_inst (
+ .clk(clk),
+ .rst(rst),
+ // IP frame input
+ .s_ip_hdr_valid(udp_rx_ip_hdr_valid),
+ .s_ip_hdr_ready(udp_rx_ip_hdr_ready),
+ .s_ip_eth_dest_mac(udp_rx_ip_eth_dest_mac),
+ .s_ip_eth_src_mac(udp_rx_ip_eth_src_mac),
+ .s_ip_eth_type(udp_rx_ip_eth_type),
+ .s_ip_version(udp_rx_ip_version),
+ .s_ip_ihl(udp_rx_ip_ihl),
+ .s_ip_dscp(udp_rx_ip_dscp),
+ .s_ip_ecn(udp_rx_ip_ecn),
+ .s_ip_length(udp_rx_ip_length),
+ .s_ip_identification(udp_rx_ip_identification),
+ .s_ip_flags(udp_rx_ip_flags),
+ .s_ip_fragment_offset(udp_rx_ip_fragment_offset),
+ .s_ip_ttl(udp_rx_ip_ttl),
+ .s_ip_protocol(udp_rx_ip_protocol),
+ .s_ip_header_checksum(udp_rx_ip_header_checksum),
+ .s_ip_source_ip(udp_rx_ip_source_ip),
+ .s_ip_dest_ip(udp_rx_ip_dest_ip),
+ .s_ip_payload_axis_tdata(udp_rx_ip_payload_axis_tdata),
+ .s_ip_payload_axis_tvalid(udp_rx_ip_payload_axis_tvalid),
+ .s_ip_payload_axis_tready(udp_rx_ip_payload_axis_tready),
+ .s_ip_payload_axis_tlast(udp_rx_ip_payload_axis_tlast),
+ .s_ip_payload_axis_tuser(udp_rx_ip_payload_axis_tuser),
+ // IP frame output
+ .m_ip_hdr_valid(udp_tx_ip_hdr_valid),
+ .m_ip_hdr_ready(udp_tx_ip_hdr_ready),
+ .m_ip_eth_dest_mac(),
+ .m_ip_eth_src_mac(),
+ .m_ip_eth_type(),
+ .m_ip_version(),
+ .m_ip_ihl(),
+ .m_ip_dscp(udp_tx_ip_dscp),
+ .m_ip_ecn(udp_tx_ip_ecn),
+ .m_ip_length(udp_tx_ip_length),
+ .m_ip_identification(),
+ .m_ip_flags(),
+ .m_ip_fragment_offset(),
+ .m_ip_ttl(udp_tx_ip_ttl),
+ .m_ip_protocol(udp_tx_ip_protocol),
+ .m_ip_header_checksum(),
+ .m_ip_source_ip(udp_tx_ip_source_ip),
+ .m_ip_dest_ip(udp_tx_ip_dest_ip),
+ .m_ip_payload_axis_tdata(udp_tx_ip_payload_axis_tdata),
+ .m_ip_payload_axis_tvalid(udp_tx_ip_payload_axis_tvalid),
+ .m_ip_payload_axis_tready(udp_tx_ip_payload_axis_tready),
+ .m_ip_payload_axis_tlast(udp_tx_ip_payload_axis_tlast),
+ .m_ip_payload_axis_tuser(udp_tx_ip_payload_axis_tuser),
+ // UDP frame input
+ .s_udp_hdr_valid(s_udp_hdr_valid),
+ .s_udp_hdr_ready(s_udp_hdr_ready),
+ .s_udp_eth_dest_mac(48'd0),
+ .s_udp_eth_src_mac(48'd0),
+ .s_udp_eth_type(16'd0),
+ .s_udp_ip_version(4'd0),
+ .s_udp_ip_ihl(4'd0),
+ .s_udp_ip_dscp(s_udp_ip_dscp),
+ .s_udp_ip_ecn(s_udp_ip_ecn),
+ .s_udp_ip_identification(16'd0),
+ .s_udp_ip_flags(3'd0),
+ .s_udp_ip_fragment_offset(13'd0),
+ .s_udp_ip_ttl(s_udp_ip_ttl),
+ .s_udp_ip_header_checksum(16'd0),
+ .s_udp_ip_source_ip(s_udp_ip_source_ip),
+ .s_udp_ip_dest_ip(s_udp_ip_dest_ip),
+ .s_udp_source_port(s_udp_source_port),
+ .s_udp_dest_port(s_udp_dest_port),
+ .s_udp_length(s_udp_length),
+ .s_udp_checksum(s_udp_checksum),
+ .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata),
+ .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid),
+ .s_udp_payload_axis_tready(s_udp_payload_axis_tready),
+ .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast),
+ .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser),
+ // UDP frame output
+ .m_udp_hdr_valid(m_udp_hdr_valid),
+ .m_udp_hdr_ready(m_udp_hdr_ready),
+ .m_udp_eth_dest_mac(m_udp_eth_dest_mac),
+ .m_udp_eth_src_mac(m_udp_eth_src_mac),
+ .m_udp_eth_type(m_udp_eth_type),
+ .m_udp_ip_version(m_udp_ip_version),
+ .m_udp_ip_ihl(m_udp_ip_ihl),
+ .m_udp_ip_dscp(m_udp_ip_dscp),
+ .m_udp_ip_ecn(m_udp_ip_ecn),
+ .m_udp_ip_length(m_udp_ip_length),
+ .m_udp_ip_identification(m_udp_ip_identification),
+ .m_udp_ip_flags(m_udp_ip_flags),
+ .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset),
+ .m_udp_ip_ttl(m_udp_ip_ttl),
+ .m_udp_ip_protocol(m_udp_ip_protocol),
+ .m_udp_ip_header_checksum(m_udp_ip_header_checksum),
+ .m_udp_ip_source_ip(m_udp_ip_source_ip),
+ .m_udp_ip_dest_ip(m_udp_ip_dest_ip),
+ .m_udp_source_port(m_udp_source_port),
+ .m_udp_dest_port(m_udp_dest_port),
+ .m_udp_length(m_udp_length),
+ .m_udp_checksum(m_udp_checksum),
+ .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata),
+ .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid),
+ .m_udp_payload_axis_tready(m_udp_payload_axis_tready),
+ .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast),
+ .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser),
+ // Status
+ .rx_busy(udp_rx_busy),
+ .tx_busy(udp_tx_busy),
+ .rx_error_header_early_termination(udp_rx_error_header_early_termination),
+ .rx_error_payload_early_termination(udp_rx_error_payload_early_termination),
+ .tx_error_payload_early_termination(udp_tx_error_payload_early_termination)
+);
+
+endmodule
+
diff --git a/verilog/rtl/udp_ip_rx.v b/verilog/rtl/udp_ip_rx.v
new file mode 100644
index 0000000..13755ca
--- /dev/null
+++ b/verilog/rtl/udp_ip_rx.v
@@ -0,0 +1,564 @@
+/*
+
+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_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 [2:0] hdr_ptr_reg, hdr_ptr_next;
+reg [15:0] word_count_reg, word_count_next;
+
+reg [7:0] last_word_data_reg;
+
+reg m_udp_hdr_valid_reg, m_udp_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg;
+reg [47:0] m_eth_src_mac_reg;
+reg [15:0] m_eth_type_reg;
+reg [3:0] m_ip_version_reg;
+reg [3:0] m_ip_ihl_reg;
+reg [5:0] m_ip_dscp_reg;
+reg [1:0] m_ip_ecn_reg;
+reg [15:0] m_ip_length_reg;
+reg [15:0] m_ip_identification_reg;
+reg [2:0] m_ip_flags_reg;
+reg [12:0] m_ip_fragment_offset_reg;
+reg [7:0] m_ip_ttl_reg;
+reg [7:0] m_ip_protocol_reg;
+reg [15:0] m_ip_header_checksum_reg;
+reg [31:0] m_ip_source_ip_reg;
+reg [31:0] m_ip_dest_ip_reg;
+reg [15:0] m_udp_source_port_reg;
+reg [15:0] m_udp_dest_port_reg;
+reg [15:0] m_udp_length_reg;
+reg [15:0] m_udp_checksum_reg;
+
+reg s_ip_hdr_ready_reg, s_ip_hdr_ready_next;
+reg s_ip_payload_axis_tready_reg, s_ip_payload_axis_tready_next;
+
+reg busy_reg;
+reg error_header_early_termination_reg, error_header_early_termination_next;
+reg error_payload_early_termination_reg, 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;
+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;
+
+ hdr_ptr_next = hdr_ptr_reg;
+ word_count_next = word_count_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
+ hdr_ptr_next = 3'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;
+ word_count_next = m_udp_length_reg - 16'd8;
+
+ if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin
+ // word transfer in - store it
+ hdr_ptr_next = hdr_ptr_reg + 3'd1;
+ state_next = STATE_READ_HEADER;
+
+ case (hdr_ptr_reg)
+ 3'h0: store_udp_source_port_1 = 1'b1;
+ 3'h1: store_udp_source_port_0 = 1'b1;
+ 3'h2: store_udp_dest_port_1 = 1'b1;
+ 3'h3: store_udp_dest_port_0 = 1'b1;
+ 3'h4: store_udp_length_1 = 1'b1;
+ 3'h5: store_udp_length_0 = 1'b1;
+ 3'h6: store_udp_checksum_1 = 1'b1;
+ 3'h7: 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
+ word_count_next = word_count_reg - 16'd1;
+ if (s_ip_payload_axis_tlast) begin
+ if (word_count_reg != 16'd1) 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 (word_count_reg == 16'd1) 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;
+ s_ip_hdr_ready_reg <= 1'b0;
+ s_ip_payload_axis_tready_reg <= 1'b0;
+ last_word_data_reg <= 8'd0;
+ m_udp_hdr_valid_reg <= 1'b0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ m_ip_version_reg <= 4'd0;
+ m_ip_ihl_reg <= 4'd0;
+ m_ip_dscp_reg <= 6'd0;
+ m_ip_ecn_reg <= 2'd0;
+ m_ip_length_reg <= 16'd0;
+ m_ip_identification_reg <= 16'd0;
+ m_ip_flags_reg <= 3'd0;
+ m_ip_fragment_offset_reg <= 13'd0;
+ m_ip_ttl_reg <= 8'd0;
+ m_ip_protocol_reg <= 8'd0;
+ m_ip_header_checksum_reg <= 16'd0;
+ m_ip_source_ip_reg <= 32'd0;
+ m_ip_dest_ip_reg <= 32'd0;
+ m_udp_source_port_reg <= 16'd0;
+ m_udp_dest_port_reg <= 16'd0;
+ m_udp_length_reg <= 16'd0;
+ m_udp_checksum_reg <= 16'd0;
+ busy_reg <= 1'b0;
+ error_header_early_termination_reg <= 1'b0;
+ error_payload_early_termination_reg <= 1'b0;
+ hdr_ptr_reg <= 3'd0;
+ word_count_reg <= 16'd0;
+ end else begin
+ state_reg <= state_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;
+
+ hdr_ptr_reg <= hdr_ptr_next;
+ word_count_reg <= word_count_next;
+
+ // 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
+end
+
+// output datapath logic
+reg [7:0] m_udp_payload_axis_tdata_reg;
+reg m_udp_payload_axis_tvalid_reg, m_udp_payload_axis_tvalid_next;
+reg m_udp_payload_axis_tlast_reg;
+reg m_udp_payload_axis_tuser_reg;
+
+reg [7:0] temp_m_udp_payload_axis_tdata_reg;
+reg temp_m_udp_payload_axis_tvalid_reg, temp_m_udp_payload_axis_tvalid_next;
+reg temp_m_udp_payload_axis_tlast_reg;
+reg temp_m_udp_payload_axis_tuser_reg;
+
+// 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_tready_int_reg <= 1'b0;
+
+ m_udp_payload_axis_tdata_reg <= 8'd0;
+ m_udp_payload_axis_tvalid_reg <= 1'b0;
+ m_udp_payload_axis_tlast_reg <= 1'b0;
+ m_udp_payload_axis_tuser_reg <= 1'b0;
+
+ temp_m_udp_payload_axis_tdata_reg <= 8'd0;
+ temp_m_udp_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_udp_payload_axis_tlast_reg <= 1'b0;
+ temp_m_udp_payload_axis_tuser_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;
+
+ // 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
+end
+
+endmodule
+
diff --git a/verilog/rtl/udp_ip_tx.v b/verilog/rtl/udp_ip_tx.v
new file mode 100644
index 0000000..48b9328
--- /dev/null
+++ b/verilog/rtl/udp_ip_tx.v
@@ -0,0 +1,525 @@
+/*
+
+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_next;
+
+// datapath control signals
+reg store_udp_hdr;
+reg store_last_word;
+
+reg [2:0] hdr_ptr_reg, hdr_ptr_next;
+reg [15:0] word_count_reg, word_count_next;
+
+reg [7:0] last_word_data_reg;
+
+reg [15:0] udp_source_port_reg;
+reg [15:0] udp_dest_port_reg;
+reg [15:0] udp_length_reg;
+reg [15:0] udp_checksum_reg;
+
+reg s_udp_hdr_ready_reg, s_udp_hdr_ready_next;
+reg s_udp_payload_axis_tready_reg, s_udp_payload_axis_tready_next;
+
+reg m_ip_hdr_valid_reg, m_ip_hdr_valid_next;
+reg [47:0] m_eth_dest_mac_reg;
+reg [47:0] m_eth_src_mac_reg;
+reg [15:0] m_eth_type_reg;
+reg [3:0] m_ip_version_reg;
+reg [3:0] m_ip_ihl_reg;
+reg [5:0] m_ip_dscp_reg;
+reg [1:0] m_ip_ecn_reg;
+reg [15:0] m_ip_length_reg;
+reg [15:0] m_ip_identification_reg;
+reg [2:0] m_ip_flags_reg;
+reg [12:0] m_ip_fragment_offset_reg;
+reg [7:0] m_ip_ttl_reg;
+reg [7:0] m_ip_protocol_reg;
+reg [15:0] m_ip_header_checksum_reg;
+reg [31:0] m_ip_source_ip_reg;
+reg [31:0] m_ip_dest_ip_reg;
+
+reg busy_reg;
+reg error_payload_early_termination_reg, 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;
+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;
+
+ hdr_ptr_next = hdr_ptr_reg;
+ word_count_next = word_count_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
+ hdr_ptr_next = 3'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];
+ hdr_ptr_next = 3'd1;
+ end
+ state_next = STATE_WRITE_HEADER;
+ end else begin
+ state_next = STATE_IDLE;
+ end
+ end
+ STATE_WRITE_HEADER: begin
+ // write header state
+ word_count_next = udp_length_reg - 16'd8;
+
+ if (m_ip_payload_axis_tready_int_reg) begin
+ // word transfer out
+ hdr_ptr_next = hdr_ptr_reg + 3'd1;
+ m_ip_payload_axis_tvalid_int = 1'b1;
+ state_next = STATE_WRITE_HEADER;
+ case (hdr_ptr_reg)
+ 3'h0: m_ip_payload_axis_tdata_int = udp_source_port_reg[15: 8];
+ 3'h1: m_ip_payload_axis_tdata_int = udp_source_port_reg[ 7: 0];
+ 3'h2: m_ip_payload_axis_tdata_int = udp_dest_port_reg[15: 8];
+ 3'h3: m_ip_payload_axis_tdata_int = udp_dest_port_reg[ 7: 0];
+ 3'h4: m_ip_payload_axis_tdata_int = udp_length_reg[15: 8];
+ 3'h5: m_ip_payload_axis_tdata_int = udp_length_reg[ 7: 0];
+ 3'h6: m_ip_payload_axis_tdata_int = udp_checksum_reg[15: 8];
+ 3'h7: 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
+ word_count_next = word_count_reg - 16'd1;
+ if (s_udp_payload_axis_tlast) begin
+ if (word_count_reg != 16'd1) 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 (word_count_reg == 16'd1) 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;
+ s_udp_hdr_ready_reg <= 1'b0;
+ s_udp_payload_axis_tready_reg <= 1'b0;
+ m_ip_hdr_valid_reg <= 1'b0;
+ m_eth_dest_mac_reg <= 48'd0;
+ m_eth_src_mac_reg <= 48'd0;
+ m_eth_type_reg <= 16'd0;
+ m_ip_version_reg <= 4'd0;
+ m_ip_ihl_reg <= 4'd0;
+ m_ip_dscp_reg <= 6'd0;
+ m_ip_ecn_reg <= 2'd0;
+ m_ip_length_reg <= 16'd0;
+ m_ip_identification_reg <= 16'd0;
+ m_ip_flags_reg <= 3'd0;
+ m_ip_fragment_offset_reg <= 13'd0;
+ m_ip_ttl_reg <= 8'd0;
+ m_ip_protocol_reg <= 8'd0;
+ m_ip_header_checksum_reg <= 16'd0;
+ m_ip_source_ip_reg <= 32'd0;
+ m_ip_dest_ip_reg <= 32'd0;
+ busy_reg <= 1'b0;
+ error_payload_early_termination_reg <= 1'b0;
+ hdr_ptr_reg <= 3'd0;
+ word_count_reg <= 16'd0;
+ last_word_data_reg <= 8'd0;
+ udp_source_port_reg <= 16'd0;
+ udp_dest_port_reg <= 16'd0;
+ udp_length_reg <= 16'd0;
+ udp_checksum_reg <= 16'd0;
+ 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;
+
+ 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;
+
+ hdr_ptr_reg <= hdr_ptr_next;
+ word_count_reg <= word_count_next;
+
+ // 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
+end
+
+// output datapath logic
+reg [7:0] m_ip_payload_axis_tdata_reg;
+reg m_ip_payload_axis_tvalid_reg, m_ip_payload_axis_tvalid_next;
+reg m_ip_payload_axis_tlast_reg;
+reg m_ip_payload_axis_tuser_reg;
+
+reg [7:0] temp_m_ip_payload_axis_tdata_reg;
+reg temp_m_ip_payload_axis_tvalid_reg, temp_m_ip_payload_axis_tvalid_next;
+reg temp_m_ip_payload_axis_tlast_reg;
+reg temp_m_ip_payload_axis_tuser_reg;
+
+// 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_tready_int_reg <= 1'b0;
+
+ m_ip_payload_axis_tdata_reg <= 8'd0;
+ m_ip_payload_axis_tvalid_reg <= 1'b0;
+ m_ip_payload_axis_tlast_reg <= 1'b0;
+ m_ip_payload_axis_tuser_reg <= 1'b0;
+
+ temp_m_ip_payload_axis_tdata_reg <= 8'd0;
+ temp_m_ip_payload_axis_tvalid_reg <= 1'b0;
+ temp_m_ip_payload_axis_tlast_reg <= 1'b0;
+ temp_m_ip_payload_axis_tuser_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;
+
+ // 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
+end
+
+endmodule
+