Files
b210-k7-fpga/lib/sim/control/gearbox_2x1/gearbox_2x1_tb.sv
T
Wade Fife b7bd308e9c fpga: lib: Add 2 to 1 gearbox module
Original-commit: 7f36cced81fb05d3cc107a0a0773fcdfc26f8d64
2021-06-03 11:26:54 -05:00

281 lines
8.0 KiB
Systemverilog

//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: gearbox_2x1_tb
//
// Description: Testbench for gearbox_2x1.
//
`default_nettype none
module gearbox_2x1_tb #(
parameter IN_WORDS = 2,
parameter OUT_WORDS = 1,
parameter BIG_ENDIAN = 0,
parameter PHASE = 1
);
`include "test_exec.svh"
import PkgTestExec::*;
localparam WORD_W = 4;
// Clock periods
localparam real SLOW_CLK_PER_NS = 10.0;
localparam real FAST_CLK_PER_NS = SLOW_CLK_PER_NS / 2.0;
localparam real CLK_IN_PER_NS = (IN_WORDS > OUT_WORDS) ?
SLOW_CLK_PER_NS : FAST_CLK_PER_NS;
localparam real CLK_OUT_PER_NS = (OUT_WORDS > IN_WORDS) ?
SLOW_CLK_PER_NS : FAST_CLK_PER_NS;
// Give the faster clock a T/2 phase offset
localparam FAST_CLK_PHASE = PHASE * (FAST_CLK_PER_NS / 2.0);
localparam real IN_PHASE = (IN_WORDS > OUT_WORDS) ? 0.0 : FAST_CLK_PHASE;
localparam real OUT_PHASE = (OUT_WORDS > IN_WORDS) ? 0.0 : FAST_CLK_PHASE;
// Get gearbox total input and output widths
localparam IN_WORD_W = WORD_W * IN_WORDS;
localparam OUT_WORD_W = WORD_W * OUT_WORDS;
// Number of simulation cycles to run for
localparam NUM_TEST_CYCLES = 50000;
//---------------------------------------------------------------------------
// Clocks and Resets
//---------------------------------------------------------------------------
bit i_clk;
bit o_clk;
bit i_rst;
bit o_rst;
sim_clock_gen #(.PERIOD(CLK_IN_PER_NS), .AUTOSTART(0), .PHASE(IN_PHASE))
clk_gen_in (.clk(i_clk), .rst(i_rst));
sim_clock_gen #(.PERIOD(CLK_OUT_PER_NS),.AUTOSTART(0), .PHASE(OUT_PHASE))
clk_gen_out (.clk(o_clk), .rst(o_rst));
//---------------------------------------------------------------------------
// Device Under Test (DUT)
//---------------------------------------------------------------------------
bit [ IN_WORD_W-1:0] i_tdata = { IN_WORD_W { 1'bX }};
bit i_tvalid = 0;
bit [OUT_WORD_W-1:0] o_tdata;
bit o_tvalid;
gearbox_2x1 #(
.WORD_W (WORD_W),
.IN_WORDS (IN_WORDS),
.OUT_WORDS (OUT_WORDS),
.BIG_ENDIAN (BIG_ENDIAN)
) gearbox_2x1_i (
.i_clk (i_clk),
.i_rst (i_rst),
.i_tdata (i_tdata),
.i_tvalid (i_tvalid),
.o_clk (o_clk),
.o_rst (o_rst),
.o_tdata (o_tdata),
.o_tvalid (o_tvalid)
);
//---------------------------------------------------------------------------
// Input Generator
//---------------------------------------------------------------------------
bit enable_input = 0;
longint i_count;
mailbox #(bit [IN_WORD_W-1:0]) i_queue = new;
initial forever begin : gen_input
clk_gen_in.clk_wait_r();
if (i_rst || !enable_input) begin
i_tdata <= { IN_WORD_W { 1'bX }};
i_tvalid <= 1'b0;
continue;
end
// Input randomly to the gearbox
if ($urandom() % 2 == 0) begin
bit [ IN_WORD_W-1:0] next_input;
next_input = $urandom();
i_count = i_count + 1;
i_tdata <= next_input;
i_tvalid <= 1'b1;
i_queue.put(next_input);
end else begin
i_tdata <= { IN_WORD_W { 1'bX }};
i_tvalid <= 1'b0;
end
end : gen_input
//---------------------------------------------------------------------------
// Packer/Unpacker
//---------------------------------------------------------------------------
//
// Take the data from i_queue and repack it into o_queue with the correct
// width and using the correct endianness.
//
//---------------------------------------------------------------------------
mailbox #(bit [OUT_WORD_W-1:0]) o_queue = new;
initial if (IN_WORDS > OUT_WORDS) begin : unpacker
bit [IN_WORD_W-1:0] in_word;
forever begin
i_queue.get(in_word);
if (BIG_ENDIAN) begin
for (int i = IN_WORDS/OUT_WORDS-1; i >= 0; i--) begin
o_queue.put(in_word[OUT_WORD_W*i +: OUT_WORD_W]);
end
end else begin
for (int i = 0; i < IN_WORDS/OUT_WORDS; i++) begin
o_queue.put(in_word[OUT_WORD_W*i +: OUT_WORD_W]);
end
end
end
end else begin : packer
bit [OUT_WORD_W-1:0] out_word;
forever begin
if (BIG_ENDIAN) begin
for (int i = OUT_WORDS/IN_WORDS-1; i >= 0; i--) begin
i_queue.get(out_word[IN_WORD_W*i +: IN_WORD_W]);
end
end else begin
for (int i = 0; i < OUT_WORDS/IN_WORDS; i++) begin
i_queue.get(out_word[IN_WORD_W*i +: IN_WORD_W]);
end
end
o_queue.put(out_word);
end
end
//---------------------------------------------------------------------------
// Output Checker
//---------------------------------------------------------------------------
longint o_count;
bit [OUT_WORD_W-1:0] expected, actual;
initial forever begin : check_output
string msg;
clk_gen_out.clk_wait_r();
if (o_rst) begin
continue;
end
if (o_tvalid) begin
o_count++;
actual = o_tdata;
o_queue.get(expected);
msg = $sformatf("Output didn't match expected value! Expected 0x%0X, received 0x%0X.",
expected, actual);
`ASSERT_ERROR(actual == expected, msg);
end
end : check_output
//---------------------------------------------------------------------------
// Main Test Process
//---------------------------------------------------------------------------
initial begin : tb_main
string msg;
string tb_name;
int min_out_words, max_out_words;
// Initialize the test exec object for this testbench
tb_name = $sformatf( {
"gearbox_2x1_tb\n",
"IN_WORDS = %01d\n",
"OUT_WORDS = %01d\n",
"BIG_ENDIAN = %01d\n",
"PHASE = %01d" },
IN_WORDS, OUT_WORDS, BIG_ENDIAN, PHASE
);
test.start_tb(tb_name);
// Don't start the clocks until after start_tb() returns. This ensures that
// the clocks aren't toggling while other instances of this testbench are
// running, which speeds up simulation time.
clk_gen_in.start();
clk_gen_out.start();
//--------------------------------
// Reset
//--------------------------------
test.start_test("Reset", 10us);
clk_gen_in.reset();
clk_gen_out.reset();
if (i_rst) @i_rst;
if (o_rst) @o_rst;
test.end_test();
//--------------------------------
// Test Sequences
//--------------------------------
test.start_test("Random data", 10ms);
enable_input <= 1;
// Let it run for a while
clk_gen_in.clk_wait_r(NUM_TEST_CYCLES);
// Stop inputting and wait long enough for any data to propagate through
enable_input <= 0;
repeat (8*OUT_WORDS) @i_clk;
repeat (8*IN_WORDS) @o_clk;
// Calculate how many words we expect to have received on the output. We
// might be in the middle of a word, so it might be less than what was
// input.
if (OUT_WORDS > IN_WORDS) begin
min_out_words = ((i_count * IN_WORDS) / OUT_WORDS) * OUT_WORDS;
max_out_words = min_out_words;
end else begin
min_out_words = (i_count-1) * IN_WORDS;
max_out_words = i_count * IN_WORDS;
end
// Make sure the word counts match
msg = $sformatf("Word counts don't match. Input %0d, output %0d.",
IN_WORDS*i_count, OUT_WORDS*o_count);
`ASSERT_ERROR(o_count*OUT_WORDS >= min_out_words &&
o_count*OUT_WORDS <= max_out_words , msg);
msg = $sformatf("Only %0d words input. Expected about %0d.",
i_count*IN_WORDS, 0.5*NUM_TEST_CYCLES*IN_WORDS);
`ASSERT_ERROR(i_count > 0.4*NUM_TEST_CYCLES, msg);
$display("Tested %0d output words", o_count);
test.end_test();
//--------------------------------
// Finish Up
//--------------------------------
// 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_in.kill();
clk_gen_out.kill();
end : tb_main
endmodule : gearbox_2x1_tb
`default_nettype wire