fpga: x400: refactor 100GEth LBUS to AXI interface
Original-commit: a218a6a503e5e4baddb939affa57b5c960ffbc84
This commit is contained in:
@@ -1129,7 +1129,7 @@ module eth_100g #(
|
|||||||
lbus_t lbus_tx [3:0];
|
lbus_t lbus_tx [3:0];
|
||||||
logic lbus_tx_rdyout;
|
logic lbus_tx_rdyout;
|
||||||
|
|
||||||
eth_100g_lbus2axi #(.NUM_SEG(4)) lbus2axi (
|
eth_100g_lbus2axis lbus2axi (
|
||||||
.axis(eth100g_rx),
|
.axis(eth100g_rx),
|
||||||
.lbus_in(lbus_rx)
|
.lbus_in(lbus_rx)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
//
|
//
|
||||||
// Module: eth_100g_lbus2axi
|
// Module: eth_100g_lbus2axis
|
||||||
//
|
//
|
||||||
// Description:
|
// Description:
|
||||||
// Translate from lbus (xilinx segmented ifc) to
|
// Translate from lbus (xilinx segmented ifc) to
|
||||||
@@ -11,20 +11,16 @@
|
|||||||
//
|
//
|
||||||
// Built using example provided from Xilinx
|
// Built using example provided from Xilinx
|
||||||
//
|
//
|
||||||
// Parameters:
|
|
||||||
// - FIFO_DEPTH - FIFO will be 2** deep
|
|
||||||
// - NUM_SEG - Number of lbus segments coming in
|
|
||||||
//
|
|
||||||
// Notes on timing difficulty
|
// Notes on timing difficulty
|
||||||
// The path back to pop is challenged
|
// The path back to pop is challenged
|
||||||
// -LBUS is popped out of the FIFO (SRL read can be slow)
|
// -LBUS is popped out of the AXI flop
|
||||||
// -LBUS is rotated N to 1 Mux (N= number of segments) For 100g N=4
|
// -LBUS is rotated N to 1 Mux (N= number of segments) For 100g N=4
|
||||||
// -Find where EOP is (search for the first 1)
|
// -Find where EOP is (search for the first 1)
|
||||||
// -Unrotate the number of words and use that to calculate pop
|
// -Unrotate the number of words and use that to calculate pop
|
||||||
|
// -pop is used to update the AXI flop registers
|
||||||
//
|
//
|
||||||
// Fifo Output
|
// Fifo Output
|
||||||
// Data starts from the SRL and is indexed by the read pointer
|
// Data starts from the AXI flop
|
||||||
// Data_Valid comes from a comparison on fullness
|
|
||||||
// Invalid control is forced to zero (necessary for algorithm)
|
// Invalid control is forced to zero (necessary for algorithm)
|
||||||
// It's not necessary to force all the data to zero just the control plane.
|
// It's not necessary to force all the data to zero just the control plane.
|
||||||
//
|
//
|
||||||
@@ -35,16 +31,11 @@
|
|||||||
//
|
//
|
||||||
// eop is specifically inspected in a 4in,4out function to find a pseudo
|
// eop is specifically inspected in a 4in,4out function to find a pseudo
|
||||||
// one hot. this is unrotated along with enable, and combined with
|
// one hot. this is unrotated along with enable, and combined with
|
||||||
// datavalid to determine the next pop, which controls incrementing of the
|
// datavalid to determine the next pop.
|
||||||
// rd_pointer.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
module eth_100g_lbus2axis
|
||||||
import PkgEth100gLbus::*;
|
import PkgEth100gLbus::*;
|
||||||
|
|
||||||
module eth_100g_lbus2axi #(
|
|
||||||
parameter FIFO_DEPTH = 5,
|
|
||||||
parameter NUM_SEG = 4
|
|
||||||
)
|
|
||||||
(
|
(
|
||||||
|
|
||||||
// AXIS IF
|
// AXIS IF
|
||||||
@@ -68,8 +59,6 @@ module eth_100g_lbus2axi #(
|
|||||||
//FIFO Logic
|
//FIFO Logic
|
||||||
logic push;
|
logic push;
|
||||||
logic [NUM_SEG-1:0] pop;
|
logic [NUM_SEG-1:0] pop;
|
||||||
|
|
||||||
logic [NUM_SEG-1:0] full;
|
|
||||||
logic [NUM_SEG-1:0] empty;
|
logic [NUM_SEG-1:0] empty;
|
||||||
|
|
||||||
// always push the fifo on all lanes
|
// always push the fifo on all lanes
|
||||||
@@ -77,131 +66,62 @@ module eth_100g_lbus2axi #(
|
|||||||
|
|
||||||
// For each lane of incoming data place it into a separate FIFO
|
// For each lane of incoming data place it into a separate FIFO
|
||||||
generate
|
generate
|
||||||
genvar b1,gseg1;
|
|
||||||
begin : gen_seg_fifo
|
begin : gen_seg_fifo
|
||||||
for(gseg1 = 0; gseg1 < NUM_SEG; gseg1=gseg1+1) begin
|
for(genvar gseg1 = 0; gseg1 < NUM_SEG; gseg1=gseg1+1) begin
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
// INLINE FIFO
|
// INLINE FIFO
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
logic input_fifo_i_tready;
|
||||||
|
logic input_fifo_o_tvalid;
|
||||||
|
logic input_fifo_o_tready;
|
||||||
|
logic input_flop_o_tvalid;
|
||||||
|
lbus_t lbus_fifo;
|
||||||
|
|
||||||
// simulation error if we push a full fifo
|
// simulation error if we push a full fifo
|
||||||
|
// synopsys translate_off
|
||||||
always_comb begin
|
always_comb begin
|
||||||
if (push) begin
|
if (push) begin
|
||||||
assert (!full[gseg1]) else $error("Pushing full fifo!");
|
assert (input_fifo_i_tready) else $error("Pushing full fifo!");
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
// synopsys translate_on
|
||||||
|
|
||||||
// limit fanout to improve timing
|
// input FIFO used for storing data using SRLC32E primitives
|
||||||
(* max_fanout = 75 *) logic [4:0] a;
|
axi_fifo_short #(
|
||||||
|
.WIDTH($bits(lbus_in[gseg1]))
|
||||||
for (b1=0;b1<SEG_DATA_WIDTH;b1=b1+1) begin : gen_srl_data
|
) input_fifo (
|
||||||
SRLC32E srl_data(
|
.clk(axis.clk),
|
||||||
.Q(lbus_fout_p[gseg1].data[b1]), .Q31(),
|
.reset(axis.rst),
|
||||||
.A(a),
|
.clear(1'b0),
|
||||||
.CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].data[b1])
|
.i_tdata(lbus_in[gseg1]),
|
||||||
);
|
.i_tvalid(push),
|
||||||
end
|
.i_tready(input_fifo_i_tready),
|
||||||
for (b1=0;b1<SEG_MTY_WIDTH;b1=b1+1) begin : gen_srl_mty
|
.o_tdata(lbus_fifo),
|
||||||
SRLC32E srl_mty(
|
.o_tvalid(input_fifo_o_tvalid),
|
||||||
.Q(lbus_fout_p[gseg1].mty[b1]), .Q31(),
|
.o_tready(input_fifo_o_tready),
|
||||||
.A(a),
|
.space(),
|
||||||
.CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].mty[b1])
|
.occupied()
|
||||||
);
|
|
||||||
end
|
|
||||||
SRLC32E srl_err(
|
|
||||||
.Q(lbus_fout_p[gseg1].err), .Q31(),
|
|
||||||
.A(a),
|
|
||||||
.CE(push),.CLK(axis.clk),.D(lbus_in[gseg1].err)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// empty on prebuffer and SRL
|
// output flop attached to register to break critical timing paths
|
||||||
logic my_empty;
|
axi_fifo_flop2 #(
|
||||||
always @(posedge axis.clk)
|
.WIDTH($bits(lbus_in[gseg1]))
|
||||||
begin
|
) critical_fifo_flop (
|
||||||
if(axis.rst) begin
|
.clk(axis.clk),
|
||||||
a <= 0;
|
.reset(axis.rst),
|
||||||
my_empty <= 1;
|
.clear(1'b0),
|
||||||
full[gseg1] <= 0;
|
.i_tdata(lbus_fifo),
|
||||||
end else if(pop[gseg1] & ~push) begin
|
.i_tvalid(input_fifo_o_tvalid),
|
||||||
full[gseg1] <= 0;
|
.i_tready(input_fifo_o_tready),
|
||||||
if(a==0) begin
|
.o_tdata(lbus_fout_p[gseg1]),
|
||||||
my_empty <= 1;
|
.o_tvalid(input_flop_o_tvalid),
|
||||||
end else begin
|
.o_tready(pop[gseg1]),
|
||||||
a <= a - 1;
|
.space(),
|
||||||
end
|
.occupied()
|
||||||
end else if(push & ~pop[gseg1]) begin
|
);
|
||||||
my_empty <= 0;
|
|
||||||
if(~my_empty) begin
|
|
||||||
a <= a + 1;
|
|
||||||
end
|
|
||||||
if(a == 30) begin
|
|
||||||
full[gseg1] <= 1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// FIFO for time sensitive control signals. This creates a separate 31 deep fifo from
|
assign empty[gseg1] = ~input_flop_o_tvalid;
|
||||||
// DFF's on just 3 signals. The data signals continue to use an SRL to save space.
|
|
||||||
// The design bellow is a FIFO followed by a single DFF regsiter that is automatically
|
|
||||||
// prefilled when the FIFO has data.
|
|
||||||
logic [4:0] w_ptr,r_ptr,r_ptr_d,fullness;
|
|
||||||
logic [31:0] ena_mem, sop_mem, eop_mem;
|
|
||||||
|
|
||||||
// Final fifo stage after memory to remove address muxing from timing path
|
|
||||||
// this adds one clock of latency to empty flag as it will take 2 clocks to propagate
|
|
||||||
// into fifo.
|
|
||||||
|
|
||||||
//push critical timing signals to final flop. This adds 1 clock of latency on
|
|
||||||
// the final empty flag, but removes muxing of the memory elements
|
|
||||||
logic push_dff;
|
|
||||||
|
|
||||||
//using r_ptr_d to avoid extra latency in fullness change
|
|
||||||
always_comb begin
|
|
||||||
if (pop[gseg1]) begin
|
|
||||||
r_ptr_d = r_ptr+1;
|
|
||||||
end else begin
|
|
||||||
r_ptr_d = r_ptr;
|
|
||||||
end
|
|
||||||
fullness = w_ptr-r_ptr_d;
|
|
||||||
push_dff = (fullness != 0) & (pop[gseg1] | empty[gseg1]);
|
|
||||||
end
|
|
||||||
|
|
||||||
// speedier fifo implementation on these three control signals
|
|
||||||
// THE goal of this complexity is to have the outputs be a direct FF output
|
|
||||||
// instead of a muxed memory output.
|
|
||||||
always @(posedge axis.clk)
|
|
||||||
begin
|
|
||||||
if(axis.rst) begin
|
|
||||||
ena_mem <= '0;
|
|
||||||
sop_mem <= '0;
|
|
||||||
eop_mem <= '0;
|
|
||||||
lbus_fout_p[gseg1].ena <= 1'b0;
|
|
||||||
lbus_fout_p[gseg1].sop <= 1'b0;
|
|
||||||
lbus_fout_p[gseg1].eop <= 1'b0;
|
|
||||||
w_ptr <= 0;
|
|
||||||
r_ptr <= 0;
|
|
||||||
empty[gseg1] <= 1'b1;
|
|
||||||
end else begin
|
|
||||||
if(push) begin
|
|
||||||
ena_mem[w_ptr] <= lbus_in[gseg1].ena;
|
|
||||||
sop_mem[w_ptr] <= lbus_in[gseg1].sop;
|
|
||||||
eop_mem[w_ptr] <= lbus_in[gseg1].eop;
|
|
||||||
w_ptr <= w_ptr+1;
|
|
||||||
end
|
|
||||||
|
|
||||||
r_ptr <= r_ptr_d;
|
|
||||||
|
|
||||||
if (push_dff) begin
|
|
||||||
empty[gseg1] <= 1'b0;
|
|
||||||
lbus_fout_p[gseg1].ena <= ena_mem[r_ptr_d];
|
|
||||||
lbus_fout_p[gseg1].sop <= sop_mem[r_ptr_d];
|
|
||||||
lbus_fout_p[gseg1].eop <= eop_mem[r_ptr_d];
|
|
||||||
end else if (pop[gseg1]) begin
|
|
||||||
empty[gseg1] <= 1'b1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// clear the enables if this fifo segment is not valid
|
// clear the enables if this fifo segment is not valid
|
||||||
always_comb begin
|
always_comb begin
|
||||||
@@ -289,74 +209,26 @@ module eth_100g_lbus2axi #(
|
|||||||
////////////////// Calculate Pop ///////////
|
////////////////// Calculate Pop ///////////
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
// After rotation figure out how far till eop
|
// After rotation figure out how far till eop
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
// one-hot to thermometer code
|
|
||||||
// The goal is to find how far down till we reach the first eop
|
|
||||||
// This represents the bytes we will trasnfer this clock
|
|
||||||
//==========================================================================
|
|
||||||
// Xilinx example
|
|
||||||
// case (in_reqs)
|
|
||||||
// 4'b1000: onehot2thermo = 4'b1111;
|
|
||||||
//
|
|
||||||
// 4'b1100: onehot2thermo = 4'b0111;
|
|
||||||
// 4'b0100: onehot2thermo = 4'b0111;
|
|
||||||
//
|
|
||||||
// 4'b1110: onehot2thermo = 4'b0011;
|
|
||||||
// 4'b0110: onehot2thermo = 4'b0011;
|
|
||||||
// 4'b0010: onehot2thermo = 4'b0011;
|
|
||||||
//
|
|
||||||
// 4'b1111: onehot2thermo = 4'b0001;
|
|
||||||
// 4'b0111: onehot2thermo = 4'b0001;
|
|
||||||
// 4'b0011: onehot2thermo = 4'b0001;
|
|
||||||
// 4'b0001: onehot2thermo = 4'b0001;
|
|
||||||
//
|
|
||||||
// default: onehot2thermo = 4'b0000;
|
|
||||||
// endcase
|
|
||||||
logic [NUM_SEG-1:0] rot_xfer_now;
|
logic [NUM_SEG-1:0] rot_xfer_now;
|
||||||
logic [NUM_SEG-1:0] mask [NUM_SEG-1:0];
|
|
||||||
logic [NUM_SEG-1:0] m1hot [NUM_SEG-1:0];
|
|
||||||
logic [NUM_SEG-1:0] meop [NUM_SEG-1:0];
|
|
||||||
logic [NUM_SEG-1:0] match;
|
|
||||||
|
|
||||||
always_comb begin
|
|
||||||
rot_xfer_now = '0;
|
|
||||||
foreach (rot_eop[s]) begin
|
|
||||||
// The function
|
|
||||||
// XXX1=>0001
|
|
||||||
// XX10=>0011
|
|
||||||
// X100=>0111
|
|
||||||
// 1000=>1111
|
|
||||||
// MASK
|
|
||||||
// 2**(0+1)-1 = 0001
|
|
||||||
// 2**(1+1)-1 = 0011
|
|
||||||
// 2**(2+1)-1 = 0111
|
|
||||||
// 2**(3+1)-1 = 1111
|
|
||||||
mask[s] = 2**(s+1)-1; // Constant
|
|
||||||
// MASK
|
|
||||||
// 2**0 = 0001
|
|
||||||
// 2**1 = 0010
|
|
||||||
// 2**2 = 0100
|
|
||||||
// 2**3 = 1000
|
|
||||||
m1hot[s] = 2**s; // Constant
|
|
||||||
// Mask valid_eop
|
|
||||||
meop[s] = rot_eop & mask[s];
|
|
||||||
// compare against 1hot
|
|
||||||
match[s] = meop[s] == m1hot[s];
|
|
||||||
if (match[s]) begin
|
|
||||||
rot_xfer_now = mask[s];
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// unrotate the values and calculate pop
|
// unrotate the values and calculate pop
|
||||||
logic [NUM_SEG-1:0] xfer_now;
|
logic [NUM_SEG-1:0] xfer_now;
|
||||||
logic [NUM_SEG-1:0] filler_seg;
|
logic [NUM_SEG-1:0] filler_seg;
|
||||||
|
|
||||||
always_comb begin
|
always_comb begin
|
||||||
|
rot_xfer_now = '0;
|
||||||
|
if (eop) begin
|
||||||
|
for (int i = 0; i < NUM_SEG; i = i + 1) begin
|
||||||
|
rot_xfer_now[i] = '1;
|
||||||
|
if (rot_eop[i]) begin
|
||||||
|
break;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if (send_idle)
|
if (send_idle)
|
||||||
xfer_now = '0;
|
xfer_now = '0;
|
||||||
else if (no_eop | no_sop)
|
else if (no_eop)
|
||||||
xfer_now = '1;
|
xfer_now = '1;
|
||||||
else
|
else
|
||||||
// rotate left
|
// rotate left
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
// Description:
|
// Description:
|
||||||
//
|
//
|
||||||
// Testbench for eth_100g_lbus2axi.
|
// Testbench for eth_100g_lbus2axis.
|
||||||
//
|
//
|
||||||
|
|
||||||
module lbus_axi_tb #(
|
module lbus_axi_tb #(
|
||||||
@@ -52,7 +52,7 @@ module lbus_axi_tb #(
|
|||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
lbus_t lbus_in [NUM_SEG-1:0];
|
lbus_t lbus_in [NUM_SEG-1:0];
|
||||||
|
|
||||||
eth_100g_lbus2axi #(.FIFO_DEPTH(5),.NUM_SEG(NUM_SEG)) DUT (
|
eth_100g_lbus2axis DUT (
|
||||||
.axis(axis),
|
.axis(axis),
|
||||||
.lbus_in(lbus_in)
|
.lbus_in(lbus_in)
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user