Cleanup in prep to merge the rvvi branch into main.

This commit is contained in:
Rose Thompson 2024-07-19 15:48:20 -05:00
parent ce2cc48642
commit 0d40b8c933
21 changed files with 10 additions and 6312 deletions

View file

@ -2,6 +2,7 @@ rtl/eth_mac_mii_fifo.sv
rtl/eth_mac_mii.sv
rtl/mii_phy_if.sv
rtl/ssio_ddr_in.sv
rtl/ssio_sdr_in.sv
rtl/eth_mac_1g.sv
rtl/axis_gmii_rx.sv
rtl/lfsr.sv

@ -1 +1 @@
Subproject commit 13c33ff1a82348691a40d78cf2bab10cbf4f76b2
Subproject commit 43990ab4fd0c8d34dbc1be5cd8d4f3ed3e33f853

View file

@ -32,30 +32,25 @@ set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe2]
connect_debug_port u_ila_0/probe2 [get_nets [list wallypipelinedsoc/core/InstrValidM ]]
create_debug_port u_ila_0 probe
set_property port_width 4 [get_debug_ports u_ila_0/probe3]
set_property port_width 1 [get_debug_ports u_ila_0/probe3]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe3]
connect_debug_port u_ila_0/probe3 [get_nets [list {ethernet/eth_mac_1g_mii_inst/mii_phy_if_inst/mac_mii_txd[0]} {ethernet/eth_mac_1g_mii_inst/mii_phy_if_inst/mac_mii_txd[1]} {ethernet/eth_mac_1g_mii_inst/mii_phy_if_inst/mac_mii_txd[2]} {ethernet/eth_mac_1g_mii_inst/mii_phy_if_inst/mac_mii_txd[3]} ]]
connect_debug_port u_ila_0/probe3 [get_nets [list {RvviAxiRlast}]]
create_debug_port u_ila_0 probe
set_property port_width 4 [get_debug_ports u_ila_0/probe4]
set_property port_width 1 [get_debug_ports u_ila_0/probe4]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe4]
connect_debug_port u_ila_0/probe4 [get_nets [list {packetizer/CurrState[0]} {packetizer/CurrState[1]} {packetizer/CurrState[2]} {packetizer/CurrState[3]}]]
connect_debug_port u_ila_0/probe4 [get_nets [list {RvviAxiRvalid}]]
create_debug_port u_ila_0 probe
set_property port_width 1 [get_debug_ports u_ila_0/probe5]
set_property port_width 4 [get_debug_ports u_ila_0/probe5]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe5]
connect_debug_port u_ila_0/probe5 [get_nets [list {ethernet/eth_mac_1g_mii_inst/mii_phy_if_inst/mac_mii_tx_en} ]]
connect_debug_port u_ila_0/probe5 [get_nets [list {packetizer/CurrState[0]} {packetizer/CurrState[1]} {packetizer/CurrState[2]} {packetizer/CurrState[3]}]]
create_debug_port u_ila_0 probe
set_property port_width 64 [get_debug_ports u_ila_0/probe6]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe6]
connect_debug_port u_ila_0/probe6 [get_nets [list {wallypipelinedsoc/core/PCM[0]} {wallypipelinedsoc/core/PCM[1]} {wallypipelinedsoc/core/PCM[2]} {wallypipelinedsoc/core/PCM[3]} {wallypipelinedsoc/core/PCM[4]} {wallypipelinedsoc/core/PCM[5]} {wallypipelinedsoc/core/PCM[6]} {wallypipelinedsoc/core/PCM[7]} {wallypipelinedsoc/core/PCM[8]} {wallypipelinedsoc/core/PCM[9]} {wallypipelinedsoc/core/PCM[10]} {wallypipelinedsoc/core/PCM[11]} {wallypipelinedsoc/core/PCM[12]} {wallypipelinedsoc/core/PCM[13]} {wallypipelinedsoc/core/PCM[14]} {wallypipelinedsoc/core/PCM[15]} {wallypipelinedsoc/core/PCM[16]} {wallypipelinedsoc/core/PCM[17]} {wallypipelinedsoc/core/PCM[18]} {wallypipelinedsoc/core/PCM[19]} {wallypipelinedsoc/core/PCM[20]} {wallypipelinedsoc/core/PCM[21]} {wallypipelinedsoc/core/PCM[22]} {wallypipelinedsoc/core/PCM[23]} {wallypipelinedsoc/core/PCM[24]} {wallypipelinedsoc/core/PCM[25]} {wallypipelinedsoc/core/PCM[26]} {wallypipelinedsoc/core/PCM[27]} {wallypipelinedsoc/core/PCM[28]} {wallypipelinedsoc/core/PCM[29]} {wallypipelinedsoc/core/PCM[30]} {wallypipelinedsoc/core/PCM[31]} {wallypipelinedsoc/core/PCM[32]} {wallypipelinedsoc/core/PCM[33]} {wallypipelinedsoc/core/PCM[34]} {wallypipelinedsoc/core/PCM[35]} {wallypipelinedsoc/core/PCM[36]} {wallypipelinedsoc/core/PCM[37]} {wallypipelinedsoc/core/PCM[38]} {wallypipelinedsoc/core/PCM[39]} {wallypipelinedsoc/core/PCM[40]} {wallypipelinedsoc/core/PCM[41]} {wallypipelinedsoc/core/PCM[42]} {wallypipelinedsoc/core/PCM[43]} {wallypipelinedsoc/core/PCM[44]} {wallypipelinedsoc/core/PCM[45]} {wallypipelinedsoc/core/PCM[46]} {wallypipelinedsoc/core/PCM[47]} {wallypipelinedsoc/core/PCM[48]} {wallypipelinedsoc/core/PCM[49]} {wallypipelinedsoc/core/PCM[50]} {wallypipelinedsoc/core/PCM[51]} {wallypipelinedsoc/core/PCM[52]} {wallypipelinedsoc/core/PCM[53]} {wallypipelinedsoc/core/PCM[54]} {wallypipelinedsoc/core/PCM[55]} {wallypipelinedsoc/core/PCM[56]} {wallypipelinedsoc/core/PCM[57]} {wallypipelinedsoc/core/PCM[58]} {wallypipelinedsoc/core/PCM[59]} {wallypipelinedsoc/core/PCM[60]} {wallypipelinedsoc/core/PCM[61]} {wallypipelinedsoc/core/PCM[62]} {wallypipelinedsoc/core/PCM[63]} ]]
#create_debug_port u_ila_0 probe
#set_property port_width 1 [get_debug_ports u_ila_0/probe7]
#set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe7]
#connect_debug_port u_ila_0/probe7 [get_nets [list {IlaTrigger} ]]
create_debug_port u_ila_0 probe
set_property port_width 1 [get_debug_ports u_ila_0/probe7]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe7]
@ -81,16 +76,6 @@ set_property port_width 3 [get_debug_ports u_ila_0/probe11]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe11]
connect_debug_port u_ila_0/probe11 [get_nets [list {triggergen/CurrState[0]} {triggergen/CurrState[1]} {triggergen/CurrState[2]}]]
create_debug_port u_ila_0 probe
set_property port_width 1 [get_debug_ports u_ila_0/probe12]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe12]
connect_debug_port u_ila_0/probe12 [get_nets [list {RvviAxiRlast}]]
create_debug_port u_ila_0 probe
set_property port_width 1 [get_debug_ports u_ila_0/probe13]
set_property PROBE_TYPE DATA_AND_TRIGGER [get_debug_ports u_ila_0/probe13]
connect_debug_port u_ila_0/probe13 [get_nets [list {RvviAxiRvalid}]]
# the debug hub has issues with the clocks from the mmcm so lets give up an connect to the 100Mhz input clock.
#connect_debug_port dbg_hub/clk [get_nets default_100mhz_clk]

View file

@ -51,6 +51,8 @@ PreProcessFiles:
$(MAKE) -C ../../sim deriv
rm -rf ../src/CopiedFiles_do_not_add_to_repo/
cp -r ../../src/ ../src/CopiedFiles_do_not_add_to_repo/
cp -r ../../addins/verilog-ethernet/*/*.sv ../src/CopiedFiles_do_not_add_to_repo/rvvi
cp -r ../../addins/verilog-ethernet/*/*/*/*.sv ../src/CopiedFiles_do_not_add_to_repo/rvvi
mkdir ../src/CopiedFiles_do_not_add_to_repo/config/
cp ../../config/deriv/fpga/config.vh ../src/CopiedFiles_do_not_add_to_repo/config/
./insert_debug_comment.sh

View file

@ -1,324 +0,0 @@
/*
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

View file

@ -1,909 +0,0 @@
/*
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

View file

@ -1,377 +0,0 @@
/*
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

View file

@ -1,357 +0,0 @@
/*
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

View file

@ -1,457 +0,0 @@
/*
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

View file

@ -1,408 +0,0 @@
/*
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

View file

@ -1,643 +0,0 @@
/*
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

View file

@ -1,175 +0,0 @@
/*
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

View file

@ -1,339 +0,0 @@
/*
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

View file

@ -1,446 +0,0 @@
/*
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

View file

@ -1,447 +0,0 @@
/*
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

View file

@ -1,420 +0,0 @@
/*
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

View file

@ -1,220 +0,0 @@
/*
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

View file

@ -1,312 +0,0 @@
/*
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

View file

@ -1,140 +0,0 @@
/*
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,
(* mark_debug = "true" *) input wire [3:0] mac_mii_txd,
(* mark_debug = "true" *) 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

View file

@ -1,150 +0,0 @@
/*
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

View file

@ -1,166 +0,0 @@
/*
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