fpga: rfnoc: Add support for 512-bit CHDR widths

This fixes the rfnoc_null_src_sink, chdr_crossbar_nxn, and
chdr_stream_endpoint blocks so that wider CHDR widths are properly
supported. It also updates PkgChdrBfm to able to properly test these
blocks. The testbenches have been updated to test both 64 and 512-bit
widths.


Original-commit: 3af8dcaacfa4bf36dcae3bdbf0b353385b7063c6
This commit is contained in:
Andrew Moch
2020-06-18 09:09:34 -05:00
committed by Wade Fife
parent 8875582763
commit 55881469ac
19 changed files with 599 additions and 420 deletions
@@ -29,10 +29,11 @@ $(RFNOC_OOT_SRCS) \
#-------------------------------------------------
# Testbench Specific
#-------------------------------------------------
SIM_TOP = rfnoc_block_null_src_sink_tb
SIM_TOP = rfnoc_block_null_src_sink_all_tb
SIM_SRCS = \
$(abspath rfnoc_block_null_src_sink_tb.sv) \
$(abspath rfnoc_block_null_src_sink_all_tb.sv) \
# MODELSIM_USER_DO = $(abspath wave.do)
@@ -23,6 +23,8 @@
module noc_shell_null_src_sink #(
parameter [9:0] THIS_PORTID = 10'd0,
parameter CHDR_W = 64,
parameter ITEM_W = 32,
parameter NIPC = 2,
parameter [5:0] MTU = 10
) (
//---------------------
@@ -82,8 +84,8 @@ module noc_shell_null_src_sink #(
output wire axis_data_clk,
output wire axis_data_rst,
// Payload Stream to User Logic: sink
output wire [32*2-1:0] m_sink_payload_tdata,
output wire [2-1:0] m_sink_payload_tkeep,
output wire [ITEM_W*NIPC-1:0] m_sink_payload_tdata,
output wire [NIPC-1:0] m_sink_payload_tkeep,
output wire m_sink_payload_tlast,
output wire m_sink_payload_tvalid,
input wire m_sink_payload_tready,
@@ -94,8 +96,8 @@ module noc_shell_null_src_sink #(
output wire m_sink_context_tvalid,
input wire m_sink_context_tready,
// Payload Stream to User Logic: loop
output wire [32*2-1:0] m_loop_payload_tdata,
output wire [2-1:0] m_loop_payload_tkeep,
output wire [ITEM_W*NIPC-1:0] m_loop_payload_tdata,
output wire [NIPC-1:0] m_loop_payload_tkeep,
output wire m_loop_payload_tlast,
output wire m_loop_payload_tvalid,
input wire m_loop_payload_tready,
@@ -106,8 +108,8 @@ module noc_shell_null_src_sink #(
output wire m_loop_context_tvalid,
input wire m_loop_context_tready,
// Payload Stream from User Logic: source
input wire [32*2-1:0] s_source_payload_tdata,
input wire [1:0] s_source_payload_tkeep,
input wire [ITEM_W*NIPC-1:0] s_source_payload_tdata,
input wire [NIPC-1:0] s_source_payload_tkeep,
input wire s_source_payload_tlast,
input wire s_source_payload_tvalid,
output wire s_source_payload_tready,
@@ -118,8 +120,8 @@ module noc_shell_null_src_sink #(
input wire s_source_context_tvalid,
output wire s_source_context_tready,
// Payload Stream from User Logic: loop
input wire [32*2-1:0] s_loop_payload_tdata,
input wire [1:0] s_loop_payload_tkeep,
input wire [ITEM_W*NIPC-1:0] s_loop_payload_tdata,
input wire [NIPC-1:0] s_loop_payload_tkeep,
input wire s_loop_payload_tlast,
input wire s_loop_payload_tvalid,
output wire s_loop_payload_tready,
@@ -233,8 +235,8 @@ module noc_shell_null_src_sink #(
chdr_to_axis_pyld_ctxt #(
.CHDR_W (CHDR_W),
.ITEM_W (32),
.NIPC (2),
.ITEM_W (ITEM_W),
.NIPC (NIPC),
.SYNC_CLKS (1),
.CONTEXT_FIFO_SIZE ($clog2(2)),
.PAYLOAD_FIFO_SIZE ($clog2(2)),
@@ -266,8 +268,8 @@ module noc_shell_null_src_sink #(
chdr_to_axis_pyld_ctxt #(
.CHDR_W (CHDR_W),
.ITEM_W (32),
.NIPC (2),
.ITEM_W (ITEM_W),
.NIPC (NIPC),
.SYNC_CLKS (1),
.CONTEXT_FIFO_SIZE ($clog2(2)),
.PAYLOAD_FIFO_SIZE ($clog2(2)),
@@ -303,8 +305,8 @@ module noc_shell_null_src_sink #(
axis_pyld_ctxt_to_chdr #(
.CHDR_W (CHDR_W),
.ITEM_W (32),
.NIPC (2),
.ITEM_W (ITEM_W),
.NIPC (NIPC),
.SYNC_CLKS (1),
.CONTEXT_FIFO_SIZE ($clog2(2)),
.PAYLOAD_FIFO_SIZE ($clog2(2)),
@@ -338,8 +340,8 @@ module noc_shell_null_src_sink #(
axis_pyld_ctxt_to_chdr #(
.CHDR_W (CHDR_W),
.ITEM_W (32),
.NIPC (2),
.ITEM_W (ITEM_W),
.NIPC (NIPC),
.SYNC_CLKS (1),
.CONTEXT_FIFO_SIZE ($clog2(2)),
.PAYLOAD_FIFO_SIZE ($clog2(2)),
@@ -4,17 +4,33 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rfnoc_block_null_src_sink
//
// Description:
//
// This block can source, sink, or loopback data. Each port is used for a
// specific purpose. The RFNoC CHDR ports are mapped is as follows:
//
// Input Port 0 : Sink
// Input Port 1 : Loopback in
// Output Port 0 : Source
// Output Port 1 : Loopback out
//
// Parameters:
// THIS_PORTID : Control crossbar port to which this block is connected
// CHDR_W : AXIS-CHDR data bus width
// MTU : Maximum transmission unit (i.e., maximum packet size in
// CHDR words is 2**MTU).
// ITEM_W : Item width
// NIPC : Items per Clock
//
// Signals:
module rfnoc_block_null_src_sink #(
parameter [9:0] THIS_PORTID = 10'd0,
parameter CHDR_W = 64,
parameter NIPC = 2,
parameter [5:0] MTU = 10
parameter [5:0] MTU = 10,
parameter ITEM_W = 32,
parameter NIPC = CHDR_W/ITEM_W
)(
// RFNoC Framework Clocks and Resets
input wire rfnoc_chdr_clk,
@@ -72,7 +88,7 @@ module rfnoc_block_null_src_sink #(
reg ctrlport_resp_ack;
reg [31:0] ctrlport_resp_data;
wire [(32*NIPC)-1:0] src_pyld_tdata , loop_pyld_tdata ;
wire [(ITEM_W*NIPC)-1:0] src_pyld_tdata , loop_pyld_tdata ;
wire [NIPC-1:0] src_pyld_tkeep , loop_pyld_tkeep ;
wire src_pyld_tlast , snk_pyld_tlast , loop_pyld_tlast ;
wire src_pyld_tvalid, snk_pyld_tvalid, loop_pyld_tvalid;
@@ -88,6 +104,8 @@ module rfnoc_block_null_src_sink #(
// ---------------------------
noc_shell_null_src_sink #(
.THIS_PORTID (THIS_PORTID),
.NIPC (NIPC),
.ITEM_W (ITEM_W),
.CHDR_W (CHDR_W),
.MTU (MTU)
) noc_shell_null_src_sink_i (
@@ -257,18 +275,17 @@ module rfnoc_block_null_src_sink #(
end
end
assign src_pyld_tdata = {NIPC{{~src_line_cnt[15:0], src_line_cnt[15:0]}}};
assign src_pyld_tdata = {NIPC{{~src_line_cnt[ITEM_W/2-1:0], src_line_cnt[ITEM_W/2-1:0]}}};
assign src_pyld_tkeep = {NIPC{1'b1}};
assign src_pyld_tlast = (lines_left == 12'd0);
assign src_pyld_tvalid = (state == ST_PYLD);
assign src_ctxt_tdata = chdr_build_header(
6'd0, 1'b0, 1'b0, CHDR_PKT_TYPE_DATA, CHDR_NO_MDATA, src_pkt_cnt[15:0], reg_src_bpp, 16'd0);
assign src_ctxt_tuser = CONTEXT_FIELD_HDR;
assign src_ctxt_tuser = CHDR_W > 64 ? CONTEXT_FIELD_HDR_TS : CONTEXT_FIELD_HDR;
assign src_ctxt_tlast = 1'b1;
assign src_ctxt_tvalid = (state == ST_HDR && reg_src_en);
// Register Interface
// ---------------------------
always @(posedge rfnoc_chdr_clk) begin
@@ -294,7 +311,7 @@ module rfnoc_block_null_src_sink #(
if (ctrlport_req_rd) begin
case(ctrlport_req_addr)
REG_CTRL_STATUS:
ctrlport_resp_data <= {NIPC[7:0], 8'd32, state, 12'h0, reg_src_en, reg_clear_cnts};
ctrlport_resp_data <= {NIPC[7:0],ITEM_W[7:0], state, 12'h0, reg_src_en, reg_clear_cnts};
REG_SRC_LINES_PER_PKT:
ctrlport_resp_data <= {20'h0, reg_src_lpp};
REG_SRC_BYTES_PER_PKT:
@@ -0,0 +1,26 @@
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: chdr_stream_endpoint_all_tb
//
// Description: Testbench for chdr_stream_endpoint that runs multiple widths
//
module rfnoc_block_null_src_sink_all_tb#(
/* no PARAM */
)(
/* no IO */
);
rfnoc_block_null_src_sink_tb #(.TEST_NAME("64B"),.CHDR_W(64)) CHDR64 ();
rfnoc_block_null_src_sink_tb #(.TEST_NAME("512B"),.CHDR_W(512)) CHDR512 ();
// Wait for all done
bit clk,rst;
sim_clock_gen #(100.0) clk_gen (clk, rst);
always_ff@(posedge clk)
if (CHDR64.test.done && CHDR512.test.done) $finish(1);
endmodule
@@ -1,5 +1,5 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
// Copyright 2020 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
@@ -9,9 +9,15 @@
`default_nettype none
module rfnoc_block_null_src_sink_tb;
module rfnoc_block_null_src_sink_tb #(
parameter TEST_NAME = "rfnoc_block_null_src_sink_tb",
parameter CHDR_W = 64
)(
/* no IO */
);
// Include macros and time declarations for use with PkgTestExec
`define TEST_EXEC_OBJ test
`include "test_exec.svh"
import PkgTestExec::*;
@@ -23,10 +29,10 @@ module rfnoc_block_null_src_sink_tb;
localparam NOC_ID = 32'h0000_0001;
localparam [9:0] THIS_PORTID = 10'h17;
localparam [15:0] THIS_EPID = 16'hDEAD;
localparam int CHDR_W = 64;
localparam int ITEM_W = 32;
localparam int NIPC = CHDR_W/ITEM_W; // Expected Data generation only works for full words
localparam int SPP = 201;
localparam int LPP = ((SPP+1)/2);
localparam int LPP = SPP % NIPC == 0 ? SPP/NIPC : SPP/NIPC+1;
localparam int NUM_PKTS = 50;
localparam int PORT_SRCSNK = 0;
@@ -49,6 +55,8 @@ module rfnoc_block_null_src_sink_tb;
AxiStreamIf #(CHDR_W) s0_chdr (rfnoc_chdr_clk); // Optional data iface
AxiStreamIf #(CHDR_W) s1_chdr (rfnoc_chdr_clk); // Optional data iface
TestExec test = new();
typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t;
// Bus functional model for a software block controller
@@ -58,29 +66,30 @@ module rfnoc_block_null_src_sink_tb;
rfnoc_block_null_src_sink #(
.THIS_PORTID (THIS_PORTID),
.CHDR_W (CHDR_W),
.NIPC (2),
.ITEM_W (ITEM_W),
.NIPC (NIPC),
.MTU (10)
) dut (
.rfnoc_chdr_clk (backend.chdr_clk),
.rfnoc_ctrl_clk (backend.ctrl_clk),
.rfnoc_core_config (backend.slave.cfg),
.rfnoc_core_status (backend.slave.sts),
.s_rfnoc_chdr_tdata ({m1_chdr.slave.tdata , m0_chdr.slave.tdata }),
.s_rfnoc_chdr_tlast ({m1_chdr.slave.tlast , m0_chdr.slave.tlast }),
.s_rfnoc_chdr_tvalid({m1_chdr.slave.tvalid , m0_chdr.slave.tvalid }),
.s_rfnoc_chdr_tready({m1_chdr.slave.tready , m0_chdr.slave.tready }),
.m_rfnoc_chdr_tdata ({s1_chdr.master.tdata , s0_chdr.master.tdata }),
.m_rfnoc_chdr_tlast ({s1_chdr.master.tlast , s0_chdr.master.tlast }),
.m_rfnoc_chdr_tvalid({s1_chdr.master.tvalid, s0_chdr.master.tvalid}),
.m_rfnoc_chdr_tready({s1_chdr.master.tready, s0_chdr.master.tready}),
.s_rfnoc_ctrl_tdata (m_ctrl.slave.tdata ),
.s_rfnoc_ctrl_tlast (m_ctrl.slave.tlast ),
.s_rfnoc_ctrl_tvalid(m_ctrl.slave.tvalid ),
.s_rfnoc_ctrl_tready(m_ctrl.slave.tready ),
.m_rfnoc_ctrl_tdata (s_ctrl.master.tdata ),
.m_rfnoc_ctrl_tlast (s_ctrl.master.tlast ),
.m_rfnoc_ctrl_tvalid(s_ctrl.master.tvalid),
.m_rfnoc_ctrl_tready(s_ctrl.master.tready)
.rfnoc_core_config (backend.cfg),
.rfnoc_core_status (backend.sts),
.s_rfnoc_chdr_tdata ({m1_chdr.tdata , m0_chdr.tdata }),
.s_rfnoc_chdr_tlast ({m1_chdr.tlast , m0_chdr.tlast }),
.s_rfnoc_chdr_tvalid({m1_chdr.tvalid , m0_chdr.tvalid }),
.s_rfnoc_chdr_tready({m1_chdr.tready , m0_chdr.tready }),
.m_rfnoc_chdr_tdata ({s1_chdr.tdata , s0_chdr.tdata }),
.m_rfnoc_chdr_tlast ({s1_chdr.tlast , s0_chdr.tlast }),
.m_rfnoc_chdr_tvalid({s1_chdr.tvalid, s0_chdr.tvalid}),
.m_rfnoc_chdr_tready({s1_chdr.tready, s0_chdr.tready}),
.s_rfnoc_ctrl_tdata (m_ctrl.tdata ),
.s_rfnoc_ctrl_tlast (m_ctrl.tlast ),
.s_rfnoc_ctrl_tvalid(m_ctrl.tvalid ),
.s_rfnoc_ctrl_tready(m_ctrl.tready ),
.m_rfnoc_ctrl_tdata (s_ctrl.tdata ),
.m_rfnoc_ctrl_tlast (s_ctrl.tlast ),
.m_rfnoc_ctrl_tvalid(s_ctrl.tvalid),
.m_rfnoc_ctrl_tready(s_ctrl.tready)
);
// ----------------------------------------
@@ -95,7 +104,7 @@ module rfnoc_block_null_src_sink_tb;
// Initialize
// ----------------------------------------
test.start_tb("rfnoc_block_null_src_sink_tb");
test.start_tb({TEST_NAME,"rfnoc_block_null_src_sink_tb"});
// Start the stream endpoint BFM
blk_ctrl = new(backend, m_ctrl, s_ctrl);
@@ -107,7 +116,7 @@ module rfnoc_block_null_src_sink_tb;
// Startup block (Software initialization)
// ----------------------------------------
test.start_test("Flush block then reset it");
test.start_test({TEST_NAME,"Flush block then reset it"});
begin
test.start_timeout(timeout, 10us, "Waiting for flush_and_reset");
#100; //Wait for GSR to deassert
@@ -118,7 +127,7 @@ module rfnoc_block_null_src_sink_tb;
// Run Tests
// ----------------------------------------
test.start_test("Read Block Info");
test.start_test({TEST_NAME,"Read Block Info"});
begin
test.start_timeout(timeout, 1us, "Waiting for block info response");
// Get static block info and validate it
@@ -130,19 +139,20 @@ module rfnoc_block_null_src_sink_tb;
// Read status register and validate it
blk_ctrl.reg_read(dut.REG_CTRL_STATUS, rvalue);
`ASSERT_ERROR(rvalue[31:24] == 2, "Incorrect NIPC Value");
`ASSERT_ERROR(rvalue[31:24] == NIPC, "Incorrect NIPC Value");
`ASSERT_ERROR(rvalue[23:16] == ITEM_W, "Incorrect ITEM_W Value");
test.end_timeout(timeout);
end
test.end_test();
test.start_test("Stream Data Through Loopback Port");
test.start_test({TEST_NAME,"Stream Data Through Loopback Port m1->s1"});
begin
// Send and receive packets
repeat (NUM_PKTS) begin
chdr_word_t rx_data[$];
int rx_bytes;
automatic ItemDataBuff #(logic[ITEM_W-1:0]) tx_dbuff = new, rx_dbuff = new;
automatic ItemDataBuff #(logic[ITEM_W-1:0],CHDR_W) tx_dbuff = new;
automatic ItemDataBuff #(logic[ITEM_W-1:0],CHDR_W) rx_dbuff = new;
for (int i = 0; i < SPP; i++)
tx_dbuff.put($urandom());
test.start_timeout(timeout, 5us, "Waiting for pkt to loop back");
@@ -173,16 +183,16 @@ module rfnoc_block_null_src_sink_tb;
end
test.end_test();
test.start_test("Stream Data To Sink Port");
test.start_test({TEST_NAME,"Stream Data To Sink Port m0"});
begin
// Send packets
repeat (NUM_PKTS) begin
chdr_word_t rx_data[$];
int rx_bytes;
automatic ItemDataBuff #(logic[ITEM_W-1:0]) tx_dbuff = new;
automatic ItemDataBuff #(logic[ITEM_W-1:0],CHDR_W) tx_dbuff = new;
for (int i = 0; i < SPP; i++)
tx_dbuff.put($urandom());
test.start_timeout(timeout, 5us, "Waiting for pkt to loop back");
test.start_timeout(timeout, 5us, "Waiting to send packet");
blk_ctrl.send(PORT_SRCSNK, tx_dbuff.to_chdr_payload(), tx_dbuff.get_bytes());
test.end_timeout(timeout);
end
@@ -208,11 +218,12 @@ module rfnoc_block_null_src_sink_tb;
end
test.end_test();
test.start_test("Stream Data From Source Port");
test.start_test({TEST_NAME,"Stream Data From Source Port s0"});
begin
// Turn on the source for some time then stop it
blk_ctrl.reg_write(dut.REG_SRC_LINES_PER_PKT, LPP-1);
blk_ctrl.reg_write(dut.REG_SRC_BYTES_PER_PKT, (LPP+1)*8);
// A line is generated as NIPC Items
blk_ctrl.reg_write(dut.REG_SRC_BYTES_PER_PKT, (LPP+1)*ITEM_W/8*NIPC);
blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b10);
repeat ((NUM_PKTS / 10) * LPP) @(posedge rfnoc_chdr_clk);
blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b00);
@@ -228,7 +239,7 @@ module rfnoc_block_null_src_sink_tb;
test.start_timeout(timeout, 5us, "Waiting for pkt to arrive");
exp_data.delete();
for (int i = p*LPP; i < (p+1)*LPP; i++)
exp_data.push_back({~i[15:0], i[15:0], ~i[15:0], i[15:0]});
exp_data.push_back({NIPC{{~i[ITEM_W/2-1:0], i[ITEM_W/2-1:0]}}});
blk_ctrl.recv(PORT_SRCSNK, rx_data, rx_bytes);
`ASSERT_ERROR(blk_ctrl.compare_data(exp_data, rx_data), "Data mismatch");
test.end_timeout(timeout);
@@ -236,7 +247,7 @@ module rfnoc_block_null_src_sink_tb;
end
test.end_test();
test.start_test("Clear Counts");
test.start_test({TEST_NAME,"Clear Counts"});
begin
test.start_timeout(timeout, 1us, "Waiting for clear and readbacks");
// Clear
@@ -266,7 +277,7 @@ module rfnoc_block_null_src_sink_tb;
// Finish Up
// ----------------------------------------
// Display final statistics and results
test.end_tb();
test.end_tb(.finish(0));
end
endmodule
+2 -2
View File
@@ -411,7 +411,7 @@ module chdr_stream_output #(
if (CHDR_W < 128)
state <= ST_STRC_W1;
else
state <= ST_STRC_WAIT;
state <= fc_resync_req ? ST_PASS_DATA : ST_STRC_WAIT;
end
// ST_STRC_W1
@@ -527,7 +527,7 @@ module chdr_stream_output #(
assign msg_o_tready = msg_o_tvalid && (state == ST_PASS_DATA || state == ST_STRC_WAIT);
// Acknowledge a flow control resync command
assign fc_resync_ack = fc_resync_req && (state == ST_STRC_W1) &&
assign fc_resync_ack = fc_resync_req && (state == ST_STRC_W1 || state == ST_STRC_W0) &&
chdr_out_tvalid && chdr_out_tready && chdr_out_tlast;
// ---------------------------------------------------
+26 -23
View File
@@ -238,7 +238,6 @@ module chdr_to_axis_ctrl #(
end else if (ct2ch_tvalid && ct2ch_tready) begin
case (ct2ch_state)
ST_CHDR_HDR: begin
if (!ct2ch_tlast)
ct2ch_state <= ST_CTRL_HDR;
end
ST_CTRL_HDR: begin
@@ -264,30 +263,27 @@ module chdr_to_axis_ctrl #(
// Hold the first line to generate info for the outgoing CHDR header
assign ct2ch_wctrl_tready = (ct2ch_state == ST_CTRL_HDR || ct2ch_state == ST_CTRL_BODY) ? ct2ch_tready : 1'b0;
wire [7:0] ct2ch_32bit_lines = 8'd3 + // Header + OpWord
(axis_ctrl_get_has_time(ct2ch_wctrl_tdata[31:0]) ? 8'd2 : 8'd0) + // Timestamp
({4'h0, axis_ctrl_get_num_data(ct2ch_wctrl_tdata[31:0])}); // Data words
wire [7:0] ct2ch_num_data = {4'h0, axis_ctrl_get_num_data(ct2ch_wctrl_tdata[31:0])};
wire [7:0] ct2ch_timestamp = axis_ctrl_get_has_time(ct2ch_wctrl_tdata[31:0]) ? 8'd2 : 8'd0;
wire [7:0] ct2ch_32bit_lines = CHDR_W/32 + // CHDR header
8'd3 + // CTL Header + OpWord
ct2ch_timestamp + // Timestamp
ct2ch_num_data; // Data words
wire [15:0] ct2ch_chdr_lines = 16'd1 + // CHDR header
ct2ch_32bit_lines[7:$clog2(CHDR_W/32)] + // Convert 32-bit lines to CHDR_W
(|ct2ch_32bit_lines[$clog2(CHDR_W/32)-1:0]); // Residue
reg [CHDR_W-1:0] ct2ch_sm_tdata,ct2ch_chdr_hdr;
reg [63:0] ct2ch_ctrl_hdr;
reg [63:0] ct2ch_chdr_tdata;
always @(*) begin
case (ct2ch_state)
ST_CHDR_HDR: begin
ct2ch_chdr_tdata = chdr_build_header(
ct2ch_chdr_hdr = 0;
ct2ch_chdr_hdr = chdr_build_header(
6'd0, /* VC */
1'b0, 1'b0, /* eob, eov */
CHDR_PKT_TYPE_CTRL,
CHDR_NO_MDATA,
ct2ch_seqnum,
(ct2ch_chdr_lines << $clog2(CHDR_W/8)), /* length in bytes */
axis_ctrl_get_rem_dst_epid(ct2ch_wctrl_tdata[63:32])
);
end
ST_CTRL_HDR: begin
ct2ch_chdr_tdata = {
(ct2ch_32bit_lines << $clog2(32/8)), /* length in bytes */
axis_ctrl_get_rem_dst_epid(ct2ch_wctrl_tdata[63:32]));
ct2ch_ctrl_hdr = {
axis_ctrl_build_hdr_hi(
10'd0, /* Unused in CHDR Control payload */
this_epid /* This is the SrcEPID */
@@ -302,18 +298,25 @@ module chdr_to_axis_ctrl #(
)
};
end
always @(*) begin
case (ct2ch_state)
ST_CHDR_HDR: begin
// regardless of width CHDR is always a full word
ct2ch_sm_tdata = ct2ch_chdr_hdr;
end
ST_CTRL_HDR: begin
ct2ch_sm_tdata = ct2ch_wctrl_tdata;
ct2ch_sm_tdata[63:0] = ct2ch_ctrl_hdr;
end
default: begin
ct2ch_chdr_tdata = ct2ch_wctrl_tdata[63:0];
ct2ch_sm_tdata = ct2ch_wctrl_tdata;
end
endcase
end
// Output signals
assign ct2ch_tdata[63:0] = ct2ch_chdr_tdata;
assign ct2ch_tlast = ct2ch_wctrl_tlast;
assign ct2ch_tdata = ct2ch_sm_tdata;
assign ct2ch_tlast = ct2ch_wctrl_tlast && (ct2ch_state != ST_CHDR_HDR);
assign ct2ch_tvalid = ct2ch_wctrl_tvalid;
generate if (CHDR_W > 64) begin
assign ct2ch_tdata[CHDR_W-1:64] = ct2ch_wctrl_tdata[CHDR_W-1:64];
end endgenerate
endmodule // chdr_to_axis_ctrl
@@ -34,9 +34,10 @@ $(RFNOC_CORE_SRCS) \
# Testbench Specific
#-------------------------------------------------
# Define only one toplevel module
SIM_TOP = chdr_crossbar_nxn_tb
SIM_TOP = chdr_crossbar_nxn_all_tb
SIM_SRCS = \
$(abspath chdr_crossbar_nxn_all_tb.sv) \
$(abspath chdr_crossbar_nxn_tb.sv) \
$(abspath ../crossbar_tb.sv) \
$(abspath ../chdr_traffic_source_sim.sv) \
@@ -0,0 +1,26 @@
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: chdr_crossbar_nxn
//
// Description: Testbench for chdr_crossbar_nxn that runs multiple widths
//
module chdr_crossbar_nxn_all_tb#(
/* no PARAM */
)(
/* no IO */
);
chdr_crossbar_nxn_tb #(.TEST_NAME("64B"),.CHDR_W(64)) CHDR64 ();
chdr_crossbar_nxn_tb #(.TEST_NAME("512B"),.CHDR_W(512)) CHDR512 ();
// Wait for all done
bit clk,rst;
sim_clock_gen #(100.0) clk_gen (clk, rst);
always_ff@(posedge clk)
if (CHDR64.impl.done && CHDR512.impl.done) $finish(1);
endmodule
@@ -6,12 +6,17 @@
`timescale 1ns/1ps
module chdr_crossbar_nxn_tb();
module chdr_crossbar_nxn_tb#(
parameter TEST_NAME = "chdr_crossbar_nxn_tb",
parameter CHDR_W = 64
)(
/* no IO */
);
crossbar_tb #(
.TEST_NAME ("chdr_crossbar_nxn_tb"),
.TEST_NAME (TEST_NAME ),
.ROUTER_IMPL ("chdr_crossbar_nxn" ), // Router implementation
.ROUTER_PORTS (10 ), // Number of ports
.ROUTER_DWIDTH (64 ), // Router datapath width
.ROUTER_DWIDTH (CHDR_W ), // Router datapath width
.MTU_LOG2 (7 ), // log2 of max packet size for router
.NUM_MASTERS (10 ), // Number of data generators in test
.TEST_MAX_PACKETS (100 ), // How many packets to stream per test case?
@@ -88,7 +88,7 @@ module chdr_traffic_sink_sim #(
s_chdr.reset();
while (1) begin
// A session begins on the posedge of start_stb
while (~start_stb) @(posedge clk);
while (start_stb !== 1) @(posedge clk);
session = session + 1;
$sformat(filename, "%s/pkts_node%05d_inj%03d_lpp%05d_traffic%c_sess%04d.csv",
FILE_PATH, NODE_ID, injection_rate, lines_per_pkt, traffic_patt, session);
@@ -107,7 +107,7 @@ module chdr_traffic_source_sim #(
m_chdr.reset();
while (1) begin
// A generation session begins on the posedge of start_stb
while (~start_stb) @(posedge clk);
while (start_stb !== 1) @(posedge clk);
curr_pkt_num = 'd0;
m_chdr.reset();
num_samps_xferd = 'd0;
@@ -58,7 +58,13 @@ module crossbar_tb #(
//----------------------------------------------------
// Instantiate traffic generators, checkers, buses
//----------------------------------------------------
`ifndef WORKING_DIR
// For XSim
localparam FILE_PATH = {`WORKING_DIR, "/data/", ROUTER_IMPL};
`else
// For ModelSim
localparam FILE_PATH = {"./work", "/data/", ROUTER_IMPL};
`endif
// Data buses
axis_t #(.DWIDTH(ROUTER_DWIDTH), .NUM_STREAMS(ROUTER_PORTS)) src2rtr_axis (.clk(clk));
@@ -75,6 +81,7 @@ module crossbar_tb #(
logic [31:0] set_num_pkts_to_send;
logic snk_start_stb = 0;
logic src_start_stb = 0;
bit done = 0;
wire [63:0] session_duration [0:ROUTER_PORTS-1];
wire [ROUTER_PORTS-1:0] src_active;
@@ -423,6 +430,7 @@ module crossbar_tb #(
`TEST_CASE_DONE(1)
`TEST_BENCH_DONE
done = 1;
end // initial begin
endmodule
@@ -29,11 +29,12 @@ $(RFNOC_XBAR_SRCS) \
#-------------------------------------------------
# Testbench Specific
#-------------------------------------------------
SIM_TOP = chdr_stream_endpoint_tb
SIM_TOP = chdr_stream_endpoint_all_tb
SIM_SRCS = \
$(abspath lossy_xport_model.v) \
$(abspath chdr_stream_endpoint_tb.sv) \
$(abspath chdr_stream_endpoint_all_tb.sv) \
#-------------------------------------------------
# Bottom-of-Makefile
@@ -0,0 +1,26 @@
//
// Copyright 2020 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: chdr_stream_endpoint_all_tb
//
// Description: Testbench for chdr_stream_endpoint that runs multiple widths
//
module chdr_stream_endpoint_all_tb#(
/* no PARAM */
)(
/* no IO */
);
chdr_stream_endpoint_tb #(.TEST_NAME("64B"),.CHDR_W(64)) CHDR64 ();
chdr_stream_endpoint_tb #(.TEST_NAME("512B"),.CHDR_W(512)) CHDR512 ();
// Wait for all done
bit clk,rst;
sim_clock_gen #(100.0) clk_gen (clk, rst);
always_ff@(posedge clk)
if (CHDR64.test.done && CHDR512.test.done) $finish(1);
endmodule
@@ -1,5 +1,5 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
// Copyright 2020 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
@@ -8,14 +8,18 @@
`default_nettype none
module chdr_stream_endpoint_tb;
module chdr_stream_endpoint_tb#(
parameter TEST_NAME = "chdr_stream_endpoint_tb",
parameter CHDR_W = 64
)(
/* no IO */
);
// ----------------------------------------
// Global settings
// ----------------------------------------
// Include macros and time declarations for use with PkgTestExec
`define TEST_EXEC_OBJ test
`include "test_exec.svh"
import PkgTestExec::*;
@@ -34,7 +38,6 @@ module chdr_stream_endpoint_tb;
localparam int FAST_STALL_PROB = 0;
localparam int SLOW_STALL_PROB = 35;
localparam int CHDR_W = 64;
localparam int MTU = 7;
localparam [15:0] PROTOVER = {8'd1, 8'd0};
localparam [15:0] DEV_ID = 16'hBEEF;
@@ -108,14 +111,14 @@ module chdr_stream_endpoint_tb;
.m_axis_chdr_tlast (a2c_chdr_tlast ),
.m_axis_chdr_tvalid (a2c_chdr_tvalid ),
.m_axis_chdr_tready (a2c_chdr_tready ),
.s_axis_data_tdata ({m_a1_data.slave.tdata , m_a0_data.slave.tdata }),
.s_axis_data_tlast ({m_a1_data.slave.tlast , m_a0_data.slave.tlast }),
.s_axis_data_tvalid ({m_a1_data.slave.tvalid , m_a0_data.slave.tvalid }),
.s_axis_data_tready ({m_a1_data.slave.tready , m_a0_data.slave.tready }),
.m_axis_data_tdata ({s_a1_data.master.tdata , s_a0_data.master.tdata }),
.m_axis_data_tlast ({s_a1_data.master.tlast , s_a0_data.master.tlast }),
.m_axis_data_tvalid ({s_a1_data.master.tvalid, s_a0_data.master.tvalid}),
.m_axis_data_tready ({s_a1_data.master.tready, s_a0_data.master.tready}),
.s_axis_data_tdata ({m_a1_data.tdata , m_a0_data.tdata }),
.s_axis_data_tlast ({m_a1_data.tlast , m_a0_data.tlast }),
.s_axis_data_tvalid ({m_a1_data.tvalid , m_a0_data.tvalid }),
.s_axis_data_tready ({m_a1_data.tready , m_a0_data.tready }),
.m_axis_data_tdata ({s_a1_data.tdata , s_a0_data.tdata }),
.m_axis_data_tlast ({s_a1_data.tlast , s_a0_data.tlast }),
.m_axis_data_tvalid ({s_a1_data.tvalid, s_a0_data.tvalid}),
.m_axis_data_tready ({s_a1_data.tready, s_a0_data.tready}),
.s_axis_ctrl_tdata (a_ctrl_out_tdata ),
.s_axis_ctrl_tlast (a_ctrl_loop_tlast ),
.s_axis_ctrl_tvalid (a_ctrl_loop_tvalid ),
@@ -157,14 +160,14 @@ module chdr_stream_endpoint_tb;
.m_axis_chdr_tlast (b2c_chdr_tlast ),
.m_axis_chdr_tvalid (b2c_chdr_tvalid ),
.m_axis_chdr_tready (b2c_chdr_tready ),
.s_axis_data_tdata ({m_b1_data.slave.tdata , m_b0_data.slave.tdata }),
.s_axis_data_tlast ({m_b1_data.slave.tlast , m_b0_data.slave.tlast }),
.s_axis_data_tvalid ({m_b1_data.slave.tvalid , m_b0_data.slave.tvalid }),
.s_axis_data_tready ({m_b1_data.slave.tready , m_b0_data.slave.tready }),
.m_axis_data_tdata ({s_b1_data.master.tdata , s_b0_data.master.tdata }),
.m_axis_data_tlast ({s_b1_data.master.tlast , s_b0_data.master.tlast }),
.m_axis_data_tvalid ({s_b1_data.master.tvalid, s_b0_data.master.tvalid}),
.m_axis_data_tready ({s_b1_data.master.tready, s_b0_data.master.tready}),
.s_axis_data_tdata ({m_b1_data.tdata , m_b0_data.tdata }),
.s_axis_data_tlast ({m_b1_data.tlast , m_b0_data.tlast }),
.s_axis_data_tvalid ({m_b1_data.tvalid , m_b0_data.tvalid }),
.s_axis_data_tready ({m_b1_data.tready , m_b0_data.tready }),
.m_axis_data_tdata ({s_b1_data.tdata , s_b0_data.tdata }),
.m_axis_data_tlast ({s_b1_data.tlast , s_b0_data.tlast }),
.m_axis_data_tvalid ({s_b1_data.tvalid, s_b0_data.tvalid}),
.m_axis_data_tready ({s_b1_data.tready, s_b0_data.tready}),
.s_axis_ctrl_tdata (b_ctrl_out_tdata ),
.s_axis_ctrl_tlast (b_ctrl_loop_tlast ),
.s_axis_ctrl_tvalid (b_ctrl_loop_tvalid ),
@@ -194,14 +197,14 @@ module chdr_stream_endpoint_tb;
.clk (rfnoc_chdr_clk),
.reset (rfnoc_chdr_rst),
.device_id (DEV_ID),
.s_axis_tdata ({b2c_chdr_tdata, a2c_chdr_tdata, m_tb_chdr.slave.tdata }),
.s_axis_tlast ({b2c_chdr_tlast, a2c_chdr_tlast, m_tb_chdr.slave.tlast }),
.s_axis_tvalid ({b2c_chdr_tvalid, a2c_chdr_tvalid, m_tb_chdr.slave.tvalid }),
.s_axis_tready ({b2c_chdr_tready, a2c_chdr_tready, m_tb_chdr.slave.tready }),
.m_axis_tdata ({c2bx_chdr_tdata, c2ax_chdr_tdata, s_tb_chdr.master.tdata }),
.m_axis_tlast ({c2bx_chdr_tlast, c2ax_chdr_tlast, s_tb_chdr.master.tlast }),
.m_axis_tvalid ({c2bx_chdr_tvalid, c2ax_chdr_tvalid, s_tb_chdr.master.tvalid}),
.m_axis_tready ({c2bx_chdr_tready, c2ax_chdr_tready, s_tb_chdr.master.tready}),
.s_axis_tdata ({b2c_chdr_tdata, a2c_chdr_tdata, m_tb_chdr.tdata }),
.s_axis_tlast ({b2c_chdr_tlast, a2c_chdr_tlast, m_tb_chdr.tlast }),
.s_axis_tvalid ({b2c_chdr_tvalid, a2c_chdr_tvalid, m_tb_chdr.tvalid }),
.s_axis_tready ({b2c_chdr_tready, a2c_chdr_tready, m_tb_chdr.tready }),
.m_axis_tdata ({c2bx_chdr_tdata, c2ax_chdr_tdata, s_tb_chdr.tdata }),
.m_axis_tlast ({c2bx_chdr_tlast, c2ax_chdr_tlast, s_tb_chdr.tlast }),
.m_axis_tvalid ({c2bx_chdr_tvalid, c2ax_chdr_tvalid, s_tb_chdr.tvalid}),
.m_axis_tready ({c2bx_chdr_tready, c2ax_chdr_tready, s_tb_chdr.tready}),
.ext_rtcfg_stb ('0),
.ext_rtcfg_addr ('0),
.ext_rtcfg_data ('0),
@@ -272,6 +275,7 @@ module chdr_stream_endpoint_tb;
// ----------------------------------------
// Test Utilities
// ----------------------------------------
TestExec test = new();
integer cached_mgmt_seqnum = 0;
integer cached_ctrl_seqnum = 0;
integer cached_data_seqnum = 0;
@@ -406,6 +410,7 @@ module chdr_stream_endpoint_tb;
if (VERBOSE) begin $write("ExpRx"); exp_chdr.print(); end
// Validate contents
exp_chdr.disable_comparing_beyond_length = 1;
`ASSERT_ERROR(exp_chdr.equal(rx_chdr),
"Received CHDR control packet was incorrect");
end
@@ -529,7 +534,7 @@ module chdr_stream_endpoint_tb;
// ----------------------------------------
timeout_t timeout;
string tc_label;
bit stop_responder = 0;
automatic bit stop_responder = 0;
logic [31:0] seq_err_count;
logic [31:0] route_err_count;
logic [31:0] data_err_count;
@@ -543,9 +548,10 @@ module chdr_stream_endpoint_tb;
b_rterr_prob = 0;
b_lossy_input = 0;
// Initialize
// ----------------------------------------
test.start_tb("chdr_stream_endpoint_tb");
test.start_tb({TEST_NAME,"chdr_stream_endpoint_tb"});
// Start the BFMs
a0_data_bfm.run();
@@ -562,7 +568,7 @@ module chdr_stream_endpoint_tb;
rfnoc_ctrl_clk_gen.reset();
rfnoc_chdr_clk_gen.reset();
test.start_test("Wait for reset");
test.start_test({TEST_NAME,"Wait for reset"});
test.start_timeout(timeout, 1us, "Waiting for reset");
while (rfnoc_ctrl_rst) @(posedge rfnoc_ctrl_clk);
while (rfnoc_chdr_rst) @(posedge rfnoc_chdr_clk);
@@ -572,7 +578,7 @@ module chdr_stream_endpoint_tb;
// Discover Topology
// ----------------------------------------
test.start_test("Discover Topology");
test.start_test({TEST_NAME,"Discover Topology"});
begin
automatic chdr_header_t tx_mgmt_hdr, rx_mgmt_hdr;
automatic chdr_mgmt_t tx_mgmt_pl, rx_mgmt_pl;
@@ -664,7 +670,7 @@ module chdr_stream_endpoint_tb;
// Configure Routes to Stream Endpoints A and B
// ----------------------------------------
test.start_test("Configure Routes");
test.start_test({TEST_NAME,"Configure Routes"});
begin
automatic chdr_header_t tx_mgmt_hdr, rx_mgmt_hdr;
automatic chdr_mgmt_t tx_mgmt_pl, rx_mgmt_pl;
@@ -701,13 +707,13 @@ module chdr_stream_endpoint_tb;
// Configure Stream Endpoints
// ----------------------------------------
test.start_test("Configure Stream Endpoints");
test.start_test({TEST_NAME,"Configure Stream Endpoints"});
begin
automatic chdr_header_t tx_mgmt_hdr, rx_mgmt_hdr;
automatic chdr_mgmt_t tx_mgmt_pl, rx_mgmt_pl;
automatic chdr_mgmt_op_t exp_mgmt_op;
logic [15:0] epids[2] = {EPID_A, EPID_B};
automatic logic [15:0] epids[2] = {EPID_A, EPID_B};
foreach (epids[i]) begin
// Generic management header
tx_mgmt_pl.header = '{
@@ -752,13 +758,13 @@ module chdr_stream_endpoint_tb;
// Setup a stream between Endpoint A and B
// ----------------------------------------
test.start_test("Setup bidirectional stream between endpoints A and B");
test.start_test({TEST_NAME,"Setup bidirectional stream between endpoints A and B"});
begin
automatic chdr_header_t tx_mgmt_hdr, rx_mgmt_hdr;
automatic chdr_mgmt_t tx_mgmt_pl, rx_mgmt_pl;
automatic chdr_mgmt_op_t exp_mgmt_op;
logic [15:0] epids[2] = {EPID_A, EPID_B};
automatic logic [15:0] epids[2] = {EPID_A, EPID_B};
foreach (epids[i]) begin
// Generic management header
tx_mgmt_pl.header = '{
@@ -859,7 +865,7 @@ module chdr_stream_endpoint_tb;
cached_ctrl_seqnum = 0;
for (int cfg = 0; cfg < 2; cfg++) begin
$sformat(tc_label, "Control Xact to A (%s)", (cfg?"Slow":"Fast"));
test.start_test(tc_label);
test.start_test({TEST_NAME,tc_label});
begin
tb_chdr_bfm.set_master_stall_prob(cfg?SLOW_STALL_PROB:FAST_STALL_PROB);
tb_chdr_bfm.set_slave_stall_prob(cfg?SLOW_STALL_PROB:FAST_STALL_PROB);
@@ -874,7 +880,7 @@ module chdr_stream_endpoint_tb;
cached_ctrl_seqnum = 0;
for (int cfg = 0; cfg < 2; cfg++) begin
$sformat(tc_label, "Control Xact to B (%s)", (cfg?"Slow":"Fast"));
test.start_test(tc_label);
test.start_test({TEST_NAME,tc_label});
begin
tb_chdr_bfm.set_master_stall_prob(cfg?SLOW_STALL_PROB:FAST_STALL_PROB);
tb_chdr_bfm.set_slave_stall_prob(cfg?SLOW_STALL_PROB:FAST_STALL_PROB);
@@ -892,7 +898,7 @@ module chdr_stream_endpoint_tb;
automatic logic slv_cfg = cfg[1];
$sformat(tc_label, "Stream Data from A to B (%s Mst, %s Slv)",
(mst_cfg?"Slow":"Fast"), (slv_cfg?"Slow":"Fast"));
test.start_test(tc_label);
test.start_test({TEST_NAME,tc_label});
begin
set_unidir_stall_prob(EPID_A, EPID_B,
mst_cfg?SLOW_STALL_PROB:FAST_STALL_PROB,
@@ -911,7 +917,7 @@ module chdr_stream_endpoint_tb;
automatic logic slv_cfg = cfg[1];
$sformat(tc_label, "Stream Data from B to A (%s Mst, %s Slv)",
(mst_cfg?"Slow":"Fast"), (slv_cfg?"Slow":"Fast"));
test.start_test(tc_label);
test.start_test({TEST_NAME,tc_label});
begin
set_unidir_stall_prob(EPID_B, EPID_A,
mst_cfg?SLOW_STALL_PROB:FAST_STALL_PROB,
@@ -929,7 +935,7 @@ module chdr_stream_endpoint_tb;
automatic logic slv_cfg = cfg[1];
$sformat(tc_label, "Stream Data between A <=> B simultaneously (%s Mst, %s Slv)",
(mst_cfg?"Slow":"Fast"), (slv_cfg?"Slow":"Fast"));
test.start_test(tc_label);
test.start_test({TEST_NAME,tc_label});
begin
set_bidir_stall_prob(
mst_cfg?SLOW_STALL_PROB:FAST_STALL_PROB,
@@ -950,7 +956,7 @@ module chdr_stream_endpoint_tb;
automatic logic slv_cfg = cfg[1];
$sformat(tc_label, "Stream Data and Control between A <=> B (%s Mst, %s Slv)",
(mst_cfg?"Slow":"Fast"), (slv_cfg?"Slow":"Fast"));
test.start_test(tc_label);
test.start_test({TEST_NAME,tc_label});
begin
tb_chdr_bfm.set_master_stall_prob(mst_cfg?SLOW_STALL_PROB:FAST_STALL_PROB);
tb_chdr_bfm.set_slave_stall_prob(slv_cfg?SLOW_STALL_PROB:FAST_STALL_PROB);
@@ -976,9 +982,9 @@ module chdr_stream_endpoint_tb;
// Check zero sequence errors after streaming
// ----------------------------------------
test.start_test("Check zero sequence errors after streaming");
test.start_test({TEST_NAME,"Check zero sequence errors after streaming"});
begin
logic [15:0] epids[2] = {EPID_A, EPID_B};
automatic logic [15:0] epids[2] = {EPID_A, EPID_B};
foreach (epids[i]) begin
mgmt_read_err_counts(epids[i], seq_err_count, route_err_count, data_err_count);
`ASSERT_ERROR(seq_err_count == 32'd0, "Check NoErrs: Incorrect seq error count");
@@ -988,9 +994,13 @@ module chdr_stream_endpoint_tb;
end
test.end_test();
// Force sequence error
// Note: Occasional StreamCommand to resynch occur, which
// can cause the count to be greater than just the number data packets
// that are sent, so the comparisons are to > instead of ==
// ----------------------------------------
test.start_test("Force sequence error");
test.start_test({TEST_NAME,"Force sequence error"});
begin
// First sequence error
send_recv_data_packets(EPID_A, EPID_B, 1, cached_data_seqnum++, 1);
@@ -999,7 +1009,7 @@ module chdr_stream_endpoint_tb;
b_seqerr_prob = 0;
repeat (100) @(posedge rfnoc_chdr_clk); // Wait for sequence error to reach the upstream port
mgmt_read_err_counts(EPID_A, seq_err_count, route_err_count, data_err_count);
`ASSERT_ERROR(seq_err_count == 32'd1, "Force SeqErr: Incorrect seq error count");
`ASSERT_ERROR(seq_err_count > 32'd0, "Force SeqErr: Incorrect seq error count");
`ASSERT_ERROR(route_err_count == 32'd0, "Force SeqErr: Incorrect route error count");
`ASSERT_ERROR(data_err_count == 32'd0, "Force SeqErr: Incorrect data error count");
@@ -1010,7 +1020,7 @@ module chdr_stream_endpoint_tb;
b_seqerr_prob = 0;
repeat (100) @(posedge rfnoc_chdr_clk); // Wait for sequence error to reach the upstream port
mgmt_read_err_counts(EPID_A, seq_err_count, route_err_count, data_err_count);
`ASSERT_ERROR(seq_err_count > 32'd1, "Force SeqErr: Incorrect seq error count");
`ASSERT_ERROR(seq_err_count > 32'd2, "Force SeqErr: Incorrect seq error count");
`ASSERT_ERROR(route_err_count == 32'd0, "Force SeqErr: Incorrect route error count");
`ASSERT_ERROR(data_err_count == 32'd0, "Force SeqErr: Incorrect data error count");
end
@@ -1018,7 +1028,7 @@ module chdr_stream_endpoint_tb;
// Force routing error
// ----------------------------------------
test.start_test("Force routing error");
test.start_test({TEST_NAME,"Force routing error"});
begin
logic [31:0] old_route_err_count;
// First sequence error
@@ -1048,13 +1058,13 @@ module chdr_stream_endpoint_tb;
// Setup a stream between Endpoint A and B
// ----------------------------------------
test.start_test("Reconfigure flow control (reset state)");
test.start_test({TEST_NAME,"Reconfigure flow control (reset state)"});
begin
automatic chdr_header_t tx_mgmt_hdr, rx_mgmt_hdr;
automatic chdr_mgmt_t tx_mgmt_pl, rx_mgmt_pl;
automatic chdr_mgmt_op_t exp_mgmt_op;
logic [15:0] epids[2] = {EPID_A, EPID_B};
automatic logic [15:0] epids[2] = {EPID_A, EPID_B};
foreach (epids[i]) begin
// Generic management header
tx_mgmt_pl.header = '{
@@ -1112,9 +1122,9 @@ module chdr_stream_endpoint_tb;
// Check zero errors after reinit
// ----------------------------------------
test.start_test("Check zero errors after reinit");
test.start_test({TEST_NAME,"Check zero errors after reinit"});
begin
logic [15:0] epids[2] = {EPID_A, EPID_B};
automatic logic [15:0] epids[2] = {EPID_A, EPID_B};
foreach (epids[i]) begin
mgmt_read_err_counts(epids[i], seq_err_count, route_err_count, data_err_count);
`ASSERT_ERROR(seq_err_count == 32'd0, "Check NoErrs: Incorrect seq error count");
@@ -1126,7 +1136,7 @@ module chdr_stream_endpoint_tb;
// Stream data between A <=> B simultaneously
// ----------------------------------------
test.start_test("Stream Data between A <=> B with a lossy link");
test.start_test({TEST_NAME,"Stream Data between A <=> B with a lossy link"});
begin
cached_data_seqnum = 0;
set_bidir_stall_prob(FAST_STALL_PROB, SLOW_STALL_PROB);
@@ -1145,7 +1155,8 @@ module chdr_stream_endpoint_tb;
// Finish Up
// ----------------------------------------
// Display final statistics and results
test.end_tb();
test.end_tb(.finish(0));
end
endmodule
`default_nettype wire
+203 -163
View File
@@ -1,5 +1,5 @@
//
// Copyright 2019 Ettus Research, A National Instruments Company
// Copyright 2020 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
@@ -10,7 +10,6 @@
//
package PkgChdrBfm;
import PkgChdrUtils::*;
@@ -21,10 +20,13 @@ package PkgChdrBfm;
// CHDR Packet Class
//---------------------------------------------------------------------------
class ChdrPacket #(int CHDR_W = 64);
class ChdrPacket #(parameter int CHDR_W = 64,
parameter int USER_WIDTH = 1);
typedef ChdrPacket #(CHDR_W) ChdrPacket_t;
typedef ChdrPacket #(CHDR_W,USER_WIDTH) ChdrPacket_t;
typedef AxiStreamPacket #(CHDR_W, USER_WIDTH) AxisPacket_t;
typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t;
typedef AxisPacket_t::data_t data_t;
const int BYTES_PER_CHDR_W = CHDR_W / 8;
@@ -33,6 +35,8 @@ package PkgChdrBfm;
chdr_word_t metadata[$];
chdr_word_t data[$];
bit disable_comparing_beyond_length = 1;
extern function ChdrPacket_t copy();
extern function bit equal(ChdrPacket_t packet);
extern function string sprint(bit pretty = 1);
@@ -72,17 +76,22 @@ package PkgChdrBfm;
output ctrl_word_t ctrl_data[$],
output chdr_timestamp_t ctrl_timestamp);
// Helper methods
extern function int header_bytes();
extern function int mdata_bytes();
extern function int data_bytes();
extern function void update_lengths();
extern function void update_lengths(int payload_bytes = 0);
extern function string sprint_raw();
extern function string sprint_pretty();
extern function bit chdr_word_queues_equal(ref chdr_word_t a[$], ref chdr_word_t b[$]);
extern function bit chdr_word_queues_equal(ref chdr_word_t a[$],
ref chdr_word_t b[$],
input int data_byte_length = -1);
// AXI-Stream/CHDR Conversion Functions
extern function void axis_to_chdr (AxisPacket_t axis_packet);
extern function AxisPacket_t chdr_to_axis ();
endclass : ChdrPacket;
@@ -123,12 +132,6 @@ package PkgChdrBfm;
extern function bit try_get_chdr(output ChdrPacket_t chdr_packet);
extern task peek_chdr(output ChdrPacket_t chdr_packet);
extern function bit try_peek_chdr(output ChdrPacket_t chdr_packet);
// AXI-Stream/CHDR Conversion Functions
extern function ChdrPacket_t axis_to_chdr (AxisPacket_t axis_packet);
extern function AxisPacket_t chdr_to_axis (ChdrPacket_t chdr_packet);
endclass : ChdrBfm
@@ -151,8 +154,17 @@ package PkgChdrBfm;
// Return true if this packet equals that of the argument
function bit ChdrPacket::equal(ChdrPacket_t packet);
int payload_length;
payload_length = header.length;
payload_length -= BYTES_PER_CHDR_W; // subtract header bytes
if (CHDR_W == 64) begin // subtract TS bytes if not in header word
if (header.pkt_type == CHDR_DATA_WITH_TS) begin
payload_length -= BYTES_PER_CHDR_W;
end
end
payload_length -= packet.metadata.size() * BYTES_PER_CHDR_W; // subtract metadata length
if (header != packet.header) return 0;
if (!chdr_word_queues_equal(data, packet.data)) return 0;
if (!chdr_word_queues_equal(data, packet.data,payload_length)) return 0;
if (!chdr_word_queues_equal(metadata, packet.metadata)) return 0;
if (header.pkt_type == CHDR_DATA_WITH_TS && timestamp !== packet.timestamp) return 0;
return 1;
@@ -176,6 +188,7 @@ package PkgChdrBfm;
return str;
endfunction : sprint_raw
// Format the contents of the packet into a string (dissect contents)
function string ChdrPacket::sprint_pretty();
string str;
@@ -236,6 +249,7 @@ package PkgChdrBfm;
return str;
endfunction : sprint_pretty
function string ChdrPacket::sprint(bit pretty = 1);
if (pretty)
return sprint_pretty();
@@ -243,6 +257,7 @@ package PkgChdrBfm;
return sprint_raw();
endfunction: sprint
// Print the contents of the packet
function void ChdrPacket::print(bit pretty = 1);
$display(sprint(pretty));
@@ -433,6 +448,7 @@ package PkgChdrBfm;
input chdr_timestamp_t ctrl_timestamp = 0
);
bit partial_word;
int byte_count = 0;
ctrl_word_t mandatory_data;
ChdrData #(CHDR_W, 64)::item_queue_t data64;
@@ -442,10 +458,12 @@ package PkgChdrBfm;
// Insert word 0 of control payload
data64.push_back(ctrl_header);
byte_count+=8;
// Insert word 1 of control payload, if timestamp is used
if (ctrl_header.has_time) begin
data64.push_back(ctrl_timestamp);
byte_count+=8;
end
// Make sure the amount of data passed matches the header
@@ -457,6 +475,7 @@ package PkgChdrBfm;
// and first word of control data.
mandatory_data = (ctrl_header.num_data > 0) ? ctrl_data[0] : '0;
data64.push_back({mandatory_data, ctrl_op_word[31:0]});
byte_count+=8;
// We have a half CHDR word if num_data is even
partial_word = (ctrl_header.num_data[0] == '0);
@@ -465,15 +484,17 @@ package PkgChdrBfm;
if (i == ctrl_data.size()-1) begin
// num_data must be even in this case, so last word is half filled
data64.push_back({ 32'b0, ctrl_data[i] });
byte_count+=4;
end else begin
data64.push_back({ ctrl_data[i+1], ctrl_data[i] });
byte_count+=8;
end
end
// Convert from 64-bit words to CHDR_W-bit words
data = ChdrData #(CHDR_W, 64)::item_to_chdr(data64);
update_lengths();
update_lengths(.payload_bytes(byte_count));
endfunction : write_ctrl
@@ -530,7 +551,6 @@ package PkgChdrBfm;
while (dptr < data32.size()) begin
ctrl_data.push_back(data32[dptr++]);
end
endfunction : read_ctrl
@@ -559,19 +579,28 @@ package PkgChdrBfm;
// Update the length and num_mdata header fields of the packet based on the
// size of the metadata queue and the data queue.
function void ChdrPacket::update_lengths();
function void ChdrPacket::update_lengths(int payload_bytes = 0);
int num_bytes;
int num_mdata;
int my_payload_bytes;
// Calculate NumMData based on the size of metadata queue
num_mdata = metadata.size();
assert(num_mdata < 2**$bits(chdr_num_mdata_t)) else
$fatal(1, "ChdrPacket::update_lengths(): Calculated NumMData exceeds maximum size");
if (payload_bytes == 0) begin
// Calculate if optional argument not provided
my_payload_bytes = data.size() * BYTES_PER_CHDR_W;
end else begin
// Use optional argument if provided
my_payload_bytes = payload_bytes;
end
// Calculate the Length field
num_bytes = header_bytes() + // Header
num_mdata * BYTES_PER_CHDR_W + // Metadata
data.size() * BYTES_PER_CHDR_W; // Payload
my_payload_bytes; // Payload
assert(num_bytes < 2**$bits(chdr_length_t)) else
$fatal(1, "ChdrPacket::update_lengths(): Calculated Length exceeds maximum size");
@@ -581,27 +610,168 @@ package PkgChdrBfm;
endfunction : update_lengths
// Returns 1 if the queues have the same contents, otherwise returns 0. This
// function is equivalent to (a == b), but this doesn't work correctly yet in
// Vivado 2018.3.
function automatic bit ChdrPacket::chdr_word_queues_equal(ref chdr_word_t a[$], ref chdr_word_t b[$]);
// Returns 1 if the queues have the same contents, up to the
// data_byte_length, if present.
function automatic bit ChdrPacket::chdr_word_queues_equal(
ref chdr_word_t a[$],
ref chdr_word_t b[$],
input int data_byte_length = -1
);
chdr_word_t x, y;
int bytes_remaining = data_byte_length;
if (a.size() != b.size()) return 0;
foreach (a[i]) begin
x = a[i];
y = b[i];
if(data_byte_length > 0 && // only if optional argument is valid
disable_comparing_beyond_length && // only if optional feature is enabled
bytes_remaining < BYTES_PER_CHDR_W) begin // only compare bytes on last word
for (int b = 0; b < bytes_remaining*8; b += 8) begin
if (x[b+:8] !== y[b+:8]) return 0;
end
end else begin
if (x !== y) return 0;
end
bytes_remaining -= BYTES_PER_CHDR_W;
end
return 1;
endfunction : chdr_word_queues_equal
// Convert the data payload of an AXI Stream packet data structure to a CHDR
// packet data structure.
function void ChdrPacket::axis_to_chdr (AxisPacket_t axis_packet);
enum int { ST_HEADER, ST_TIMESTAMP, ST_METADATA, ST_PAYLOAD } rx_state;
data_t word;
int num_rx_mdata;
int num_rx_bytes;
ChdrPacket_t chdr_packet = new();
rx_state = ST_HEADER;
for(int i = 0; i < axis_packet.data.size(); i++) begin
word = axis_packet.data[i];
case (rx_state)
ST_HEADER : begin
num_rx_bytes += BYTES_PER_CHDR_W;
header = word[63:0];
// Depending on the size of the word, we could have just the header
// or both the header and the timestamp in this word.
if (header.pkt_type == CHDR_DATA_WITH_TS) begin
if (CHDR_W >= 128) begin
timestamp = word[127:64];
rx_state = ST_METADATA;
end else begin
rx_state = ST_TIMESTAMP;
end
end else begin
rx_state = ST_METADATA;
end
// Check if there's no metadata, in which case we can skip it
if (rx_state == ST_METADATA && header.num_mdata == 0) begin
rx_state = ST_PAYLOAD;
end
end
ST_TIMESTAMP : begin
num_rx_bytes += BYTES_PER_CHDR_W;
timestamp = word;
rx_state = (header.num_mdata > 0) ? ST_METADATA : ST_PAYLOAD;
end
ST_METADATA : begin
metadata.push_back(word);
num_rx_mdata++;
num_rx_bytes += BYTES_PER_CHDR_W;
if (num_rx_mdata == header.num_mdata) rx_state = ST_PAYLOAD;
end
ST_PAYLOAD : begin
data.push_back(word);
num_rx_bytes += BYTES_PER_CHDR_W;
end
endcase
end
assert (rx_state == ST_PAYLOAD) else begin
$error("ChdrPacket::axis_to_chdr: Malformed CHDR packet");
end
// Check length field, noting that the last word may be partially filled
assert (header.length >= num_rx_bytes-(BYTES_PER_CHDR_W-1) &&
header.length <= num_rx_bytes) else begin
$error("ChdrPacket::axis_to_chdr: Incorrect CHDR length");
end
endfunction : axis_to_chdr
// Convert a CHDR packet data structure to a an AXI-Stream packet data
// structure.
function ChdrPacket::AxisPacket_t ChdrPacket::chdr_to_axis ();
int num_words, expected_words;
data_t bus_word = 0;
AxisPacket_t axis_packet = new();
// Check that we have the right number of metadata words
assert (metadata.size() == header.num_mdata) else begin
$error("ChdrPacket::chdr_to_axis: Packet metadata size doesn't match header NumMData field");
end
// Calculate the number of words needed to represent this packet
num_words = data.size() + metadata.size();
if (header.pkt_type == CHDR_DATA_WITH_TS && CHDR_W == 64) begin
// Add two words, one for header and one for timestamp
num_words += 2;
end else begin
// Add one word only for header (which may or may not include a timestamp)
num_words += 1;
end
// Calculate the number of words represented by the Length field
expected_words = header.length / BYTES_PER_CHDR_W;
if (header.length % BYTES_PER_CHDR_W != 0) expected_words++;
// Make sure length field matches actual packet length
assert (num_words == expected_words) else begin
$error("ChdrPacket::chdr_to_axis: Packet size doesn't match header Length field");
end
// Insert header
bus_word[63:0] = header;
if (CHDR_W == 64) begin
axis_packet.data.push_back(bus_word);
if (header.pkt_type == CHDR_DATA_WITH_TS) begin
// Insert timestamp
axis_packet.data.push_back(timestamp);
end
end else begin
// Copy the timestamp word from the header, regardless of whether or not
// this packet uses the timestamp field.
bus_word[127:64] = timestamp;
axis_packet.data.push_back(bus_word);
end
// Insert metadata
foreach (metadata[i]) begin
bus_word = metadata[i];
axis_packet.data.push_back(bus_word);
end
// Insert payload
foreach (data[i]) begin
bus_word = data[i];
axis_packet.data.push_back(bus_word);
end
return axis_packet;
endfunction : chdr_to_axis
//---------------------------------------------------------------------------
// CHDR BFM Class Methods
//---------------------------------------------------------------------------
// Class constructor. This must be given an interface for the master
// connection and an interface for the slave connection.
function ChdrBfm::new (
@@ -619,7 +789,7 @@ package PkgChdrBfm;
task ChdrBfm::put_chdr (ChdrPacket_t chdr_packet);
AxisPacket_t axis_packet;
axis_packet = chdr_to_axis(chdr_packet);
axis_packet = chdr_packet.chdr_to_axis();
super.put(axis_packet);
endtask : put_chdr
@@ -630,7 +800,7 @@ package PkgChdrBfm;
AxisPacket_t axis_packet;
bit status;
axis_packet = chdr_to_axis(chdr_packet);
axis_packet = chdr_packet.chdr_to_axis();
return super.try_put(axis_packet);
endfunction : try_put_chdr
@@ -639,7 +809,8 @@ package PkgChdrBfm;
task ChdrBfm::get_chdr (output ChdrPacket_t chdr_packet);
AxisPacket_t axis_packet;
super.get(axis_packet);
chdr_packet = axis_to_chdr(axis_packet);
chdr_packet = new();
chdr_packet.axis_to_chdr(axis_packet);
endtask : get_chdr
@@ -648,7 +819,8 @@ package PkgChdrBfm;
function bit ChdrBfm::try_get_chdr (output ChdrPacket_t chdr_packet);
AxisPacket_t axis_packet;
if (!super.try_get(axis_packet)) return 0;
chdr_packet = axis_to_chdr(axis_packet);
chdr_packet = new();
chdr_packet.axis_to_chdr(axis_packet);
return 1;
endfunction : try_get_chdr
@@ -658,7 +830,8 @@ package PkgChdrBfm;
task ChdrBfm::peek_chdr (output ChdrPacket_t chdr_packet);
AxisPacket_t axis_packet;
super.peek(axis_packet);
chdr_packet = axis_to_chdr(axis_packet);
chdr_packet = new();
chdr_packet.axis_to_chdr(axis_packet);
endtask : peek_chdr
@@ -667,142 +840,9 @@ package PkgChdrBfm;
function bit ChdrBfm::try_peek_chdr (output ChdrPacket_t chdr_packet);
AxisPacket_t axis_packet;
if (!super.try_get(axis_packet)) return 0;
chdr_packet = axis_to_chdr(axis_packet);
chdr_packet = new();
chdr_packet.axis_to_chdr(axis_packet);
return 1;
endfunction : try_peek_chdr
// Convert the data payload of an AXI Stream packet data structure to a CHDR
// packet data structure.
function ChdrBfm::ChdrPacket_t ChdrBfm::axis_to_chdr (AxisPacket_t axis_packet);
enum int { ST_HEADER, ST_TIMESTAMP, ST_METADATA, ST_PAYLOAD } rx_state;
data_t word;
int num_rx_mdata;
int num_rx_bytes;
ChdrPacket_t chdr_packet = new();
rx_state = ST_HEADER;
for(int i = 0; i < axis_packet.data.size(); i++) begin
word = axis_packet.data[i];
case (rx_state)
ST_HEADER : begin
num_rx_bytes += BYTES_PER_CHDR_W;
chdr_packet.header = word[63:0];
// Depending on the size of the word, we could have just the header
// or both the header and the timestamp in this word.
if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS) begin
if (CHDR_W >= 128) begin
chdr_packet.timestamp = word[127:64];
rx_state = ST_METADATA;
end else begin
rx_state = ST_TIMESTAMP;
end
end else begin
rx_state = ST_METADATA;
end
// Check if there's no metadata, in which case we can skip it
if (rx_state == ST_METADATA && chdr_packet.header.num_mdata == 0) begin
rx_state = ST_PAYLOAD;
end
end
ST_TIMESTAMP : begin
num_rx_bytes += BYTES_PER_CHDR_W;
chdr_packet.timestamp = word;
rx_state = (chdr_packet.header.num_mdata > 0) ? ST_METADATA : ST_PAYLOAD;
end
ST_METADATA : begin
chdr_packet.metadata.push_back(word);
num_rx_mdata++;
num_rx_bytes += BYTES_PER_CHDR_W;
if (num_rx_mdata == chdr_packet.header.num_mdata) rx_state = ST_PAYLOAD;
end
ST_PAYLOAD : begin
chdr_packet.data.push_back(word);
num_rx_bytes += BYTES_PER_CHDR_W;
end
endcase
end
assert (rx_state == ST_PAYLOAD) else begin
$error("ChdrBfm::axis_to_chdr: Malformed CHDR packet");
end
// Check length field, noting that the last word may be partially filled
assert (chdr_packet.header.length >= num_rx_bytes-(BYTES_PER_CHDR_W-1) &&
chdr_packet.header.length <= num_rx_bytes) else begin
$error("ChdrBfm::axis_to_chdr: Incorrect CHDR length");
end
return chdr_packet;
endfunction : axis_to_chdr
// Convert a CHDR packet data structure to a an AXI-Stream packet data
// structure.
function ChdrBfm::AxisPacket_t ChdrBfm::chdr_to_axis (ChdrPacket_t chdr_packet);
int num_words, expected_words;
data_t bus_word = 0;
AxisPacket_t axis_packet = new();
// Check that we have the right number of metadata words
assert (chdr_packet.metadata.size() == chdr_packet.header.num_mdata) else begin
$error("ChdrBfm::chdr_to_axis: Packet metadata size doesn't match header NumMData field");
end
// Calculate the number of words needed to represent this packet
num_words = chdr_packet.data.size() + chdr_packet.metadata.size();
if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS && CHDR_W == 64) begin
// Add two words, one for header and one for timestamp
num_words += 2;
end else begin
// Add one word only for header (which may or may not include a timestamp)
num_words += 1;
end
// Calculate the number of words represented by the Length field
expected_words = chdr_packet.header.length / BYTES_PER_CHDR_W;
if (chdr_packet.header.length % BYTES_PER_CHDR_W != 0) expected_words++;
// Make sure length field matches actual packet length
assert (num_words == expected_words) else begin
$error("ChdrBfm::chdr_to_axis: Packet size doesn't match header Length field");
end
// Insert header
bus_word[63:0] = chdr_packet.header;
if (CHDR_W == 64) begin
axis_packet.data.push_back(bus_word);
if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS) begin
// Insert timestamp
axis_packet.data.push_back(chdr_packet.timestamp);
end
end else begin
// Copy the timestamp word from the header, regardless of whether or not
// this packet uses the timestamp field.
bus_word[127:64] = chdr_packet.timestamp;
axis_packet.data.push_back(bus_word);
end
// Insert metadata
foreach (chdr_packet.metadata[i]) begin
bus_word = chdr_packet.metadata[i];
axis_packet.data.push_back(bus_word);
end
// Insert payload
foreach (chdr_packet.data[i]) begin
bus_word = chdr_packet.data[i];
axis_packet.data.push_back(bus_word);
end
return axis_packet;
endfunction : chdr_to_axis
endpackage : PkgChdrBfm
+1 -1
View File
@@ -134,7 +134,7 @@ package PkgRfnocItemUtils;
// Check if the contents of two buffers is equal
function bit equal(
ItemDataBuff #(item_t) rhs
ItemDataBuff #(item_t,CHDR_W) rhs
);
if (this.size() != rhs.size()) return 0;
for (int i = 0; i < this.size(); i++) begin
+3 -2
View File
@@ -29,6 +29,7 @@ package PkgTestExec;
int num_assertions; // Number of assertions checked for the current test
time start_time, end_time; // Start and end time of the testbench
bit stop_on_error = 1; // Configuration option to stop when an error occurs
bit done = 0; // Flag that sets when tb is finished
bit test_status[$]; // Pass/fail status of each test
timeout_t tb_timeout; // Handle to timeout for the overall testbench
@@ -54,7 +55,7 @@ package PkgTestExec;
// Get the sempahore, to prevent multiple overlapping instances of the
// same testbench.
test_sem.get();
done = 0;
$display("========================================================");
$display("TESTBENCH STARTED: %s", tb_name);
$display("========================================================");
@@ -83,7 +84,6 @@ package PkgTestExec;
assert (num_started == num_finished) else begin
$fatal(1, "Testbench ended before test completed");
end
end_time = $time;
$display("========================================================");
$display("TESTBENCH FINISHED: %s", tb_name);
@@ -98,6 +98,7 @@ package PkgTestExec;
end_timeout(tb_timeout);
done = 1;
if (finish) $finish();
// Release the semaphore to allow new instances of the testbench to run