diff --git a/src/rvvi/axis_adapter.sv b/src/rvvi/axis_adapter.sv new file mode 100644 index 000000000..8807a03b7 --- /dev/null +++ b/src/rvvi/axis_adapter.sv @@ -0,0 +1,324 @@ +/* + +Copyright (c) 2014-2023 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 + +`resetall +`default_nettype none + +/* + * AXI4-Stream bus width adapter + */ +module axis_adapter # +( + // 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+7)/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+7)/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 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + 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 + */ + 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 +); + +// force keep width to 1 when disabled +localparam S_BYTE_LANES = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +localparam M_BYTE_LANES = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus byte sizes (must be identical) +localparam S_BYTE_SIZE = S_DATA_WIDTH / S_BYTE_LANES; +localparam M_BYTE_SIZE = M_DATA_WIDTH / M_BYTE_LANES; + +// bus width assertions +initial begin + if (S_BYTE_SIZE * S_BYTE_LANES != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisible (instance %m)"); + $finish; + end + + if (M_BYTE_SIZE * M_BYTE_LANES != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisible (instance %m)"); + $finish; + end + + if (S_BYTE_SIZE != M_BYTE_SIZE) begin + $error("Error: byte size mismatch (instance %m)"); + $finish; + end +end + +generate + +if (M_BYTE_LANES == S_BYTE_LANES) begin : bypass + // same width; bypass + + assign s_axis_tready = m_axis_tready; + + assign m_axis_tdata = s_axis_tdata; + assign m_axis_tkeep = M_KEEP_ENABLE ? s_axis_tkeep : {M_KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = s_axis_tvalid; + assign m_axis_tlast = s_axis_tlast; + assign m_axis_tid = ID_ENABLE ? s_axis_tid : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? s_axis_tdest : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? s_axis_tuser : {USER_WIDTH{1'b0}}; + +end else if (M_BYTE_LANES > S_BYTE_LANES) begin : upsize + // output is wider; upsize + + // required number of segments in wider bus + localparam SEG_COUNT = M_BYTE_LANES / S_BYTE_LANES; + // data width and keep width per segment + localparam SEG_DATA_WIDTH = M_DATA_WIDTH / SEG_COUNT; + localparam SEG_KEEP_WIDTH = M_BYTE_LANES / SEG_COUNT; + + reg [$clog2(SEG_COUNT)-1:0] seg_reg = 0; + + reg [S_DATA_WIDTH-1:0] s_axis_tdata_reg = {S_DATA_WIDTH{1'b0}}; + reg [S_KEEP_WIDTH-1:0] s_axis_tkeep_reg = {S_KEEP_WIDTH{1'b0}}; + reg s_axis_tvalid_reg = 1'b0; + reg s_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] s_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] s_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] s_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}}; + reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}}; + reg m_axis_tvalid_reg = 1'b0; + reg m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + assign s_axis_tready = !s_axis_tvalid_reg; + + assign m_axis_tdata = m_axis_tdata_reg; + assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = m_axis_tvalid_reg; + assign m_axis_tlast = m_axis_tlast_reg; + assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + + always @(posedge clk) begin + m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready; + + if (!m_axis_tvalid_reg || m_axis_tready) begin + // output register empty + + if (seg_reg == 0) begin + m_axis_tdata_reg[seg_reg*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] <= s_axis_tvalid_reg ? s_axis_tdata_reg : s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tvalid_reg ? s_axis_tkeep_reg : s_axis_tkeep; + end else begin + m_axis_tdata_reg[seg_reg*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] <= s_axis_tdata; + m_axis_tkeep_reg[seg_reg*SEG_KEEP_WIDTH +: SEG_KEEP_WIDTH] <= s_axis_tkeep; + end + m_axis_tlast_reg <= s_axis_tvalid_reg ? s_axis_tlast_reg : s_axis_tlast; + m_axis_tid_reg <= s_axis_tvalid_reg ? s_axis_tid_reg : s_axis_tid; + m_axis_tdest_reg <= s_axis_tvalid_reg ? s_axis_tdest_reg : s_axis_tdest; + m_axis_tuser_reg <= s_axis_tvalid_reg ? s_axis_tuser_reg : s_axis_tuser; + + if (s_axis_tvalid_reg) begin + // consume data from buffer + s_axis_tvalid_reg <= 1'b0; + + if (s_axis_tlast_reg || seg_reg == SEG_COUNT-1) begin + seg_reg <= 0; + m_axis_tvalid_reg <= 1'b1; + end else begin + seg_reg <= seg_reg + 1; + end + end else if (s_axis_tvalid) begin + // data direct from input + if (s_axis_tlast || seg_reg == SEG_COUNT-1) begin + seg_reg <= 0; + m_axis_tvalid_reg <= 1'b1; + end else begin + seg_reg <= seg_reg + 1; + end + end + end else if (s_axis_tvalid && s_axis_tready) begin + // store input data in skid buffer + s_axis_tdata_reg <= s_axis_tdata; + s_axis_tkeep_reg <= s_axis_tkeep; + s_axis_tvalid_reg <= 1'b1; + s_axis_tlast_reg <= s_axis_tlast; + s_axis_tid_reg <= s_axis_tid; + s_axis_tdest_reg <= s_axis_tdest; + s_axis_tuser_reg <= s_axis_tuser; + end + + if (rst) begin + seg_reg <= 0; + s_axis_tvalid_reg <= 1'b0; + m_axis_tvalid_reg <= 1'b0; + end + end + +end else begin : downsize + // output is narrower; downsize + + // required number of segments in wider bus + localparam SEG_COUNT = S_BYTE_LANES / M_BYTE_LANES; + // data width and keep width per segment + localparam SEG_DATA_WIDTH = S_DATA_WIDTH / SEG_COUNT; + localparam SEG_KEEP_WIDTH = S_BYTE_LANES / SEG_COUNT; + + reg [S_DATA_WIDTH-1:0] s_axis_tdata_reg = {S_DATA_WIDTH{1'b0}}; + reg [S_KEEP_WIDTH-1:0] s_axis_tkeep_reg = {S_KEEP_WIDTH{1'b0}}; + reg s_axis_tvalid_reg = 1'b0; + reg s_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] s_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] s_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] s_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}}; + reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}}; + reg m_axis_tvalid_reg = 1'b0; + reg m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + assign s_axis_tready = !s_axis_tvalid_reg; + + assign m_axis_tdata = m_axis_tdata_reg; + assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = m_axis_tvalid_reg; + assign m_axis_tlast = m_axis_tlast_reg; + assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + + always @(posedge clk) begin + m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready; + + if (!m_axis_tvalid_reg || m_axis_tready) begin + // output register empty + + m_axis_tdata_reg <= s_axis_tvalid_reg ? s_axis_tdata_reg : s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tvalid_reg ? s_axis_tkeep_reg : s_axis_tkeep; + m_axis_tlast_reg <= 1'b0; + m_axis_tid_reg <= s_axis_tvalid_reg ? s_axis_tid_reg : s_axis_tid; + m_axis_tdest_reg <= s_axis_tvalid_reg ? s_axis_tdest_reg : s_axis_tdest; + m_axis_tuser_reg <= s_axis_tvalid_reg ? s_axis_tuser_reg : s_axis_tuser; + + if (s_axis_tvalid_reg) begin + // buffer has data; shift out from buffer + s_axis_tdata_reg <= s_axis_tdata_reg >> SEG_DATA_WIDTH; + s_axis_tkeep_reg <= s_axis_tkeep_reg >> SEG_KEEP_WIDTH; + + m_axis_tvalid_reg <= 1'b1; + + if ((s_axis_tkeep_reg >> SEG_KEEP_WIDTH) == 0) begin + s_axis_tvalid_reg <= 1'b0; + m_axis_tlast_reg <= s_axis_tlast_reg; + end + end else if (s_axis_tvalid && s_axis_tready) begin + // buffer is empty; store from input + s_axis_tdata_reg <= s_axis_tdata >> SEG_DATA_WIDTH; + s_axis_tkeep_reg <= s_axis_tkeep >> SEG_KEEP_WIDTH; + s_axis_tlast_reg <= s_axis_tlast; + s_axis_tid_reg <= s_axis_tid; + s_axis_tdest_reg <= s_axis_tdest; + s_axis_tuser_reg <= s_axis_tuser; + + m_axis_tvalid_reg <= 1'b1; + + if ((s_axis_tkeep >> SEG_KEEP_WIDTH) == 0) begin + s_axis_tvalid_reg <= 1'b0; + m_axis_tlast_reg <= s_axis_tlast; + end else begin + s_axis_tvalid_reg <= 1'b1; + end + end + end else if (s_axis_tvalid && s_axis_tready) begin + // store input data + s_axis_tdata_reg <= s_axis_tdata; + s_axis_tkeep_reg <= s_axis_tkeep; + s_axis_tvalid_reg <= 1'b1; + s_axis_tlast_reg <= s_axis_tlast; + s_axis_tid_reg <= s_axis_tid; + s_axis_tdest_reg <= s_axis_tdest; + s_axis_tuser_reg <= s_axis_tuser; + end + + if (rst) begin + s_axis_tvalid_reg <= 1'b0; + m_axis_tvalid_reg <= 1'b0; + end + end + +end + +endgenerate + +endmodule + +`resetall diff --git a/src/rvvi/axis_async_fifo.sv b/src/rvvi/axis_async_fifo.sv new file mode 100644 index 000000000..1d8b6244f --- /dev/null +++ b/src/rvvi/axis_async_fifo.sv @@ -0,0 +1,909 @@ +/* + +Copyright (c) 2014-2023 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 + +`resetall +`default_nettype none + +/* + * 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+7)/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 RAM pipeline registers + parameter RAM_PIPELINE = 1, + // use output FIFO + // When set, the RAM read enable and pipeline clock enables are removed + parameter OUTPUT_FIFO_ENABLE = 0, + // 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 larger than FIFO + // Requires FRAME_FIFO set + parameter DROP_OVERSIZE_FRAME = FRAME_FIFO, + // Drop frames marked bad + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_WHEN_FULL = 0, + // Mark incoming frames as bad frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO to be clear + parameter MARK_WHEN_FULL = 0, + // Enable pause request input + parameter PAUSE_ENABLE = 0, + // Pause between frames + parameter FRAME_PAUSE = FRAME_FIFO +) +( + /* + * AXI input + */ + input wire s_clk, + input wire s_rst, + 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, + input wire m_rst, + 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, + + /* + * Pause + */ + input wire s_pause_req, + output wire s_pause_ack, + input wire m_pause_req, + output wire m_pause_ack, + + /* + * Status + */ + output wire [$clog2(DEPTH):0] s_status_depth, + output wire [$clog2(DEPTH):0] s_status_depth_commit, + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire [$clog2(DEPTH):0] m_status_depth, + output wire [$clog2(DEPTH):0] m_status_depth_commit, + 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); + +parameter OUTPUT_FIFO_ADDR_WIDTH = RAM_PIPELINE < 2 ? 3 : $clog2(RAM_PIPELINE*2+7); + +// check configuration +initial begin + if (FRAME_FIFO && !LAST_ENABLE) begin + $error("Error: FRAME_FIFO set requires LAST_ENABLE set (instance %m)"); + $finish; + end + + if (DROP_OVERSIZE_FRAME && !FRAME_FIFO) begin + $error("Error: DROP_OVERSIZE_FRAME set requires FRAME_FIFO set (instance %m)"); + $finish; + end + + if (DROP_BAD_FRAME && !(FRAME_FIFO && DROP_OVERSIZE_FRAME)) begin + $error("Error: DROP_BAD_FRAME set requires FRAME_FIFO and DROP_OVERSIZE_FRAME set (instance %m)"); + $finish; + end + + if (DROP_WHEN_FULL && !(FRAME_FIFO && DROP_OVERSIZE_FRAME)) begin + $error("Error: DROP_WHEN_FULL set requires FRAME_FIFO and DROP_OVERSIZE_FRAME set (instance %m)"); + $finish; + end + + if ((DROP_BAD_FRAME || MARK_WHEN_FULL) && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin + $error("Error: Invalid USER_BAD_FRAME_MASK value (instance %m)"); + $finish; + end + + if (MARK_WHEN_FULL && FRAME_FIFO) begin + $error("Error: MARK_WHEN_FULL is not compatible with FRAME_FIFO (instance %m)"); + $finish; + end + + if (MARK_WHEN_FULL && !LAST_ENABLE) begin + $error("Error: MARK_WHEN_FULL set requires LAST_ENABLE set (instance %m)"); + $finish; + end +end + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +function [ADDR_WIDTH:0] bin2gray(input [ADDR_WIDTH:0] b); + bin2gray = b ^ (b >> 1); +endfunction + +function [ADDR_WIDTH:0] gray2bin(input [ADDR_WIDTH:0] g); + integer i; + for (i = 0; i <= ADDR_WIDTH; i = i + 1) begin + gray2bin[i] = ^(g >> i); + end +endfunction + +reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_commit_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_sync_commit_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_conv_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_conv_reg = {ADDR_WIDTH+1{1'b0}}; + +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 = {ADDR_WIDTH+1{1'b0}}; +(* SHREG_EXTRACT = "NO" *) +reg [ADDR_WIDTH:0] wr_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}}; +(* SHREG_EXTRACT = "NO" *) +reg [ADDR_WIDTH:0] wr_ptr_commit_sync_reg = {ADDR_WIDTH+1{1'b0}}; +(* SHREG_EXTRACT = "NO" *) +reg [ADDR_WIDTH:0] rd_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}}; +(* SHREG_EXTRACT = "NO" *) +reg [ADDR_WIDTH:0] rd_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}}; + +reg wr_ptr_update_valid_reg = 1'b0; +reg wr_ptr_update_reg = 1'b0; +(* SHREG_EXTRACT = "NO" *) +reg wr_ptr_update_sync1_reg = 1'b0; +(* SHREG_EXTRACT = "NO" *) +reg wr_ptr_update_sync2_reg = 1'b0; +(* SHREG_EXTRACT = "NO" *) +reg wr_ptr_update_sync3_reg = 1'b0; +(* SHREG_EXTRACT = "NO" *) +reg wr_ptr_update_ack_sync1_reg = 1'b0; +(* SHREG_EXTRACT = "NO" *) +reg wr_ptr_update_ack_sync2_reg = 1'b0; + +(* SHREG_EXTRACT = "NO" *) +reg s_rst_sync1_reg = 1'b1; +(* SHREG_EXTRACT = "NO" *) +reg s_rst_sync2_reg = 1'b1; +(* SHREG_EXTRACT = "NO" *) +reg s_rst_sync3_reg = 1'b1; +(* SHREG_EXTRACT = "NO" *) +reg m_rst_sync1_reg = 1'b1; +(* SHREG_EXTRACT = "NO" *) +reg m_rst_sync2_reg = 1'b1; +(* SHREG_EXTRACT = "NO" *) +reg m_rst_sync3_reg = 1'b1; + +(* ramstyle = "no_rw_check" *) +reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0]; +reg mem_read_data_valid_reg = 1'b0; + +(* shreg_extract = "no" *) +reg [WIDTH-1:0] m_axis_pipe_reg[RAM_PIPELINE+1-1:0]; +reg [RAM_PIPELINE+1-1:0] m_axis_tvalid_pipe_reg = 0; + +// 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}}}); +// empty when pointers match exactly +wire empty = FRAME_FIFO ? (rd_ptr_reg == wr_ptr_commit_sync_reg) : (rd_ptr_gray_reg == wr_ptr_gray_sync2_reg); +// overflow within packet +wire full_wr = wr_ptr_reg == (wr_ptr_commit_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); + +// control signals +reg write; +reg read; +reg store_output; + +reg s_frame_reg = 1'b0; +reg m_frame_reg = 1'b0; + +reg drop_frame_reg = 1'b0; +reg mark_frame_reg = 1'b0; +reg send_frame_reg = 1'b0; +reg overflow_reg = 1'b0; +reg bad_frame_reg = 1'b0; +reg good_frame_reg = 1'b0; + +reg m_drop_frame_reg = 1'b0; +reg m_terminate_frame_reg = 1'b0; + +reg [ADDR_WIDTH:0] s_depth_reg = 0; +reg [ADDR_WIDTH:0] s_depth_commit_reg = 0; +reg [ADDR_WIDTH:0] m_depth_reg = 0; +reg [ADDR_WIDTH:0] m_depth_commit_reg = 0; + +reg overflow_sync1_reg = 1'b0; +reg overflow_sync2_reg = 1'b0; +reg overflow_sync3_reg = 1'b0; +reg overflow_sync4_reg = 1'b0; +reg bad_frame_sync1_reg = 1'b0; +reg bad_frame_sync2_reg = 1'b0; +reg bad_frame_sync3_reg = 1'b0; +reg bad_frame_sync4_reg = 1'b0; +reg good_frame_sync1_reg = 1'b0; +reg good_frame_sync2_reg = 1'b0; +reg good_frame_sync3_reg = 1'b0; +reg good_frame_sync4_reg = 1'b0; + +assign s_axis_tready = (FRAME_FIFO ? (!full || (full_wr && DROP_OVERSIZE_FRAME) || DROP_WHEN_FULL) : (!full || MARK_WHEN_FULL)) && !s_rst_sync3_reg; + +wire [WIDTH-1:0] s_axis; + +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 | mark_frame_reg; + 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] = mark_frame_reg ? USER_BAD_FRAME_VALUE : s_axis_tuser; +endgenerate + +wire [WIDTH-1:0] m_axis = m_axis_pipe_reg[RAM_PIPELINE+1-1]; + +wire m_axis_tready_pipe; +wire m_axis_tvalid_pipe = m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1]; + +wire [DATA_WIDTH-1:0] m_axis_tdata_pipe = m_axis[DATA_WIDTH-1:0]; +wire [KEEP_WIDTH-1:0] m_axis_tkeep_pipe = KEEP_ENABLE ? m_axis[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +wire m_axis_tlast_pipe = LAST_ENABLE ? m_axis[LAST_OFFSET] | m_terminate_frame_reg : 1'b1; +wire [ID_WIDTH-1:0] m_axis_tid_pipe = ID_ENABLE ? m_axis[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +wire [DEST_WIDTH-1:0] m_axis_tdest_pipe = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +wire [USER_WIDTH-1:0] m_axis_tuser_pipe = USER_ENABLE ? (m_terminate_frame_reg ? USER_BAD_FRAME_VALUE : m_axis[USER_OFFSET +: USER_WIDTH]) : {USER_WIDTH{1'b0}}; + +wire m_axis_tready_out; +wire m_axis_tvalid_out; + +wire [DATA_WIDTH-1:0] m_axis_tdata_out; +wire [KEEP_WIDTH-1:0] m_axis_tkeep_out; +wire m_axis_tlast_out; +wire [ID_WIDTH-1:0] m_axis_tid_out; +wire [DEST_WIDTH-1:0] m_axis_tdest_out; +wire [USER_WIDTH-1:0] m_axis_tuser_out; + +wire pipe_ready; + +assign s_status_depth = (KEEP_ENABLE && KEEP_WIDTH > 1) ? {s_depth_reg, {$clog2(KEEP_WIDTH){1'b0}}} : s_depth_reg; +assign s_status_depth_commit = (KEEP_ENABLE && KEEP_WIDTH > 1) ? {s_depth_commit_reg, {$clog2(KEEP_WIDTH){1'b0}}} : s_depth_commit_reg; +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_depth = (KEEP_ENABLE && KEEP_WIDTH > 1) ? {m_depth_reg, {$clog2(KEEP_WIDTH){1'b0}}} : m_depth_reg; +assign m_status_depth_commit = (KEEP_ENABLE && KEEP_WIDTH > 1) ? {m_depth_commit_reg, {$clog2(KEEP_WIDTH){1'b0}}} : m_depth_commit_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 m_clk or posedge m_rst) begin + if (m_rst) begin + s_rst_sync1_reg <= 1'b1; + end else begin + s_rst_sync1_reg <= 1'b0; + end +end + +always @(posedge s_clk) begin + s_rst_sync2_reg <= s_rst_sync1_reg; + s_rst_sync3_reg <= s_rst_sync2_reg; +end + +always @(posedge s_clk or posedge s_rst) begin + if (s_rst) begin + m_rst_sync1_reg <= 1'b1; + end else begin + m_rst_sync1_reg <= 1'b0; + end +end + +always @(posedge m_clk) begin + m_rst_sync2_reg <= m_rst_sync1_reg; + m_rst_sync3_reg <= m_rst_sync2_reg; +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_commit_reg <= wr_ptr_commit_reg; + wr_ptr_update_reg <= !wr_ptr_update_ack_sync2_reg; + end + end + + if (s_axis_tready && s_axis_tvalid && LAST_ENABLE) begin + // track input frame status + s_frame_reg <= !s_axis_tlast; + end + + if (s_rst_sync3_reg && LAST_ENABLE) begin + // if sink side is reset during transfer, drop partial frame + if (s_frame_reg && !(s_axis_tready && s_axis_tvalid && s_axis_tlast)) begin + drop_frame_reg <= 1'b1; + end + if (s_axis_tready && s_axis_tvalid && !s_axis_tlast) begin + drop_frame_reg <= 1'b1; + end + end + + if (FRAME_FIFO) begin + // frame FIFO mode + if (s_axis_tready && s_axis_tvalid) begin + // transfer in + if ((full && DROP_WHEN_FULL) || (full_wr && DROP_OVERSIZE_FRAME) || 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_commit_reg; + wr_ptr_reg <= wr_ptr_temp; + wr_ptr_gray_reg <= bin2gray(wr_ptr_temp); + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b1; + end + end else begin + 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 <= bin2gray(wr_ptr_temp); + if (s_axis_tlast || (!DROP_OVERSIZE_FRAME && (full_wr || send_frame_reg))) begin + // end of frame or send frame + send_frame_reg <= !s_axis_tlast; + if (s_axis_tlast && 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_commit_reg; + wr_ptr_reg <= wr_ptr_temp; + wr_ptr_gray_reg <= bin2gray(wr_ptr_temp); + bad_frame_reg <= 1'b1; + end else begin + // good packet or packet overflow, update write pointer + wr_ptr_temp = wr_ptr_reg + 1; + wr_ptr_reg <= wr_ptr_temp; + wr_ptr_commit_reg <= wr_ptr_temp; + wr_ptr_gray_reg <= bin2gray(wr_ptr_temp); + + 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_commit_reg <= wr_ptr_temp; + 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 <= s_axis_tlast; + end + end + end + end else if (s_axis_tvalid && full_wr && FRAME_FIFO && !DROP_OVERSIZE_FRAME) begin + // data valid with packet overflow + // update write pointer + send_frame_reg <= 1'b1; + wr_ptr_temp = wr_ptr_reg; + wr_ptr_reg <= wr_ptr_temp; + wr_ptr_commit_reg <= wr_ptr_temp; + wr_ptr_gray_reg <= bin2gray(wr_ptr_temp); + + 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_commit_reg <= wr_ptr_temp; + 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 + end + end else begin + // normal FIFO mode + if (s_axis_tready && s_axis_tvalid) begin + if (drop_frame_reg && LAST_ENABLE) begin + // currently dropping frame + if (s_axis_tlast) begin + // end of frame + if (!full && mark_frame_reg && MARK_WHEN_FULL) begin + // terminate marked frame + mark_frame_reg <= 1'b0; + 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_commit_reg <= wr_ptr_temp; + wr_ptr_gray_reg <= bin2gray(wr_ptr_temp); + end + // end of frame, clear drop flag + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b1; + end + end else if ((full || mark_frame_reg) && MARK_WHEN_FULL) begin + // full or marking frame + // drop frame; mark if this isn't the first cycle + drop_frame_reg <= 1'b1; + mark_frame_reg <= mark_frame_reg || s_frame_reg; + if (s_axis_tlast) begin + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b1; + end + end else begin + // transfer in + 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_commit_reg <= wr_ptr_temp; + wr_ptr_gray_reg <= bin2gray(wr_ptr_temp); + end + end else if ((!full && !drop_frame_reg && mark_frame_reg) && MARK_WHEN_FULL) begin + // terminate marked frame + mark_frame_reg <= 1'b0; + 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_commit_reg <= wr_ptr_temp; + wr_ptr_gray_reg <= bin2gray(wr_ptr_temp); + end + end + + if (s_rst_sync3_reg) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_commit_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_sync_commit_reg <= {ADDR_WIDTH+1{1'b0}}; + + wr_ptr_update_valid_reg <= 1'b0; + wr_ptr_update_reg <= 1'b0; + end + + if (s_rst) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_commit_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_sync_commit_reg <= {ADDR_WIDTH+1{1'b0}}; + + wr_ptr_update_valid_reg <= 1'b0; + wr_ptr_update_reg <= 1'b0; + + s_frame_reg <= 1'b0; + + drop_frame_reg <= 1'b0; + mark_frame_reg <= 1'b0; + send_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + end +end + +// Write-side status +always @(posedge s_clk) begin + rd_ptr_conv_reg <= gray2bin(rd_ptr_gray_sync2_reg); + s_depth_reg <= wr_ptr_reg - rd_ptr_conv_reg; + s_depth_commit_reg <= wr_ptr_commit_reg - rd_ptr_conv_reg; +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) 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 + wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg; + wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg; + if (FRAME_FIFO && wr_ptr_update_sync2_reg ^ wr_ptr_update_sync3_reg) begin + wr_ptr_commit_sync_reg <= wr_ptr_sync_commit_reg; + end + 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 (FRAME_FIFO && m_rst_sync3_reg) begin + wr_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}}; + end + + if (m_rst) begin + wr_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_commit_sync_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) 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) 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_pipe) begin + // output ready; invalidate stage + m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1] <= 1'b0; + m_terminate_frame_reg <= 1'b0; + end + + for (j = RAM_PIPELINE+1-1; j > 0; j = j - 1) begin + if (m_axis_tready_pipe || ((~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_pipe || ~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 && !m_rst_sync3_reg && !m_drop_frame_reg && pipe_ready) 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_axis_tvalid_pipe && LAST_ENABLE) begin + // track output frame status + if (m_axis_tlast_pipe && m_axis_tready_pipe) begin + m_frame_reg <= 1'b0; + end else begin + m_frame_reg <= 1'b1; + end + end + + if (m_drop_frame_reg && (OUTPUT_FIFO_ENABLE ? pipe_ready : m_axis_tready_pipe || !m_axis_tvalid_pipe) && LAST_ENABLE) begin + // terminate frame + // (only for frame transfers interrupted by source reset) + m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-1] <= 1'b1; + m_terminate_frame_reg <= 1'b1; + m_drop_frame_reg <= 1'b0; + end + + if (m_rst_sync3_reg && LAST_ENABLE) begin + // if source side is reset during transfer, drop partial frame + + // empty output pipeline, except for last stage + if (RAM_PIPELINE > 0) begin + m_axis_tvalid_pipe_reg[RAM_PIPELINE+1-2:0] <= 0; + end + + if (m_frame_reg && (!m_axis_tvalid_pipe || (m_axis_tvalid_pipe && !m_axis_tlast_pipe)) && + !(m_drop_frame_reg || m_terminate_frame_reg)) begin + // terminate frame + m_drop_frame_reg <= 1'b1; + 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}}; + end + + if (m_rst) begin + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + rd_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + m_axis_tvalid_pipe_reg <= 0; + m_frame_reg <= 1'b0; + m_drop_frame_reg <= 1'b0; + m_terminate_frame_reg <= 1'b0; + end +end + +// Read-side status +always @(posedge m_clk) begin + wr_ptr_conv_reg <= gray2bin(wr_ptr_gray_sync2_reg); + m_depth_reg <= wr_ptr_conv_reg - rd_ptr_reg; + m_depth_commit_reg <= FRAME_FIFO ? wr_ptr_commit_sync_reg - rd_ptr_reg : wr_ptr_conv_reg - rd_ptr_reg; +end + +generate + +if (!OUTPUT_FIFO_ENABLE) begin + + assign pipe_ready = 1'b1; + + assign m_axis_tready_pipe = m_axis_tready_out; + assign m_axis_tvalid_out = m_axis_tvalid_pipe; + + assign m_axis_tdata_out = m_axis_tdata_pipe; + assign m_axis_tkeep_out = m_axis_tkeep_pipe; + assign m_axis_tlast_out = m_axis_tlast_pipe; + assign m_axis_tid_out = m_axis_tid_pipe; + assign m_axis_tdest_out = m_axis_tdest_pipe; + assign m_axis_tuser_out = m_axis_tuser_pipe; + +end else begin : output_fifo + + // output datapath logic + reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; + reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; + reg m_axis_tvalid_reg = 1'b0; + reg m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_wr_ptr_reg = 0; + reg [OUTPUT_FIFO_ADDR_WIDTH+1-1:0] out_fifo_rd_ptr_reg = 0; + reg out_fifo_half_full_reg = 1'b0; + + wire out_fifo_full = out_fifo_wr_ptr_reg == (out_fifo_rd_ptr_reg ^ {1'b1, {OUTPUT_FIFO_ADDR_WIDTH{1'b0}}}); + wire out_fifo_empty = out_fifo_wr_ptr_reg == out_fifo_rd_ptr_reg; + + (* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *) + reg [DATA_WIDTH-1:0] out_fifo_tdata[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + (* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *) + reg [KEEP_WIDTH-1:0] out_fifo_tkeep[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + (* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *) + reg out_fifo_tlast[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + (* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *) + reg [ID_WIDTH-1:0] out_fifo_tid[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + (* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *) + reg [DEST_WIDTH-1:0] out_fifo_tdest[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + (* ram_style = "distributed", ramstyle = "no_rw_check, mlab" *) + reg [USER_WIDTH-1:0] out_fifo_tuser[2**OUTPUT_FIFO_ADDR_WIDTH-1:0]; + + assign pipe_ready = !out_fifo_half_full_reg; + + assign m_axis_tready_pipe = 1'b1; + + assign m_axis_tdata_out = m_axis_tdata_reg; + assign m_axis_tkeep_out = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid_out = m_axis_tvalid_reg; + assign m_axis_tlast_out = LAST_ENABLE ? m_axis_tlast_reg : 1'b1; + assign m_axis_tid_out = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; + assign m_axis_tdest_out = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser_out = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + + always @(posedge m_clk) begin + m_axis_tvalid_reg <= m_axis_tvalid_reg && !m_axis_tready_out; + + out_fifo_half_full_reg <= $unsigned(out_fifo_wr_ptr_reg - out_fifo_rd_ptr_reg) >= 2**(OUTPUT_FIFO_ADDR_WIDTH-1); + + if (!out_fifo_full && m_axis_tvalid_pipe) begin + out_fifo_tdata[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_tdata_pipe; + out_fifo_tkeep[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_tkeep_pipe; + out_fifo_tlast[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_tlast_pipe; + out_fifo_tid[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_tid_pipe; + out_fifo_tdest[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_tdest_pipe; + out_fifo_tuser[out_fifo_wr_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]] <= m_axis_tuser_pipe; + out_fifo_wr_ptr_reg <= out_fifo_wr_ptr_reg + 1; + end + + if (!out_fifo_empty && (!m_axis_tvalid_reg || m_axis_tready_out)) begin + m_axis_tdata_reg <= out_fifo_tdata[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_tkeep_reg <= out_fifo_tkeep[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_tvalid_reg <= 1'b1; + m_axis_tlast_reg <= out_fifo_tlast[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_tid_reg <= out_fifo_tid[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_tdest_reg <= out_fifo_tdest[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + m_axis_tuser_reg <= out_fifo_tuser[out_fifo_rd_ptr_reg[OUTPUT_FIFO_ADDR_WIDTH-1:0]]; + out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1; + end + + if (m_rst) begin + out_fifo_wr_ptr_reg <= 0; + out_fifo_rd_ptr_reg <= 0; + m_axis_tvalid_reg <= 1'b0; + end + end + +end + +if (PAUSE_ENABLE) begin : pause + + // Pause logic + reg pause_reg = 1'b0; + reg pause_frame_reg = 1'b0; + + reg s_pause_req_sync1_reg; + reg s_pause_req_sync2_reg; + reg s_pause_req_sync3_reg; + reg s_pause_ack_sync1_reg; + reg s_pause_ack_sync2_reg; + reg s_pause_ack_sync3_reg; + + always @(posedge s_clk) begin + s_pause_req_sync1_reg <= s_pause_req; + s_pause_ack_sync2_reg <= s_pause_ack_sync1_reg; + s_pause_ack_sync3_reg <= s_pause_ack_sync2_reg; + end + + always @(posedge m_clk) begin + s_pause_req_sync2_reg <= s_pause_req_sync1_reg; + s_pause_req_sync3_reg <= s_pause_req_sync2_reg; + s_pause_ack_sync1_reg <= pause_reg; + end + + assign m_axis_tready_out = m_axis_tready && !pause_reg; + assign m_axis_tvalid = m_axis_tvalid_out && !pause_reg; + + assign m_axis_tdata = m_axis_tdata_out; + assign m_axis_tkeep = m_axis_tkeep_out; + assign m_axis_tlast = m_axis_tlast_out; + assign m_axis_tid = m_axis_tid_out; + assign m_axis_tdest = m_axis_tdest_out; + assign m_axis_tuser = m_axis_tuser_out; + + assign s_pause_ack = s_pause_ack_sync3_reg; + assign m_pause_ack = pause_reg; + + always @(posedge m_clk) begin + if (FRAME_PAUSE) begin + if (pause_reg) begin + // paused; update pause status + pause_reg <= m_pause_req || s_pause_req_sync3_reg; + end else if (m_axis_tvalid_out) begin + // frame transfer; set frame bit + pause_frame_reg <= 1'b1; + if (m_axis_tready && m_axis_tlast) begin + // end of frame; clear frame bit and update pause status + pause_frame_reg <= 1'b0; + pause_reg <= m_pause_req || s_pause_req_sync3_reg; + end + end else if (!pause_frame_reg) begin + // idle; update pause status + pause_reg <= m_pause_req || s_pause_req_sync3_reg; + end + end else begin + pause_reg <= m_pause_req || s_pause_req_sync3_reg; + end + + if (m_rst) begin + pause_frame_reg <= 1'b0; + pause_reg <= 1'b0; + end + end + +end else begin + + assign m_axis_tready_out = m_axis_tready; + assign m_axis_tvalid = m_axis_tvalid_out; + + assign m_axis_tdata = m_axis_tdata_out; + assign m_axis_tkeep = m_axis_tkeep_out; + assign m_axis_tlast = m_axis_tlast_out; + assign m_axis_tid = m_axis_tid_out; + assign m_axis_tdest = m_axis_tdest_out; + assign m_axis_tuser = m_axis_tuser_out; + + assign s_pause_ack = 1'b0; + assign m_pause_ack = 1'b0; + +end + +endgenerate + +endmodule + +`resetall diff --git a/src/rvvi/axis_async_fifo_adapter.sv b/src/rvvi/axis_async_fifo_adapter.sv new file mode 100644 index 000000000..d1e71b9fc --- /dev/null +++ b/src/rvvi/axis_async_fifo_adapter.sv @@ -0,0 +1,377 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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+7)/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+7)/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 RAM pipeline registers in FIFO + parameter RAM_PIPELINE = 1, + // use output FIFO + // When set, the RAM read enable and pipeline clock enables are removed + parameter OUTPUT_FIFO_ENABLE = 0, + // 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 larger than FIFO + // Requires FRAME_FIFO set + parameter DROP_OVERSIZE_FRAME = FRAME_FIFO, + // Drop frames marked bad + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO and DROP_OVERSIZE_FRAME set + parameter DROP_WHEN_FULL = 0, + // Mark incoming frames as bad frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO to be clear + parameter MARK_WHEN_FULL = 0, + // Enable pause request input + parameter PAUSE_ENABLE = 0, + // Pause between frames + parameter FRAME_PAUSE = FRAME_FIFO +) +( + /* + * 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, + + /* + * Pause + */ + input wire s_pause_req, + output wire s_pause_ack, + input wire m_pause_req, + output wire m_pause_ack, + + /* + * Status + */ + output wire [$clog2(DEPTH):0] s_status_depth, + output wire [$clog2(DEPTH):0] s_status_depth_commit, + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire [$clog2(DEPTH):0] m_status_depth, + output wire [$clog2(DEPTH):0] m_status_depth_commit, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +// force keep width to 1 when disabled +localparam S_BYTE_LANES = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +localparam M_BYTE_LANES = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus byte sizes (must be identical) +localparam S_BYTE_SIZE = S_DATA_WIDTH / S_BYTE_LANES; +localparam M_BYTE_SIZE = M_DATA_WIDTH / M_BYTE_LANES; +// output bus is wider +localparam EXPAND_BUS = M_BYTE_LANES > S_BYTE_LANES; +// total data and keep widths +localparam DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; +localparam KEEP_WIDTH = EXPAND_BUS ? M_BYTE_LANES : S_BYTE_LANES; + +// bus width assertions +initial begin + if (S_BYTE_SIZE * S_BYTE_LANES != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisible (instance %m)"); + $finish; + end + + if (M_BYTE_SIZE * M_BYTE_LANES != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisible (instance %m)"); + $finish; + end + + if (S_BYTE_SIZE != M_BYTE_SIZE) begin + $error("Error: byte 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_BYTE_LANES > S_BYTE_LANES) begin : upsize_pre + + // 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) + ); + +end else begin : bypass_pre + + 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; + +end + +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), + .RAM_PIPELINE(RAM_PIPELINE), + .OUTPUT_FIFO_ENABLE(OUTPUT_FIFO_ENABLE), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_OVERSIZE_FRAME(DROP_OVERSIZE_FRAME), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL), + .MARK_WHEN_FULL(MARK_WHEN_FULL), + .PAUSE_ENABLE(PAUSE_ENABLE), + .FRAME_PAUSE(FRAME_PAUSE) +) +fifo_inst ( + // AXI input + .s_clk(s_clk), + .s_rst(s_rst), + .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_rst(m_rst), + .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), + // Pause + .s_pause_req(s_pause_req), + .s_pause_ack(s_pause_ack), + .m_pause_req(m_pause_req), + .m_pause_ack(m_pause_ack), + // Status + .s_status_depth(s_status_depth), + .s_status_depth_commit(s_status_depth_commit), + .s_status_overflow(s_status_overflow), + .s_status_bad_frame(s_status_bad_frame), + .s_status_good_frame(s_status_good_frame), + .m_status_depth(m_status_depth), + .m_status_depth_commit(m_status_depth_commit), + .m_status_overflow(m_status_overflow), + .m_status_bad_frame(m_status_bad_frame), + .m_status_good_frame(m_status_good_frame) +); + +if (M_BYTE_LANES < S_BYTE_LANES) begin : downsize_post + + // input wider, adapt width after 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(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 else begin : bypass_post + + 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 + +endgenerate + +endmodule + +`resetall diff --git a/src/rvvi/axis_gmii_rx.sv b/src/rvvi/axis_gmii_rx.sv new file mode 100644 index 000000000..037fce77a --- /dev/null +++ b/src/rvvi/axis_gmii_rx.sv @@ -0,0 +1,357 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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, + + /* + * Configuration + */ + input wire cfg_rx_enable, + + /* + * 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_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg mii_odd = 1'b0; +reg in_frame = 1'b0; + +reg [DATA_WIDTH-1:0] gmii_rxd_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d2 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d3 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d4 = {DATA_WIDTH{1'b0}}; + +reg gmii_rx_dv_d0 = 1'b0; +reg gmii_rx_dv_d1 = 1'b0; +reg gmii_rx_dv_d2 = 1'b0; +reg gmii_rx_dv_d3 = 1'b0; +reg gmii_rx_dv_d4 = 1'b0; + +reg gmii_rx_er_d0 = 1'b0; +reg gmii_rx_er_d1 = 1'b0; +reg gmii_rx_er_d2 = 1'b0; +reg gmii_rx_er_d3 = 1'b0; +reg gmii_rx_er_d4 = 1'b0; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg start_packet_int_reg = 1'b0; +reg start_packet_reg = 1'b0; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = 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; + + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + if (!clk_enable) begin + // clock disabled - hold state + state_next = state_reg; + end else if (mii_select && !mii_odd) begin + // MII even cycle - hold state + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (gmii_rx_dv_d4 && !gmii_rx_er_d4 && gmii_rxd_d4 == ETH_SFD && cfg_rx_enable) begin + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + m_axis_tdata_next = gmii_rxd_d4; + m_axis_tvalid_next = 1'b1; + + if (gmii_rx_dv_d4 && gmii_rx_er_d4) begin + // error + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else if (!gmii_rx_dv) begin + // end of packet + m_axis_tlast_next = 1'b1; + if (gmii_rx_er_d0 || gmii_rx_er_d1 || gmii_rx_er_d2 || gmii_rx_er_d3) begin + // error received in FCS bytes + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + end else if ({gmii_rxd_d0, gmii_rxd_d1, gmii_rxd_d2, gmii_rxd_d3} == ~crc_next) begin + // FCS good + m_axis_tuser_next = 1'b0; + end else begin + // FCS bad + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_WAIT_LAST: begin + // wait for end of packet + + if (~gmii_rx_dv) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase + end +end + +always @(posedge clk) begin + state_reg <= state_next; + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + start_packet_int_reg <= 1'b0; + start_packet_reg <= 1'b0; + + if (start_packet_int_reg) begin + ptp_ts_reg <= ptp_ts; + start_packet_reg <= 1'b1; + end + + if (clk_enable) begin + if (mii_select) begin + mii_odd <= !mii_odd; + + if (in_frame) begin + in_frame <= gmii_rx_dv; + end else if (gmii_rx_dv && {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == ETH_SFD) begin + in_frame <= 1'b1; + start_packet_int_reg <= 1'b1; + mii_odd <= 1'b1; + end + + 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_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; + + 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_dv_d0 <= gmii_rx_dv; + gmii_rx_er_d0 <= gmii_rx_er; + end + end else begin + if (in_frame) begin + in_frame <= gmii_rx_dv; + end else if (gmii_rx_dv && gmii_rxd == ETH_SFD) begin + in_frame <= 1'b1; + start_packet_int_reg <= 1'b1; + end + + 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_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; + + 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 + + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_int_reg <= 1'b0; + start_packet_reg <= 1'b0; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + in_frame <= 1'b0; + mii_odd <= 1'b0; + + gmii_rx_dv_d0 <= 1'b0; + gmii_rx_dv_d1 <= 1'b0; + gmii_rx_dv_d2 <= 1'b0; + gmii_rx_dv_d3 <= 1'b0; + gmii_rx_dv_d4 <= 1'b0; + end +end + +endmodule + +`resetall diff --git a/src/rvvi/axis_gmii_tx.sv b/src/rvvi/axis_gmii_tx.sv new file mode 100644 index 000000000..f2936039f --- /dev/null +++ b/src/rvvi/axis_gmii_tx.sv @@ -0,0 +1,457 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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_TS_CTRL_IN_TUSER = 0, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TS_ENABLE ? (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + (PTP_TS_CTRL_IN_TUSER ? 1 : 0) : 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] cfg_ifg, + input wire cfg_tx_enable, + + /* + * Status + */ + output wire start_packet, + output wire error_underflow +); + +parameter MIN_LEN_WIDTH = $clog2(MIN_FRAME_LENGTH-4-1+1); + +// 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_IFG = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [7:0] s_tdata_reg = 8'd0, s_tdata_next; + +reg mii_odd_reg = 1'b0, mii_odd_next; +reg [3:0] mii_msn_reg = 4'b0, mii_msn_next; + +reg frame_reg = 1'b0, frame_next; +reg frame_error_reg = 1'b0, frame_error_next; +reg [7:0] frame_ptr_reg = 0, frame_ptr_next; +reg [MIN_LEN_WIDTH-1:0] frame_min_count_reg = 0, frame_min_count_next; + +reg [7:0] gmii_txd_reg = 8'd0, gmii_txd_next; +reg gmii_tx_en_reg = 1'b0, gmii_tx_en_next; +reg gmii_tx_er_reg = 1'b0, gmii_tx_er_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; + +reg start_packet_int_reg = 1'b0, start_packet_int_next; +reg start_packet_reg = 1'b0, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign gmii_txd = gmii_txd_reg; +assign gmii_tx_en = gmii_tx_en_reg; +assign gmii_tx_er = gmii_tx_er_reg; + +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_next = frame_reg; + frame_error_next = frame_error_reg; + frame_ptr_next = frame_ptr_reg; + frame_min_count_next = frame_min_count_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; + + if (start_packet_reg && PTP_TS_ENABLE) begin + m_axis_ptp_ts_next = ptp_ts; + if (PTP_TS_CTRL_IN_TUSER) begin + m_axis_ptp_ts_tag_next = s_axis_tuser >> 2; + m_axis_ptp_ts_valid_next = s_axis_tuser[1]; + end else begin + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_next = 1'b1; + end + end + + gmii_txd_next = {DATA_WIDTH{1'b0}}; + gmii_tx_en_next = 1'b0; + gmii_tx_er_next = 1'b0; + + start_packet_int_next = start_packet_int_reg; + start_packet_next = 1'b0; + error_underflow_next = 1'b0; + + if (s_axis_tvalid && s_axis_tready) begin + frame_next = !s_axis_tlast; + end + + 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; + if (start_packet_int_reg) begin + start_packet_int_next = 1'b0; + start_packet_next = 1'b1; + end + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + mii_odd_next = 1'b0; + frame_ptr_next = 1; + + frame_error_next = 1'b0; + frame_min_count_next = MIN_FRAME_LENGTH-4-1; + + if (s_axis_tvalid && cfg_tx_enable) begin + mii_odd_next = 1'b1; + 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 + 1; + + gmii_txd_next = ETH_PRE; + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg == 6) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + state_next = STATE_PREAMBLE; + end else if (frame_ptr_reg == 7) begin + // end of preamble; start payload + frame_ptr_next = 0; + if (s_axis_tready_reg) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + end + gmii_txd_next = ETH_SFD; + if (mii_select) begin + start_packet_int_next = 1'b1; + end else begin + start_packet_next = 1'b1; + end + 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; + + if (frame_min_count_reg) begin + frame_min_count_next = frame_min_count_reg - 1; + end + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + s_tdata_next = s_axis_tdata; + + if (!s_axis_tvalid || s_axis_tlast) begin + s_axis_tready_next = frame_next; // drop frame + frame_error_next = !s_axis_tvalid || s_axis_tuser[0]; + error_underflow_next = !s_axis_tvalid; + + state_next = STATE_LAST; + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last payload word + + update_crc = 1'b1; + s_axis_tready_next = 1'b0; + + mii_odd_next = 1'b1; + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + if (ENABLE_PADDING && frame_min_count_reg) begin + frame_min_count_next = frame_min_count_reg - 1; + s_tdata_next = 8'd0; + state_next = STATE_PAD; + end else begin + frame_ptr_next = 0; + state_next = STATE_FCS; + end + end + STATE_PAD: begin + // send padding + s_axis_tready_next = frame_next; // drop frame + + update_crc = 1'b1; + mii_odd_next = 1'b1; + + gmii_txd_next = 8'd0; + gmii_tx_en_next = 1'b1; + + s_tdata_next = 8'd0; + + if (frame_min_count_reg) begin + frame_min_count_next = frame_min_count_reg - 1; + state_next = STATE_PAD; + end else begin + frame_ptr_next = 0; + state_next = STATE_FCS; + end + end + STATE_FCS: begin + // send FCS + s_axis_tready_next = frame_next; // drop frame + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 1; + + 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; + gmii_tx_er_next = frame_error_reg; + + if (frame_ptr_reg < 3) begin + state_next = STATE_FCS; + end else begin + frame_ptr_next = 0; + state_next = STATE_IFG; + end + end + STATE_IFG: begin + // send IFG + s_axis_tready_next = frame_next; // drop frame + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 1; + + if (frame_ptr_reg < cfg_ifg-1 || frame_reg) 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 + state_reg <= state_next; + + frame_reg <= frame_next; + frame_error_reg <= frame_error_next; + frame_ptr_reg <= frame_ptr_next; + frame_min_count_reg <= frame_min_count_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + + mii_odd_reg <= mii_odd_next; + mii_msn_reg <= mii_msn_next; + + s_tdata_reg <= s_tdata_next; + + s_axis_tready_reg <= s_axis_tready_next; + + gmii_txd_reg <= gmii_txd_next; + gmii_tx_en_reg <= gmii_tx_en_next; + gmii_tx_er_reg <= gmii_tx_er_next; + + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + start_packet_int_reg <= start_packet_int_next; + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + if (rst) begin + state_reg <= STATE_IDLE; + + frame_reg <= 1'b0; + + 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_int_reg <= 1'b0; + start_packet_reg <= 1'b0; + error_underflow_reg <= 1'b0; + end +end + +endmodule + +`resetall diff --git a/src/rvvi/eth_axis_tx.sv b/src/rvvi/eth_axis_tx.sv new file mode 100644 index 000000000..b4287b281 --- /dev/null +++ b/src/rvvi/eth_axis_tx.sv @@ -0,0 +1,408 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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 BYTE_LANES = KEEP_ENABLE ? KEEP_WIDTH : 1; + +parameter HDR_SIZE = 14; + +parameter CYCLE_COUNT = (HDR_SIZE+BYTE_LANES-1)/BYTE_LANES; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +parameter OFFSET = HDR_SIZE % BYTE_LANES; + +// bus width assertions +initial begin + if (BYTE_LANES * 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 = 1'b0, send_eth_header_next; +reg send_eth_payload_reg = 1'b0, send_eth_payload_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg flush_save; +reg transfer_in_save; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +reg [DATA_WIDTH-1:0] save_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] save_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg save_eth_payload_axis_tlast_reg = 1'b0; +reg save_eth_payload_axis_tuser_reg = 1'b0; + +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 = 1'b0; + +// 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 = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign busy = busy_reg; + +always @* begin + 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; + + m_axis_tdata_int = shift_eth_payload_axis_tdata; + m_axis_tkeep_int = shift_eth_payload_axis_tkeep; + m_axis_tlast_int = shift_eth_payload_axis_tlast; + m_axis_tuser_int = shift_eth_payload_axis_tuser; + + 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_tvalid_int = 1'b1; + + 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/BYTE_LANES) begin \ + m_axis_tdata_int[(offset%BYTE_LANES)*8 +: 8] = field; \ + m_axis_tkeep_int[offset%BYTE_LANES] = 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/BYTE_LANES) 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; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_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 if both output registers are empty +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && !m_axis_tvalid_reg); + +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 + 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 + + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end +end + +endmodule + +`resetall diff --git a/src/rvvi/eth_mac_1g.sv b/src/rvvi/eth_mac_1g.sv new file mode 100644 index 000000000..6e0592fac --- /dev/null +++ b/src/rvvi/eth_mac_1g.sv @@ -0,0 +1,643 @@ +/* + +Copyright (c) 2015-2023 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 + +`resetall +`default_nettype none + +/* + * 1G Ethernet MAC + */ +module eth_mac_1g # +( + parameter DATA_WIDTH = 8, + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_FMT_TOD = 1, + parameter PTP_TS_WIDTH = PTP_TS_FMT_TOD ? 96 : 64, + parameter TX_PTP_TS_CTRL_IN_TUSER = 0, + parameter TX_PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter TX_USER_WIDTH = (PTP_TS_ENABLE ? (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + (TX_PTP_TS_CTRL_IN_TUSER ? 1 : 0) : 0) + 1, + parameter RX_USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1, + parameter PFC_ENABLE = 0, + parameter PAUSE_ENABLE = PFC_ENABLE +) +( + 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 [PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [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, + + /* + * Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE) + */ + input wire tx_lfc_req, + input wire tx_lfc_resend, + input wire rx_lfc_en, + output wire rx_lfc_req, + input wire rx_lfc_ack, + + /* + * Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC) + */ + input wire [7:0] tx_pfc_req, + input wire tx_pfc_resend, + input wire [7:0] rx_pfc_en, + output wire [7:0] rx_pfc_req, + input wire [7:0] rx_pfc_ack, + + /* + * Pause interface + */ + input wire tx_lfc_pause_en, + input wire tx_pause_req, + output wire tx_pause_ack, + + /* + * 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, + output wire stat_tx_mcf, + output wire stat_rx_mcf, + output wire stat_tx_lfc_pkt, + output wire stat_tx_lfc_xon, + output wire stat_tx_lfc_xoff, + output wire stat_tx_lfc_paused, + output wire stat_tx_pfc_pkt, + output wire [7:0] stat_tx_pfc_xon, + output wire [7:0] stat_tx_pfc_xoff, + output wire [7:0] stat_tx_pfc_paused, + output wire stat_rx_lfc_pkt, + output wire stat_rx_lfc_xon, + output wire stat_rx_lfc_xoff, + output wire stat_rx_lfc_paused, + output wire stat_rx_pfc_pkt, + output wire [7:0] stat_rx_pfc_xon, + output wire [7:0] stat_rx_pfc_xoff, + output wire [7:0] stat_rx_pfc_paused, + + /* + * Configuration + */ + input wire [7:0] cfg_ifg, + input wire cfg_tx_enable, + input wire cfg_rx_enable, + input wire [47:0] cfg_mcf_rx_eth_dst_mcast, + input wire cfg_mcf_rx_check_eth_dst_mcast, + input wire [47:0] cfg_mcf_rx_eth_dst_ucast, + input wire cfg_mcf_rx_check_eth_dst_ucast, + input wire [47:0] cfg_mcf_rx_eth_src, + input wire cfg_mcf_rx_check_eth_src, + input wire [15:0] cfg_mcf_rx_eth_type, + input wire [15:0] cfg_mcf_rx_opcode_lfc, + input wire cfg_mcf_rx_check_opcode_lfc, + input wire [15:0] cfg_mcf_rx_opcode_pfc, + input wire cfg_mcf_rx_check_opcode_pfc, + input wire cfg_mcf_rx_forward, + input wire cfg_mcf_rx_enable, + input wire [47:0] cfg_tx_lfc_eth_dst, + input wire [47:0] cfg_tx_lfc_eth_src, + input wire [15:0] cfg_tx_lfc_eth_type, + input wire [15:0] cfg_tx_lfc_opcode, + input wire cfg_tx_lfc_en, + input wire [15:0] cfg_tx_lfc_quanta, + input wire [15:0] cfg_tx_lfc_refresh, + input wire [47:0] cfg_tx_pfc_eth_dst, + input wire [47:0] cfg_tx_pfc_eth_src, + input wire [15:0] cfg_tx_pfc_eth_type, + input wire [15:0] cfg_tx_pfc_opcode, + input wire cfg_tx_pfc_en, + input wire [8*16-1:0] cfg_tx_pfc_quanta, + input wire [8*16-1:0] cfg_tx_pfc_refresh, + input wire [15:0] cfg_rx_lfc_opcode, + input wire cfg_rx_lfc_en, + input wire [15:0] cfg_rx_pfc_opcode, + input wire cfg_rx_pfc_en +); + +parameter MAC_CTRL_ENABLE = PAUSE_ENABLE || PFC_ENABLE; +parameter TX_USER_WIDTH_INT = MAC_CTRL_ENABLE ? (PTP_TS_ENABLE ? (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1 : 0) + 1 : TX_USER_WIDTH; + +wire [DATA_WIDTH-1:0] tx_axis_tdata_int; +wire tx_axis_tvalid_int; +wire tx_axis_tready_int; +wire tx_axis_tlast_int; +wire [TX_USER_WIDTH_INT-1:0] tx_axis_tuser_int; + +wire [DATA_WIDTH-1:0] rx_axis_tdata_int; +wire rx_axis_tvalid_int; +wire rx_axis_tlast_int; +wire [RX_USER_WIDTH-1:0] rx_axis_tuser_int; + +axis_gmii_rx #( + .DATA_WIDTH(DATA_WIDTH), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(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_int), + .m_axis_tvalid(rx_axis_tvalid_int), + .m_axis_tlast(rx_axis_tlast_int), + .m_axis_tuser(rx_axis_tuser_int), + .ptp_ts(rx_ptp_ts), + .clk_enable(rx_clk_enable), + .mii_select(rx_mii_select), + .cfg_rx_enable(cfg_rx_enable), + .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(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .PTP_TS_CTRL_IN_TUSER(MAC_CTRL_ENABLE ? PTP_TS_ENABLE : TX_PTP_TS_CTRL_IN_TUSER), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH_INT) +) +axis_gmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata_int), + .s_axis_tvalid(tx_axis_tvalid_int), + .s_axis_tready(tx_axis_tready_int), + .s_axis_tlast(tx_axis_tlast_int), + .s_axis_tuser(tx_axis_tuser_int), + .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), + .cfg_ifg(cfg_ifg), + .cfg_tx_enable(cfg_tx_enable), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow) +); + +generate + +if (MAC_CTRL_ENABLE) begin : mac_ctrl + + localparam MCF_PARAMS_SIZE = PFC_ENABLE ? 18 : 2; + + wire tx_mcf_valid; + wire tx_mcf_ready; + wire [47:0] tx_mcf_eth_dst; + wire [47:0] tx_mcf_eth_src; + wire [15:0] tx_mcf_eth_type; + wire [15:0] tx_mcf_opcode; + wire [MCF_PARAMS_SIZE*8-1:0] tx_mcf_params; + + wire rx_mcf_valid; + wire [47:0] rx_mcf_eth_dst; + wire [47:0] rx_mcf_eth_src; + wire [15:0] rx_mcf_eth_type; + wire [15:0] rx_mcf_opcode; + wire [MCF_PARAMS_SIZE*8-1:0] rx_mcf_params; + + // terminate LFC pause requests from RX internally on TX side + wire tx_pause_req_int; + wire rx_lfc_ack_int; + + reg tx_lfc_req_sync_reg_1 = 1'b0; + reg tx_lfc_req_sync_reg_2 = 1'b0; + reg tx_lfc_req_sync_reg_3 = 1'b0; + + always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + tx_lfc_req_sync_reg_1 <= 1'b0; + end else begin + tx_lfc_req_sync_reg_1 <= rx_lfc_req; + end + end + + always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_lfc_req_sync_reg_2 <= 1'b0; + tx_lfc_req_sync_reg_3 <= 1'b0; + end else begin + tx_lfc_req_sync_reg_2 <= tx_lfc_req_sync_reg_1; + tx_lfc_req_sync_reg_3 <= tx_lfc_req_sync_reg_2; + end + end + + reg rx_lfc_ack_sync_reg_1 = 1'b0; + reg rx_lfc_ack_sync_reg_2 = 1'b0; + reg rx_lfc_ack_sync_reg_3 = 1'b0; + + always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + rx_lfc_ack_sync_reg_1 <= 1'b0; + end else begin + rx_lfc_ack_sync_reg_1 <= tx_lfc_pause_en ? tx_pause_ack : 0; + end + end + + always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_lfc_ack_sync_reg_2 <= 1'b0; + rx_lfc_ack_sync_reg_3 <= 1'b0; + end else begin + rx_lfc_ack_sync_reg_2 <= rx_lfc_ack_sync_reg_1; + rx_lfc_ack_sync_reg_3 <= rx_lfc_ack_sync_reg_2; + end + end + + assign tx_pause_req_int = tx_pause_req || (tx_lfc_pause_en ? tx_lfc_req_sync_reg_3 : 0); + + assign rx_lfc_ack_int = rx_lfc_ack || rx_lfc_ack_sync_reg_3; + + // handle PTP TS enable bit in tuser + wire [TX_USER_WIDTH_INT-1:0] tx_axis_tuser_in; + + if (PTP_TS_ENABLE && !TX_PTP_TS_CTRL_IN_TUSER) begin + assign tx_axis_tuser_in = {tx_axis_tuser[TX_USER_WIDTH-1:1], 1'b1, tx_axis_tuser[0]}; + end else begin + assign tx_axis_tuser_in = tx_axis_tuser; + end + + mac_ctrl_tx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(TX_USER_WIDTH_INT), + .MCF_PARAMS_SIZE(MCF_PARAMS_SIZE) + ) + mac_ctrl_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + + /* + * AXI stream input + */ + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(1'b1), + .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_in), + + /* + * AXI stream output + */ + .m_axis_tdata(tx_axis_tdata_int), + .m_axis_tkeep(), + .m_axis_tvalid(tx_axis_tvalid_int), + .m_axis_tready(tx_axis_tready_int), + .m_axis_tlast(tx_axis_tlast_int), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_axis_tuser_int), + + /* + * MAC control frame interface + */ + .mcf_valid(tx_mcf_valid), + .mcf_ready(tx_mcf_ready), + .mcf_eth_dst(tx_mcf_eth_dst), + .mcf_eth_src(tx_mcf_eth_src), + .mcf_eth_type(tx_mcf_eth_type), + .mcf_opcode(tx_mcf_opcode), + .mcf_params(tx_mcf_params), + .mcf_id(0), + .mcf_dest(0), + .mcf_user(0), + + /* + * Pause interface + */ + .tx_pause_req(tx_pause_req_int), + .tx_pause_ack(tx_pause_ack), + + /* + * Status + */ + .stat_tx_mcf(stat_tx_mcf) + ); + + mac_ctrl_rx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(RX_USER_WIDTH), + .USE_READY(0), + .MCF_PARAMS_SIZE(MCF_PARAMS_SIZE) + ) + mac_ctrl_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + + /* + * AXI stream input + */ + .s_axis_tdata(rx_axis_tdata_int), + .s_axis_tkeep(1'b1), + .s_axis_tvalid(rx_axis_tvalid_int), + .s_axis_tready(), + .s_axis_tlast(rx_axis_tlast_int), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_axis_tuser_int), + + /* + * AXI stream output + */ + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(1'b1), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + + /* + * MAC control frame interface + */ + .mcf_valid(rx_mcf_valid), + .mcf_eth_dst(rx_mcf_eth_dst), + .mcf_eth_src(rx_mcf_eth_src), + .mcf_eth_type(rx_mcf_eth_type), + .mcf_opcode(rx_mcf_opcode), + .mcf_params(rx_mcf_params), + .mcf_id(), + .mcf_dest(), + .mcf_user(), + + /* + * Configuration + */ + .cfg_mcf_rx_eth_dst_mcast(cfg_mcf_rx_eth_dst_mcast), + .cfg_mcf_rx_check_eth_dst_mcast(cfg_mcf_rx_check_eth_dst_mcast), + .cfg_mcf_rx_eth_dst_ucast(cfg_mcf_rx_eth_dst_ucast), + .cfg_mcf_rx_check_eth_dst_ucast(cfg_mcf_rx_check_eth_dst_ucast), + .cfg_mcf_rx_eth_src(cfg_mcf_rx_eth_src), + .cfg_mcf_rx_check_eth_src(cfg_mcf_rx_check_eth_src), + .cfg_mcf_rx_eth_type(cfg_mcf_rx_eth_type), + .cfg_mcf_rx_opcode_lfc(cfg_mcf_rx_opcode_lfc), + .cfg_mcf_rx_check_opcode_lfc(cfg_mcf_rx_check_opcode_lfc), + .cfg_mcf_rx_opcode_pfc(cfg_mcf_rx_opcode_pfc), + .cfg_mcf_rx_check_opcode_pfc(cfg_mcf_rx_check_opcode_pfc), + .cfg_mcf_rx_forward(cfg_mcf_rx_forward), + .cfg_mcf_rx_enable(cfg_mcf_rx_enable), + + /* + * Status + */ + .stat_rx_mcf(stat_rx_mcf) + ); + + mac_pause_ctrl_tx #( + .MCF_PARAMS_SIZE(MCF_PARAMS_SIZE), + .PFC_ENABLE(PFC_ENABLE) + ) + mac_pause_ctrl_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + + /* + * MAC control frame interface + */ + .mcf_valid(tx_mcf_valid), + .mcf_ready(tx_mcf_ready), + .mcf_eth_dst(tx_mcf_eth_dst), + .mcf_eth_src(tx_mcf_eth_src), + .mcf_eth_type(tx_mcf_eth_type), + .mcf_opcode(tx_mcf_opcode), + .mcf_params(tx_mcf_params), + + /* + * Pause (IEEE 802.3 annex 31B) + */ + .tx_lfc_req(tx_lfc_req), + .tx_lfc_resend(tx_lfc_resend), + + /* + * Priority Flow Control (PFC) (IEEE 802.3 annex 31D) + */ + .tx_pfc_req(tx_pfc_req), + .tx_pfc_resend(tx_pfc_resend), + + /* + * Configuration + */ + .cfg_tx_lfc_eth_dst(cfg_tx_lfc_eth_dst), + .cfg_tx_lfc_eth_src(cfg_tx_lfc_eth_src), + .cfg_tx_lfc_eth_type(cfg_tx_lfc_eth_type), + .cfg_tx_lfc_opcode(cfg_tx_lfc_opcode), + .cfg_tx_lfc_en(cfg_tx_lfc_en), + .cfg_tx_lfc_quanta(cfg_tx_lfc_quanta), + .cfg_tx_lfc_refresh(cfg_tx_lfc_refresh), + .cfg_tx_pfc_eth_dst(cfg_tx_pfc_eth_dst), + .cfg_tx_pfc_eth_src(cfg_tx_pfc_eth_src), + .cfg_tx_pfc_eth_type(cfg_tx_pfc_eth_type), + .cfg_tx_pfc_opcode(cfg_tx_pfc_opcode), + .cfg_tx_pfc_en(cfg_tx_pfc_en), + .cfg_tx_pfc_quanta(cfg_tx_pfc_quanta), + .cfg_tx_pfc_refresh(cfg_tx_pfc_refresh), + .cfg_quanta_step(tx_mii_select ? (4*256)/512 : (8*256)/512), + .cfg_quanta_clk_en(tx_clk_enable), + + /* + * Status + */ + .stat_tx_lfc_pkt(stat_tx_lfc_pkt), + .stat_tx_lfc_xon(stat_tx_lfc_xon), + .stat_tx_lfc_xoff(stat_tx_lfc_xoff), + .stat_tx_lfc_paused(stat_tx_lfc_paused), + .stat_tx_pfc_pkt(stat_tx_pfc_pkt), + .stat_tx_pfc_xon(stat_tx_pfc_xon), + .stat_tx_pfc_xoff(stat_tx_pfc_xoff), + .stat_tx_pfc_paused(stat_tx_pfc_paused) + ); + + mac_pause_ctrl_rx #( + .MCF_PARAMS_SIZE(18), + .PFC_ENABLE(PFC_ENABLE) + ) + mac_pause_ctrl_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + + /* + * MAC control frame interface + */ + .mcf_valid(rx_mcf_valid), + .mcf_eth_dst(rx_mcf_eth_dst), + .mcf_eth_src(rx_mcf_eth_src), + .mcf_eth_type(rx_mcf_eth_type), + .mcf_opcode(rx_mcf_opcode), + .mcf_params(rx_mcf_params), + + /* + * Pause (IEEE 802.3 annex 31B) + */ + .rx_lfc_en(rx_lfc_en), + .rx_lfc_req(rx_lfc_req), + .rx_lfc_ack(rx_lfc_ack_int), + + /* + * Priority Flow Control (PFC) (IEEE 802.3 annex 31D) + */ + .rx_pfc_en(rx_pfc_en), + .rx_pfc_req(rx_pfc_req), + .rx_pfc_ack(rx_pfc_ack), + + /* + * Configuration + */ + .cfg_rx_lfc_opcode(cfg_rx_lfc_opcode), + .cfg_rx_lfc_en(cfg_rx_lfc_en), + .cfg_rx_pfc_opcode(cfg_rx_pfc_opcode), + .cfg_rx_pfc_en(cfg_rx_pfc_en), + .cfg_quanta_step(rx_mii_select ? (4*256)/512 : (8*256)/512), + .cfg_quanta_clk_en(rx_clk_enable), + + /* + * Status + */ + .stat_rx_lfc_pkt(stat_rx_lfc_pkt), + .stat_rx_lfc_xon(stat_rx_lfc_xon), + .stat_rx_lfc_xoff(stat_rx_lfc_xoff), + .stat_rx_lfc_paused(stat_rx_lfc_paused), + .stat_rx_pfc_pkt(stat_rx_pfc_pkt), + .stat_rx_pfc_xon(stat_rx_pfc_xon), + .stat_rx_pfc_xoff(stat_rx_pfc_xoff), + .stat_rx_pfc_paused(stat_rx_pfc_paused) + ); + +end else begin + + assign tx_axis_tdata_int = tx_axis_tdata; + assign tx_axis_tvalid_int = tx_axis_tvalid; + assign tx_axis_tready = tx_axis_tready_int; + assign tx_axis_tlast_int = tx_axis_tlast; + assign tx_axis_tuser_int = tx_axis_tuser; + + assign rx_axis_tdata = rx_axis_tdata_int; + assign rx_axis_tvalid = rx_axis_tvalid_int; + assign rx_axis_tlast = rx_axis_tlast_int; + assign rx_axis_tuser = rx_axis_tuser_int; + + assign rx_lfc_req = 0; + assign rx_pfc_req = 0; + assign tx_pause_ack = 0; + + assign stat_tx_mcf = 0; + assign stat_rx_mcf = 0; + assign stat_tx_lfc_pkt = 0; + assign stat_tx_lfc_xon = 0; + assign stat_tx_lfc_xoff = 0; + assign stat_tx_lfc_paused = 0; + assign stat_tx_pfc_pkt = 0; + assign stat_tx_pfc_xon = 0; + assign stat_tx_pfc_xoff = 0; + assign stat_tx_pfc_paused = 0; + assign stat_rx_lfc_pkt = 0; + assign stat_rx_lfc_xon = 0; + assign stat_rx_lfc_xoff = 0; + assign stat_rx_lfc_paused = 0; + assign stat_rx_pfc_pkt = 0; + assign stat_rx_pfc_xon = 0; + assign stat_rx_pfc_xoff = 0; + assign stat_rx_pfc_paused = 0; + +end + +endgenerate + +endmodule + +`resetall diff --git a/src/rvvi/eth_mac_mii.sv b/src/rvvi/eth_mac_mii.sv new file mode 100644 index 000000000..212f4b84a --- /dev/null +++ b/src/rvvi/eth_mac_mii.sv @@ -0,0 +1,175 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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] cfg_ifg, + input wire cfg_tx_enable, + input wire cfg_rx_enable +); + +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) +); + // *** there a bunch of missing pins +/* verilator lint_off PINMISSING */ +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), + .cfg_ifg(cfg_ifg), + .cfg_tx_enable(cfg_tx_enable), + .cfg_rx_enable(cfg_rx_enable) +); +/* verilator lint_on PINMISSING */ + +endmodule + +`resetall diff --git a/src/rvvi/eth_mac_mii_fifo.sv b/src/rvvi/eth_mac_mii_fifo.sv new file mode 100644 index 000000000..a285ceeed --- /dev/null +++ b/src/rvvi/eth_mac_mii_fifo.sv @@ -0,0 +1,339 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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_RAM_PIPELINE = 1, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_OVERSIZE_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_BAD_FRAME = TX_DROP_OVERSIZE_FRAME, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FIFO_RAM_PIPELINE = 1, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_OVERSIZE_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_BAD_FRAME = RX_DROP_OVERSIZE_FRAME, + parameter RX_DROP_WHEN_FULL = RX_DROP_OVERSIZE_FRAME +) +( + 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] cfg_ifg, + input wire cfg_tx_enable, + input wire cfg_rx_enable +); + +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 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +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 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_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 + + // *** there are a bunch of missing pins + /* verilator lint_off PINMISSING */ +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), + .cfg_ifg(cfg_ifg), + .cfg_tx_enable(cfg_tx_enable), + .cfg_rx_enable(cfg_rx_enable) +); + +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), + .RAM_PIPELINE(TX_FIFO_RAM_PIPELINE), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_OVERSIZE_FRAME(TX_DROP_OVERSIZE_FRAME), + .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), + .RAM_PIPELINE(RX_FIFO_RAM_PIPELINE), + .FRAME_FIFO(RX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_OVERSIZE_FRAME(RX_DROP_OVERSIZE_FRAME), + .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) +); +/* verilator lint_on PINMISSING */ + +endmodule + +`resetall diff --git a/src/rvvi/lfsr.sv b/src/rvvi/lfsr.sv new file mode 100644 index 000000000..4b404fe94 --- /dev/null +++ b/src/rvvi/lfsr.sv @@ -0,0 +1,446 @@ +/* + +Copyright (c) 2016-2023 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 + +`resetall +`default_nettype none + +/* + * Parametrizable combinatorial parallel LFSR/CRC + */ +module lfsr # +( + // width of LFSR + parameter LFSR_WIDTH = 31, + // LFSR polynomial + parameter LFSR_POLY = 31'h10000001, + // LFSR configuration: "GALOIS", "FIBONACCI" + parameter LFSR_CONFIG = "FIBONACCI", + // LFSR feed forward enable + parameter LFSR_FEED_FORWARD = 0, + // bit-reverse input and output + parameter REVERSE = 0, + // width of data input + parameter DATA_WIDTH = 8, + // implementation style: "AUTO", "LOOP", "REDUCTION" + parameter STYLE = "AUTO" +) +( + input wire [DATA_WIDTH-1:0] data_in, + input wire [LFSR_WIDTH-1:0] state_in, + output wire [DATA_WIDTH-1:0] data_out, + output wire [LFSR_WIDTH-1:0] state_out +); + +/* + +Fully parametrizable combinatorial parallel LFSR/CRC module. Implements an unrolled LFSR +next state computation, shifting DATA_WIDTH bits per pass through the module. Input data +is XORed with LFSR feedback path, tie data_in to zero if this is not required. + +Works in two parts: statically computes a set of bit masks, then uses these bit masks to +select bits for XORing to compute the next state. + +Ports: + +data_in + +Data bits to be shifted through the LFSR (DATA_WIDTH bits) + +state_in + +LFSR/CRC current state input (LFSR_WIDTH bits) + +data_out + +Data bits shifted out of LFSR (DATA_WIDTH bits) + +state_out + +LFSR/CRC next state output (LFSR_WIDTH bits) + +Parameters: + +LFSR_WIDTH + +Specify width of LFSR/CRC register + +LFSR_POLY + +Specify the LFSR/CRC polynomial in hex format. For example, the polynomial + +x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + +would be represented as + +32'h04c11db7 + +Note that the largest term (x^32) is suppressed. This term is generated automatically based +on LFSR_WIDTH. + +LFSR_CONFIG + +Specify the LFSR configuration, either Fibonacci or Galois. Fibonacci is generally used +for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators, +scramblers, and descrambers, while Galois is generally used for cyclic redundancy check +generators and checkers. + +Fibonacci style (example for 64b66b scrambler, 0x8000000001) + + DIN (LSB first) + | + V + (+)<---------------------------(+)<-----------------------------. + | ^ | + | .----. .----. .----. | .----. .----. .----. | + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--' + | '----' '----' '----' '----' '----' '----' + V + DOUT + +Galois style (example for CRC16, 0x8005) + + ,-------------------+-------------------------+----------(+)<-- DIN (MSB first) + | | | ^ + | .----. .----. V .----. .----. V .----. | + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |--+---> DOUT + '----' '----' '----' '----' '----' + +LFSR_FEED_FORWARD + +Generate feed forward instead of feed back LFSR. Enable this for PRBS checking and self- +synchronous descrambling. + +Fibonacci feed-forward style (example for 64b66b descrambler, 0x8000000001) + + DIN (LSB first) + | + | .----. .----. .----. .----. .----. .----. + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--. + | '----' '----' '----' | '----' '----' '----' | + | V | + (+)<---------------------------(+)------------------------------' + | + V + DOUT + +Galois feed-forward style + + ,-------------------+-------------------------+------------+--- DIN (MSB first) + | | | | + | .----. .----. V .----. .----. V .----. V + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |->(+)-> DOUT + '----' '----' '----' '----' '----' + +REVERSE + +Bit-reverse LFSR input and output. Shifts MSB first by default, set REVERSE for LSB first. + +DATA_WIDTH + +Specify width of input and output data bus. The module will perform one shift per input +data bit, so if the input data bus is not required tie data_in to zero and set DATA_WIDTH +to the required number of shifts per clock cycle. + +STYLE + +Specify implementation style. Can be "AUTO", "LOOP", or "REDUCTION". When "AUTO" +is selected, implemenation will be "LOOP" or "REDUCTION" based on synthesis translate +directives. "REDUCTION" and "LOOP" are functionally identical, however they simulate +and synthesize differently. "REDUCTION" is implemented with a loop over a Verilog +reduction operator. "LOOP" is implemented as a doubly-nested loop with no reduction +operator. "REDUCTION" is very fast for simulation in iverilog and synthesizes well in +Quartus but synthesizes poorly in ISE, likely due to large inferred XOR gates causing +problems with the optimizer. "LOOP" synthesizes will in both ISE and Quartus. "AUTO" +will default to "REDUCTION" when simulating and "LOOP" for synthesizers that obey +synthesis translate directives. + +Settings for common LFSR/CRC implementations: + +Name Configuration Length Polynomial Initial value Notes +CRC16-IBM Galois, bit-reverse 16 16'h8005 16'hffff +CRC16-CCITT Galois 16 16'h1021 16'h1d0f +CRC32 Galois, bit-reverse 32 32'h04c11db7 32'hffffffff Ethernet FCS; invert final output +CRC32C Galois, bit-reverse 32 32'h1edc6f41 32'hffffffff iSCSI, Intel CRC32 instruction; 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 + +*/ + +function [LFSR_WIDTH+DATA_WIDTH-1:0] lfsr_mask(input [31:0] index); + reg [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0]; + reg [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0]; + reg [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0]; + reg [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0]; + + reg [LFSR_WIDTH-1:0] state_val; + reg [DATA_WIDTH-1:0] data_val; + + reg [DATA_WIDTH-1:0] data_mask; + + integer i, j; + + begin + // init bit masks + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + lfsr_mask_state[i] = 0; + lfsr_mask_state[i][i] = 1'b1; + lfsr_mask_data[i] = 0; + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + output_mask_state[i] = 0; + if (i < LFSR_WIDTH) begin + output_mask_state[i][i] = 1'b1; + end + output_mask_data[i] = 0; + end + + // simulate shift register + if (LFSR_CONFIG == "FIBONACCI") begin + // Fibonacci configuration + for (data_mask = {1'b1, {DATA_WIDTH-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ data_mask; + + // add XOR inputs from correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if ((LFSR_POLY >> j) & 1) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = data_mask; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + end + end else if (LFSR_CONFIG == "GALOIS") begin + // Galois configuration + for (data_mask = {1'b1, {DATA_WIDTH-1{1'b0}}}; data_mask != 0; data_mask = data_mask >> 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ data_mask; + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = data_mask; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + + // add XOR inputs at correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if ((LFSR_POLY >> j) & 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + end + end else begin + $error("Error: unknown configuration setting!"); + $finish; + end + + // reverse bits if selected + if (REVERSE) begin + if (index < LFSR_WIDTH) begin + state_val = 0; + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_val[i] = lfsr_mask_state[LFSR_WIDTH-index-1][LFSR_WIDTH-i-1]; + end + + data_val = 0; + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + data_val[i] = lfsr_mask_data[LFSR_WIDTH-index-1][DATA_WIDTH-i-1]; + end + end else begin + state_val = 0; + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_val[i] = output_mask_state[DATA_WIDTH-(index-LFSR_WIDTH)-1][LFSR_WIDTH-i-1]; + end + + data_val = 0; + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + data_val[i] = output_mask_data[DATA_WIDTH-(index-LFSR_WIDTH)-1][DATA_WIDTH-i-1]; + end + end + end else begin + if (index < LFSR_WIDTH) begin + state_val = lfsr_mask_state[index]; + data_val = lfsr_mask_data[index]; + end else begin + state_val = output_mask_state[index-LFSR_WIDTH]; + data_val = output_mask_data[index-LFSR_WIDTH]; + end + end + lfsr_mask = {data_val, state_val}; + end +endfunction + +// 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 : lfsr_state + wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n); + assign state_out[n] = ^({data_in, state_in} & mask); + end + for (n = 0; n < DATA_WIDTH; n = n + 1) begin : lfsr_data + wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n+LFSR_WIDTH); + assign data_out[n] = ^({data_in, state_in} & mask); + 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 + + for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : lfsr_state + wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n); + + reg state_reg; + + assign state_out[n] = state_reg; + + integer i; + + always @* begin + state_reg = 1'b0; + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + if (mask[i]) begin + state_reg = state_reg ^ state_in[i]; + end + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + if (mask[i+LFSR_WIDTH]) begin + state_reg = state_reg ^ data_in[i]; + end + end + end + end + for (n = 0; n < DATA_WIDTH; n = n + 1) begin : lfsr_data + wire [LFSR_WIDTH+DATA_WIDTH-1:0] mask = lfsr_mask(n+LFSR_WIDTH); + + reg data_reg; + + assign data_out[n] = data_reg; + + integer i; + + always @* begin + data_reg = 1'b0; + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + if (mask[i]) begin + data_reg = data_reg ^ state_in[i]; + end + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + if (mask[i+LFSR_WIDTH]) begin + data_reg = data_reg ^ data_in[i]; + end + end + end + end + +end else begin + + initial begin + $error("Error: unknown style setting!"); + $finish; + end + +end + +endgenerate + +endmodule + +`resetall diff --git a/src/rvvi/mac_ctrl_rx.sv b/src/rvvi/mac_ctrl_rx.sv new file mode 100644 index 000000000..f7171887b --- /dev/null +++ b/src/rvvi/mac_ctrl_rx.sv @@ -0,0 +1,447 @@ +/* + +Copyright (c) 2023 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 + +`resetall +`default_nettype none + +/* + * MAC control receive + */ +module mac_ctrl_rx # +( + 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, + parameter USE_READY = 0, + parameter MCF_PARAMS_SIZE = 18 +) +( + input wire clk, + input wire rst, + + /* + * AXI stream input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * MAC control frame interface + */ + output wire mcf_valid, + output wire [47:0] mcf_eth_dst, + output wire [47:0] mcf_eth_src, + output wire [15:0] mcf_eth_type, + output wire [15:0] mcf_opcode, + output wire [MCF_PARAMS_SIZE*8-1:0] mcf_params, + output wire [ID_WIDTH-1:0] mcf_id, + output wire [DEST_WIDTH-1:0] mcf_dest, + output wire [USER_WIDTH-1:0] mcf_user, + + /* + * Configuration + */ + input wire [47:0] cfg_mcf_rx_eth_dst_mcast, + input wire cfg_mcf_rx_check_eth_dst_mcast, + input wire [47:0] cfg_mcf_rx_eth_dst_ucast, + input wire cfg_mcf_rx_check_eth_dst_ucast, + input wire [47:0] cfg_mcf_rx_eth_src, + input wire cfg_mcf_rx_check_eth_src, + input wire [15:0] cfg_mcf_rx_eth_type, + input wire [15:0] cfg_mcf_rx_opcode_lfc, + input wire cfg_mcf_rx_check_opcode_lfc, + input wire [15:0] cfg_mcf_rx_opcode_pfc, + input wire cfg_mcf_rx_check_opcode_pfc, + input wire cfg_mcf_rx_forward, + input wire cfg_mcf_rx_enable, + + /* + * Status + */ + output wire stat_rx_mcf +); + +parameter BYTE_LANES = KEEP_ENABLE ? KEEP_WIDTH : 1; + +parameter HDR_SIZE = 60; + +parameter CYCLE_COUNT = (HDR_SIZE+BYTE_LANES-1)/BYTE_LANES; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +parameter OFFSET = HDR_SIZE % BYTE_LANES; + +// check configuration +initial begin + if (BYTE_LANES * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (MCF_PARAMS_SIZE > 44) begin + $error("Error: Maximum MCF_PARAMS_SIZE is 44 bytes (instance %m)"); + $finish; + end +end + +/* + +MAC control frame + + Field Length + Destination MAC address 6 octets [01:80:C2:00:00:01] + Source MAC address 6 octets + Ethertype 2 octets [0x8808] + Opcode 2 octets + Parameters 0-44 octets + +This module manages the reception of MAC control frames. Incoming frames are +checked based on the ethertype and (optionally) MAC addresses. Matching control +frames are marked by setting tuser[0] on the data output and forwarded through +a separate interface for processing. + +*/ + +reg read_mcf_reg = 1'b1, read_mcf_next; +reg mcf_frame_reg = 1'b0, mcf_frame_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +// 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 = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg mcf_valid_reg = 0, mcf_valid_next; +reg [47:0] mcf_eth_dst_reg = 0, mcf_eth_dst_next; +reg [47:0] mcf_eth_src_reg = 0, mcf_eth_src_next; +reg [15:0] mcf_eth_type_reg = 0, mcf_eth_type_next; +reg [15:0] mcf_opcode_reg = 0, mcf_opcode_next; +reg [MCF_PARAMS_SIZE*8-1:0] mcf_params_reg = 0, mcf_params_next; +reg [ID_WIDTH-1:0] mcf_id_reg = 0, mcf_id_next; +reg [DEST_WIDTH-1:0] mcf_dest_reg = 0, mcf_dest_next; +reg [USER_WIDTH-1:0] mcf_user_reg = 0, mcf_user_next; + +reg stat_rx_mcf_reg = 1'b0, stat_rx_mcf_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign mcf_valid = mcf_valid_reg; +assign mcf_eth_dst = mcf_eth_dst_reg; +assign mcf_eth_src = mcf_eth_src_reg; +assign mcf_eth_type = mcf_eth_type_reg; +assign mcf_opcode = mcf_opcode_reg; +assign mcf_params = mcf_params_reg; +assign mcf_id = mcf_id_reg; +assign mcf_dest = mcf_dest_reg; +assign mcf_user = mcf_user_reg; + +assign stat_rx_mcf = stat_rx_mcf_reg; + +wire mcf_eth_dst_mcast_match = mcf_eth_dst_next == cfg_mcf_rx_eth_dst_mcast; +wire mcf_eth_dst_ucast_match = mcf_eth_dst_next == cfg_mcf_rx_eth_dst_ucast; +wire mcf_eth_src_match = mcf_eth_src_next == cfg_mcf_rx_eth_src; +wire mcf_eth_type_match = mcf_eth_type_next == cfg_mcf_rx_eth_type; +wire mcf_opcode_lfc_match = mcf_opcode_next == cfg_mcf_rx_opcode_lfc; +wire mcf_opcode_pfc_match = mcf_opcode_next == cfg_mcf_rx_opcode_pfc; + +wire mcf_eth_dst_match = ((mcf_eth_dst_mcast_match && cfg_mcf_rx_check_eth_dst_mcast) || + (mcf_eth_dst_ucast_match && cfg_mcf_rx_check_eth_dst_ucast) || + (!cfg_mcf_rx_check_eth_dst_mcast && !cfg_mcf_rx_check_eth_dst_ucast)); + +wire mcf_opcode_match = ((mcf_opcode_lfc_match && cfg_mcf_rx_check_opcode_lfc) || + (mcf_opcode_pfc_match && cfg_mcf_rx_check_opcode_pfc) || + (!cfg_mcf_rx_check_opcode_lfc && !cfg_mcf_rx_check_opcode_pfc)); + +wire mcf_match = (mcf_eth_dst_match && + (mcf_eth_src_match || !cfg_mcf_rx_check_eth_src) && + mcf_eth_type_match && mcf_opcode_match); + +integer k; + +always @* begin + read_mcf_next = read_mcf_reg; + mcf_frame_next = mcf_frame_reg; + ptr_next = ptr_reg; + + // pass through data + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + s_axis_tready_next = m_axis_tready_int_early || !USE_READY; + + mcf_valid_next = 1'b0; + mcf_eth_dst_next = mcf_eth_dst_reg; + mcf_eth_src_next = mcf_eth_src_reg; + mcf_eth_type_next = mcf_eth_type_reg; + mcf_opcode_next = mcf_opcode_reg; + mcf_params_next = mcf_params_reg; + mcf_id_next = mcf_id_reg; + mcf_dest_next = mcf_dest_reg; + mcf_user_next = mcf_user_reg; + + stat_rx_mcf_next = 1'b0; + + if ((s_axis_tready || !USE_READY) && s_axis_tvalid) begin + if (read_mcf_reg) begin + ptr_next = ptr_reg + 1; + + mcf_id_next = s_axis_tid; + mcf_dest_next = s_axis_tdest; + mcf_user_next = s_axis_tuser; + + `define _HEADER_FIELD_(offset, field) \ + if (ptr_reg == offset/BYTE_LANES) begin \ + field = s_axis_tdata[(offset%BYTE_LANES)*8 +: 8]; \ + end + + `_HEADER_FIELD_(0, mcf_eth_dst_next[5*8 +: 8]) + `_HEADER_FIELD_(1, mcf_eth_dst_next[4*8 +: 8]) + `_HEADER_FIELD_(2, mcf_eth_dst_next[3*8 +: 8]) + `_HEADER_FIELD_(3, mcf_eth_dst_next[2*8 +: 8]) + `_HEADER_FIELD_(4, mcf_eth_dst_next[1*8 +: 8]) + `_HEADER_FIELD_(5, mcf_eth_dst_next[0*8 +: 8]) + `_HEADER_FIELD_(6, mcf_eth_src_next[5*8 +: 8]) + `_HEADER_FIELD_(7, mcf_eth_src_next[4*8 +: 8]) + `_HEADER_FIELD_(8, mcf_eth_src_next[3*8 +: 8]) + `_HEADER_FIELD_(9, mcf_eth_src_next[2*8 +: 8]) + `_HEADER_FIELD_(10, mcf_eth_src_next[1*8 +: 8]) + `_HEADER_FIELD_(11, mcf_eth_src_next[0*8 +: 8]) + `_HEADER_FIELD_(12, mcf_eth_type_next[1*8 +: 8]) + `_HEADER_FIELD_(13, mcf_eth_type_next[0*8 +: 8]) + `_HEADER_FIELD_(14, mcf_opcode_next[1*8 +: 8]) + `_HEADER_FIELD_(15, mcf_opcode_next[0*8 +: 8]) + + if (ptr_reg == 0/BYTE_LANES) begin + // ensure params field gets cleared + mcf_params_next = 0; + end + + for (k = 0; k < MCF_PARAMS_SIZE; k = k + 1) begin + if (ptr_reg == (16+k)/BYTE_LANES) begin + mcf_params_next[k*8 +: 8] = s_axis_tdata[((16+k)%BYTE_LANES)*8 +: 8]; + end + end + + if (ptr_reg == 15/BYTE_LANES && (!KEEP_ENABLE || s_axis_tkeep[13%BYTE_LANES])) begin + // record match at end of opcode field + mcf_frame_next = mcf_match && cfg_mcf_rx_enable; + end + + if (ptr_reg == (HDR_SIZE-1)/BYTE_LANES) begin + read_mcf_next = 1'b0; + end + + `undef _HEADER_FIELD_ + end + + if (s_axis_tlast) begin + if (s_axis_tuser[0]) begin + // frame marked invalid + end else if (mcf_frame_next) begin + if (!cfg_mcf_rx_forward) begin + // mark frame invalid + m_axis_tuser_int[0] = 1'b1; + end + // transfer out MAC control frame + mcf_valid_next = 1'b1; + stat_rx_mcf_next = 1'b1; + end + + read_mcf_next = 1'b1; + mcf_frame_next = 1'b0; + ptr_next = 0; + end + end +end + +always @(posedge clk) begin + read_mcf_reg <= read_mcf_next; + mcf_frame_reg <= mcf_frame_next; + ptr_reg <= ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + mcf_valid_reg <= mcf_valid_next; + mcf_eth_dst_reg <= mcf_eth_dst_next; + mcf_eth_src_reg <= mcf_eth_src_next; + mcf_eth_type_reg <= mcf_eth_type_next; + mcf_opcode_reg <= mcf_opcode_next; + mcf_params_reg <= mcf_params_next; + mcf_id_reg <= mcf_id_next; + mcf_dest_reg <= mcf_dest_next; + mcf_user_reg <= mcf_user_next; + + stat_rx_mcf_reg <= stat_rx_mcf_next; + + if (rst) begin + read_mcf_reg <= 1'b1; + mcf_frame_reg <= 1'b0; + ptr_reg <= 0; + s_axis_tready_reg <= 1'b0; + mcf_valid_reg <= 1'b0; + stat_rx_mcf_reg <= 1'b0; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_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_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_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_axis_tready_int_early = m_axis_tready || !USE_READY || (!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 || !USE_READY || !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 || !USE_READY) 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 + 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_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_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_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_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_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end + + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end +end + +endmodule + +`resetall diff --git a/src/rvvi/mac_ctrl_tx.sv b/src/rvvi/mac_ctrl_tx.sv new file mode 100644 index 000000000..3d321fd64 --- /dev/null +++ b/src/rvvi/mac_ctrl_tx.sv @@ -0,0 +1,420 @@ +/* + +Copyright (c) 2023 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 + +`resetall +`default_nettype none + +/* + * MAC control transmit + */ +module mac_ctrl_tx # +( + 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, + parameter MCF_PARAMS_SIZE = 18 +) +( + input wire clk, + input wire rst, + + /* + * AXI stream input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * MAC control frame interface + */ + input wire mcf_valid, + output wire mcf_ready, + input wire [47:0] mcf_eth_dst, + input wire [47:0] mcf_eth_src, + input wire [15:0] mcf_eth_type, + input wire [15:0] mcf_opcode, + input wire [MCF_PARAMS_SIZE*8-1:0] mcf_params, + input wire [ID_WIDTH-1:0] mcf_id, + input wire [DEST_WIDTH-1:0] mcf_dest, + input wire [USER_WIDTH-1:0] mcf_user, + + /* + * Pause interface + */ + input wire tx_pause_req, + output wire tx_pause_ack, + + /* + * Status + */ + output wire stat_tx_mcf +); + +parameter BYTE_LANES = KEEP_ENABLE ? KEEP_WIDTH : 1; + +parameter HDR_SIZE = 60; + +parameter CYCLE_COUNT = (HDR_SIZE+BYTE_LANES-1)/BYTE_LANES; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +parameter OFFSET = HDR_SIZE % BYTE_LANES; + +// check configuration +initial begin + if (BYTE_LANES * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (MCF_PARAMS_SIZE > 44) begin + $error("Error: Maximum MCF_PARAMS_SIZE is 44 bytes (instance %m)"); + $finish; + end +end + +/* + +MAC control frame + + Field Length + Destination MAC address 6 octets [01:80:C2:00:00:01] + Source MAC address 6 octets + Ethertype 2 octets [0x8808] + Opcode 2 octets + Parameters 0-44 octets + +This module manages the transmission of MAC control frames. Control frames +are accepted in parallel, serialized, and merged at a higher priority with +data traffic. + +*/ + +reg send_data_reg = 1'b0, send_data_next; +reg send_mcf_reg = 1'b0, send_mcf_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; +reg mcf_ready_reg = 1'b0, mcf_ready_next; +reg tx_pause_ack_reg = 1'b0, tx_pause_ack_next; +reg stat_tx_mcf_reg = 1'b0, stat_tx_mcf_next; + +// 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 = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; +assign mcf_ready = mcf_ready_reg; +assign tx_pause_ack = tx_pause_ack_reg; +assign stat_tx_mcf = stat_tx_mcf_reg; + +integer k; + +always @* begin + send_data_next = send_data_reg; + send_mcf_next = send_mcf_reg; + ptr_next = ptr_reg; + + s_axis_tready_next = 1'b0; + mcf_ready_next = 1'b0; + tx_pause_ack_next = tx_pause_ack_reg; + stat_tx_mcf_next = 1'b0; + + m_axis_tdata_int = 0; + m_axis_tkeep_int = 0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tid_int = 0; + m_axis_tdest_int = 0; + m_axis_tuser_int = 0; + + if (!send_data_reg && !send_mcf_reg) begin + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + s_axis_tready_next = m_axis_tready_int_early && !tx_pause_req; + tx_pause_ack_next = tx_pause_req; + if (s_axis_tvalid && s_axis_tready) begin + s_axis_tready_next = m_axis_tready_int_early; + tx_pause_ack_next = 1'b0; + m_axis_tvalid_int = 1'b1; + if (s_axis_tlast) begin + s_axis_tready_next = m_axis_tready_int_early && !mcf_valid && !mcf_ready; + send_data_next = 1'b0; + end else begin + send_data_next = 1'b1; + end + end else if (mcf_valid) begin + s_axis_tready_next = 1'b0; + ptr_next = 0; + send_mcf_next = 1'b1; + mcf_ready_next = (CYCLE_COUNT == 1) && m_axis_tready_int_early; + end + end + + if (send_data_reg) begin + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + s_axis_tready_next = m_axis_tready_int_early; + if (s_axis_tvalid && s_axis_tready) begin + m_axis_tvalid_int = 1'b1; + if (s_axis_tlast) begin + s_axis_tready_next = m_axis_tready_int_early && !tx_pause_req; + send_data_next = 1'b0; + if (mcf_valid) begin + s_axis_tready_next = 1'b0; + ptr_next = 0; + send_mcf_next = 1'b1; + mcf_ready_next = (CYCLE_COUNT == 1) && m_axis_tready_int_early; + end + end else begin + send_data_next = 1'b1; + end + end + end + + if (send_mcf_reg) begin + mcf_ready_next = (CYCLE_COUNT == 1 || ptr_reg == CYCLE_COUNT-1) && m_axis_tready_int_early; + if (m_axis_tready_int_reg) begin + ptr_next = ptr_reg + 1; + + m_axis_tvalid_int = 1'b1; + m_axis_tid_int = mcf_id; + m_axis_tdest_int = mcf_dest; + m_axis_tuser_int = mcf_user; + + `define _HEADER_FIELD_(offset, field) \ + if (ptr_reg == offset/BYTE_LANES) begin \ + m_axis_tdata_int[(offset%BYTE_LANES)*8 +: 8] = field; \ + m_axis_tkeep_int[offset%BYTE_LANES] = 1'b1; \ + end + + `_HEADER_FIELD_(0, mcf_eth_dst[5*8 +: 8]) + `_HEADER_FIELD_(1, mcf_eth_dst[4*8 +: 8]) + `_HEADER_FIELD_(2, mcf_eth_dst[3*8 +: 8]) + `_HEADER_FIELD_(3, mcf_eth_dst[2*8 +: 8]) + `_HEADER_FIELD_(4, mcf_eth_dst[1*8 +: 8]) + `_HEADER_FIELD_(5, mcf_eth_dst[0*8 +: 8]) + `_HEADER_FIELD_(6, mcf_eth_src[5*8 +: 8]) + `_HEADER_FIELD_(7, mcf_eth_src[4*8 +: 8]) + `_HEADER_FIELD_(8, mcf_eth_src[3*8 +: 8]) + `_HEADER_FIELD_(9, mcf_eth_src[2*8 +: 8]) + `_HEADER_FIELD_(10, mcf_eth_src[1*8 +: 8]) + `_HEADER_FIELD_(11, mcf_eth_src[0*8 +: 8]) + `_HEADER_FIELD_(12, mcf_eth_type[1*8 +: 8]) + `_HEADER_FIELD_(13, mcf_eth_type[0*8 +: 8]) + `_HEADER_FIELD_(14, mcf_opcode[1*8 +: 8]) + `_HEADER_FIELD_(15, mcf_opcode[0*8 +: 8]) + + for (k = 0; k < HDR_SIZE-16; k = k + 1) begin + if (ptr_reg == (16+k)/BYTE_LANES) begin + if (k < MCF_PARAMS_SIZE) begin + m_axis_tdata_int[((16+k)%BYTE_LANES)*8 +: 8] = mcf_params[k*8 +: 8]; + end else begin + m_axis_tdata_int[((16+k)%BYTE_LANES)*8 +: 8] = 0; + end + m_axis_tkeep_int[(16+k)%BYTE_LANES] = 1'b1; + end + end + + if (ptr_reg == (HDR_SIZE-1)/BYTE_LANES) begin + s_axis_tready_next = m_axis_tready_int_early && !tx_pause_req; + mcf_ready_next = 1'b0; + m_axis_tlast_int = 1'b1; + send_mcf_next = 1'b0; + stat_tx_mcf_next = 1'b1; + end else begin + mcf_ready_next = (ptr_next == CYCLE_COUNT-1) && m_axis_tready_int_early; + end + + `undef _HEADER_FIELD_ + end + end +end + +always @(posedge clk) begin + send_data_reg <= send_data_next; + send_mcf_reg <= send_mcf_next; + ptr_reg <= ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + mcf_ready_reg <= mcf_ready_next; + tx_pause_ack_reg <= tx_pause_ack_next; + stat_tx_mcf_reg <= stat_tx_mcf_next; + + if (rst) begin + send_data_reg <= 1'b0; + send_mcf_reg <= 1'b0; + ptr_reg <= 0; + s_axis_tready_reg <= 1'b0; + mcf_ready_reg <= 1'b0; + tx_pause_ack_reg <= 1'b0; + stat_tx_mcf_reg <= 1'b0; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_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_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_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_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 + 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_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_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_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_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_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end + + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end +end + +endmodule + +`resetall diff --git a/src/rvvi/mac_pause_ctrl_rx.sv b/src/rvvi/mac_pause_ctrl_rx.sv new file mode 100644 index 000000000..e95c07bf9 --- /dev/null +++ b/src/rvvi/mac_pause_ctrl_rx.sv @@ -0,0 +1,220 @@ +/* + +Copyright (c) 2023 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 + +`resetall +`default_nettype none + +/* + * PFC and pause frame receive handling + */ +module mac_pause_ctrl_rx # +( + parameter MCF_PARAMS_SIZE = 18, + parameter PFC_ENABLE = 1 +) +( + input wire clk, + input wire rst, + + /* + * MAC control frame interface + */ + input wire mcf_valid, + input wire [47:0] mcf_eth_dst, + input wire [47:0] mcf_eth_src, + input wire [15:0] mcf_eth_type, + input wire [15:0] mcf_opcode, + input wire [MCF_PARAMS_SIZE*8-1:0] mcf_params, + + /* + * Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE) + */ + input wire rx_lfc_en, + output wire rx_lfc_req, + input wire rx_lfc_ack, + + /* + * Priority Flow Control (PFC) (IEEE 802.3 annex 31D PFC) + */ + input wire [7:0] rx_pfc_en, + output wire [7:0] rx_pfc_req, + input wire [7:0] rx_pfc_ack, + + /* + * Configuration + */ + input wire [15:0] cfg_rx_lfc_opcode, + input wire cfg_rx_lfc_en, + input wire [15:0] cfg_rx_pfc_opcode, + input wire cfg_rx_pfc_en, + input wire [9:0] cfg_quanta_step, + input wire cfg_quanta_clk_en, + + /* + * Status + */ + output wire stat_rx_lfc_pkt, + output wire stat_rx_lfc_xon, + output wire stat_rx_lfc_xoff, + output wire stat_rx_lfc_paused, + output wire stat_rx_pfc_pkt, + output wire [7:0] stat_rx_pfc_xon, + output wire [7:0] stat_rx_pfc_xoff, + output wire [7:0] stat_rx_pfc_paused +); + +localparam QFB = 8; + +// check configuration +initial begin + if (MCF_PARAMS_SIZE < (PFC_ENABLE ? 18 : 2)) begin + $error("Error: MCF_PARAMS_SIZE too small for requested configuration (instance %m)"); + $finish; + end +end + +reg lfc_req_reg = 1'b0, lfc_req_next; +reg [7:0] pfc_req_reg = 8'd0, pfc_req_next; + +reg [16+QFB-1:0] lfc_quanta_reg = 0, lfc_quanta_next; +reg [16+QFB-1:0] pfc_quanta_reg[0:7], pfc_quanta_next[0:7]; + +reg stat_rx_lfc_pkt_reg = 1'b0, stat_rx_lfc_pkt_next; +reg stat_rx_lfc_xon_reg = 1'b0, stat_rx_lfc_xon_next; +reg stat_rx_lfc_xoff_reg = 1'b0, stat_rx_lfc_xoff_next; +reg stat_rx_pfc_pkt_reg = 1'b0, stat_rx_pfc_pkt_next; +reg [7:0] stat_rx_pfc_xon_reg = 0, stat_rx_pfc_xon_next; +reg [7:0] stat_rx_pfc_xoff_reg = 0, stat_rx_pfc_xoff_next; + +assign rx_lfc_req = lfc_req_reg; +assign rx_pfc_req = pfc_req_reg; + +assign stat_rx_lfc_pkt = stat_rx_lfc_pkt_reg; +assign stat_rx_lfc_xon = stat_rx_lfc_xon_reg; +assign stat_rx_lfc_xoff = stat_rx_lfc_xoff_reg; +assign stat_rx_lfc_paused = lfc_req_reg; +assign stat_rx_pfc_pkt = stat_rx_pfc_pkt_reg; +assign stat_rx_pfc_xon = stat_rx_pfc_xon_reg; +assign stat_rx_pfc_xoff = stat_rx_pfc_xoff_reg; +assign stat_rx_pfc_paused = pfc_req_reg; + +integer k; + +initial begin + for (k = 0; k < 8; k = k + 1) begin + pfc_quanta_reg[k] = 0; + end +end + +always @* begin + stat_rx_lfc_pkt_next = 1'b0; + stat_rx_lfc_xon_next = 1'b0; + stat_rx_lfc_xoff_next = 1'b0; + stat_rx_pfc_pkt_next = 1'b0; + stat_rx_pfc_xon_next = 0; + stat_rx_pfc_xoff_next = 0; + + if (cfg_quanta_clk_en && rx_lfc_ack) begin + if (lfc_quanta_reg > cfg_quanta_step) begin + lfc_quanta_next = lfc_quanta_reg - cfg_quanta_step; + end else begin + lfc_quanta_next = 0; + end + end else begin + lfc_quanta_next = lfc_quanta_reg; + end + + lfc_req_next = (lfc_quanta_reg != 0) && rx_lfc_en && cfg_rx_lfc_en; + + for (k = 0; k < 8; k = k + 1) begin + if (cfg_quanta_clk_en && rx_pfc_ack[k]) begin + if (pfc_quanta_reg[k] > cfg_quanta_step) begin + pfc_quanta_next[k] = pfc_quanta_reg[k] - cfg_quanta_step; + end else begin + pfc_quanta_next[k] = 0; + end + end else begin + pfc_quanta_next[k] = pfc_quanta_reg[k]; + end + + pfc_req_next[k] = (pfc_quanta_reg[k] != 0) && rx_pfc_en[k] && cfg_rx_pfc_en; + end + + if (mcf_valid) begin + if (mcf_opcode == cfg_rx_lfc_opcode && cfg_rx_lfc_en) begin + stat_rx_lfc_pkt_next = 1'b1; + stat_rx_lfc_xon_next = {mcf_params[7:0], mcf_params[15:8]} == 0; + stat_rx_lfc_xoff_next = {mcf_params[7:0], mcf_params[15:8]} != 0; + lfc_quanta_next = {mcf_params[7:0], mcf_params[15:8], {QFB{1'b0}}}; + end else if (PFC_ENABLE && mcf_opcode == cfg_rx_pfc_opcode && cfg_rx_pfc_en) begin + stat_rx_pfc_pkt_next = 1'b1; + for (k = 0; k < 8; k = k + 1) begin + if (mcf_params[k+8]) begin + stat_rx_pfc_xon_next[k] = {mcf_params[16+(k*16)+0 +: 8], mcf_params[16+(k*16)+8 +: 8]} == 0; + stat_rx_pfc_xoff_next[k] = {mcf_params[16+(k*16)+0 +: 8], mcf_params[16+(k*16)+8 +: 8]} != 0; + pfc_quanta_next[k] = {mcf_params[16+(k*16)+0 +: 8], mcf_params[16+(k*16)+8 +: 8], {QFB{1'b0}}}; + end + end + end + end +end + +always @(posedge clk) begin + lfc_req_reg <= lfc_req_next; + pfc_req_reg <= pfc_req_next; + + lfc_quanta_reg <= lfc_quanta_next; + for (k = 0; k < 8; k = k + 1) begin + pfc_quanta_reg[k] <= pfc_quanta_next[k]; + end + + stat_rx_lfc_pkt_reg <= stat_rx_lfc_pkt_next; + stat_rx_lfc_xon_reg <= stat_rx_lfc_xon_next; + stat_rx_lfc_xoff_reg <= stat_rx_lfc_xoff_next; + stat_rx_pfc_pkt_reg <= stat_rx_pfc_pkt_next; + stat_rx_pfc_xon_reg <= stat_rx_pfc_xon_next; + stat_rx_pfc_xoff_reg <= stat_rx_pfc_xoff_next; + + if (rst) begin + lfc_req_reg <= 1'b0; + pfc_req_reg <= 8'd0; + lfc_quanta_reg <= 0; + for (k = 0; k < 8; k = k + 1) begin + pfc_quanta_reg[k] <= 0; + end + + stat_rx_lfc_pkt_reg <= 1'b0; + stat_rx_lfc_xon_reg <= 1'b0; + stat_rx_lfc_xoff_reg <= 1'b0; + stat_rx_pfc_pkt_reg <= 1'b0; + stat_rx_pfc_xon_reg <= 0; + stat_rx_pfc_xoff_reg <= 0; + end +end + +endmodule + +`resetall diff --git a/src/rvvi/mac_pause_ctrl_tx.sv b/src/rvvi/mac_pause_ctrl_tx.sv new file mode 100644 index 000000000..20566d888 --- /dev/null +++ b/src/rvvi/mac_pause_ctrl_tx.sv @@ -0,0 +1,312 @@ +/* + +Copyright (c) 2023 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 + +`resetall +`default_nettype none + +/* + * PFC and pause frame transmit handling + */ +module mac_pause_ctrl_tx # +( + parameter MCF_PARAMS_SIZE = 18, + parameter PFC_ENABLE = 1 +) +( + input wire clk, + input wire rst, + + /* + * MAC control frame interface + */ + output wire mcf_valid, + input wire mcf_ready, + output wire [47:0] mcf_eth_dst, + output wire [47:0] mcf_eth_src, + output wire [15:0] mcf_eth_type, + output wire [15:0] mcf_opcode, + output wire [MCF_PARAMS_SIZE*8-1:0] mcf_params, + + /* + * Link-level Flow Control (LFC) (IEEE 802.3 annex 31B PAUSE) + */ + input wire tx_lfc_req, + input wire tx_lfc_resend, + + /* + * Priority Flow Control (PFC) (IEEE 802.3 annex 31D) + */ + input wire [7:0] tx_pfc_req, + input wire tx_pfc_resend, + + /* + * Configuration + */ + input wire [47:0] cfg_tx_lfc_eth_dst, + input wire [47:0] cfg_tx_lfc_eth_src, + input wire [15:0] cfg_tx_lfc_eth_type, + input wire [15:0] cfg_tx_lfc_opcode, + input wire cfg_tx_lfc_en, + input wire [15:0] cfg_tx_lfc_quanta, + input wire [15:0] cfg_tx_lfc_refresh, + input wire [47:0] cfg_tx_pfc_eth_dst, + input wire [47:0] cfg_tx_pfc_eth_src, + input wire [15:0] cfg_tx_pfc_eth_type, + input wire [15:0] cfg_tx_pfc_opcode, + input wire cfg_tx_pfc_en, + input wire [8*16-1:0] cfg_tx_pfc_quanta, + input wire [8*16-1:0] cfg_tx_pfc_refresh, + input wire [9:0] cfg_quanta_step, + input wire cfg_quanta_clk_en, + + /* + * Status + */ + output wire stat_tx_lfc_pkt, + output wire stat_tx_lfc_xon, + output wire stat_tx_lfc_xoff, + output wire stat_tx_lfc_paused, + output wire stat_tx_pfc_pkt, + output wire [7:0] stat_tx_pfc_xon, + output wire [7:0] stat_tx_pfc_xoff, + output wire [7:0] stat_tx_pfc_paused +); + +localparam QFB = 8; + +// check configuration +initial begin + if (MCF_PARAMS_SIZE < (PFC_ENABLE ? 18 : 2)) begin + $error("Error: MCF_PARAMS_SIZE too small for requested configuration (instance %m)"); + $finish; + end +end + +reg lfc_req_reg = 1'b0, lfc_req_next; +reg lfc_act_reg = 1'b0, lfc_act_next; +reg lfc_send_reg = 1'b0, lfc_send_next; +reg [7:0] pfc_req_reg = 8'd0, pfc_req_next; +reg [7:0] pfc_act_reg = 8'd0, pfc_act_next; +reg [7:0] pfc_en_reg = 8'd0, pfc_en_next; +reg pfc_send_reg = 1'b0, pfc_send_next; + +reg [16+QFB-1:0] lfc_refresh_reg = 0, lfc_refresh_next; +reg [16+QFB-1:0] pfc_refresh_reg[0:7], pfc_refresh_next[0:7]; + +reg stat_tx_lfc_pkt_reg = 1'b0, stat_tx_lfc_pkt_next; +reg stat_tx_lfc_xon_reg = 1'b0, stat_tx_lfc_xon_next; +reg stat_tx_lfc_xoff_reg = 1'b0, stat_tx_lfc_xoff_next; +reg stat_tx_pfc_pkt_reg = 1'b0, stat_tx_pfc_pkt_next; +reg [7:0] stat_tx_pfc_xon_reg = 0, stat_tx_pfc_xon_next; +reg [7:0] stat_tx_pfc_xoff_reg = 0, stat_tx_pfc_xoff_next; + +// MAC control interface +reg mcf_pfc_sel_reg = PFC_ENABLE != 0, mcf_pfc_sel_next; +reg mcf_valid_reg = 1'b0, mcf_valid_next; + +wire [2*8-1:0] mcf_lfc_params; +assign mcf_lfc_params[16*0 +: 16] = lfc_req_reg ? {cfg_tx_lfc_quanta[0 +: 8], cfg_tx_lfc_quanta[8 +: 8]} : 0; + +wire [18*8-1:0] mcf_pfc_params; +assign mcf_pfc_params[16*0 +: 16] = {pfc_en_reg, 8'd0}; +assign mcf_pfc_params[16*1 +: 16] = pfc_req_reg[0] ? {cfg_tx_pfc_quanta[16*0+0 +: 8], cfg_tx_pfc_quanta[16*0+8 +: 8]} : 0; +assign mcf_pfc_params[16*2 +: 16] = pfc_req_reg[1] ? {cfg_tx_pfc_quanta[16*1+0 +: 8], cfg_tx_pfc_quanta[16*1+8 +: 8]} : 0; +assign mcf_pfc_params[16*3 +: 16] = pfc_req_reg[2] ? {cfg_tx_pfc_quanta[16*2+0 +: 8], cfg_tx_pfc_quanta[16*2+8 +: 8]} : 0; +assign mcf_pfc_params[16*4 +: 16] = pfc_req_reg[3] ? {cfg_tx_pfc_quanta[16*3+0 +: 8], cfg_tx_pfc_quanta[16*3+8 +: 8]} : 0; +assign mcf_pfc_params[16*5 +: 16] = pfc_req_reg[4] ? {cfg_tx_pfc_quanta[16*4+0 +: 8], cfg_tx_pfc_quanta[16*4+8 +: 8]} : 0; +assign mcf_pfc_params[16*6 +: 16] = pfc_req_reg[5] ? {cfg_tx_pfc_quanta[16*5+0 +: 8], cfg_tx_pfc_quanta[16*5+8 +: 8]} : 0; +assign mcf_pfc_params[16*7 +: 16] = pfc_req_reg[6] ? {cfg_tx_pfc_quanta[16*6+0 +: 8], cfg_tx_pfc_quanta[16*6+8 +: 8]} : 0; +assign mcf_pfc_params[16*8 +: 16] = pfc_req_reg[7] ? {cfg_tx_pfc_quanta[16*7+0 +: 8], cfg_tx_pfc_quanta[16*7+8 +: 8]} : 0; + +assign mcf_valid = mcf_valid_reg; +assign mcf_eth_dst = (PFC_ENABLE && mcf_pfc_sel_reg) ? cfg_tx_pfc_eth_dst : cfg_tx_lfc_eth_dst; +assign mcf_eth_src = (PFC_ENABLE && mcf_pfc_sel_reg) ? cfg_tx_pfc_eth_src : cfg_tx_lfc_eth_src; +assign mcf_eth_type = (PFC_ENABLE && mcf_pfc_sel_reg) ? cfg_tx_pfc_eth_type : cfg_tx_lfc_eth_type; +assign mcf_opcode = (PFC_ENABLE && mcf_pfc_sel_reg) ? cfg_tx_pfc_opcode : cfg_tx_lfc_opcode; +assign mcf_params = (PFC_ENABLE && mcf_pfc_sel_reg) ? mcf_pfc_params : mcf_lfc_params; + +assign stat_tx_lfc_pkt = stat_tx_lfc_pkt_reg; +assign stat_tx_lfc_xon = stat_tx_lfc_xon_reg; +assign stat_tx_lfc_xoff = stat_tx_lfc_xoff_reg; +assign stat_tx_lfc_paused = lfc_req_reg; +assign stat_tx_pfc_pkt = stat_tx_pfc_pkt_reg; +assign stat_tx_pfc_xon = stat_tx_pfc_xon_reg; +assign stat_tx_pfc_xoff = stat_tx_pfc_xoff_reg; +assign stat_tx_pfc_paused = pfc_req_reg; + +integer k; + +initial begin + for (k = 0; k < 8; k = k + 1) begin + pfc_refresh_reg[k] = 0; + end +end + +always @* begin + lfc_req_next = lfc_req_reg; + lfc_act_next = lfc_act_reg; + lfc_send_next = lfc_send_reg | tx_lfc_resend; + pfc_req_next = pfc_req_reg; + pfc_act_next = pfc_act_reg; + pfc_en_next = pfc_en_reg; + pfc_send_next = pfc_send_reg | tx_pfc_resend; + + mcf_pfc_sel_next = mcf_pfc_sel_reg; + mcf_valid_next = mcf_valid_reg && !mcf_ready; + + stat_tx_lfc_pkt_next = 1'b0; + stat_tx_lfc_xon_next = 1'b0; + stat_tx_lfc_xoff_next = 1'b0; + stat_tx_pfc_pkt_next = 1'b0; + stat_tx_pfc_xon_next = 0; + stat_tx_pfc_xoff_next = 0; + + if (cfg_quanta_clk_en) begin + if (lfc_refresh_reg > cfg_quanta_step) begin + lfc_refresh_next = lfc_refresh_reg - cfg_quanta_step; + end else begin + lfc_refresh_next = 0; + if (lfc_req_reg) begin + lfc_send_next = 1'b1; + end + end + end else begin + lfc_refresh_next = lfc_refresh_reg; + end + + for (k = 0; k < 8; k = k + 1) begin + if (cfg_quanta_clk_en) begin + if (pfc_refresh_reg[k] > cfg_quanta_step) begin + pfc_refresh_next[k] = pfc_refresh_reg[k] - cfg_quanta_step; + end else begin + pfc_refresh_next[k] = 0; + if (pfc_req_reg[k]) begin + pfc_send_next = 1'b1; + end + end + end else begin + pfc_refresh_next[k] = pfc_refresh_reg[k]; + end + end + + if (cfg_tx_lfc_en) begin + if (!mcf_valid_reg) begin + if (lfc_req_reg != tx_lfc_req) begin + lfc_req_next = tx_lfc_req; + lfc_act_next = lfc_act_reg | tx_lfc_req; + lfc_send_next = 1'b1; + end + + if (lfc_send_reg && !(PFC_ENABLE && cfg_tx_pfc_en && pfc_send_reg)) begin + mcf_pfc_sel_next = 1'b0; + mcf_valid_next = lfc_act_reg; + lfc_act_next = lfc_req_reg; + lfc_refresh_next = lfc_req_reg ? {cfg_tx_lfc_refresh, {QFB{1'b0}}} : 0; + lfc_send_next = 1'b0; + + stat_tx_lfc_pkt_next = lfc_act_reg; + stat_tx_lfc_xon_next = lfc_act_reg && !lfc_req_reg; + stat_tx_lfc_xoff_next = lfc_act_reg && lfc_req_reg; + end + end + end + + if (PFC_ENABLE && cfg_tx_pfc_en) begin + if (!mcf_valid_reg) begin + if (pfc_req_reg != tx_pfc_req) begin + pfc_req_next = tx_pfc_req; + pfc_act_next = pfc_act_reg | tx_pfc_req; + pfc_send_next = 1'b1; + end + + if (pfc_send_reg) begin + mcf_pfc_sel_next = 1'b1; + mcf_valid_next = pfc_act_reg != 0; + pfc_en_next = pfc_act_reg; + pfc_act_next = pfc_req_reg; + for (k = 0; k < 8; k = k + 1) begin + pfc_refresh_next[k] = pfc_req_reg[k] ? {cfg_tx_pfc_refresh[16*k +: 16], {QFB{1'b0}}} : 0; + end + pfc_send_next = 1'b0; + + stat_tx_pfc_pkt_next = pfc_act_reg != 0; + stat_tx_pfc_xon_next = pfc_act_reg & ~pfc_req_reg; + stat_tx_pfc_xoff_next = pfc_act_reg & pfc_req_reg; + end + end + end +end + +always @(posedge clk) begin + lfc_req_reg <= lfc_req_next; + lfc_act_reg <= lfc_act_next; + lfc_send_reg <= lfc_send_next; + pfc_req_reg <= pfc_req_next; + pfc_act_reg <= pfc_act_next; + pfc_en_reg <= pfc_en_next; + pfc_send_reg <= pfc_send_next; + + mcf_pfc_sel_reg <= mcf_pfc_sel_next; + mcf_valid_reg <= mcf_valid_next; + + lfc_refresh_reg <= lfc_refresh_next; + for (k = 0; k < 8; k = k + 1) begin + pfc_refresh_reg[k] <= pfc_refresh_next[k]; + end + + stat_tx_lfc_pkt_reg <= stat_tx_lfc_pkt_next; + stat_tx_lfc_xon_reg <= stat_tx_lfc_xon_next; + stat_tx_lfc_xoff_reg <= stat_tx_lfc_xoff_next; + stat_tx_pfc_pkt_reg <= stat_tx_pfc_pkt_next; + stat_tx_pfc_xon_reg <= stat_tx_pfc_xon_next; + stat_tx_pfc_xoff_reg <= stat_tx_pfc_xoff_next; + + if (rst) begin + lfc_req_reg <= 1'b0; + lfc_act_reg <= 1'b0; + lfc_send_reg <= 1'b0; + pfc_req_reg <= 0; + pfc_act_reg <= 0; + pfc_send_reg <= 0; + mcf_pfc_sel_reg <= PFC_ENABLE != 0; + mcf_valid_reg <= 1'b0; + lfc_refresh_reg <= 0; + for (k = 0; k < 8; k = k + 1) begin + pfc_refresh_reg[k] <= 0; + end + + stat_tx_lfc_pkt_reg <= 1'b0; + stat_tx_lfc_xon_reg <= 1'b0; + stat_tx_lfc_xoff_reg <= 1'b0; + stat_tx_pfc_pkt_reg <= 1'b0; + stat_tx_pfc_xon_reg <= 0; + stat_tx_pfc_xoff_reg <= 0; + end +end + +endmodule + +`resetall diff --git a/src/rvvi/mii_phy_if.sv b/src/rvvi/mii_phy_if.sv new file mode 100644 index 000000000..340d7ad24 --- /dev/null +++ b/src/rvvi/mii_phy_if.sv @@ -0,0 +1,140 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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 = 4'd0; +(* IOB = "TRUE" *) +reg phy_mii_tx_en_reg = 1'b0, phy_mii_tx_er_reg = 1'b0; + +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 + 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 + +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 = 4'hf; +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 = 4'hf; +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 + +`resetall diff --git a/src/rvvi/ssio_ddr_in.sv b/src/rvvi/ssio_ddr_in.sv new file mode 100644 index 000000000..5060f4d27 --- /dev/null +++ b/src/rvvi/ssio_ddr_in.sv @@ -0,0 +1,150 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * Generic source synchronous DDR input + */ +module ssio_ddr_in # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-6, 7-series + // Use BUFG for Virtex-5, Spartan-6, Ultrascale + parameter CLOCK_INPUT_STYLE = "BUFG", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q1, + output wire [WIDTH-1:0] output_q2 +); + +wire clk_int; +wire clk_io; + +generate + +if (TARGET == "XILINX") begin + + // use Xilinx clocking primitives + + if (CLOCK_INPUT_STYLE == "BUFG") begin + + // buffer RX clock + BUFG + clk_bufg ( + .I(input_clk), + .O(clk_int) + ); + + // pass through RX clock to logic and input buffers + assign clk_io = clk_int; + assign output_clk = clk_int; + + end else if (CLOCK_INPUT_STYLE == "BUFR") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to logic + BUFR #( + .BUFR_DIVIDE("BYPASS") + ) + clk_bufr ( + .I(clk_int), + .O(output_clk), + .CE(1'b1), + .CLR(1'b0) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end + +end else begin + + // pass through RX clock to input buffers + assign clk_io = input_clk; + + // pass through RX clock to logic + assign clk_int = input_clk; + assign output_clk = clk_int; + +end + +endgenerate + +iddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(WIDTH) +) +data_iddr_inst ( + .clk(clk_io), + .d(input_d), + .q1(output_q1), + .q2(output_q2) +); + +endmodule + +`resetall diff --git a/src/rvvi/ssio_sdr_in.sv b/src/rvvi/ssio_sdr_in.sv new file mode 100644 index 000000000..351cadc1d --- /dev/null +++ b/src/rvvi/ssio_sdr_in.sv @@ -0,0 +1,166 @@ +/* + +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 + +`resetall +`default_nettype none + +/* + * 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 = {WIDTH{1'b0}}; + +assign output_q = output_q_reg; + +always @(posedge clk_io) begin + output_q_reg <= input_d; +end + +endmodule + +`resetall