aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim/general
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/sim/general')
-rw-r--r--fpga/usrp3/sim/general/Makefile.srcs13
-rw-r--r--fpga/usrp3/sim/general/sim_clks_rsts.vh94
-rw-r--r--fpga/usrp3/sim/general/sim_exec_report.vh138
-rw-r--r--fpga/usrp3/sim/general/sim_file_io.svh125
-rw-r--r--fpga/usrp3/sim/general/sim_math.vh276
5 files changed, 646 insertions, 0 deletions
diff --git a/fpga/usrp3/sim/general/Makefile.srcs b/fpga/usrp3/sim/general/Makefile.srcs
new file mode 100644
index 000000000..90ce1a028
--- /dev/null
+++ b/fpga/usrp3/sim/general/Makefile.srcs
@@ -0,0 +1,13 @@
+#
+# Copyright 2015 Ettus Research LLC
+#
+
+##################################################
+# General Simulation Libraries/Headers
+##################################################
+SIM_GENERAL_SRCS = $(abspath $(addprefix $(BASE_DIR)/../sim/general/, \
+sim_clks_rsts.vh \
+sim_exec_report.vh \
+sim_math.vh \
+sim_file_io.svh \
+))
diff --git a/fpga/usrp3/sim/general/sim_clks_rsts.vh b/fpga/usrp3/sim/general/sim_clks_rsts.vh
new file mode 100644
index 000000000..62de98faa
--- /dev/null
+++ b/fpga/usrp3/sim/general/sim_clks_rsts.vh
@@ -0,0 +1,94 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+
+// Generates a persistent clock that starts at t=0 and runs forever
+//
+// Usage: `DEFINE_CLK(clk_name,period,duty_cycle)
+// where
+// - clk_name: The clock net to be generated
+// - period: Period of the clock in simulator ticks
+// - duty_cycle: Percentage duty cycle
+//
+`define DEFINE_CLK(clk_name, period, duty_cycle) \
+ reg clk_name = 0; \
+ always begin \
+ clk_name = 1; \
+ #((1.0*period)*(1.0*duty_cycle/100)); \
+ clk_name = 0; \
+ #((1.000*period)*(1.0-(1.0*duty_cycle/100))); \
+ end
+
+// Generates a persistent clock that starts at t=0 and runs forever
+//
+// Usage: `DEFINE_CLK(clk_name,period,duty_cycle)
+// where
+// - clk_name: The clock net to be generated
+// - period: Period of the clock in simulator ticks
+// - duty_cycle: Percentage duty cycle
+//
+`define DEFINE_DIFF_CLK(clk_name_p, clk_name_n, period, duty_cycle) \
+ reg clk_name_p = 0; \
+ reg clk_name_n = 1; \
+ always begin \
+ clk_name_p = 1; \
+ clk_name_n = 0; \
+ #((1.0*period)*(1.0*duty_cycle/100)); \
+ clk_name_p = 0; \
+ clk_name_n = 1; \
+ #((1.000*period)*(1.0-(1.0*duty_cycle/100))); \
+ end
+
+// Generates a clock that starts at the specified time and runs forever
+//
+// Usage: `DEFINE_LATE_START_CLK(clk_name,period,duty_cycle,start_time,start_time_res)
+// where
+// - clk_name: The clock net to be generated
+// - period: Period of the clock in simulator ticks
+// - duty_cycle: Percentage duty cycle
+// - start_time: Start time for clock in simulator ticks
+// - start_time_res: Start time resolution (must be > timescale increment and < start_time)
+//
+`define DEFINE_LATE_START_CLK(clk_name, period, duty_cycle, start_time, start_time_res) \
+ reg clk_name = 0; \
+ reg clk_name``_locked = 0; \
+ always begin \
+ while (!clk_name``_locked) #start_time_res; \
+ clk_name = 1; \
+ #((1.0*period)*(1.0*duty_cycle/100)); \
+ clk_name = 0; \
+ #((1.000*period)*(1.0-(1.0*duty_cycle/100))); \
+ end \
+ initial begin \
+ #(start_time) clk_name``_locked = 1; \
+ end
+
+// Generates an active high reset
+//
+// Usage: `DEFINE_RESET(reset_name,reset_time,reset_duration)
+// where
+// - reset_name: The reset net to be generated
+// - reset_time: Time at which reset will be asserted (i.e. rst=1)
+// - reset_duration: Duration of reset assertion
+//
+`define DEFINE_RESET(reset_name, reset_time, reset_duration) \
+ reg reset_name = (reset_time==0); \
+ initial begin \
+ #(reset_time) reset_name = 1; \
+ #(reset_time+reset_duration) reset_name = 0; \
+ end
+
+// Generates an active low reset
+//
+// Usage: `DEFINE_RESET_N(reset_name,reset_time,reset_duration)
+// where
+// - reset_name: The reset net to be generated
+// - reset_time: Time at which reset will be asserted (i.e. rst=0)
+// - reset_duration: Duration of reset assertion
+//
+`define DEFINE_RESET_N(reset_name, reset_time, reset_duration) \
+ reg reset_name = (reset_time!=0); \
+ initial begin \
+ #(reset_time) reset_name = 0; \
+ #(reset_time+reset_duration) reset_name = 1; \
+ end
diff --git a/fpga/usrp3/sim/general/sim_exec_report.vh b/fpga/usrp3/sim/general/sim_exec_report.vh
new file mode 100644
index 000000000..a6ede6a25
--- /dev/null
+++ b/fpga/usrp3/sim/general/sim_exec_report.vh
@@ -0,0 +1,138 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+
+// Initializes state for a test bench.
+// This macro *must be* called within the testbench module but
+// outside the primary initial block
+// Its sets up boilerplate code for:
+// - Logging to console
+// - Test execution tracking
+// - Gathering test results
+// - Bounding execution time based on the SIM_TIMEOUT_US vdef
+
+`ifndef SIM_TIMEOUT_US
+`define SIM_TIMEOUT_US 100000 // Default: 100 ms
+`endif
+
+// Usage: `TEST_BENCH_INIT(test_name,min_tc_run_count,ns_per_tick)
+// where
+// - tb_name: Name of the testbench. (Only used during reporting)
+// - min_tc_run_count: Number of test cases in testbench. (Used to detect stalls and inf-loops)
+// - ns_per_tick: The time_unit_base from the timescale declaration
+//
+`define TEST_BENCH_INIT(tb_name, min_tc_run_count, ns_per_tick) \
+ localparam sim_time_increment = 100; \
+ reg tc_running = 0; \
+ reg tc_failed = 0; \
+ reg tc_all_done = 0; \
+ real sim_time = 0.0; \
+ integer tc_run_count = 0; \
+ integer tc_pass_count = 0; \
+ \
+ initial begin : tb_timekeeper \
+ #0; \
+ $timeformat(-9, 0, " ns", 10); \
+ $display("========================================================"); \
+ $display("TESTBENCH STARTED: %s", tb_name); \
+ $display("========================================================"); \
+ if (1000.0*`SIM_TIMEOUT_US < sim_time_increment) begin \
+ $error("Total simulation time less than simulation step size!"); \
+ end \
+ tc_running = 0; \
+ tc_failed = 0; \
+ tc_run_count = 0; \
+ tc_pass_count = 0; \
+ while (~tc_all_done & sim_time < 1000.0*`SIM_TIMEOUT_US) begin \
+ #(sim_time_increment); \
+ sim_time += sim_time_increment; \
+ end \
+ $display("========================================================"); \
+ $display("TESTBENCH FINISHED: %s", tb_name); \
+ $display(" - Time elapsed: %0t%s", $realtime(), (sim_time >= 1000.0*`SIM_TIMEOUT_US) ? " (Timed out!)" : ""); \
+ $display(" - Tests Expected: %0d", min_tc_run_count); \
+ $display(" - Tests Run: %0d", tc_run_count); \
+ $display(" - Tests Passed: %0d", tc_pass_count); \
+ $display("Result: %s", ((tc_run_count>=min_tc_run_count)&&(tc_run_count==tc_pass_count)?"PASSED ":"FAILED!!!")); \
+ $display("========================================================"); \
+ $finish; \
+ end
+
+// Ends test bench. Place after final test.
+//
+// Usage: `TEST_BENCH_DONE
+`define TEST_BENCH_DONE tc_all_done = 1;
+
+// Indicates the start of a test case
+// This macro *must be* called inside the primary initial block
+//
+// Usage: `TEST_CASE_START(test_name)
+// where
+// - test_name: The name of the test.
+//
+`define TEST_CASE_START(test_name) \
+ #0; \
+ tc_running = 1; \
+ tc_failed = 0; \
+ tc_run_count = tc_run_count + 1; \
+ $display("[TEST CASE %3d] (t=%09d) BEGIN: %s...", tc_run_count, $time, test_name);
+
+// Indicates the end of a test case
+// This macro *must be* called inside the primary initial block
+// The pass/fail status of test case is determined based on the
+// the user specified outcome and the number of fatal or error
+// ASSERTs triggered in the test case.
+//
+// Usage: `TEST_CASE_DONE(test_result)
+// where
+// - test_result: User specified outcome
+//
+`define TEST_CASE_DONE(result) \
+ #0; \
+ tc_running = 0; \
+ $display("[TEST CASE %3d] (t=%09d) DONE... %s", tc_run_count, $time, ((((result)===1'b1)&~tc_failed)?"Passed":"FAILED")); \
+ if (((result)===1'b1)&~tc_failed) tc_pass_count = tc_pass_count + 1;
+
+// Wrapper around a an assert.
+// ASSERT_FATAL throws an error assertion and halts the simulator
+// if cond is not satisfied
+//
+// Usage: `ASSERT_FATAL(cond,msg)
+// where
+// - cond: Condition for the assert
+// - msg: Message for the assert
+//
+`define ASSERT_FATAL(cond, msg) \
+ assert(cond) else begin \
+ tc_failed = 1; \
+ $error(msg); \
+ tc_all_done = 1; \
+ #(sim_time_increment); \
+ end
+
+// Wrapper around a an assert.
+// ASSERT_ERROR throws an error assertion and fails the test case
+// if cond is not satisfied. The simulator will *not* halt
+//
+// Usage: `ASSERT_ERROR(cond,msg)
+// where
+// - cond: Condition for the assert
+// - msg: Message for the assert
+//
+`define ASSERT_ERROR(cond, msg) \
+ assert(cond) else begin \
+ tc_failed = 1; \
+ $error(msg); \
+ end
+
+// Wrapper around a an assert.
+// ASSERT_WARNING throws an warning assertion but does not fail the
+// test case if cond is not satisfied. The simulator will *not* halt
+//
+// Usage: `ASSERT_WARNING(cond,msg)
+// where
+// - cond: Condition for the assert
+// - msg: Message for the assert
+//
+`define ASSERT_WARN(cond, msg) \
+ assert(cond) else $warning(msg);
diff --git a/fpga/usrp3/sim/general/sim_file_io.svh b/fpga/usrp3/sim/general/sim_file_io.svh
new file mode 100644
index 000000000..97fadd741
--- /dev/null
+++ b/fpga/usrp3/sim/general/sim_file_io.svh
@@ -0,0 +1,125 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+
+`ifndef WORKING_DIR
+ `define WORKING_DIR "."
+`endif
+
+`define ABSPATH(name) {`WORKING_DIR, "/", name}
+
+typedef enum {
+ READ, WRITE, APPEND
+} fopen_mode_t;
+
+typedef enum {
+ HEX, DEC, OCT, BIN, FLOAT
+} fformat_t;
+
+// Create a handle to a data_file with
+// - FILENAME: Name of the file
+// - FORMAT: Data format (HEX, DEC, OCT, BIN, FLOAT)
+// - DWIDTH: Width of each element stored in the file (one line per word)
+//
+
+//TODO: We would ideally use a class but that is not
+// supported by most simulators.
+interface data_file_t #(
+ parameter FILENAME = "test.hex",
+ parameter FORMAT = HEX,
+ parameter DWIDTH = 64
+) (input clk);
+ bit is_open;
+ integer handle;
+
+ // Open the data file for reading or writing.
+ //
+ // Usage: open(mode)
+ // where
+ // - mode: RW mode (Choose from: READ, WRITE, APPEND)
+ //
+ function open(fopen_mode_t mode = READ);
+ if (mode == APPEND)
+ handle = $fopen(`ABSPATH(FILENAME), "a");
+ else if (mode == WRITE)
+ handle = $fopen(`ABSPATH(FILENAME), "w");
+ else
+ handle = $fopen(`ABSPATH(FILENAME), "r");
+
+ if (handle == 0) begin
+ $error("Could not open file: %s", `ABSPATH(FILENAME));
+ $finish();
+ end
+ is_open = 1;
+ endfunction
+
+
+ // Close an open data file. No-op if file isn't already open
+ //
+ // Usage: close()
+ //
+ function close();
+ $fclose(handle);
+ handle = 0;
+ is_open = 0;
+ endfunction
+
+ // Is end-of-file reached.
+ //
+ // Usage: is_eof() Returns eof
+ // where
+ // - eof: A boolean
+ //
+ function logic is_eof();
+ return ($feof(handle));
+ endfunction
+
+ // Read a line from the datafile
+ //
+ // Usage: readline() Returns data
+ // where
+ // - data: A logic array of width DWIDTH containing the read word
+ //
+ function logic [DWIDTH-1:0] readline();
+ automatic logic [DWIDTH-1:0] word = 64'h0;
+ automatic integer status;
+
+ if (FORMAT == HEX)
+ status = $fscanf(handle, "%x\n", word);
+ else if (FORMAT == DEC)
+ status = $fscanf(handle, "%d\n", word);
+ else if (FORMAT == OCT)
+ status = $fscanf(handle, "%o\n", word);
+ else if (FORMAT == BIN)
+ status = $fscanf(handle, "%b\n", word);
+ else if (FORMAT == DEC)
+ status = $fscanf(handle, "%g\n", word);
+ else
+ $error("Invalid format");
+
+ return word;
+ endfunction
+
+ // Write a line to the datafile
+ //
+ // Usage: writeline(data)
+ // where
+ // - data: A logic array of width DWIDTH to write to the file
+ //
+ function void writeline(logic [DWIDTH-1:0] word);
+ if (FORMAT == HEX)
+ $fdisplay(handle, "%x", word);
+ else if (FORMAT == DEC)
+ $fdisplay(handle, "%d", word);
+ else if (FORMAT == OCT)
+ $fdisplay(handle, "%o", word);
+ else if (FORMAT == BIN)
+ $fdisplay(handle, "%b", word);
+ else if (FORMAT == DEC)
+ $fdisplay(handle, "%g", word);
+ else
+ $error("Invalid format");
+ endfunction
+
+endinterface
+
diff --git a/fpga/usrp3/sim/general/sim_math.vh b/fpga/usrp3/sim/general/sim_math.vh
new file mode 100644
index 000000000..a08870740
--- /dev/null
+++ b/fpga/usrp3/sim/general/sim_math.vh
@@ -0,0 +1,276 @@
+// All code take from the HDLCon paper:
+// "Verilog Transcendental Functions for Numerical Testbenches"
+//
+// Authored by:
+// Mark G. Arnold marnold@co.umist.ac.uk,
+// Colin Walter c.walter@co.umist.ac.uk
+// Freddy Engineer freddy.engineer@xilinx.com
+//
+
+
+
+// The sine function is approximated with a polynomial which works
+// for -π/2 < x < π/2. (This polynomial, by itself, was used as a
+// Verilog example in [2]; unfortunately there was a typo with the
+// coefficients. The correct coefficients together with an error
+// analysis are given in [3].) For arguments outside of -π/2 < x < π/2,
+// the identities sin(x) = -sin(-x) and sin(x) = -sin(x-π) allow the
+// argument to be shifted to be within this range. The latter identity
+// can be applied repeatedly. Doing so could cause inaccuracies for
+// very large arguments, but in practice the errors are acceptable
+// if the Verilog simulator uses double-precision floating point.
+
+function real sin;
+ input x;
+ real x;
+ real x1,y,y2,y3,y5,y7,sum,sign;
+ begin
+ sign = 1.0;
+ x1 = x;
+ if (x1<0)
+ begin
+ x1 = -x1;
+ sign = -1.0;
+ end
+ while (x1 > 3.14159265/2.0)
+ begin
+ x1 = x1 - 3.14159265;
+ sign = -1.0*sign;
+ end
+ y = x1*2/3.14159265;
+ y2 = y*y;
+ y3 = y*y2;
+ y5 = y3*y2;
+ y7 = y5*y2;
+ sum = 1.570794*y - 0.645962*y3 +
+ 0.079692*y5 - 0.004681712*y7;
+ sin = sign*sum;
+ end
+endfunction
+
+// The cosine and tangent are computed from the sine:
+function real cos;
+ input x;
+ real x;
+ begin
+ cos = sin(x + 3.14159265/2.0);
+ end
+endfunction
+
+
+function real tan;
+ input x;
+ real x;
+ begin
+ tan = sin(x)/cos(x);
+ end
+endfunction
+
+// The base-two exponential (antilogarithm) function, 2x, is computed by
+// examining the bits of the argument, and for those bits of the argument
+// that are 1, multiplying the result by the corresponding power of a base
+// very close to one. For example, if there were only two bits after
+// the radix point, the base would be the fourth root of two, 1.1892.
+// This number is squared on each iteration: 1.4142, 2.0, 4.0, 16.0.
+// So, if x is 101.112, the function computes 25.75 as 1.1892*1.4142*2.0*16.0 = 53.81.
+// In general, for k bits of precision, the base would be the 2k root of two.
+// Since we need about 23 bits of accuracy for our function, the base we use
+// is the 223 root of two, 1.000000082629586. This constant poses a problem
+// to some Verilog parsers, so we construct it in two parts. The following
+// function computes the appropriate root of two by repeatedly squaring this constant:
+
+function real rootof2;
+ input n;
+ integer n;
+ real power;
+ integer i;
+
+ begin
+ power = 0.82629586;
+ power = power / 10000000.0;
+ power = power + 1.0;
+ i = -23;
+
+ if (n >= 1)
+ begin
+ power = 2.0;
+ i = 0;
+ end
+
+ for (i=i; i< n; i=i+1)
+ begin
+ power = power * power;
+ end
+ rootof2 = power;
+ end
+endfunction // if
+
+// This function is used for computing both antilogarithms and logarithms.
+// This routine is never called with n less than -23, thus no validity check
+// need be performed. When n>0, the exponentiation begins with 2.0 in order to
+// improve accuracy.
+// For computing the antilogarithm, we make use of the identity ex = 2x/ln(2),
+// and then proceed as in the example above. The constant 1/ln(2) = 1.44269504.
+// Here is the natural exponential function:
+
+function real exp;
+ input x;
+ real x;
+ real x1,power,prod;
+ integer i;
+ begin
+ x1 = fabs(x)*1.44269504;
+ if (x1 > 255.0)
+ begin
+ exp = 0.0;
+ if (x>0.0)
+ begin
+ $display("exp illegal argument:",x);
+ $stop;
+ end
+ end
+ else
+ begin
+ prod = 1.0;
+ power = 128.0;
+ for (i=7; i>=-23; i=i-1)
+ begin
+ if (x1 > power)
+ begin
+ prod = prod * rootof2(i);
+ x1 = x1 - power;
+ end
+ power = power / 2.0;
+ end
+ if (x < 0)
+ exp = 1.0/prod;
+ else
+ exp = prod;
+ end
+ end
+endfunction // fabs
+
+// The function prints an error message if the argument is too large
+// (greater than about 180). All error messages in this package are
+// followed by $stop to allow the designer to use the debugging
+// features of Verilog to determine the cause of the error, and
+// possibly to resume the simulation. An argument of less than
+// about –180 simply returns zero with no error. The main loop
+// assumes a positive argument. A negative argument is computed as 1/e-x.
+// The logarithm function prints an error message for arguments less
+// than or equal to zero because the real-valued logarithm is not
+// defined for such arguments. The loop here requires an argument
+// greater than or equal to one. For arguments between zero and one,
+// this code uses the identity ln(1/x) = -ln(x).
+
+function real log;
+ input x;
+ real x;
+ real re,log2;
+ integer i;
+ begin
+ if (x <= 0.0)
+ begin
+ $display("log illegal argument:",x);
+ $stop;
+ log = 0;
+ end
+ else
+ begin
+ if (x<1.0)
+ re = 1.0/x;
+ else
+ re = x;
+ log2 = 0.0;
+ for (i=7; i>=-23; i=i-1)
+ begin
+ if (re > rootof2(i))
+ begin
+ re = re/rootof2(i);
+ log2 = 2.0*log2 + 1.0;
+ end
+ else
+ log2 = log2*2;
+ end
+ if (x < 1.0)
+ log = -log2/12102203.16;
+ else
+ log = log2/12102203.16;
+ end
+ end
+endfunction
+
+// The code only divides re by rootof2(i) when the re is larger
+// (so that the quotient will be greater than 1.0). Each time
+// such a division occurs, a bit that is 1 is recorded in the
+// whole number result (multiply by 2 and add 1). Otherwise,
+// a zero is recorded (multiply by 2). At the end of the loop,
+// log2 will contain 223 log2|x|. We divide by 223 and use the
+// identity ln(x) = log2(x)/log2(e). The constant 12102203.16 is 223 log2(e).
+// The log(x) and exp(x)functions are used to implement the pow(x,y) and sqrt(x) functions:
+
+function real pow;
+ input x,y;
+ real x,y;
+ begin
+ if (x<0.0)
+ begin
+ $display("pow illegal argument:",x);
+ $stop;
+ end
+ pow = exp(y*log(x));
+ end
+endfunction
+
+function real sqrt;
+ input x;
+ real x;
+ begin
+ if (x<0.0)
+ begin
+ $display("sqrt illegal argument:",x);
+ $stop;
+ end
+ sqrt = exp(0.5*log(x));
+ end
+endfunction
+
+// The arctangent [3,7] is computed as a continued fraction,
+// using the identities tan-1(x) = -tan-1(-x) and tan-1(x) = π/2 - tan-1(1/x)
+// to reduce the range to 0 < x < 1:
+
+function real atan;
+ input x;
+ real x;
+ real x1,x2,sign,bias;
+ real d3,s3;
+ begin
+ sign = 1.0;
+ bias = 0.0;
+ x1 = x;
+ if (x1 < 0.0)
+ begin
+ x1 = -x1;
+ sign = -1.0;
+ end
+ if (x1 > 1.0)
+ begin
+ x1 = 1.0/x1;
+ bias = sign*3.14159265/2.0;
+ sign = -1.0*sign;
+ end
+ x2 = x1*x1;
+ d3 = x2 + 1.44863154;
+ d3 = 0.26476862 / d3;
+ s3 = x2 + 3.3163354;
+ d3 = s3 - d3;
+ d3 = 7.10676 / d3;
+ s3 = 6.762139 + x2;
+ d3 = s3 - d3;
+ d3 = 3.7092563 / d3;
+ d3 = d3 + 0.17465544;
+ atan = sign*x1*d3+bias;
+ end
+endfunction
+
+// The other functions (asin(x) and acos(x)) are computed from the arctangent.