fpga: x400: Add support for X410 motherboard FPGA

Co-authored-by: Andrew Moch <Andrew.Moch@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com>
Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com>
Co-authored-by: Max Köhler <max.koehler@ni.com>
Co-authored-by: Michael Auchter <michael.auchter@ni.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>
Co-authored-by: Hector Rubio <hrubio@ni.com>


Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
This commit is contained in:
Wade Fife
2021-06-10 11:56:58 -05:00
committed by Aaron Rossetto
co-authored by Andrew Moch Daniel Jepson Javier Valenzuela Joerg Hofrichter Kumaran Subramoniam Max Köhler Michael Auchter Paul Butler Hector Rubio
parent bfef20ea45
commit 61782b02d7
205 changed files with 299634 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
#
# Copyright 2021 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
#-------------------------------------------------
# Top-of-Makefile
#-------------------------------------------------
# Define BASE_DIR to point to the "top" dir
BASE_DIR = $(abspath ../../../../../top)
# Include viv_sim_preamble after defining BASE_DIR
include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
#-------------------------------------------------
# Design Specific
#-------------------------------------------------
# Include makefiles and sources for the DUT and its dependencies
include $(BASE_DIR)/../lib/axi4s_sv/Makefile.srcs
include $(BASE_DIR)/../lib/fifo/Makefile.srcs
# If you generate the Xilinx CORE with an AXI interface you can find Xilinx's LBUS translators here
#$(abspath ../../../build-ip/xczu28drffvg1517-1e/eth_100g_bd/eth_100g_bd/ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/eth_100g_bd_cmac_usplus_0_0_lbus2axis_segmented_top.v) \
#$(abspath ../../../build-ip/xczu28drffvg1517-1e/eth_100g_bd/eth_100g_bd/ip/eth_100g_bd_cmac_usplus_0_0/eth_100g_bd_cmac_usplus_0_0/example_design/eth_100g_bd_cmac_usplus_0_0_axis2lbus_segmented_top.v) \
DESIGN_SRCS = $(abspath \
$(abspath ../eth_100g_axis2lbus.sv) \
$(abspath ../eth_100g_lbus2axis.sv) \
$(FIFO_SRCS) \
$(AXI4S_SV_SRCS) \
)
#-------------------------------------------------
# Testbench Specific
#-------------------------------------------------
MODELSIM_LIBS += secureip unimacro_ver unisims_ver xilinx_vip xpm fifo_generator_v13_2_4
MODELSIM_ARGS += glbl -t 1fs
# Define toplevel module
TB_TOP_MODULE ?= lbus_all_tb
SIM_TOP = $(TB_TOP_MODULE)
SIM_SRCS = \
$(abspath ../PkgEth100gLbus.sv) \
$(abspath axi_lbus_tb.sv) \
$(abspath lbus_axi_tb.sv) \
$(abspath $(TB_TOP_MODULE).sv) \
$(VIVADO_PATH)/data/verilog/src/glbl.v \
# Suppressing the following worthless reminder.
#* Warning: M:/usrp4-hw/oss-repo/fpga/usrp3/lib/axi4s_sv/axi4s_remove_bytes.sv(228): (vlog-2583) [SVCHK] -
# Extra checking for conflicts with always_comb and always_latch variables is done at vopt time
SVLOG_ARGS = -suppress 2583 -keep_delta
VLOG_ARGS = -keep_delta
#-------------------------------------------------
# Bottom-of-Makefile
#-------------------------------------------------
# Include all simulator specific makefiles here
# Each should define a unique target to simulate
# e.g. xsim, vsim, etc and a common "clean" target
include $(BASE_DIR)/../tools/make/viv_simulator.mak
@@ -0,0 +1,285 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: axi_lbus_tb
//
// Description:
//
// Testbench for eth_interface
//
module axi_lbus_tb #(
parameter TEST_NAME = ""
)(
/* no IO */
);
// Include macros and time declarations for use with PkgTestExec
`define TEST_EXEC_OBJ test
`include "test_exec.svh"
import PkgAxiStreamBfm::*;
import PkgEthernet::*;
import PkgTestExec::*;
import PkgEth100gLbus::*;
localparam DATA_WIDTH = 512;
localparam USER_WIDTH = $clog2(DATA_WIDTH/8)+1;
localparam NUM_SEG = 4;
localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
localparam SEG_BYTES = SEG_DATA_WIDTH/8;
//----------------------------------------------------
// clocks
//----------------------------------------------------
logic clk;
logic rst;
sim_clock_gen #(.PERIOD(5), .AUTOSTART(1))
clk_gen (.clk(clk), .rst(rst));
//----------------------------------------------------
//interfaces
//----------------------------------------------------
AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
axis (clk, rst);
//----------------------------------------------------
// DUT
//----------------------------------------------------
lbus_t lbus_out [NUM_SEG-1:0];
lbus_t lbus_g [NUM_SEG-1:0];
logic lbus_rdy = 1;
eth_100g_axi2lbus #(.FIFO_DEPTH(5),.NUM_SEG(NUM_SEG)) DUT (
.axis(axis),
.lbus_rdy(lbus_rdy),
.lbus_out(lbus_out)
);
//----------------------------------------------------
// Xilinx golden model
// When Xilinx is generated with an AXI interface xilinx prints
// an axi2lbus converter that has trouble meeting timing
// this is preserved to allow comparison to that
// TODO: remove when timing is passing and refactor is done
//----------------------------------------------------
/*
eth_100g_bd_cmac_usplus_0_0_axis2lbus_segmented_top GOLD (
.core_clk(clk),
.core_rst(rst),
// AXIS IF
.axis_tvalid(axis.tvalid),
.axis_tready(),
.axis_tdata(axis.tdata),
.axis_tlast(axis.tlast),
.axis_tkeep(axis.tkeep),
.axis_tuser(1'b0),
// LBUS IF
.lbus_rdyout(lbus_rdy),
.lbus_ovfout(1'b0),
.lbus_unfout(1'b0),
// Segment 0
.lbus_ena0(lbus_g[0].ena),
.lbus_data0(lbus_g[0].data),
.lbus_sop0(lbus_g[0].sop),
.lbus_eop0(lbus_g[0].eop),
.lbus_mty0(lbus_g[0].mty),
.lbus_err0(lbus_g[0].err),
// Segment 1
.lbus_ena1(lbus_g[1].ena),
.lbus_data1(lbus_g[1].data),
.lbus_sop1(lbus_g[1].sop),
.lbus_eop1(lbus_g[1].eop),
.lbus_mty1(lbus_g[1].mty),
.lbus_err1(lbus_g[1].err),
// Segment 2
.lbus_ena2(lbus_g[2].ena),
.lbus_data2(lbus_g[2].data),
.lbus_sop2(lbus_g[2].sop),
.lbus_eop2(lbus_g[2].eop),
.lbus_mty2(lbus_g[2].mty),
.lbus_err2(lbus_g[2].err),
// Segment 3
.lbus_ena3(lbus_g[3].ena),
.lbus_data3(lbus_g[3].data),
.lbus_sop3(lbus_g[3].sop),
.lbus_eop3(lbus_g[3].eop),
.lbus_mty3(lbus_g[3].mty),
.lbus_err3(lbus_g[3].err)
);
*/
//----------------------------------------------------
//BFMS
//----------------------------------------------------
TestExec test = new();
AxiStreamBfm #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH)) axi =
new(.slave(null),.master(axis));
//---------------------------------------------------------------------------
// Tests
//---------------------------------------------------------------------------
// use Test timeout to check reset goes away
task test_reset();
test.start_test({TEST_NAME,"Wait for Reset"}, 10us);
wait(!rst);
repeat (10) @(posedge clk);
test.end_test();
endtask : test_reset
typedef AxiStreamPacket #(DATA_WIDTH,USER_WIDTH) AxisPacket_t;
typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
task automatic check_lbus(AxisPacket_t packets[$]);
int idle_insert = 0;
int byte_count = 0;
int first_clk = 1;
// loop over packets
foreach(packets[i]) begin
automatic raw_pkt_t pay;
pay = packets[i].dump_bytes;
first_clk=1;
while (pay.size() > 0) begin
@(posedge clk);
if (lbus_rdy) begin
if (lbus_out[0].ena) begin
for (int s=0; s < NUM_SEG; s++) begin
byte_count = 0;
for (int b = SEG_DATA_WIDTH/8-1; b >= 0; b--) begin
if (pay.size() > 0) begin
assert (lbus_out[s].data[b*8 +: 8] == pay.pop_front()) else $error("Data Mismatch");
byte_count++;
end
end
assert (lbus_out[s].ena == (byte_count > 0) ) else $error("Ena Mismatch");
if (lbus_out[s].ena) begin
if (first_clk && s==0) begin
assert (lbus_out[s].sop == 1) else $error("Sop not set");
end else begin
assert (lbus_out[s].sop == 0) else $error("Sop Set");
end
assert (lbus_out[s].mty == (SEG_BYTES-byte_count) % SEG_BYTES) else $error("Mty Mismatch");
assert (lbus_out[s].eop != (pay.size() > 0) ) else $error("Eop Mismatch");
assert (lbus_out[s].err == 0) else $error("Err Mismatch"); // not checking error yet
end else begin
assert (lbus_out[s].sop == 0) else $error("Sop Set while disabled");
assert (lbus_out[s].mty == 0) else $error("Mty set while disabled");
assert (lbus_out[s].eop == 0) else $error("Eop set while disabled");
assert (lbus_out[s].err == 0) else $error("Err set while disabled");
end
end // segment loop
first_clk=0;
end // first segment is enabled
end else begin //not lbus_rdy
for (int s=0; s < NUM_SEG; s++) begin
assert (lbus_out[s].ena == 0) else $error("Idle Ena set");
assert (lbus_out[s].err == 0) else $error("Idle Err set");
assert (lbus_out[s].sop == 0) else $error("Idle Sop set");
assert (lbus_out[s].eop == 0) else $error("Idle Eop set");
assert (lbus_out[s].mty == 0) else $error("Idle Mty non zero");
end
end
end // while pay.size > 0
end // foreach packet
endtask : check_lbus;
task automatic test_transfers(int num_samples[$]);
automatic AxisPacket_t send[$];
automatic AxisPacket_t expected[$];
automatic int sample_sum = 0;
test.start_test({TEST_NAME,"Test Transfer"}, 200us);
foreach (num_samples[i]) begin
automatic raw_pkt_t pay;
expected[i] = new;
send[i] = new;
get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256),
.ramp_inc(1),.pkt(pay),.SWIDTH(8));
sample_sum += num_samples[i];
send[i].push_bytes(pay);
// rebuild the expected packet for comparison without the preamble
expected[i].push_bytes(pay);
end
fork
begin // tx_thread
foreach (send[i]) begin
axi.put(send[i]);
end
end
begin //rx_thread
check_lbus(expected);
end
join
test.end_test();
endtask : test_transfers
//----------------------------------------------------
// Main test loop
//----------------------------------------------------
initial begin : tb_main
automatic int num_samples[$];
automatic int random_value;
clk_gen.reset();
// stalling is not allowed by whoever is reading the MAC
// i.e. ready is ignored
axi.set_master_stall_prob(0);
axi.run();
test_reset();
$display("Fixed Sequences");
num_samples = {64,65,66,67,68,69,70,71};
test_transfers(num_samples);
num_samples = {64,128,256,512,256,64,64,64};
test_transfers(num_samples);
num_samples = {65,64,64,64,64,64,64,64};
test_transfers(num_samples);
num_samples = {65,64,80,80,80,80,80,80};
test_transfers(num_samples);
num_samples = {64,64,96,80,96,80,96,80};
test_transfers(num_samples);
$display("Tons of 64");
num_samples.delete();
repeat(1000) begin
num_samples.push_back(64);
end
test_transfers(num_samples);
$display("Tons of 65");
num_samples.delete();
repeat(1000) begin
num_samples.push_back(65);
end
test_transfers(num_samples);
$display("Tons of Random");
num_samples.delete();
repeat(1000) begin
random_value = $urandom_range(64,512);
num_samples.push_back(random_value);
end
test_transfers(num_samples);
// End the TB, but don't $finish, since we don't want to kill other
// instances of this testbench that may be running.
test.end_tb(0);
// Kill the clocks to end this instance of the testbench
clk_gen.kill();
end // initial begin
endmodule
@@ -0,0 +1,22 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: lbus_all_tb
//
// Description:
//
// Testbench for LBU<->AXI
//
module lbus_all_tb #(
/* no PARAM */
)(
/* no IO */
);
lbus_axi_tb #(.TEST_NAME("L2A")) L2A ();
axi_lbus_tb #(.TEST_NAME("A2L")) A2L ();
endmodule
@@ -0,0 +1,343 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: lbus_axi_tb
//
// Description:
//
// Testbench for eth_interface
//
module lbus_axi_tb #(
parameter TEST_NAME = ""
)(
/* no IO */
);
// Include macros and time declarations for use with PkgTestExec
`define TEST_EXEC_OBJ test
`include "test_exec.svh"
import PkgAxiStreamBfm::*;
import PkgEthernet::*;
import PkgTestExec::*;
import PkgEth100gLbus::*;
localparam DATA_WIDTH = 512;
localparam USER_WIDTH = $clog2(DATA_WIDTH/8)+1;
localparam NUM_SEG = 4;
localparam SEG_DATA_WIDTH = DATA_WIDTH/NUM_SEG;
localparam SEG_BYTES = SEG_DATA_WIDTH/8;
//----------------------------------------------------
// clocks
//----------------------------------------------------
logic clk;
logic rst;
sim_clock_gen #(.PERIOD(5), .AUTOSTART(1))
clk_gen (.clk(clk), .rst(rst));
//----------------------------------------------------
//interfaces
//----------------------------------------------------
AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
axis (clk, rst);
AxiStreamIf #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH))
axig (clk, rst);
//----------------------------------------------------
// DUT
//----------------------------------------------------
lbus_t lbus_in [NUM_SEG-1:0];
eth_100g_lbus2axi #(.FIFO_DEPTH(5),.NUM_SEG(NUM_SEG)) DUT (
.axis(axis),
.lbus_in(lbus_in)
);
//----------------------------------------------------
// Xilinx golden model
// When Xilinx is generated with an AXI interface xilinx prints
// a lbus2axis converter that has trouble meeting timing
// this is preserved to allow comparison to that
// TODO: remove when timing is passing and refactor is done
//----------------------------------------------------
/*
eth_100g_bd_cmac_usplus_0_0_lbus2axis_segmented_top GOLD (
.core_clk(clk),
.core_rst(rst),
// AXIS IF
.axis_tvalid(axig.tvalid),
.axis_tdata(axig.tdata),
.axis_tlast(axig.tlast),
.axis_tkeep(axig.tkeep),
.axis_tuser(),
// Segment 0
.lbus_ena0(lbus_in[0].ena),
.lbus_data0(lbus_in[0].data),
.lbus_sop0(lbus_in[0].sop),
.lbus_eop0(lbus_in[0].eop),
.lbus_mty0(lbus_in[0].mty),
.lbus_err0(lbus_in[0].err),
// Segment 1
.lbus_ena1(lbus_in[1].ena),
.lbus_data1(lbus_in[1].data),
.lbus_sop1(lbus_in[1].sop),
.lbus_eop1(lbus_in[1].eop),
.lbus_mty1(lbus_in[1].mty),
.lbus_err1(lbus_in[1].err),
// Segment 2
.lbus_ena2(lbus_in[2].ena),
.lbus_data2(lbus_in[2].data),
.lbus_sop2(lbus_in[2].sop),
.lbus_eop2(lbus_in[2].eop),
.lbus_mty2(lbus_in[2].mty),
.lbus_err2(lbus_in[2].err),
// Segment 3
.lbus_ena3(lbus_in[3].ena),
.lbus_data3(lbus_in[3].data),
.lbus_sop3(lbus_in[3].sop),
.lbus_eop3(lbus_in[3].eop),
.lbus_mty3(lbus_in[3].mty),
.lbus_err3(lbus_in[3].err)
);
*/
//----------------------------------------------------
//BFMS
//----------------------------------------------------
TestExec test = new();
AxiStreamBfm #(.DATA_WIDTH(DATA_WIDTH),.USER_WIDTH(USER_WIDTH)) axi =
new(.slave(axis),.master(null));
//---------------------------------------------------------------------------
// Tests
//---------------------------------------------------------------------------
// use Test timeout to check reset goes away
task test_reset();
test.start_test({TEST_NAME,"Wait for Reset"}, 10us);
wait(!rst);
repeat (10) @(posedge clk);
test.end_test();
endtask : test_reset
typedef AxiStreamPacket #(DATA_WIDTH,USER_WIDTH) AxisPacket_t;
typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
task automatic clear_lbus_in();
for (int i=0 ; i < NUM_SEG ; ++i) begin
lbus_in[i].data = 'b0;
lbus_in[i].mty = 'b0;
lbus_in[i].sop = 1'b0;
lbus_in[i].eop = 1'b0;
lbus_in[i].err = 1'b0;
lbus_in[i].ena = 1'b0;
end
endtask : clear_lbus_in;
task automatic send_lbus(AxisPacket_t packets[$]);
int seg = 0;
int b = 0;
int idle_insert = 0;
// loop over packets
foreach(packets[i]) begin
automatic raw_pkt_t pay;
pay = packets[i].dump_bytes;
// set for the first segment
lbus_in[seg].sop = 1;
// empty this packets payload
while (pay.size() > 0) begin
lbus_in[seg].ena = 1;
lbus_in[seg].data |= pay.pop_front()<<(SEG_DATA_WIDTH-b*8-8);
if (pay.size() == 0) begin
lbus_in[seg].eop = 1;
lbus_in[seg].mty = SEG_DATA_WIDTH-1-b;
end
if (seg == NUM_SEG-1 && b == SEG_DATA_WIDTH/8-1) begin
@(posedge clk);
clear_lbus_in();
idle_insert++;
// insert period idle period
if (idle_insert > 2) begin
@(posedge clk);
idle_insert =0;
end
end
b = (b+1) % (SEG_DATA_WIDTH/8);
if (b==0) begin
seg = (seg+1) % NUM_SEG;
end
end // pay.size > 0
if (b != 0) begin
seg = (seg+1) % NUM_SEG;
if (seg == 0) begin
@(posedge clk);
clear_lbus_in();
idle_insert++;
// insert period idle period
if (idle_insert > 2) begin
@(posedge clk);
idle_insert =0;
end
end
b = 0;
end
// 25% of the time we start a new packet
if ($urandom_range(99) < 25) begin
b = 0;
seg = 0;
// 25% of the time add an idle cycle
if ($urandom_range(99) < 25) begin
@(posedge clk);
clear_lbus_in();
idle_insert++;
// insert period idle period
if (idle_insert > 2) begin
idle_insert =0;
@(posedge clk);
end
@(posedge clk);
end else begin
@(posedge clk);
clear_lbus_in();
idle_insert++;
// insert period idle period
if (idle_insert > 2) begin
idle_insert =0;
@(posedge clk);
end
end
end
end // foreach packet
@(posedge clk);
clear_lbus_in();
endtask : send_lbus;
task automatic compare_packet(AxisPacket_t actual, expected);
automatic XportPacket_t actual_copy = new();
automatic XportPacket_t expected_copy = new();
actual_copy.import_axis(actual);
expected_copy.import_axis(expected);
expected_copy.tkeep_to_tuser();
actual_copy.clear_unused_bytes();
if (!expected_copy.equal(actual_copy)) begin
$display("Expected");
expected_copy.print();
$display("Actual");
actual_copy.print();
if (!expected_copy.equal(actual_copy))
$error("ERROR :: packet mismatch");
end
endtask : compare_packet;
task automatic test_transfers(int num_samples[$]);
automatic AxisPacket_t send[$];
automatic AxisPacket_t expected[$];
automatic int sample_sum = 0;
test.start_test({TEST_NAME,"Test Transfer"}, 200us);
foreach (num_samples[i]) begin
automatic raw_pkt_t pay;
expected[i] = new;
send[i] = new;
get_ramp_raw_pkt(.num_samps(num_samples[i]),.ramp_start((sample_sum)%256),
.ramp_inc(1),.pkt(pay),.SWIDTH(8));
sample_sum += num_samples[i];
send[i].push_bytes(pay);
// rebuild the expected packet for comparison without the preamble
expected[i].push_bytes(pay);
end
fork
begin // tx_thread
send_lbus(send);
end
begin //rx_thread
foreach(expected[i]) begin
automatic AxisPacket_t actual;
axi.get(actual);
compare_packet(actual,expected[i]);
end
end
join
test.end_test();
endtask : test_transfers
//----------------------------------------------------
// Main test loop
//----------------------------------------------------
initial begin : tb_main
automatic int num_samples[$];
automatic int random_value;
clk_gen.reset();
// stalling is not allowed by whoever is reading the MAC
// i.e. ready is ignored
axi.slave_tready_init = 1'b1;
axi.set_master_stall_prob(0);
axi.set_slave_stall_prob(0);
axi.run();
clear_lbus_in();
test_reset();
$display("Fixed Sequences");
num_samples = {64,65,66,67,68,69,70,71};
test_transfers(num_samples);
num_samples = {64,128,256,512,256,64,64,64};
test_transfers(num_samples);
num_samples = {65,64,64,64,64,64,64,64};
test_transfers(num_samples);
num_samples = {65,64,80,80,80,80,80,80};
test_transfers(num_samples);
num_samples = {64,64,96,80,96,80,96,80};
test_transfers(num_samples);
$display("Tons of 64");
num_samples.delete();
repeat(1000) begin
num_samples.push_back(64);
end
test_transfers(num_samples);
$display("Tons of 65");
num_samples.delete();
repeat(1000) begin
num_samples.push_back(65);
end
test_transfers(num_samples);
$display("Tons of Random");
num_samples.delete();
repeat(1000) begin
random_value = $urandom_range(64,512);
num_samples.push_back(random_value);
end
test_transfers(num_samples);
// End the TB, but don't $finish, since we don't want to kill other
// instances of this testbench that may be running.
test.end_tb(0);
// Kill the clocks to end this instance of the testbench
clk_gen.kill();
end // initial begin
endmodule