aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim/rfnoc/PkgTestExec.sv
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/sim/rfnoc/PkgTestExec.sv')
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgTestExec.sv297
1 files changed, 297 insertions, 0 deletions
diff --git a/fpga/usrp3/sim/rfnoc/PkgTestExec.sv b/fpga/usrp3/sim/rfnoc/PkgTestExec.sv
new file mode 100644
index 000000000..ba4455912
--- /dev/null
+++ b/fpga/usrp3/sim/rfnoc/PkgTestExec.sv
@@ -0,0 +1,297 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: PkgTestExec
+//
+// Description: This package provides infrastructure for tracking the state of
+// testbench execution and the results of each test.
+//
+
+
+
+package PkgTestExec;
+
+ timeunit 1ns;
+ timeprecision 1ps;
+
+ typedef enum { SEV_INFO, SEV_WARNING, SEV_ERROR, SEV_FATAL } severity_t;
+
+ typedef std::process timeout_t;
+
+ class TestExec;
+
+ string tb_name; // Name of the testbench
+ string current_test; // Name of the current test being run
+ int num_started, num_finished; // Number of tests started and finished
+ int num_passed; // Number of tests that have passed
+ 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 test_status[$]; // Pass/fail status of each test
+
+ timeout_t tb_timeout; // Handle to timeout for the overall testbench
+ timeout_t test_timeout; // Handle to timeout for current test
+
+ semaphore test_sem; // Testbench semaphore
+
+
+ function new();
+ $timeformat(-9, 0, " ns", 12);
+ this.test_sem = new(1);
+ endfunction : new
+
+
+ // Call start_tb() at the start of a testbench to initialize state tracking
+ // properties.
+ //
+ // time_limit: Max simulation time allowed before at timeout occurs.
+ // This is a catch-all, in case things go very wrong during
+ // simulation and cause it to never end.
+ //
+ task start_tb(string tb_name, realtime time_limit = 10ms);
+ // Get the sempahore, to prevent multiple overlapping instances of the
+ // same testbench.
+ test_sem.get();
+
+ $display("========================================================");
+ $display("TESTBENCH STARTED: %s", tb_name);
+ $display("========================================================");
+ this.tb_name = tb_name;
+ start_time = $time;
+ test_status = {};
+ num_started = 0;
+ num_finished = 0;
+ num_passed = 0;
+ start_timeout(
+ tb_timeout,
+ time_limit,
+ $sformatf("Testbench \"%s\" time limit exceeded", tb_name),
+ SEV_FATAL
+ );
+ endtask : start_tb
+
+
+ // Call end_tb() at the end of a testbench to report final statistics and,
+ // optionally, end simulation.
+ //
+ // finish: Set to 1 (default) to cause $finish() to be called at the
+ // end of simulation, cuasing the simulator to close.
+ //
+ function void end_tb(bit finish = 1);
+ 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);
+ $display(" - Time elapsed: %0t", end_time - start_time);
+ $display(" - Tests Run: %0d", num_finished);
+ $display(" - Tests Passed: %0d", num_passed);
+ $display(" - Tests Failed: %0d", num_started - num_passed);
+ $display("Result: %s",
+ (num_started == num_passed) && (num_finished > 0)
+ ? "PASSED " : "FAILED!!!");
+ $display("========================================================");
+
+ end_timeout(tb_timeout);
+
+ if (finish) $finish();
+
+ // Release the semaphore to allow new instances of the testbench to run
+ test_sem.put();
+ endfunction : end_tb
+
+
+ // Call at the start of each test with the name of the test.
+ //
+ // test_name: String name for the test to be started
+ //
+ task start_test(string test_name, realtime time_limit = 0);
+ // Make sure a there isn't already a test running
+ assert (num_started == num_finished) else begin
+ $fatal(1, "Test started before completing previous test");
+ end
+
+ // Create a timeout for this test
+ if (time_limit > 0) begin
+ start_timeout(
+ test_timeout,
+ time_limit,
+ $sformatf("Test \"%s\" time limit exceeded", test_name),
+ SEV_FATAL
+ );
+ end
+
+ current_test = test_name;
+ num_started++;
+ $display("[TEST CASE %3d] (t = %t) BEGIN: %s...", num_started, $time, test_name);
+ test_status.push_back(1); // Set status to 1 (passed) initially
+ num_assertions = 0;
+ endtask : start_test
+
+
+ // Call end_test() at the end of each test.
+ //
+ // test_result: Optional value to indicate the overall pass/fail result
+ // of the test. Use non-zero for pass, 0 for fail.
+ //
+ task end_test(int test_result = 1);
+ bit passed;
+
+ assert (num_started == num_finished + 1) else begin
+ $fatal(1, "No test running");
+ end
+
+ end_timeout(test_timeout);
+
+ passed = test_status[num_started-1] && test_result;
+ num_finished++;
+ $display("[TEST CASE %3d] (t = %t) DONE... %s",
+ num_started, $time, passed ? "Passed" : "FAILED");
+
+ if (passed) num_passed++;
+
+ current_test = "";
+ endtask : end_test
+
+
+ // Assert the given expression and call $error() if it fails. Simulation
+ // will also be stopped (using $stop) if stop_on_error is true.
+ //
+ // expr: The expression value to be asserted
+ // message: String to report if the assertion fails
+ //
+ function void assert_error(int expr, string message = "");
+ num_assertions++;
+ assert (expr) else begin
+ test_status[num_started] = 0;
+ $error(message);
+ if (stop_on_error) $stop;
+ end
+ endfunction : assert_error
+
+
+ // Assert the given expression and call $fatal() if it fails.
+ //
+ // expr: The expression value to be asserted
+ // message: String to report if the assertion fails
+ //
+ function void assert_fatal(int expr, string message = "");
+ num_assertions++;
+ assert (expr) else begin
+ test_status[num_started] = 0;
+ $fatal(1, message);
+ end
+ endfunction : assert_fatal
+
+
+ // Assert the given expression and call $warning() if it fails.
+ //
+ // expr: The expression value to be asserted
+ // message: String to report if the assertion fails
+ //
+ function void assert_warning(int expr, string message = "");
+ num_assertions++;
+ assert (expr) else begin
+ $warning(message);
+ end
+ endfunction : assert_warning
+
+
+ // Assert the given expression and call the appropriate severity or fatal
+ // task if the expression fails.
+ //
+ // expr: The expression value to be asserted
+ // message: String to report if the assertion fails
+ // severity: Indicates the type of severity task that should be used if
+ // the assertion fails ($info, $warning, $error, $fatal).
+ // Default value is SEV_ERROR.
+ //
+ function void assert_sev(
+ int expr,
+ string message = "",
+ severity_t severity = SEV_ERROR
+ );
+ num_assertions++;
+ assert (expr) else begin
+ case (severity)
+ SEV_INFO: begin
+ $info(message);
+ end
+ SEV_WARNING: begin
+ $warning(message);
+ end
+ SEV_ERROR: begin
+ $error(message);
+ test_status[num_started] = 0;
+ if (stop_on_error) $stop;
+ end
+ default: begin // SEV_FATAL
+ test_status[num_started] = 0;
+ $fatal(1, message);
+ end
+ endcase
+ end
+ endfunction : assert_sev
+
+
+ // Create a timeout that will expire after the indicated delay, causing an
+ // error if the timeout is not canceled before the delay has elapsed.
+ //
+ // handle: A handle to the timeout process created. Use this
+ // handle to end the timeout.
+ // timeout_delay: Amount of time to wait before the timeout expires.
+ // message: String message to display if the timeout expires.
+ // severity: Indicates the type of severity task that should be
+ // used if the timeout expires. Default is SEV_ERROR.
+ //
+ task start_timeout(
+ output timeout_t handle,
+ input realtime timeout_delay,
+ input string message = "Timeout",
+ input severity_t severity = SEV_ERROR
+ );
+ mailbox #(std::process) pid = new(1);
+ fork
+ begin : timeout
+ pid.put(process::self());
+ #(timeout_delay);
+ assert_sev(0, {"Timeout: ", message}, severity);
+ end
+ join_none
+ #0; // Start the timeout process so we can get its process ID
+
+ // Return the process ID
+ pid.get(handle);
+ endtask
+
+
+ // Cancel the timeout with the given handle. This kills the process
+ // running the timeout delay.
+ //
+ // handle: Handle created by start_timeout() for the timeout process.
+ //
+ function void end_timeout(timeout_t handle);
+ if (handle != null)
+ if (handle.status != process::FINISHED) handle.kill();
+ endfunction;
+
+ endclass : TestExec
+
+
+ //---------------------------------------------------------------------------
+ // Test Object
+ //---------------------------------------------------------------------------
+ //
+ // This is the default TestExec object instance shared by all instances of
+ // the running the testbench.
+ //
+ //---------------------------------------------------------------------------
+
+ TestExec test = new();
+
+endpackage : PkgTestExec
+