diff --git a/dv/uvm/icache/dv/Makefile b/dv/uvm/icache/dv/Makefile
index 008a9c9f..2ec0f1e3 100644
--- a/dv/uvm/icache/dv/Makefile
+++ b/dv/uvm/icache/dv/Makefile
@@ -25,7 +25,7 @@ default-seed := 123
SEED=$(if $(filter 1,$(RESEED)),$(default-seed),)
# Specify which tests to run. Defaults to the empty string, which
-# means dvsim will run its default (the "sanity" suite).
+# means dvsim will run its default (the "smoke" suite of tests).
TESTS=
ibex-top := ../../../..
diff --git a/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson b/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson
index 737eac4b..dbc7920e 100644
--- a/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson
+++ b/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson
@@ -23,7 +23,7 @@
// Import additional common sim cfg files.
import_cfgs: [
// Project wide common sim cfg file
- "{proj_root}/vendor/lowrisc_ip/dv/data/common_sim_cfg.hjson"
+ "{proj_root}/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson"
]
build_modes: [
@@ -111,7 +111,7 @@
// List of regressions.
regressions: [
{
- name: sanity
+ name: smoke
tests: ["ibex_icache_sanity",
"ibex_icache_passthru",
"ibex_icache_caching",
diff --git a/vendor/lowrisc_ip.lock.hjson b/vendor/lowrisc_ip.lock.hjson
index 9498185d..6a129b9d 100644
--- a/vendor/lowrisc_ip.lock.hjson
+++ b/vendor/lowrisc_ip.lock.hjson
@@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/lowRISC/opentitan
- rev: 92e9242424c72c59008e267dd3779e2af5ec8e83
+ rev: e619fc60c6b9c755043eba65a41dc47815612834
}
}
diff --git a/vendor/lowrisc_ip.vendor.hjson b/vendor/lowrisc_ip.vendor.hjson
index 786dc7c5..3e744b54 100644
--- a/vendor/lowrisc_ip.vendor.hjson
+++ b/vendor/lowrisc_ip.vendor.hjson
@@ -11,20 +11,10 @@
}
mapping: [
- // We have to apply a patch to the vendored files from hw/dv/data,
- // because they contain an OpenTitan specific path.
- {
- from: "hw/dv/data",
- to: "dv/data",
- patch_dir: "dv_data"
- },
-
{from: "hw/dv/sv/common_ifs", to: "dv/sv/common_ifs"},
{from: "hw/dv/sv/csr_utils", to: "dv/sv/csr_utils"},
{from: "hw/dv/sv/dv_base_reg", to: "dv/sv/dv_base_reg"},
{from: "hw/dv/sv/mem_model", to: "dv/sv/mem_model"},
- {from: "hw/dv/tools", to: "dv/tools"},
- {from: "hw/dv/verilator", to: "dv/verilator"},
// We apply a patch to fix the bus_params_pkg core file name when
// vendoring in dv_lib and dv_utils. This allows us to have an
@@ -39,6 +29,15 @@
to: "dv/sv/dv_utils",
patch_dir: "dv_utils",
},
+ // We have to apply a patch to the vendored files from hw/dv/tools
+ // because they contain OpenTitan specific paths.
+ {
+ from: "hw/dv/tools",
+ to: "dv/tools",
+ patch_dir: "dv_tools"
+ },
+
+ {from: "hw/dv/verilator", to: "dv/verilator"},
{from: "hw/ip/prim", to: "ip/prim"},
{from: "hw/ip/prim_generic", to: "ip/prim_generic"},
diff --git a/vendor/lowrisc_ip/dv/sv/common_ifs/index.md b/vendor/lowrisc_ip/dv/sv/common_ifs/index.md
index d54a46cb..f28e41bb 100644
--- a/vendor/lowrisc_ip/dv/sv/common_ifs/index.md
+++ b/vendor/lowrisc_ip/dv/sv/common_ifs/index.md
@@ -8,43 +8,55 @@ connecting dut signals. They are described in detail below.
### `clk_if`
This is a passive clock interface that is used to wait for clock events in
-testbenches.
-This interface has two clocking blocks: `cb` and `cbn` for synchronizing to
-positive and negative clock edges. This interface consists of following tasks:
+testbenches. This interface has two clocking blocks, `cb` and `cbn`, for
+synchronizing to positive and negative clock edges, respectively. The interface
+also has the following tasks:
* `wait_clks`: waits for specified number of positive clock edges
* `wait_n_clks`: waits for specified number of negative clock edges
### `clk_rst_if`
-This interface provides the ability to drive / sample clock and reset signal.
-It provides various methods related to clock and reset generation. These
-methods can be categorized into `setup methods` and `drive / sample` methods.
-Following are `setup methods` of `pins_if`:
-* `set_freq_mhz`: set the clk frequency in mhz and calclate period in ns
-* `set_duty_cycle`: set the duty cycle (1-99)
-* `set_active`: enables `clk` and `rst_n` generation
- typically, called at t=0 (from tb top)
-* `set_period_ns`: set the clk period in ns and calculate frequency in mhz
-* `set_jitter_chance_pc`: set jitter chance in percentage (0 - 100)
- * 0: do not add any jitter
- * 100: add jitter on every clock edge
-* `set_max_jitter_ps`: set maximum jitter in ps
-Following are `drive / sample` methods of `pins_if`:
-* `wait_for_reset`: wait for rst_n to assert and then deassert
-* `apply_reset`: apply reset with specified scheme out of following:
- * fullly synchronous reset
- * async assert, sync dessert
- * async assert, async dessert
- * clk gated when reset asserted
-* `add_jitter`: add jitter to `clk_hi` and `clk_lo` half periods based on
- `jitter_chance_pc`
-* `start_clk`: start / ungate clock
+
+Unlike `clk_if`, this interface can generate a clock and a reset signal. These
+are connected as `inout` signals and the interface observes them passively
+unless the `set_active` function is called.
+
+Just like `clk_if`, this interface has clocking blocks `cb` and `cbn`, together
+with `wait_clks` and `wait_n_clks` utility tasks. It also has
+* `wait_for_reset`: wait for a reset signalled on `rst_n`
+
+To generate a clock signal, call `set_active` at the start of the simulation.
+This is typically called from an `initial` block in the testbench. To configure
+the frequency and duty cycle of the generated clock, use the following
+functions:
+* `set_freq_mhz` / `set_freq_khz`: set the clock frequency in MHz / KHz. This
+ is 50MHz by default.
+* `set_period_ns`: set the clock period in nanoseconds. This is 20ns by default
+ (giving a clock period of 50MHz).
+* `set_duty_cycle`: set the duty cycle (as a percentage: 1 - 99). This is 50 by
+ default.
+
+The clock can also have jitter added. This is generated as an offset in
+picoseconds added to randomly selected clock half-periods. It can be enabled
+and configured with:
+* `set_jitter_chance_pc`: set the percentage probability of adding a jitter to
+ a given half-period. By default, this is 0 and the clock has no jitter.
+* `set_max_jitter_ps`: set the maximum jitter to add to each clock half-period
+ in picoseconds. This is 1000ps (1 ns) by default.
+
+To start and stop the clock or apply a reset, use the following tasks. These
+will have no effect if `set_active` has not been called.
+* `start_clk`: start the clock. The clock is started by default, so this
+ task is only needed after a call to `stop_clk`.
* `stop_clk`: stop / gate the clk
-* `wait_clks`: waits for specified number of positive clock edges
-* `wait_n_clks`: waits for specified number of negative clock edges
+* `apply_reset`: signal a reset on `rst_n`. The length of this reset and
+ whether it is synchronous or not can be configured with arguments to the
+ function.
### `pins_if`
-This paramterized interface provides the ability to drive / sample any signal
+
+This parameterized interface provides the ability to drive or sample any signal
in the DUT.
+
```systemverilog
interface pins_if #(
parameter int Width = 1
@@ -52,30 +64,23 @@ interface pins_if #(
inout [Width-1:0] pins
);
```
-The member `pins` is inout type and it can be connected to any of input or
-output port within of dut to drive or sample them. `pins` can be driven either
-internally using `pins_o` and `pins_oe` signals, that constitute a tri-state
-buffer implementation. This provide an ability to disconnects `pins` by driving
-them to high impedance state. `pins` may also be driven through an external
-driver that it gets connected to. This interface also provides capability
-to drive weak pull-up or pull-down on `pins` in case of no internal or external
-drivers. The members `pins_pu` and `pins_pd` control weak pull-up or pull-down
-functionality. Following diagram explains working of `pins_if`:
-## `pins_if` block diagram
+By default, it behaves as a passive interface. The values of the pins can be
+read with the following functions:
+* `sample`: sample and return all the pin values
+* `sample_pin`: sample just the given pin
+
+The interface can also be configured to drive, pull up, or pull down its
+outputs. To do this, call
+* `drive` / `drive_pin`: Drive the output to the given value.
+* `drive_en` / `drive_en_pin`: Configure output enable; when enabled, this
+ drives value previously stored by a call to `drive` or `drive_pin`.
+* `set_pullup_en` / `set_pullup_en_pin`: Configure pull-up setting. If true and
+ output enable is false, drives the output to `1`.
+* `set_pulldown_en` / `set_pulldown_en_pin`: Configure pull-down setting. If
+ true and both output_enable and pull-up are false, drives the output to `0`.
+
+The diagram below gives a schematic view of `pins_if`. The driver shown is
+replicated for each bit.
+

-
-Some of the commonly used methods of `pins_if` are as follows:
-* `drive_en_pin`: Drive specified value `val` on specified index `idx` of
- `pins_oe` signal
-* `drive_en`: Drive `pins_oe` signal to specified value `val`
-* `drive_pin`: Drive specified index `idx` of pins_oe signal to 1, and the same
- index of `pins_o` to specified value `val`
- value
-* `drive`: Drive `pins_oe` to all 1's and specified value `val` on `pins_o`
-* `sample_pin`: Sample and return value of `pins[idx]` for specified index `idx`
-* `sample`: Sample and return value of `pins`
-* `set_pullup_en`: Implement pull-up on specific bits of `pins` based on
- specified value `val`
-* `set_pulldown_en`: Implement pull-down on specifc bits of `pins` based on
- specified value `val`
diff --git a/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv b/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv
index 2a419ed7..636af6fc 100644
--- a/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv
+++ b/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv
@@ -15,7 +15,6 @@ interface pins_if #(
logic [Width-1:0] pins_o; // value to be driven out
- wire [Width-1:0] pins_int; // value of pin using internal pull-up / pull-down
bit [Width-1:0] pins_oe = '0; // output enable
bit [Width-1:0] pins_pd = '0; // pull down enable
bit [Width-1:0] pins_pu = '0; // pull up enable
@@ -73,24 +72,26 @@ interface pins_if #(
endfunction
// make connections
- generate
- for (genvar i = 0; i < Width; i++) begin : each_pin
- assign pins_int[i] = pins_pd[i] ? 1'b0 :
- pins_pu[i] ? 1'b1 : 1'bz;
- // If output enable is 1, strong driver assigns pin to 'value to be driven out';
- // the external strong driver can still affect pin, if exists.
- // Else if output enable is 0, weak pullup or pulldown is applied to pin.
- // By doing this, we make sure that weak pullup or pulldown does not override
- // any 'x' value on pin, that may result due to conflicting values
- // between 'value to be driven out' and the external driver's value.
- assign pins[i] = pins_oe[i] ? pins_o[i] : 1'bz;
+ for (genvar i = 0; i < Width; i++) begin : each_pin
`ifdef VERILATOR
- assign pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
+ assign pins[i] = pins_oe[i] ? pins_o[i] :
+ pins_pu[i] ? 1'b1 :
+ pins_pd[i] ? 1'b0 : 1'bz;
`else
- assign (pull0, pull1) pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
+ // Drive the pin with pull strength based on whether pullup / pulldown is enabled.
+ assign (pull0, pull1) pins[i] = ~pins_oe[i] ? (pins_pu[i] ? 1'b1 :
+ pins_pd[i] ? 1'b0 : 1'bz) : 1'bz;
+
+
+ // If output enable is 1, strong driver assigns pin to 'value to be driven out';
+ // the external strong driver can still affect pin, if exists.
+ // Else if output enable is 0, weak pullup or pulldown is applied to pin.
+ // By doing this, we make sure that weak pullup or pulldown does not override
+ // any 'x' value on pin, that may result due to conflicting values
+ // between 'value to be driven out' and the external driver's value.
+ assign pins[i] = pins_oe[i] ? pins_o[i] : 1'bz;
`endif
- end
- endgenerate
+ end
endinterface
`endif
diff --git a/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg b/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg
index 8d9d5d00..0d624ff3 100644
--- a/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg
+++ b/vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg
@@ -1 +1,96 @@
-
\ No newline at end of file
+
+
diff --git a/vendor/lowrisc_ip/dv/sv/csr_utils/README.md b/vendor/lowrisc_ip/dv/sv/csr_utils/README.md
index b2833014..e82ff698 100644
--- a/vendor/lowrisc_ip/dv/sv/csr_utils/README.md
+++ b/vendor/lowrisc_ip/dv/sv/csr_utils/README.md
@@ -123,7 +123,7 @@ Supported CSR test sequences are:
the read results with the expected values
### CSR exclusion methodology
-The CSR test sequences listed above intend to perform a sanity check to CSR
+The CSR test sequences listed above intend to perform a basic check to CSR
read/write accesses, but do not intend to check specific DUT functionalities. Thus the
sequences might need to exclude reading or writing certain CSRs depending on the
specific testbench.
diff --git a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
index 71d076cf..73436751 100644
--- a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
+++ b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
@@ -219,14 +219,6 @@ class csr_rw_seq extends csr_base_seq;
`uvm_object_new
- rand bit do_csr_rd_check;
- rand bit do_csr_field_rd_check;
-
- constraint csr_or_field_rd_check_c {
- // at least one of them should be set
- do_csr_rd_check || do_csr_field_rd_check;
- }
-
virtual task body();
foreach (test_csrs[i]) begin
uvm_reg_data_t wdata;
@@ -243,7 +235,6 @@ class csr_rw_seq extends csr_base_seq;
`uvm_info(`gtn, $sformatf("Verifying register read/write for %0s",
test_csrs[i].get_full_name()), UVM_MEDIUM)
- `DV_CHECK_FATAL(randomize(do_csr_rd_check, do_csr_field_rd_check))
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrRwTest, m_csr_excl_item);
@@ -254,33 +245,14 @@ class csr_rw_seq extends csr_base_seq;
// register is getting the updated access information.
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker));
- // check if parent block or register is excluded from read-check
- if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWriteCheck, CsrRwTest)) begin
- `uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWriteCheck exclusion",
- test_csrs[i].get_full_name()), UVM_MEDIUM)
- continue;
- end
+ do_check_csr_or_field_rd(.csr(test_csrs[i]),
+ .blocking(0),
+ .compare(!external_checker),
+ .compare_vs_ral(1),
+ .csr_excl_type(CsrExclWriteCheck),
+ .csr_test_type(CsrRwTest),
+ .csr_excl_item(m_csr_excl_item));
- compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclWriteCheck, CsrRwTest,
- m_csr_excl_item);
- if (do_csr_rd_check) begin
- csr_rd_check(.ptr (test_csrs[i]),
- .blocking (0),
- .compare (!external_checker),
- .compare_vs_ral(1'b1),
- .compare_mask (compare_mask));
- end
- if (do_csr_field_rd_check) begin
- test_csrs[i].get_fields(test_fields);
- test_fields.shuffle();
- foreach (test_fields[j]) begin
- bit compare = !m_csr_excl_item.is_excl(test_fields[j], CsrExclWriteCheck, CsrRwTest);
- csr_rd_check(.ptr (test_fields[j]),
- .blocking (0),
- .compare (!external_checker && compare),
- .compare_vs_ral(1'b1));
- end
- end
wait_if_max_outstanding_accesses_reached();
end
endtask
@@ -392,14 +364,7 @@ class csr_bit_bash_seq extends csr_base_seq;
val = rg.get();
val[k] = ~val[k];
err_msg = $sformatf("Wrote %0s[%0d]: %0b", rg.get_full_name(), k, val[k]);
- csr_wr(.csr(rg), .value(val), .blocking(1));
-
- // if external checker is not enabled and writes are made non-blocking, then we need to
- // pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
- // might pick up stale mirrored value
- if (!external_checker) begin
- void'(rg.predict(.value(val), .kind(UVM_PREDICT_WRITE)));
- end
+ csr_wr(.csr(rg), .value(val), .blocking(1), .predict(!external_checker));
// TODO, outstanding access to same reg isn't supported in uvm_reg. Need to add another seq
// uvm_reg waits until transaction is completed, before start another read/write in same reg
@@ -442,14 +407,7 @@ class csr_aliasing_seq extends csr_base_seq;
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrAliasingTest, m_csr_excl_item);
- csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0));
-
- // if external checker is not enabled and writes are made non-blocking, then we need to
- // pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
- // might pick up stale mirrored value
- if (!external_checker) begin
- void'(test_csrs[i].predict(.value(wdata), .kind(UVM_PREDICT_WRITE)));
- end
+ csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker));
all_csrs.shuffle();
foreach (all_csrs[j]) begin
diff --git a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv
index 19b2b8e8..ae913d73 100644
--- a/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv
+++ b/vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv
@@ -13,13 +13,14 @@ package csr_utils_pkg;
`include "dv_macros.svh"
// local types and variables
- uint outstanding_accesses = 0;
- uint default_timeout_ns = 1_000_000; // 1ms
- uint default_spinwait_timeout_ns = 10_000_000; // 10ms
- string msg_id = "csr_utils";
- bit default_csr_blocking = 1;
- bit under_reset = 0;
- int max_outstanding_accesses = 100;
+ uint outstanding_accesses = 0;
+ uint default_timeout_ns = 1_000_000; // 1ms
+ uint default_spinwait_timeout_ns = 10_000_000; // 10ms
+ string msg_id = "csr_utils";
+ bit default_csr_blocking = 1;
+ uvm_check_e default_csr_check = UVM_CHECK;
+ bit under_reset = 0;
+ int max_outstanding_accesses = 100;
// csr field struct - hold field specific params
typedef struct {
@@ -174,7 +175,7 @@ package csr_utils_pkg;
endtask
task automatic csr_update(input uvm_reg csr,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input bit blocking = default_csr_blocking,
input uint timeout_ns = default_timeout_ns,
@@ -193,7 +194,7 @@ package csr_utils_pkg;
// subroutine of csr_update, don't use it directly
task automatic csr_update_sub(input uvm_reg csr,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uint timeout_ns = default_timeout_ns,
input uvm_reg_map map = null,
@@ -209,7 +210,9 @@ package csr_utils_pkg;
csr_pre_write_sub(csr, en_shadow_wr);
csr.update(.status(status), .path(path), .map(map), .prior(100));
csr_post_write_sub(csr, en_shadow_wr);
- if (check == UVM_CHECK) begin
+ // when reset occurs, all items will be dropped immediately. This may end up getting
+ // d_error = 1 from previous item on the bus. Skip checking it during reset
+ if (check == UVM_CHECK && !under_reset) begin
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
end
decrement_outstanding_access();
@@ -227,7 +230,7 @@ package csr_utils_pkg;
task automatic csr_wr(input uvm_reg csr,
input uvm_reg_data_t value,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input bit blocking = default_csr_blocking,
input bit backdoor = 0,
@@ -238,15 +241,11 @@ package csr_utils_pkg;
if (backdoor) begin
csr_poke(csr, value, check, predict);
end else if (blocking) begin
- csr_wr_sub(csr, value, check, path, timeout_ns, map, en_shadow_wr);
- if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
+ csr_wr_sub(csr, value, check, path, timeout_ns, predict, map, en_shadow_wr);
end else begin
fork
begin
- csr_wr_sub(csr, value, check, path, timeout_ns, map, en_shadow_wr);
- // predict after csr_wr_sub, to ensure predict after enable register overwrite the locked
- // registers' access information
- if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
+ csr_wr_sub(csr, value, check, path, timeout_ns, predict, map, en_shadow_wr);
end
join_none
// Add #0 to ensure that this thread starts executing before any subsequent call
@@ -257,9 +256,10 @@ package csr_utils_pkg;
// subroutine of csr_wr, don't use it directly
task automatic csr_wr_sub(input uvm_reg csr,
input uvm_reg_data_t value,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uint timeout_ns = default_timeout_ns,
+ input bit predict = 0,
input uvm_reg_map map = null,
input bit en_shadow_wr = 1);
fork
@@ -273,9 +273,14 @@ package csr_utils_pkg;
csr_pre_write_sub(csr, en_shadow_wr);
csr.write(.status(status), .value(value), .path(path), .map(map), .prior(100));
csr_post_write_sub(csr, en_shadow_wr);
- if (check == UVM_CHECK) begin
+ if (check == UVM_CHECK && !under_reset) begin
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
end
+ // Only update the predicted value if status is ok (otherwise the write isn't completed
+ // successfully and the design shouldn't have accepted the written value)
+ if (status == UVM_IS_OK && predict) begin
+ void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
+ end
decrement_outstanding_access();
end
begin
@@ -314,15 +319,16 @@ package csr_utils_pkg;
endtask
// backdoor write csr
- task automatic csr_poke(input uvm_reg csr,
- input uvm_reg_data_t value,
- input uvm_check_e check = UVM_CHECK,
- input bit predict = 0);
+ task automatic csr_poke(input uvm_reg csr,
+ input uvm_reg_data_t value,
+ input uvm_check_e check = default_csr_check,
+ input bit predict = 0,
+ input bkdr_reg_path_e kind = BkdrRegPathRtl);
uvm_status_e status;
string msg_id = {csr_utils_pkg::msg_id, "::csr_poke"};
uvm_reg_data_t old_mirrored_val = csr.get_mirrored_value();
- csr.poke(.status(status), .value(value));
+ csr.poke(.status(status), .value(value), .kind(kind.name));
if (check == UVM_CHECK && status != UVM_IS_OK) begin
string str;
uvm_hdl_path_concat paths[$];
@@ -332,26 +338,28 @@ package csr_utils_pkg;
csr.get_full_name(), str))
end
// poke always updates predict value, if predict == 0, revert back to old mirrored value
- if (!predict) begin
+ if (!predict || kind == BkdrRegPathRtlShadow) begin
void'(csr.predict(.value(old_mirrored_val), .kind(UVM_PREDICT_DIRECT)));
end
endtask
task automatic csr_rd(input uvm_object ptr, // accept reg or field
output uvm_reg_data_t value,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input bit blocking = default_csr_blocking,
input bit backdoor = 0,
input uint timeout_ns = default_timeout_ns,
input uvm_reg_map map = null);
- if (backdoor) begin
- csr_peek(ptr, value, check);
- end else if (blocking) begin
- csr_rd_sub(ptr, value, check, path, timeout_ns, map);
+ uvm_status_e status;
+ if (blocking) begin
+ csr_rd_sub(.ptr(ptr), .value(value), .status(status), .check(check), .path(path),
+ .backdoor(backdoor), .timeout_ns(timeout_ns), .map(map));
end else begin
+ `DV_CHECK_EQ(backdoor, 0, "Don't enable backdoor with blocking = 0", error, msg_id)
fork
- csr_rd_sub(ptr, value, check, path, timeout_ns, map);
+ csr_rd_sub(.ptr(ptr), .value(value), .status(status), .check(check), .path(path),
+ .backdoor(backdoor), .timeout_ns(timeout_ns), .map(map));
join_none
// Add #0 to ensure that this thread starts executing before any subsequent call
#0;
@@ -361,14 +369,20 @@ package csr_utils_pkg;
// subroutine of csr_rd, don't use it directly
task automatic csr_rd_sub(input uvm_object ptr, // accept reg or field
output uvm_reg_data_t value,
- input uvm_check_e check = UVM_CHECK,
+ output uvm_status_e status,
+ input bit backdoor = 0,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uint timeout_ns = default_timeout_ns,
input uvm_reg_map map = null);
+ if (backdoor) begin
+ csr_peek(ptr, value, check);
+ status = UVM_IS_OK;
+ return;
+ end
fork
begin : isolation_fork
csr_field_t csr_or_fld;
- uvm_status_e status;
string msg_id = {csr_utils_pkg::msg_id, "::csr_rd"};
fork
@@ -382,7 +396,7 @@ package csr_utils_pkg;
csr_or_fld.csr.read(.status(status), .value(value), .path(path), .map(map),
.prior(100));
end
- if (check == UVM_CHECK) begin
+ if (check == UVM_CHECK && !under_reset) begin
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
end
decrement_outstanding_access();
@@ -402,15 +416,16 @@ package csr_utils_pkg;
// uvm_reg::peek() returns a 2-state value, directly get data from hdl path
task automatic csr_peek(input uvm_object ptr,
output uvm_reg_data_t value,
- input uvm_check_e check = UVM_CHECK);
+ input uvm_check_e check = default_csr_check,
+ input bkdr_reg_path_e kind = BkdrRegPathRtl);
string msg_id = {csr_utils_pkg::msg_id, "::csr_peek"};
csr_field_t csr_or_fld = decode_csr_or_field(ptr);
uvm_reg csr = csr_or_fld.csr;
- if (csr.has_hdl_path()) begin
+ if (csr.has_hdl_path(kind.name)) begin
uvm_hdl_path_concat paths[$];
- csr.get_full_hdl_path(paths);
+ csr.get_full_hdl_path(paths, kind.name);
foreach (paths[0].slices[i]) begin
uvm_reg_data_t field_val;
if (uvm_hdl_read(paths[0].slices[i].path, field_val)) begin
@@ -421,7 +436,8 @@ package csr_utils_pkg;
end
end
end else begin
- `uvm_fatal(msg_id, $sformatf("No backdoor defined for %0s", csr.get_full_name()))
+ `uvm_fatal(msg_id, $sformatf("No backdoor defined for %0s path's %0s",
+ csr.get_full_name(), kind.name))
end
// if it's field, only return field value
@@ -429,7 +445,7 @@ package csr_utils_pkg;
endtask
task automatic csr_rd_check(input uvm_object ptr,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input bit blocking = default_csr_blocking,
input bit backdoor = 0,
@@ -452,8 +468,8 @@ package csr_utils_pkg;
csr_or_fld = decode_csr_or_field(ptr);
- csr_rd(.ptr(ptr), .value(obs), .check(check), .path(path), .backdoor(backdoor),
- .blocking(1), .timeout_ns(timeout_ns), .map(map));
+ csr_rd_sub(.ptr(ptr), .value(obs), .status(status), .check(check), .path(path),
+ .backdoor(backdoor), .timeout_ns(timeout_ns), .map(map));
// get mirrored value after read to make sure the read reg access is updated
if (csr_or_fld.field != null) begin
@@ -461,7 +477,7 @@ package csr_utils_pkg;
end else begin
exp = csr_or_fld.csr.get_mirrored_value();
end
- if (compare && !under_reset) begin
+ if (compare && status == UVM_IS_OK && !under_reset) begin
obs = obs & compare_mask;
exp = (compare_vs_ral ? exp : compare_value) & compare_mask;
`DV_CHECK_EQ(obs, exp, {"Regname: ", ptr.get_full_name(), " ", err_msg},
@@ -485,10 +501,55 @@ package csr_utils_pkg;
foreach (ral_csrs[i]) csr_rd_check(.ptr(ral_csrs[i]), .compare_vs_ral(1));
endtask
+ // Checks the csr value against predicted value by reading the whole CSR or individual CSR
+ // fields.
+ // This task will skip read check if the CSR field is excluded.
+ task automatic do_check_csr_or_field_rd(input uvm_reg csr,
+ input bit do_csr_field_rd_check = $urandom(),
+ input bit blocking = 0,
+ input bit compare = 1,
+ input bit compare_vs_ral = 1,
+ input csr_excl_type_e csr_excl_type = CsrNoExcl,
+ input csr_test_type_e csr_test_type = CsrRwTest,
+ input csr_excl_item csr_excl_item = null);
+ uvm_reg_data_t compare_mask;
+
+ // Check if parent block or register is excluded from read-check
+ if (csr_excl_item != null && csr_excl_item.is_excl(csr, csr_excl_type, csr_test_type)) begin
+ `uvm_info(msg_id, $sformatf("Skipping register %0s due to CsrExclWriteCheck exclusion",
+ csr.get_full_name()), UVM_MEDIUM)
+ return;
+ end
+
+ compare_mask = get_mask_excl_fields(csr, csr_excl_type, csr_test_type, csr_excl_item);
+
+ if (!do_csr_field_rd_check) begin
+ csr_rd_check(.ptr (csr),
+ .blocking (blocking),
+ .compare (compare),
+ .compare_vs_ral(compare_vs_ral),
+ .compare_mask (compare_mask));
+ end else begin
+ uvm_reg_field test_fields[$];
+ csr.get_fields(test_fields);
+ test_fields.shuffle();
+ foreach (test_fields[i]) begin
+ bit field_compare = 1;
+ if (csr_excl_item != null) begin
+ field_compare = !csr_excl_item.is_excl(test_fields[i], csr_excl_type, csr_test_type);
+ end
+ csr_rd_check(.ptr (test_fields[i]),
+ .blocking (blocking),
+ .compare (field_compare && compare),
+ .compare_vs_ral(compare_vs_ral));
+ end
+ end
+ endtask
+
// poll a csr or csr field continuously until it reads the expected value.
task automatic csr_spinwait(input uvm_object ptr,
input uvm_reg_data_t exp_data,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uint spinwait_delay_ns = 0,
@@ -538,7 +599,7 @@ package csr_utils_pkg;
task automatic mem_rd(input uvm_mem ptr,
input int offset,
output bit[31:0] data,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input bit blocking = default_csr_blocking,
input uint timeout_ns = default_timeout_ns,
input uvm_reg_map map = null);
@@ -556,7 +617,7 @@ package csr_utils_pkg;
task automatic mem_rd_sub(input uvm_mem ptr,
input int offset,
output bit[31:0] data,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uint timeout_ns = default_timeout_ns,
input uvm_reg_map map = null);
fork
@@ -568,7 +629,7 @@ package csr_utils_pkg;
begin
increment_outstanding_access();
ptr.read(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
- if (check == UVM_CHECK) begin
+ if (check == UVM_CHECK && !under_reset) begin
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
end
decrement_outstanding_access();
@@ -589,7 +650,7 @@ package csr_utils_pkg;
input bit[31:0] data,
input bit blocking = default_csr_blocking,
input uint timeout_ns = default_timeout_ns,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_reg_map map = null);
if (blocking) begin
mem_wr_sub(ptr, offset, data, timeout_ns, check, map);
@@ -606,7 +667,7 @@ package csr_utils_pkg;
input int offset,
input bit[31:0] data,
input uint timeout_ns = default_timeout_ns,
- input uvm_check_e check = UVM_CHECK,
+ input uvm_check_e check = default_csr_check,
input uvm_reg_map map = null);
fork
begin : isolation_fork
@@ -617,7 +678,7 @@ package csr_utils_pkg;
begin
increment_outstanding_access();
ptr.write(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
- if (check == UVM_CHECK) begin
+ if (check == UVM_CHECK && !under_reset) begin
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
end
decrement_outstanding_access();
@@ -641,12 +702,14 @@ package csr_utils_pkg;
uvm_reg_field flds[$];
csr.get_fields(flds);
get_mask_excl_fields = '1;
- foreach (flds[i]) begin
- if (m_csr_excl_item.is_excl(flds[i], csr_excl_type, csr_test_type)) begin
- csr_field_t fld_params = decode_csr_or_field(flds[i]);
- `uvm_info(msg_id, $sformatf("Skipping field %0s due to %0s exclusion",
- flds[i].get_full_name(), csr_excl_type.name()), UVM_MEDIUM)
- get_mask_excl_fields &= ~(fld_params.mask << fld_params.shift);
+ if (m_csr_excl_item != null) begin
+ foreach (flds[i]) begin
+ if (m_csr_excl_item.is_excl(flds[i], csr_excl_type, csr_test_type)) begin
+ csr_field_t fld_params = decode_csr_or_field(flds[i]);
+ `uvm_info(msg_id, $sformatf("Skipping field %0s due to %0s exclusion",
+ flds[i].get_full_name(), csr_excl_type.name()), UVM_MEDIUM)
+ get_mask_excl_fields &= ~(fld_params.mask << fld_params.shift);
+ end
end
end
endfunction
diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv
index e0c58908..fd1cf9e7 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv
@@ -9,7 +9,7 @@ class dv_base_reg extends uvm_reg;
local bit is_ext_reg;
local dv_base_reg locked_regs[$];
- local uvm_reg_data_t staged_shadow_val;
+ local uvm_reg_data_t staged_shadow_val, committed_val, shadowed_val;
local bit is_shadowed;
local bit shadow_wr_staged; // stage the first shadow reg write
local bit shadow_update_err;
@@ -111,6 +111,15 @@ class dv_base_reg extends uvm_reg;
return shadow_update_err;
endfunction
+ function bit get_shadow_storage_err();
+ uvm_reg_data_t mask = (1'b1 << (get_msb_pos() + 1)) - 1;
+ uvm_reg_data_t shadowed_val_temp = (~shadowed_val) & mask;
+ uvm_reg_data_t committed_val_temp = committed_val & mask;
+ `uvm_info(`gfn, $sformatf("shadow_val %0h, commmit_val %0h", shadowed_val_temp,
+ committed_val_temp), UVM_DEBUG)
+ return shadowed_val_temp != committed_val_temp;
+ endfunction
+
virtual function void clear_shadow_update_err();
shadow_update_err = 0;
endfunction
@@ -123,6 +132,10 @@ class dv_base_reg extends uvm_reg;
virtual task post_write(uvm_reg_item rw);
dv_base_reg_field fields[$];
string field_access;
+
+ // no need to update shadow value or access type if access is not OK, as access is aborted
+ if (rw.status != UVM_IS_OK) return;
+
if (is_shadowed) begin
// first write
if (!shadow_wr_staged) begin
@@ -136,6 +149,8 @@ class dv_base_reg extends uvm_reg;
shadow_update_err = 1;
return;
end
+ committed_val = staged_shadow_val;
+ shadowed_val = ~committed_val;
end
end
if (is_enable_reg()) begin
@@ -197,11 +212,44 @@ class dv_base_reg extends uvm_reg;
super.do_predict(rw, kind, be);
endfunction
+ virtual task poke (output uvm_status_e status,
+ input uvm_reg_data_t value,
+ input string kind = "BkdrRegPathRtl",
+ input uvm_sequence_base parent = null,
+ input uvm_object extension = null,
+ input string fname = "",
+ input int lineno = 0);
+ if (kind == "BkdrRegPathRtlShadow") shadowed_val = value;
+ else if (kind == "BkdrRegPathRtlCommitted") committed_val = value;
+ super.poke(status, value, kind, parent, extension, fname, lineno);
+ endtask
+
+ // callback function to update shadowed values according to specific design
+ // should only be called after post-write
+ virtual function void update_shadowed_val(uvm_reg_data_t val, bit do_predict = 1);
+ if (shadow_wr_staged) begin
+ // update value after first write
+ staged_shadow_val = val;
+ end else begin
+ // update value after second write
+ if (staged_shadow_val != val) begin
+ shadow_update_err = 1;
+ end else begin
+ shadow_update_err = 0;
+ committed_val = staged_shadow_val;
+ shadowed_val = ~committed_val;
+ end
+ end
+ if (do_predict) void'(predict(val));
+ endfunction
+
virtual function void reset(string kind = "HARD");
super.reset(kind);
if (is_shadowed) begin
shadow_update_err = 0;
shadow_wr_staged = 0;
+ committed_val = get_mirrored_value();
+ shadowed_val = ~committed_val;
// in case reset is issued during shadowed writes
void'(atomic_shadow_wr.try_get(1));
void'(atomic_en_shadow_wr.try_get(1));
diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv
index c0bbcc62..a3e4521d 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv
@@ -8,6 +8,14 @@ class dv_base_reg_block extends uvm_reg_block;
csr_excl_item csr_excl;
+ // The address mask for the register block specific to a map. This will be (1 << K) - 1 for some
+ // K. All relative offsets in the register block have addresses less than (1 << K), so an address
+ // decoder that starts by discarding all higher bits will work correctly so long as the chosen
+ // base address of the register block has no bits in common with this mask.
+ //
+ // This is set by compute_addr_mask(), which must run after locking the model.
+ protected uvm_reg_addr_t addr_mask[uvm_reg_map];
+
function new (string name = "", int has_coverage = UVM_NO_COVERAGE);
super.new(name, has_coverage);
endfunction
@@ -30,9 +38,18 @@ class dv_base_reg_block extends uvm_reg_block;
foreach (ral_regs[i]) `downcast(dv_regs[i], ral_regs[i])
endfunction
+ function dv_base_reg get_dv_base_reg_by_name(string csr_name, bit check_csr_exist = 1'b1);
+ uvm_reg csr = get_reg_by_name(csr_name);
+ `downcast(get_dv_base_reg_by_name, csr)
+ if (check_csr_exist) begin
+ `DV_CHECK_NE_FATAL(get_dv_base_reg_by_name, null,
+ $sformatf("%0s does not exist in block %0s",csr_name, get_name()))
+ end
+ endfunction
+
function void get_enable_regs(ref dv_base_reg enable_regs[$]);
dv_base_reg_block blks[$];
- this.get_dv_base_reg_blocks(blks);
+ get_dv_base_reg_blocks(blks);
if (blks.size() == 0) begin
dv_base_reg all_regs[$];
this.get_dv_base_regs(all_regs);
@@ -49,9 +66,121 @@ class dv_base_reg_block extends uvm_reg_block;
// when reset issued - the locked registers' access will be reset to original access
virtual function void reset(string kind = "HARD");
dv_base_reg enable_regs[$];
+ `uvm_info(`gfn, "Resetting RAL reg block", UVM_MEDIUM)
super.reset(kind);
get_enable_regs(enable_regs);
foreach (enable_regs[i]) enable_regs[i].set_locked_regs_access();
endfunction
+ // Internal function, used to compute the address mask for this register block.
+ //
+ // This is quite an expensive computation, so we memoize the results in addr_mask[map].
+ // Use below to get the addr map size #3317
+ // max2(biggest_reg_offset+reg_size, biggest_mem_offset+mem_size) and then round up to 2**N
+ protected function void compute_addr_mask(uvm_reg_map map);
+ uvm_reg_addr_t max_addr, max_offset;
+ uvm_reg_block blocks[$];
+ uvm_reg regs[$];
+ uvm_mem mems[$];
+ int unsigned alignment;
+
+ // TODO: assume IP only contains 1 reg block, find a better way to handle chip-level and IP
+ // with multiple reg blocks
+ get_blocks(blocks);
+ if (blocks.size > 0) begin
+ addr_mask[map] = '1;
+ return;
+ end
+
+ get_registers(regs);
+ get_memories(mems);
+ `DV_CHECK_GT_FATAL(regs.size() + mems.size(), 0)
+
+ // Walk the known registers and memories, calculating the largest byte address visible. Note
+ // that the get_offset() calls will return absolute addresses, including any base address in the
+ // default register map.
+ max_addr = 0;
+ foreach (regs[i]) begin
+ max_addr = max2(regs[i].get_offset(map) + regs[i].get_n_bytes() - 1, max_addr);
+ end
+
+ foreach (mems[i]) begin
+ uvm_reg_addr_t mem_size;
+ mem_size = mems[i].get_offset(.map(map)) + mems[i].get_size() * mems[i].get_n_bytes() - 1;
+ max_addr = max2(mem_size, max_addr);
+ end
+
+ // Subtract the base address in the default register map to get the maximum relative offset.
+ max_offset = max_addr - map.get_base_addr();
+
+ // Set alignment to be ceil(log2(biggest_offset))
+ alignment = 0;
+ while (max_offset > 0) begin
+ alignment++;
+ max_offset = max_offset >> 1;
+ end
+
+ // Note that we know alignment > 0 (because we've already checked that we have at least one
+ // register or memory).
+ `DV_CHECK_GT_FATAL(alignment, 0)
+
+ // Finally, extract a mask corresponding to the alignment
+ addr_mask[map] = (1 << alignment) - 1;
+
+ // Computed mask must be non-zero.
+ `DV_CHECK_FATAL(addr_mask[map])
+ endfunction
+
+ // Get the address mask. This should only be called after locking the model (because it depends on
+ // the layout of registers and memories in the block).
+ function uvm_reg_addr_t get_addr_mask(uvm_reg_map map = null);
+ `DV_CHECK_FATAL(is_locked())
+ if (map == null) map = get_default_map();
+ if (!addr_mask.exists(map)) compute_addr_mask(map);
+ return addr_mask[map];
+ endfunction
+
+ // Set the base address for the given register map
+ //
+ // Check that base_addr is aligned as required by the register block. If the supplied base_addr is
+ // the "magic" address '1, randomly pick an appropriately aligned base address and set it for the
+ // specified map.
+ function void set_base_addr(uvm_reg_addr_t base_addr = '1, uvm_reg_map map = null);
+ uvm_reg_addr_t mask;
+
+ if (map == null) map = get_default_map();
+ mask = get_addr_mask(map);
+
+ // If base_addr is '1, randomly pick an aligned base address
+ if (base_addr == '1) begin
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_addr, (base_addr & mask) == '0;)
+ end
+
+ // Check base addr alignment (which should be guaranteed if we just picked it, but needs
+ // checking if not).
+ `DV_CHECK_FATAL((base_addr & mask) == '0)
+ `uvm_info(`gfn, $sformatf("Setting register base address to 0x%0h", base_addr), UVM_HIGH)
+ map.set_base_addr(base_addr);
+ endfunction
+
+ // Round the given address down to the start of the containing word. For example, if the address
+ // is 'h123, it will be rounded down to 'h120.
+ //
+ // This is useful if you have a possibly misaligned address and you want to know whether it hits a
+ // register (since get_reg_by_offset needs the aligned address for the start of the register).
+ function uvm_reg_addr_t get_word_aligned_addr(uvm_reg_addr_t byte_addr);
+ uvm_reg_addr_t shift = $clog2(`UVM_REG_BYTENABLE_WIDTH);
+ return (byte_addr >> shift) << shift;
+ endfunction
+
+ // Get the absolute address (in the default register map) for the given offset. For example, if
+ // the base address is 'h100 and offset is 'h13, this will return 'h113 ('h110 if word_aligned is
+ // set).
+ function uvm_reg_addr_t get_addr_from_offset(uvm_reg_addr_t byte_offset,
+ bit word_aligned = 1'b1,
+ uvm_reg_map map = null);
+ if (map == null) map = get_default_map();
+ return (word_aligned ? get_word_aligned_addr(byte_offset) : byte_offset) + map.get_base_addr();
+ endfunction
+
endclass
diff --git a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv
index a5018ce3..b411f829 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv
@@ -37,6 +37,15 @@ package dv_base_reg_pkg;
CsrAllTests = 4'hf // all tests
} csr_test_type_e;
+ typedef enum bit[2:0] {
+ BkdrRegPathRtl, // backdoor path for reg's val in RTL
+ BkdrRegPathRtlCommitted, // backdoor path for shadow reg's committed val in RTL
+ BkdrRegPathRtlShadow, // backdoor path for shadow reg's shadow val in RTL
+ BkdrRegPathGls, // backdoor path for reg's val in GLS
+ BkdrRegPathGlsCommitted, // backdoor path for shadow reg's committed val in GLS
+ BkdrRegPathGlsShdow // backdoor path for shadow reg's shadow val in GLS
+ } bkdr_reg_path_e;
+
// package sources
// base ral
`include "csr_excl_item.sv"
diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv
index f616446b..4e293bb8 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv
@@ -46,15 +46,4 @@ class dv_base_env #(type CFG_T = dv_base_env_cfg,
scoreboard.cov = cov;
endfunction
- virtual function void end_of_elaboration_phase(uvm_phase phase);
- super.end_of_elaboration_phase(phase);
- if (cfg.has_ral) begin
- // Lock the ral model
- cfg.ral.lock_model();
- // Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
- get_csr_addrs(cfg.ral, cfg.csr_addrs);
- get_mem_addr_ranges(cfg.ral, cfg.mem_ranges);
- end
- endfunction : end_of_elaboration_phase
-
endclass
diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv
index cdb3f78f..233937e8 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv
@@ -26,11 +26,6 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
bit [bus_params_pkg::BUS_AW-1:0] csr_addrs[$];
addr_range_t mem_ranges[$];
- // ral base address and size
- bit [bus_params_pkg::BUS_AW-1:0] csr_base_addr; // base address where csr map begins
- bit [bus_params_pkg::BUS_AW:0] csr_addr_map_size; // csr addr region allocated to the ip,
- // max: 1 << bus_params_pkg::BUS_AW
-
// clk_rst_if & freq
virtual clk_rst_if clk_rst_vif;
rand clk_freq_mhz_e clk_freq_mhz;
@@ -40,44 +35,36 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
`uvm_field_int (en_scb, UVM_DEFAULT)
`uvm_field_int (en_cov, UVM_DEFAULT)
`uvm_field_int (zero_delays, UVM_DEFAULT)
- `uvm_field_int (csr_base_addr, UVM_DEFAULT)
- `uvm_field_int (csr_addr_map_size, UVM_DEFAULT)
`uvm_field_enum (clk_freq_mhz_e, clk_freq_mhz, UVM_DEFAULT)
`uvm_object_utils_end
`uvm_object_new
virtual function void initialize(bit [bus_params_pkg::BUS_AW-1:0] csr_base_addr = '1);
- initialize_csr_addr_map_size();
- `DV_CHECK_NE_FATAL(csr_addr_map_size, 0, "csr_addr_map_size can't be 0")
- // use locally randomized csr base address, unless provided as arg to this function
- if (csr_base_addr != '1) begin
- bit is_aligned;
- this.csr_base_addr = csr_base_addr;
- // check alignment
- is_aligned = ~|(this.csr_base_addr & (this.csr_addr_map_size - 1));
- `DV_CHECK_EQ_FATAL(is_aligned, 1'b1)
- end else begin
- // base address needs to be aligned to csr_addr_map_size
- `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(csr_base_addr,
- ~|(csr_base_addr & (csr_addr_map_size - 1));)
- this.csr_base_addr = csr_base_addr;
- end
// build the ral model
if (has_ral) begin
+ uvm_reg_addr_t base_addr;
+
ral = RAL_T::type_id::create("ral");
- ral.build(this.csr_base_addr, null);
+
+ // Build the register block with an arbitrary base address (we choose 0). We'll change it
+ // later.
+ ral.build(.base_addr(0), .csr_excl(null));
apply_ral_fixes();
+ ral.lock_model();
+
+ // Now the model is locked, we know its layout. Set the base address for the register block.
+ // The function internally picks a random one if we pass '1 to it, and performs an integrity
+ // check on the set address.
+ ral.set_base_addr(csr_base_addr);
+
+ // Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
+ get_csr_addrs(ral, csr_addrs);
+ get_mem_addr_ranges(ral, mem_ranges);
ral_models.push_back(ral);
end
endfunction
- // This function must be implemented in extended class to
- // initialize value of csr_addr_map_size member
- virtual function void initialize_csr_addr_map_size();
- `uvm_fatal(`gfn, "This task must be implemented in the extended class!")
- endfunction : initialize_csr_addr_map_size
-
// ral flow is limited in terms of setting correct field access policies and reset values
// We apply those fixes here - please note these fixes need to be reflected in the scoreboard
protected virtual function void apply_ral_fixes();
@@ -93,4 +80,5 @@ class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
this.under_reset = 0;
csr_utils_pkg::reset_deasserted();
endfunction
+
endclass
diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cov.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cov.sv
index d5934628..663de0d0 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cov.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cov.sv
@@ -5,13 +5,15 @@
// TODO - We are enclosing generic covergroups inside class so that we can
// take avoid tool limitation of not allowing arrays of covergroup
// Refer to Issue#375 for more details
-class dv_base_generic_cov_obj;
+class bit_toggle_cg_wrap;
// Covergroup: bit_toggle_cg
// Generic covergroup definition
- covergroup bit_toggle_cg(string name, bit toggle_cov_en = 1) with function sample(bit value);
+ covergroup bit_toggle_cg(string name, string path = "", bit toggle_cov_en = 1) with function
+ sample(bit value);
option.per_instance = 1;
- option.name = name;
+ option.name = (path == "") ? name : {path, "::", name};
+
cp_value: coverpoint value;
cp_transitions: coverpoint value {
option.weight = toggle_cov_en;
@@ -21,8 +23,8 @@ class dv_base_generic_cov_obj;
endgroup : bit_toggle_cg
// Function: new
- function new(string name = "dv_base_generic_cov_obj", bit toggle_cov_en = 1);
- bit_toggle_cg = new(name, toggle_cov_en);
+ function new(string name = "bit_toggle_cg_wrap", string path = "", bit toggle_cov_en = 1);
+ bit_toggle_cg = new(name, path, toggle_cov_en);
endfunction : new
// Function: sample
@@ -30,7 +32,7 @@ class dv_base_generic_cov_obj;
bit_toggle_cg.sample(value);
endfunction : sample
-endclass : dv_base_generic_cov_obj
+endclass : bit_toggle_cg_wrap
class dv_base_env_cov #(type CFG_T = dv_base_env_cfg) extends uvm_component;
`uvm_component_param_utils(dv_base_env_cov #(CFG_T))
diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv
index 6c080947..f2e57cf9 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv
@@ -52,12 +52,19 @@ class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
virtual function void pre_abort();
super.pre_abort();
- // use under_pre_abort flag to prevent deadloop described below:
- // when fatal_err occurred, it will skip check_phase. We add the additional check_phase call
- // here to help debugging. But if inside the check_phase there are UVM_ERRORs, and the err cnt
- // is larger than max_err_cnt, then check_phase will call pre_abort again. This will end up
- // creating a deadloop.
- if (has_uvm_fatal_occurred() && !under_pre_abort) begin
+
+ // In UVM, a fatal error normally aborts the test completely and skips the check phase. However,
+ // it's sometimes helpful to run that phase on the scoreboard anyway (it might make it easier to
+ // debug whatever went wrong), so we do that here.
+ //
+ // We only run the check phase if we were in the run phase: if something went wrong in the build
+ // or connect phase, there's not much point in "checking the run" further. We also have to be
+ // careful to avoid an infinite loop, so we set a flag to avoid doing this a second time if we
+ // have errors in the check phase.
+ if (has_uvm_fatal_occurred() &&
+ !under_pre_abort &&
+ m_current_phase.is(uvm_run_phase::get())) begin
+
under_pre_abort = 1;
check_phase(m_current_phase);
under_pre_abort = 0;
diff --git a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
index 9fb02df3..6e0d4479 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
@@ -88,6 +88,7 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
if (kind == "HARD") begin
csr_utils_pkg::reset_asserted();
cfg.clk_rst_vif.apply_reset();
+ csr_utils_pkg::clear_outstanding_access();
csr_utils_pkg::reset_deasserted();
end
if (cfg.has_ral) begin
@@ -186,13 +187,19 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
string reset_type = "HARD";
csr_write_seq m_csr_write_seq;
+ // Writing random values to CSRs might trigger assertion errors. So we disable in the entire
+ // DUT hierarchy and re-enable after resetting the DUT. See DV_ASSERT_CTRL macro defined in
+ // hw/dv/sv/dv_utils/dv_macros.svh for more details.
+ if (!enable_asserts_in_hw_reset_rand_wr) begin
+ `DV_ASSERT_CTRL_REQ("dut_assert_en", 1'b0)
+ end
+
// run write-only sequence to randomize the csr values
m_csr_write_seq = csr_write_seq::type_id::create("m_csr_write_seq");
m_csr_write_seq.models = cfg.ral_models;
- m_csr_write_seq.set_csr_excl_item(csr_excl);
m_csr_write_seq.external_checker = cfg.en_scb;
- m_csr_write_seq.en_rand_backdoor_write = 1;
- if (!enable_asserts_in_hw_reset_rand_wr) $assertoff;
+ m_csr_write_seq.en_rand_backdoor_write = 1'b1;
+ m_csr_write_seq.set_csr_excl_item(csr_excl);
m_csr_write_seq.start(null);
// run dut_shutdown before asserting reset
@@ -200,16 +207,23 @@ class dv_base_vseq #(type RAL_T = dv_base_reg_block,
// issue reset
void'($value$plusargs("do_reset=%0s", reset_type));
dut_init(reset_type);
- if (!enable_asserts_in_hw_reset_rand_wr) $asserton;
+ if (!enable_asserts_in_hw_reset_rand_wr) begin
+ `DV_ASSERT_CTRL_REQ("dut_assert_en", 1'b1)
+ end
end
// create base csr seq and pass our ral
m_csr_seq = csr_base_seq::type_id::create("m_csr_seq");
- m_csr_seq.num_test_csrs = num_test_csrs;
m_csr_seq.models = cfg.ral_models;
- m_csr_seq.set_csr_excl_item(csr_excl);
m_csr_seq.external_checker = cfg.en_scb;
+ m_csr_seq.num_test_csrs = num_test_csrs;
+ m_csr_seq.set_csr_excl_item(csr_excl);
m_csr_seq.start(null);
endtask
+ // enable/disable csr_assert
+ virtual function void set_csr_assert_en(bit enable, string path = "*");
+ uvm_config_db#(bit)::set(null, path, "csr_assert_en", enable);
+ endfunction
+
endclass
diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.core b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.core
new file mode 100644
index 00000000..5f2fb800
--- /dev/null
+++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.core
@@ -0,0 +1,17 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:dv_macros"
+description: "A collection of macros used in DV."
+
+filesets:
+ files_dv:
+ files:
+ - dv_macros.svh: {is_include_file: true}
+ file_type: systemVerilogSource
+
+targets:
+ default: &default_target
+ filesets:
+ - files_dv
diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh
index 56d8f263..e9413f84 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh
+++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh
@@ -2,26 +2,38 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
+`ifndef __DV_MACROS_SVH__
+`define __DV_MACROS_SVH__
+
+`ifdef UVM
+ `include "uvm_macros.svh"
+`endif
+
// UVM speficic macros
`ifndef gfn
+ // verilog_lint: waive macro-name-style
`define gfn get_full_name()
`endif
`ifndef gtn
+ // verilog_lint: waive macro-name-style
`define gtn get_type_name()
`endif
`ifndef gn
+ // verilog_lint: waive macro-name-style
`define gn get_name()
`endif
`ifndef gmv
+ // verilog_lint: waive macro-name-style
`define gmv(csr) csr.get_mirrored_value()
`endif
// cast base class obj holding extended class handle to extended class handle;
// throw error if cast fails
`ifndef downcast
+ // verilog_lint: waive macro-name-style
`define downcast(EXT_, BASE_, MSG_="", SEV_=fatal, ID_=`gfn) \
begin \
if (!$cast(EXT_, BASE_)) begin \
@@ -57,6 +69,11 @@
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`", this);
`endif
+// Convert arbitrary text / expression to string.
+`ifndef DV_STRINGIFY
+ `define DV_STRINGIFY(I_) `"I_`"
+`endif
+
// Common check macros used by DV_CHECK error and fatal macros.
// Note: Should not be called by user code
`ifndef DV_CHECK
@@ -71,7 +88,7 @@
`ifndef DV_CHECK_EQ
`define DV_CHECK_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ == EXP_)) begin \
+ if (!((ACT_) == (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s == %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
@@ -81,7 +98,7 @@
`ifndef DV_CHECK_NE
`define DV_CHECK_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ != EXP_)) begin \
+ if (!((ACT_) != (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s != %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
@@ -91,7 +108,7 @@
`ifndef DV_CHECK_CASE_EQ
`define DV_CHECK_CASE_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ === EXP_)) begin \
+ if (!((ACT_) === (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s === %s (0x%0h [%0b] vs 0x%0h [%0b]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
@@ -101,7 +118,7 @@
`ifndef DV_CHECK_CASE_NE
`define DV_CHECK_CASE_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ !== EXP_)) begin \
+ if (!((ACT_) !== (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s !== %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
@@ -111,7 +128,7 @@
`ifndef DV_CHECK_LT
`define DV_CHECK_LT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ < EXP_)) begin \
+ if (!((ACT_) < (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s < %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
@@ -121,7 +138,7 @@
`ifndef DV_CHECK_GT
`define DV_CHECK_GT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ > EXP_)) begin \
+ if (!((ACT_) > (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s > %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
@@ -131,7 +148,7 @@
`ifndef DV_CHECK_LE
`define DV_CHECK_LE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ <= EXP_)) begin \
+ if (!((ACT_) <= (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s <= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
@@ -141,13 +158,27 @@
`ifndef DV_CHECK_GE
`define DV_CHECK_GE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
begin \
- if (!(ACT_ >= EXP_)) begin \
+ if (!((ACT_) >= (EXP_))) begin \
`uvm_``SEV_(ID_, $sformatf("Check failed %s >= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
end \
end
`endif
+`ifndef DV_CHECK_STREQ
+ `define DV_CHECK_STREQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
+ if (!((ACT_) == (EXP_))) begin \
+ `uvm_``SEV_(ID_, $sformatf("Check failed \"%s\" == \"%s\" %s", ACT_, EXP_, MSG_)); \
+ end
+`endif
+
+`ifndef DV_CHECK_STRNE
+ `define DV_CHECK_STRNE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
+ if (!((ACT_) != (EXP_))) begin \
+ `uvm_``SEV_(ID_, $sformatf("Check failed \"%s\" != \"%s\" %s", ACT_, EXP_, MSG_)); \
+ end
+`endif
+
// Fatal version of the checks
`ifndef DV_CHECK_FATAL
`define DV_CHECK_FATAL(T_, MSG_="", ID_=`gfn) \
@@ -184,6 +215,16 @@
`DV_CHECK_GE(ACT_, EXP_, MSG_, fatal, ID_)
`endif
+`ifndef DV_CHECK_STREQ_FATAL
+ `define DV_CHECK_STREQ_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
+ `DV_CHECK_STREQ(ACT_, EXP_, MSG_, fatal, ID_)
+`endif
+
+`ifndef DV_CHECK_STRNE_FATAL
+ `define DV_CHECK_STRNE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
+ `DV_CHECK_STRNE(ACT_, EXP_, MSG_, fatal, ID_)
+`endif
+
// Shorthand for common foo.randomize() + fatal check
`ifndef DV_CHECK_RANDOMIZE_FATAL
`define DV_CHECK_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
@@ -208,16 +249,18 @@
`DV_CHECK_FATAL(std::randomize(VAR_) with {WITH_C_}, MSG_, ID_)
`endif
-// Shorthand for common this.randomize(foo) + fatal check
+// Shorthand for common cls_inst.randomize(member) + fatal check
+// Randomizes a specific member of a class instance.
`ifndef DV_CHECK_MEMBER_RANDOMIZE_FATAL
- `define DV_CHECK_MEMBER_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
- `DV_CHECK_FATAL(this.randomize(VAR_), MSG_, ID_)
+ `define DV_CHECK_MEMBER_RANDOMIZE_FATAL(VAR_, CLS_INST_=this, MSG_="Randomization failed!", ID_=`gfn) \
+ `DV_CHECK_FATAL(CLS_INST_.randomize(VAR_), MSG_, ID_)
`endif
-// Shorthand for common this.randomize(foo) with { } + fatal check
+// Shorthand for common cls_inst.randomize(member) with { } + fatal check
+// Randomizes a specific member of a class instance with inline constraints.
`ifndef DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL
- `define DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(VAR_, C_, MSG_="Randomization failed!", ID_=`gfn) \
- `DV_CHECK_FATAL(this.randomize(VAR_) with {C_}, MSG_, ID_)
+ `define DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(VAR_, C_, CLS_INST_=this, MSG_="Randomization failed!", ID_=`gfn) \
+ `DV_CHECK_FATAL(CLS_INST_.randomize(VAR_) with {C_}, MSG_, ID_)
`endif
// print static/dynamic 1d array or queue
@@ -297,15 +340,16 @@
`define GET_PARITY(val, odd=0) (^val ^ odd)
`endif
-// wait a task or statement with timer watchdog
+// Wait a task or statement with exit condition
+// Kill the thread when either the wait statement is completed or exit condition occurs
// input WAIT_ need to be a statement. Here are some examples
// `DV_SPINWAIT(wait(...);, "Wait for ...")
// `DV_SPINWAIT(
// while (1) begin
// ...
// end)
-`ifndef DV_SPINWAIT
-`define DV_SPINWAIT(WAIT_, MSG_ = "timeout occurred!", TIMEOUT_NS_ = default_spinwait_timeout_ns, ID_ =`gfn) \
+`ifndef DV_SPINWAIT_EXIT
+`define DV_SPINWAIT_EXIT(WAIT_, EXIT_, MSG_ = "exit condition occurred!", ID_ =`gfn) \
begin \
fork begin \
fork \
@@ -313,10 +357,164 @@
WAIT_ \
end \
begin \
- wait_timeout(TIMEOUT_NS_, ID_, MSG_); \
+ EXIT_ \
+ if (MSG_ != "") begin \
+ `uvm_info(ID_, MSG_, UVM_HIGH) \
+ end \
end \
join_any \
disable fork; \
end join \
end
`endif
+
+// wait a task or statement with timer watchdog
+`ifndef DV_SPINWAIT
+`define DV_SPINWAIT(WAIT_, MSG_ = "timeout occurred!", TIMEOUT_NS_ = default_spinwait_timeout_ns, ID_ =`gfn) \
+ `DV_SPINWAIT_EXIT(WAIT_, wait_timeout(TIMEOUT_NS_, ID_, MSG_);, "", ID_)
+`endif
+
+// Control assertions in the DUT.
+//
+// This macro is invoked in top level testbench that instantiates the DUT. It spawns off an initial
+// block that forever waits for a resource of type bit named by the string arg ~LABEL_~ that
+// can be set by any entity in the testbench. Based on the value set, it enables or disables the
+// assertions at the hierarchy of the provided path. The entity setting the resource value invokes
+// uvm_config_db#(bit)::set(...) and this macro calls the corresponding get.
+//
+// LABEL_ : Name of the assertion control resource bit (string).
+// HIER_ : Path to the module within which the assertion is controlled.
+// LEVELS_: Number of levels within the module to control the assertions.
+// SCOPE_ : Hierarchical string path to the testbench where this macro is invoked, example: %m.
+// ID_ : Identifier string used for UVM logs.
+`ifndef DV_ASSERT_CTRL
+`define DV_ASSERT_CTRL(LABEL_, HIER_, LEVELS_ = 0, SCOPE_ = "", ID_ = "%m") \
+ initial begin \
+ bit assert_en; \
+ forever begin \
+ uvm_config_db#(bit)::wait_modified(null, SCOPE_, LABEL_); \
+ if (!uvm_config_db#(bit)::get(null, SCOPE_, LABEL_, assert_en)) begin \
+ `uvm_fatal(ID_, $sformatf("Failed to get \"%0s\" from uvm_config_db", LABEL_)) \
+ end \
+ if (assert_en) begin \
+ `uvm_info(ID_, $sformatf("Enabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \
+ $asserton(LEVELS_, HIER_); \
+ end else begin \
+ `uvm_info(ID_, $sformatf("Disabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \
+ $assertoff(LEVELS_, HIER_); \
+ end \
+ end \
+ end
+`endif
+
+// Enable / disable assertions at a module hierarchy identified by LABEL_.
+//
+// This goes in conjunction with `DV_ASSERT_CTRL() macro above, but is invoked in the entity that is
+// sending the req to turn on / off the assertions. Note that that piece of code invoking this macro
+// does not have the information on the actual hierarchical path to the module or the levels - this
+// is 'wrapped' into the LABEL_ instead. DV user needs to uniquify the label sufficienly enough to
+// reflect it.
+//
+// LABEL_ : Name of the assertion control resource bit (string).
+// VALUE_ : Value of the control bit - 1 - enable assertions, 0 - disable assertions.
+// SCOPE_ : Hierarchical string path to the testbench where this macro is invoked, example: %m.
+`ifndef DV_ASSERT_CTRL_REQ
+`define DV_ASSERT_CTRL_REQ(LABEL_, VALUE_, SCOPE_="") \
+ begin \
+ uvm_config_db#(bit)::set(null, SCOPE_, LABEL_, VALUE_); \
+ end
+`endif
+
+// Macros for logging (info, warning, error and fatal severities).
+//
+// These are meant to be invoked in modules and interfaces that are shared between DV and Verilator
+// testbenches.
+`ifdef UVM
+`ifndef DV_INFO
+ `define DV_INFO(MSG_, VERBOSITY_ = UVM_LOW, ID_ = $sformatf("%m")) \
+ `uvm_info(ID_, MSG_, VERBOSITY_)
+`endif
+
+`ifndef DV_WARNING
+ `define DV_WARNING(MSG_, ID_ = $sformatf("%m")) \
+ `uvm_warning(ID_, MSG_)
+`endif
+
+`ifndef DV_ERROR
+ `define DV_ERROR(MSG_, ID_ = $sformatf("%m")) \
+ `uvm_error(ID_, MSG_)
+`endif
+
+`ifndef DV_FATAL
+ `define DV_FATAL(MSG_, ID_ = $sformatf("%m")) \
+ `uvm_fatal(ID_, MSG_)
+`endif
+
+`else // UVM
+
+`ifndef DV_INFO
+ `define DV_INFO(MSG_, VERBOSITY = DUMMY_, ID_ = $sformatf("%m")) \
+ $display("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
+`endif
+
+`ifndef DV_WARNING
+ `define DV_WARNING(MSG_, ID_ = $sformatf("%m")) \
+ $warning("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
+`endif
+
+`ifndef DV_ERROR
+ `define DV_ERROR(MSG_, ID_ = $sformatf("%m")) \
+ $error("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
+`endif
+
+`ifndef DV_FATAL
+ `define DV_FATAL(MSG_, ID_ = $sformatf("%m")) \
+ $fatal("%0t: (%0s:%0d) [%0s] %0s", $time, `__FILE__, `__LINE__, ID_, MSG_);
+`endif
+
+`endif // UVM
+
+// Declare array of alert interface, using parameter NUM_ALERTS and LIST_OF_ALERTS, and connect to
+// arrays of wires (alert_tx and alert_rx). User need to manually connect these wires to DUT
+// Also set each alert_if to uvm_config_db to use in env
+`ifndef DV_ALERT_IF_CONNECT
+`define DV_ALERT_IF_CONNECT \
+ alert_esc_if alert_if[NUM_ALERTS](.clk(clk), .rst_n(rst_n)); \
+ prim_alert_pkg::alert_rx_t [NUM_ALERTS-1:0] alert_rx; \
+ prim_alert_pkg::alert_tx_t [NUM_ALERTS-1:0] alert_tx; \
+ for (genvar k = 0; k < NUM_ALERTS; k++) begin : connect_alerts_pins \
+ assign alert_rx[k] = alert_if[k].alert_rx; \
+ assign alert_if[k].alert_tx = alert_tx[k]; \
+ initial begin \
+ uvm_config_db#(virtual alert_esc_if)::set(null, $sformatf("*.env.m_alert_agent_%0s", \
+ LIST_OF_ALERTS[k]), "vif", alert_if[k]); \
+ end \
+ end
+`endif
+
+// Instantiates a covergroup in an interface or module.
+//
+// This macro assumes that a covergroup of the same name as the __CG_NAME arg is defined in the
+// interface or module. It just adds some extra signals and logic to control the creation of the
+// covergroup instance with ~bit en_~. This defaults to 0. It is ORed with the external
+// __COND signal. The testbench can modify it at t = 0 based on the test being run.
+// NOTE: This is not meant to be invoked inside a class.
+//
+// __CG_NAME : Name of the covergroup.
+// __COND : External condition / expr that controls the creation of the covergroup.
+// __CG_ARGS : Arguments to covergroup instance, if any. Args MUST BE wrapped in (..).
+`ifndef DV_INSTANTIATE_CG
+`define DV_INSTANTIATE_CG(__CG_NAME, __COND = 1'b1, __CG_ARGS = ()) \
+ bit en_``__CG_NAME = 1'b0; \
+ __CG_NAME __CG_NAME``_inst; \
+ initial begin \
+ /* The #1 delay below allows any part of the tb to control the conditions first at t = 0. */ \
+ #1; \
+ if ((en_``__CG_NAME) || (__COND)) begin \
+ `DV_INFO({"Creating covergroup ", `"__CG_NAME`"}, UVM_MEDIUM) \
+ __CG_NAME``_inst = new``__CG_ARGS; \
+ end \
+ end
+`endif
+
+`endif // __DV_MACROS_SVH__
diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_report_server.sv b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_report_server.sv
index a29342e6..be40391b 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_report_server.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_report_server.sv
@@ -6,12 +6,15 @@
class dv_report_server extends uvm_default_report_server;
bit show_file_line = 1'b1;
+ // if enabled, show the relative path of the file. By default only show file name
+ bit show_file_path = 1'b0;
bit use_default_uvm_report_message_format = 1'b0;
function new (string name = "");
super.new(name);
// provide ability to override these knobs over cli
void'($value$plusargs("show_file_line=%0b", show_file_line));
+ void'($value$plusargs("show_file_path=%0b", show_file_path));
void'($value$plusargs("use_default_uvm_report_message_format=%0b",
use_default_uvm_report_message_format));
endfunction
@@ -66,7 +69,7 @@ class dv_report_server extends uvm_default_report_server;
string file_line;
if (show_file_line && filename != "") begin
- filename = get_no_hier_filename(filename);
+ if (!show_file_path) filename = get_no_hier_filename(filename);
file_line = $sformatf("(%0s:%0d) ", filename, line);
end
obj_name = {obj_name, ((obj_name != "") ? " " : "")};
diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils.core b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils.core
index 3bcf5f72..1412d31b 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils.core
+++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils.core
@@ -8,13 +8,14 @@ description: "DV utilities"
filesets:
files_dv:
depend:
+ - lowrisc:dv:dv_macros
- lowrisc:dv:common_ifs
- lowrisc:prim:assert:0.1
- lowrisc:ibex:bus_params_pkg
files:
- dv_utils_pkg.sv
- - dv_macros.svh: {is_include_file: true}
- dv_report_server.sv: {is_include_file: true}
+ - dv_vif_wrap.sv: {is_include_file: true}
file_type: systemVerilogSource
targets:
diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
index 3b47bfe0..2c99462e 100644
--- a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
+++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
@@ -85,6 +85,17 @@ package dv_utils_pkg;
return (a > b) ? a : b;
endfunction
+ // get absolute value of the input. Usage: absolute(val) or absolute(a - b)
+ function automatic uint absolute(int val);
+ return val >= 0 ? val : -val;
+ endfunction
+
+ // endian swap
+ function automatic logic [31:0] endian_swap(logic [31:0] data);
+ return {<<8{data}};
+ endfunction
+
+`ifdef UVM
// Simple function to set max errors before quitting sim
function automatic void set_max_quit_count(int n);
uvm_report_server report_server = uvm_report_server::get_server();
@@ -121,16 +132,6 @@ package dv_utils_pkg;
end
endfunction
- // get absolute value of the input. Usage: absolute(val) or absolute(a - b)
- function automatic uint absolute(int val);
- return val >= 0 ? val : -val;
- endfunction
-
- // endian swap
- function automatic logic [31:0] endian_swap(logic [31:0] data);
- return {<<8{data}};
- endfunction
-
// create a sequence by name and return the handle of uvm_sequence
function automatic uvm_sequence create_seq_by_name(string seq_name);
uvm_object obj;
@@ -149,8 +150,29 @@ package dv_utils_pkg;
end
return seq;
endfunction
+`endif
+
+ // Returns the hierarchical path to the interface / module N levels up.
+ //
+ // Meant to be invoked inside a module or interface.
+ // hier: String input of the interface / module, typically $sformatf("%m").
+ // n_levels_up: Integer number of levels up the hierarchy to omit.
+ // Example: if (hier = tb.dut.foo.bar, n_levels_up = 2), then return tb.dut
+ function automatic string get_parent_hier(string hier, int n_levels_up = 1);
+ int idx;
+ int level;
+ if (n_levels_up <= 0) return hier;
+ for (idx = hier.len() - 1; idx >= 0; idx--) begin
+ if (hier[idx] == ".") level++;
+ if (level == n_levels_up) break;
+ end
+ return (hier.substr(0, idx - 1));
+ endfunction
// sources
+`ifdef UVM
`include "dv_report_server.sv"
+ `include "dv_vif_wrap.sv"
+`endif
endpackage
diff --git a/vendor/lowrisc_ip/dv/sv/dv_utils/dv_vif_wrap.sv b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_vif_wrap.sv
new file mode 100644
index 00000000..61d99479
--- /dev/null
+++ b/vendor/lowrisc_ip/dv/sv/dv_utils/dv_vif_wrap.sv
@@ -0,0 +1,96 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// Abstract class meant to hold arbitrary virtual interface handles.
+//
+// Written primarily for an interface which implements functional coverage, this could be used
+// for other purposes as well. This abstract class provides utilities & macros to retrieve
+// virtual interface handles that are bound to a DUT's sub-modules. These sub-module interfaces must
+// self-register using the `DV_VIF_WRAP_SET_VIF()` macro (see details below). The extended class
+// then implements the `get_vifs()` method using the `DV_VIF_WRAP_GET_VIF*` macros below to retrieve
+// the sub-module interface handles it maintains.
+virtual class dv_vif_wrap;
+ string hier; // Represents the hierarchy of the parent module or interface.
+ string name; // Name of the class instance.
+
+ function new(string hier, string name = "");
+ this.hier = hier;
+ this.name = name;
+ endfunction
+
+ // Abstract method implemented by the extended class. It is recommended to invoke the helper
+ // macros below rather than manually define it.
+ pure virtual task get_vifs();
+
+endclass
+
+// Helper macros.
+//
+// These are defined in the file itself since they are tightly coupled to the class definition
+// above. These are scoped globally so that extended classes can invoke them.
+
+// Enable an interface to register itself (set its handle into the config db).
+//
+// Meant to be invoked from an interface. The macros invocation causes the interface to register
+// itself into the uvm_resource_db pool. The derived class of dv_vif_wrap retrieves the handle to
+// that interface handle from the uvm_resource db pool.
+//
+// SV LRM does not yet allow referencing the instance of an interface within itself as one
+// would in case of a class using the ~this~ keyword. However, most major simulators actually
+// support this in their own custom way. On VCS, a reference to self within the interface can be set
+// using `interface::self()` construct. On Xcelium, this is achieved by simply referencing the
+// interface name.
+//
+// _IF: The SV interface
+// _VIF: The corresponding virtual interface handle name
+// _LEVEL: # of hierarchical levels the module to which the SV interface is bound, starting at the
+// top level DUT instance.
+`define DV_VIF_WRAP_SET_VIF(_IF, _VIF, _LEVEL = 0) \
+ import uvm_pkg::*; \
+ function automatic void self_register(); \
+ string path; \
+ virtual _IF vif; \
+ /* Initial block adds another level in the path hierarchy which needs to be split out. */ \
+ /* Add one more to go one level up the interface instance. */ \
+ /* Example: tb.dut.core.u_foo_if.self_register -> tb.dut.core. */ \
+ path = dv_utils_pkg::get_parent_hier(.hier($sformatf("%m")), .n_levels_up(2 + _LEVEL)); \
+`ifdef VCS \
+ vif = interface::self(); \
+`elsif XCELIUM \
+ vif = _IF; \
+`else \
+ vif = _IF; \
+`endif \
+ uvm_pkg::uvm_resource_db#(virtual _IF)::set(path, `"_VIF`", vif); \
+ endfunction \
+ initial self_register();
+
+// Enables the retrieval of individual vifs.
+//
+// The three macros below go together to define the _get_vifs() task in the extended class.
+`define DV_VIF_WRAP_GET_VIFS_BEGIN \
+ task get_vifs(); \
+ fork \
+
+// To avoid race condition between the instant when an interface handle is set into the config db
+// and the instant when it is retrieved (in the same time step, at t = 0), the macro below invokes
+// uvm_config_db#(..)::wait_modified() to ensure that the retrieval is done only after the set.
+`define DV_VIF_WRAP_GET_VIF(_IF, _VIF) \
+ begin \
+ bit vif_found; \
+ /* At most 2 retries. */ \
+ repeat (2) begin \
+ /* Force the evaluation at the end of the time step. */ \
+ #0; \
+ if (uvm_pkg::uvm_resource_db#(virtual _IF)::read_by_name(hier, `"_VIF`", _VIF)) begin \
+ vif_found = 1'b1; \
+ break; \
+ end \
+ end \
+ `DV_CHECK_FATAL(vif_found, {`"_VIF`", " not found in the resource db"}, hier) \
+ end
+
+`define DV_VIF_WRAP_GET_VIFS_END \
+ join \
+ endtask
diff --git a/vendor/lowrisc_ip/dv/tools/Makefile b/vendor/lowrisc_ip/dv/tools/Makefile
deleted file mode 100644
index 5815adcd..00000000
--- a/vendor/lowrisc_ip/dv/tools/Makefile
+++ /dev/null
@@ -1,96 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-# directory paths
-# _ROOT are paths indicating a 'starting point'
-# _PATH paths are intermediate paths leading to a _DIR
-# _LOC are directory names without the hierarchy
-# _DIR paths are final paths used in make rules
-# All these variables can be overridden from the command line
-# Test Makefile needs to set DV_DIR, DUT_TOP, TB_TOP, FUSESOC_CORE, COMPILE_KEY and TEST_NAME
-# at minimum
-
-export SHELL := /bin/bash
-TIMESTAMP ?= $(shell date +%m.%d.%y_%H.%M.%S)
-MAKE_ROOT := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
-export PROJ_ROOT := ${MAKE_ROOT}/../../..
-export PRJ_DIR := ${PROJ_ROOT}
-export CURR_DIR := $(shell pwd)
-SCRATCH_ROOT ?= ${DV_DIR}/scratch
-
-SCRATCH_LOC ?= ${DUT_TOP}
-BUILD_LOC ?= ${COMPILE_KEY}
-RUN_LOC ?= ${TIMESTAMP}
-
-SCRATCH_PATH ?= ${SCRATCH_ROOT}/${SCRATCH_LOC}
-BUILD_DIR := ${SCRATCH_PATH}/${BUILD_LOC}
-RUN_PATH ?= ${SCRATCH_PATH}/${TEST_NAME}
-RUN_DIR := ${RUN_PATH}/${RUN_LOC}
-COV_REPORT_DIR ?= ${SCRATCH_PATH}/cov_report
-SW_BUILD_DIR ?= ${RUN_DIR}/sw_build
-SW_ROOT_DIR := ${PRJ_DIR}/sw
-SW_BUILD_DEVICE ?= fpga_nexysvideo
-
-JOB_OPTS ?=
-BUILD_JOB_OPTS ?= $(JOB_OPTS)
-RUN_JOB_OPTS ?= $(JOB_OPTS)
-
-# limit run directories upto a limit specified below
-RUN_DIR_LIMIT ?= 5
-
-# global simulation specific variables (but simulator independent)
-SEED ?= $(shell od -vAn -N4 -tu < /dev/random | xargs)
-BUILD_LOG ?= ${BUILD_DIR}/build.log
-RUN_LOG ?= ${RUN_DIR}/run.log
-UVM_VERBOSITY ?= UVM_LOW
-
-TOPS ?= ${TB_TOP} ${DUT_TOP}_bind
-
-# Options for SV build / C build / simulation run
-# CL_ prefix represents command line versions of these options - they should be empty and only
-# be set on the command line
-
-# BUILD_OPTS are passed to the simulator during the SV testbench compile step
-CL_BUILD_OPTS +=
-BUILD_OPTS += $(addprefix -top , $(TOPS))
-
-# RUN_OPTS are passed to the simulation executable that is invoked to run the simulation
-CL_RUN_OPTS +=
-RUN_OPTS += +UVM_NO_RELNOTES
-
-# SW_OPTS are options for SW apps that come with their own Makefile (example coremark)
-CL_SW_OPTS +=
-SW_OPTS += ${CL_SW_OPTS}
-
-# SW_FLAGS are compiler flags / directives that we can pass for each test using our own Makefiles
-CL_SW_FLAGS +=
-SW_FLAGS += ${CL_SW_FLAGS}
-
-# default project-wide bus widths
-TL_AW ?= 32
-TL_DW ?= 32
-TL_DBW ?= 4
-
-# add auxiliary / helper mk files
-# set simulator to vcs by default
-SIMULATOR ?= vcs
-
-# common tests/seqs
-include ${MAKE_ROOT}/common_tests.mk
-
-# tool to generate file list
-include ${MAKE_ROOT}/fusesoc.mk
-
-# modes / switches (option groups) that can be turned on easily
-include ${MAKE_ROOT}/modes.mk
-
-# add tool specific mk file
-ifeq (${SIMULATOR},vcs)
- include ${MAKE_ROOT}/vcs/vcs.mk
-else ifeq (${SIMULATOR},xcelium)
- include ${MAKE_ROOT}/xcelium/xcelium.mk
-# else add other tool specific mk files
-endif
-
-# generic rules to sequence build and run flows
-include ${MAKE_ROOT}/rules.mk
diff --git a/vendor/lowrisc_ip/dv/tools/README.md b/vendor/lowrisc_ip/dv/tools/README.md
index c1b96880..499372ef 100644
--- a/vendor/lowrisc_ip/dv/tools/README.md
+++ b/vendor/lowrisc_ip/dv/tools/README.md
@@ -1,225 +1,2 @@
-# DV build and run flow
-The Makefile system implemented here is a standardized solution for building and
-running DV sims for all ip/block/top level testbenches. This is only a stop
-gap solution until a proper build and run tool is developed and implemented. The
-way the Makefiles are structured will hopefully make the eventual transition
-easier.
-
-## Makefile Organization
-```
-- hw/ip//dv/Makefile OR
- hw/top_earlgrey/dv/Makefile
- - hw/dv/tools/Makefile
- - ral_gen.mk
- - fusesoc.mk
- - modes.mk
- - rules.mk
- - vcs/vcs.mk # if SIMULATOR=vcs
- - xcelium/xcelium.mk # if SIMULATOR=xcelium
-```
-
-### Test Makefile
-The test Makefile that serves as the entry point into the DV build and run
-flow is placed in individual 'dv' directories of each testbench. For example:
-```hw/ip/uart/dv/Makefile```
-This includes the top level Makefile, some mandatory variables that are to be
-set and exported and the actual tests.
-
-### Top level Makefile
-This includes the top level Makefile which contains generic set of variable
-names (most of which can be overridden) to indicate where, what and how to build
-and run tests, some option groups and generic set of rules. This is located at
-hw/dv/tools/Makefile
-
-### Generic Tool-independent Make variables
-The following is a list of Make variables that are used for specifying options
-to run simulations in a tool-independent way. Some variables are overridable in
-the Test Makefile (or command line) and some are appended to group / add options
-together. Please see following table for more details:
-
-Make variable | Description | Overridable (O) or Appendable (A) |
---------------|-------------|-----------------------------------|
-DV_DIR | This is the top level DV directory for the IP containing the Test Makefile. | |
-DUT_TOP | This is the top level dut module under test. | |
-TB_TOP | This is the top level tb module under test. This is used by the `.tcl` file when dumping waves. | |
-FUSESOC_CORE | This is the testbench fusesoc .core name that contains the simulation target. This .core file is typically placed in the same directory as the test Makefile. | |
-COMPILE_KEY | Users need to define COMPILE_KEY sets for building Test Makefile, CL with a unique sets of compile time options. This is to be done in the Test Makefile in this way: `ifeq ($(COMPILE_KEY),foo)` `BUILD_OPTS += +define+FOO` `endif` There is a 'default' compile key already added which implies no additional compile time options are required. Within each test specification, the COMPILE_KEY can be overridden to use the specific compile key. `ifeq ($(TEST_NAME),foo_test)` `COMPILE_KEY = foo` `# other test opts` `endif` | O (Within tests, & command line) |
-UVM_TEST | SV UVM test class to create in the test. This is set to the 'base_test' by default and is overridden in the test specifications if needed. | O (Test Makefile) |
-UVM_TEST_SEQ | SV UVM test sequence to create in the test. This is set to the 'base_test' by default and is overridden in the test specifications if needed. | O (Test Makefile) |
-TEST_NAME | Name of the test to run. These are specified in the test Makefile. By default, it is set to run the sanity test (simply calling 'make' will run this test). | O (command line only) |
-SIMULATOR | What simulator to use. Currently only 'vcs' is supported and is set by default. | O |
-BUILD_OPTS | Options to pass for build. | A (Test Makefile :: COMPILE_KEY specifications only) |
-CL_BUILD_OPTS | Pass additional build options on the command line. | A (command line only) |
-RUN_OPTS | Options to pass for run. | A (Test Makefile :: test specifications only) |
-CL_RUN_OPTS | Pass additional run options on the command line. | A (command line only) |
-WAVES | Enable wave dumps (fsdb). Set to 0 by default; override with WAVES=1. | O |
-SIMPROFILE | Turn on sim profiling (time option by default). Set to 0 by default; override with SIMPROFILE=1. | O |
-COV | Turn on coverage collection. Set to 0 by default; override with COV=1. | O |
-UVM_VERBOSITY | Verbosity level for UVM. Set to UVM_LOW by default; override with UVM_VERBOSITY=UVM_NONE / UVM_LOW / UVM_HIGH / UVM_DEBUG. | O |
-BUILD_LOC | Build directory name in the scratch area. This defaults to the COMPILE_KEY used by the corresponding test. You can override it to use a different directory name. This is helpful when there is a test / regression already running and you made some fixes and want to rerun some failing test from another terminal without affecting the existing running sims. | O |
-RUN_LOC | Run directory name in the scratch area. This defaults to a 'timestamp' value. You can override this to a specific name. Everytime a test is run, it creates a new directory with the current timestamp for the name. By overriding this with a specific name and rerunning the same test with the same directory name, you won't have to reopen waves - you can just reload. This is useful during test debug. | O |
-SCRATCH_ROOT | This is the path to the root scratch area for bulding and running tests. If SCRATCH_ROOT is not already set, it will create a `scratch` directory in `pwd` which typically is the same as `DV_DIR`. | O (command line only) |
-RUN_DIR_LIMIT | When you run tests, The flow creates a new `RUN_LOC` directory with the current timestamp (unless it is overridden). In course of debug, you may run the same test multiple times, which will eventually result in a large number of old timestamp directories in the scratch space. Without periodic cleanup, you may run out of scratch space. By default, this variable is set to 5, which means before running the test, it will prune the test area to contain no more than RUN_DIR_LIMIT number of most recent directories (including the newly created one). On the flipside, if you want to run the same test with a large number of iterations, you will need to override this variable to be set to that many iterations to prevent the flow from deleting actively running simulations. | O (command line only) |
-SEED | This is a run time parameter passed to the sim executable. It uses `od` command to generate a 32-bit random number to run the sim with a unique seed. You can override this variable on the command line to run the test with a specific seed for debug. | O (command line only) |
-XPROP | This is a compile time parameter to enable / disable X-Propagation. Set to 1 by default. | O (command line only) |
-
-This is not an exhaustive list of Make variables. Please see `./Makefile` and
-`./*.mk` for more such variables in use.
-
-### ral_gen.mk
-#### RAL generation tool specific mk file
-This lists tools and options to generate the ral model by simply running the
-command `make ral` from the same directory as the Test Makefile. For generating
-the UVM REG based RAL model, we use the same [in-house tool]({{< relref "doc/rm/register_tool" >}}) for autogenerating
-RTL with mako template.
-
-### fusesoc.mk
-#### RTL/TB filelist generation tool specific mk file
-This lists tools and options to generate the flattened filelist to supply to the
-simulator during the build step.
-
-### modes.mk
-#### Modes or option groups that can be turned on easily
-This lists common modes (which are groups of compile time and / or run time options)
-passed to the simulator for turning on a specific function. Modes listed in this
-file are meant to be common across all environments and simulator tools. Modes added
-in this file are `WAVES`, `COV`, `SIMPROFILE` and `UVM_TRACE`. These are truly
-global and can be turned on easily on the command line by setting them to 1: `COV=1`
-switch on the command line will turn on coverage. These modes may require options
-that are specific to the simulator tool, so the make variable `SIMULATOR` can be used
-to set tool specific options.
-
-### rules.mk
-#### List of Make targets and recipes
-This is the only file in the make flow that contains targets and recipes that
-are completely agnostic to the tools used. It sequences the build and run set
-of targets.
-
-### Simulator mk ({vcs, xcelium}.mk)
-#### Simulator tool specific options
-The top level Makefile uses `SIMULATOR` variable (which is set to `vcs` by
-default) to enable tool specific options. If support for new tools need to be
-added, then those tool specific mk files need to be supplied. It sets the
-following mandatory variables - `SIMCC` & `SIMX`. It also exports additional
-tool specific variables to the sub shell when the recipes are executed. This
-file has been placed in the respective tool directory.
-
-### Simulation targets
-* **build**: Compile and elaborate the testbench
-* **gen_sv_flist**: Generate the full file list which will be used by the
- simulator to build the simulation executable
-* **run**: Run the test (the simulation executable needs to be built already)
-* **env/\_reg_block.sv**: Generate the RAL model only
-* **sw_build**: Only compile the C SW test
-
-### Building and running tests
-All of the below command examples are to be run from the 'dv' directory
-containing the Test Makefile.
-
-##### Run the following command to build and run tests:
-```console
-$ make TEST_NAME=[test-name] [overrides]
-```
-
-##### To only buld the simv:
-```console
-$ make build TEST_NAME=[test-name] [overrides]
-```
-
-##### To run the sim after build is complete:
-```console
-$ make run TEST_NAME=[test-name] [overrides]
-```
-
-##### To build and run the sanity test:
-```console
-$ make
-```
-This will work provided user has specified a set a 'default' value to the
-`TEST_NAME` Make variable.
-
-##### Below are some examples
-
-###### Build and run a test:
-This builds the 'default' compile key and runs the 'uart_sanity' test
-```console
-$ make TEST_NAME=uart_sanity
-```
-
-###### Run with specific seed:
-```console
-$ make TEST_NAME= SEED=123423334
-```
-
-###### Dump waves:
-```console
-$ make TEST_NAME= SEED=123423334 WAVES=1
-```
-
-###### Run with coverage option enabled:
-```console
-$ make TEST_NAME= COV=1
-```
-
-The options.mk file lists several tool options passed to the build and run steps
-for enabling coverage collection. One of the options is adding a hier file (with
-a `-cm_hier` switch). By default, it looks for a file called `cover.cfg` in the
-'dv' directory set using the `CM_HIER` Make variable - if it exists, it adds the
-switch automatically when coverage is enabled. If another hier file is desired
- (or with another name), it can be placed anywhere as the user desires and user can
- add the following line to the Test Makefile:
-
-```gnumake
-CM_HIER := SIMPROFILE=1
-```
-
-###### Override UVM verbosity:
-```console
-$ make TEST_NAME= WAVES=1 UVM_VERBOSITY=UVM_DEBUG
-```
-
-###### Build only (This will build the 'default' compile key):
-```console
-$ make build
-```
-
-###### Build 'foo' compile key instead:
-```console
-$ make build COMPILE_KEY=foo
-```
-
-###### Build with 'FOO' preprocessor flag:
-```console
-$ make build CL_BUILD_OPTS+=+define+FOO
-```
-
-###### Run with 'FOO' runtime plusarg:
-```console
-$ make TEST_NAME= CL_RUN_OPTS+=+FOO
-```
-
-###### Run test with Xcelium:
-```
-$ make TEST_NAME= SIMULATOR=xcelium
-```
-
-###### Run test with Xcelium and dump WAVES in fsdb:
-```console
-$ make TEST_NAME= SIMULATOR=xcelium WAVES=1
-```
-
-###### Run test with Xcelium and dump WAVES in shm:
-```console
-$ make TEST_NAME= SIMULATOR=xcelium WAVES=1 DUMP=shm
-```
-
-###### Run only (This assumes the sim executable for the specified compile key is available):
-
-```console
-$ make run TEST_NAME= SEED=123423334 WAVES=1
-```
+# DV simulation flow
+TODO
diff --git a/vendor/lowrisc_ip/dv/tools/common.tcl b/vendor/lowrisc_ip/dv/tools/common.tcl
new file mode 100644
index 00000000..f9a941d1
--- /dev/null
+++ b/vendor/lowrisc_ip/dv/tools/common.tcl
@@ -0,0 +1,61 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+# Commonly used globals & procs. This file must be sourced first.
+set simulator ""
+if {[info exists ::env(SIMULATOR)]} {
+ set simulator "$::env(SIMULATOR)"
+} else {
+ puts "ERROR: tool script run without SIMULATOR environment variable."
+ quit
+}
+
+set waves "none"
+if {[info exists ::env(WAVES)]} {
+ set waves "$::env(WAVES)"
+}
+
+set tb_top "tb"
+if {[info exists ::(TB_TOP)]} {
+ set tb_top "$::env(TB_TOP)"
+ puts "WARNING: TB_TOP environment variable not set - using \"tb\" as the
+ top level testbench hierarchy."
+}
+
+# Checks if variable is defined, else throw an error and exit.
+proc checkVarExists {var} {
+ upvar $var var_
+ if {![info exists var_]} {
+ puts "ERROR: Variable \"$var\" not found."
+ quit
+ }
+}
+
+# If variable is defined, then use it, else set the default value.
+proc setDefault {var value} {
+ upvar $var var_
+ if {[info exists var_]} {
+ puts "INFO: \"$var\" is already set to \"$var_\"."
+ } else {
+ puts "INFO: Setting \"$var\" to \"$value\"."
+ set var_ $value
+ }
+ return $var_
+}
+
+proc checkEq {var value} {
+ upvar $var var_
+ if {$var_ != $value} {
+ puts "ERROR: Check failed \"$var\" == \"$value\"!. Actual: \"$var_\"."
+ quit
+ }
+}
+
+proc checkNe {var value} {
+ upvar $var var_
+ if {$var_ == $value} {
+ puts "ERROR: Check failed \"$var\" != \"$value\"!. Actual: \"$var_\"."
+ quit
+ }
+}
diff --git a/vendor/lowrisc_ip/dv/tools/common_tests.mk b/vendor/lowrisc_ip/dv/tools/common_tests.mk
deleted file mode 100644
index 12a4246f..00000000
--- a/vendor/lowrisc_ip/dv/tools/common_tests.mk
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-# Makefile option groups that can be enabled by test Makefile / command line.
-# These are generic set of option groups that apply to all testbenches.
-# These are meant to be simulator agnostic
-# Please add tool specific options with appropriate ifeq's
-
-TEST_PREFIX ?= ${DUT_TOP}
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_csr_hw_reset)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +csr_hw_reset
- RUN_OPTS += +en_scb=0
-endif
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_csr_rw)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +csr_rw
- RUN_OPTS += +en_scb=0
-endif
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_csr_bit_bash)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +csr_bit_bash
- RUN_OPTS += +en_scb=0
-endif
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_csr_aliasing)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +csr_aliasing
- RUN_OPTS += +en_scb=0
-endif
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_same_csr_outstanding)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +run_same_csr_outstanding
- RUN_OPTS += +en_scb=0
-endif
-
-# make sure DUT has memory and support this seq before run the test
-ifeq (${TEST_NAME},${TEST_PREFIX}_csr_mem_walk)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +csr_mem_walk
- RUN_OPTS += +en_scb=0
-endif
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_tl_errors)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +run_tl_errors
-endif
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_intr_test)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +run_intr_test
-endif
-
-ifeq (${TEST_NAME},${TEST_PREFIX}_stress_all_with_rand_reset)
- UVM_TEST_SEQ = ${TEST_PREFIX}_common_vseq
- RUN_OPTS += +run_stress_all_with_rand_reset
- // 10ms
- RUN_OPTS += +test_timeout_ns=10000000000
- RUN_OPTS += +stress_seq=${TEST_PREFIX}_stress_all_vseq
-endif
-
-
diff --git a/vendor/lowrisc_ip/dv/data/common_modes.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/common_modes.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/common_modes.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/common_modes.hjson
diff --git a/vendor/lowrisc_ip/dv/data/common_sim_cfg.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson
similarity index 71%
rename from vendor/lowrisc_ip/dv/data/common_sim_cfg.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson
index aaaeb3ac..a3ad1e99 100644
--- a/vendor/lowrisc_ip/dv/data/common_sim_cfg.hjson
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/common_sim_cfg.hjson
@@ -6,12 +6,12 @@
dv_root: "{proj_root}/vendor/lowrisc_ip/dv"
flow: sim
- flow_makefile: "{dv_root}/data/sim.mk"
+ flow_makefile: "{dv_root}/tools/dvsim/sim.mk"
import_cfgs: ["{proj_root}/dv/uvm/common_project_cfg.hjson",
- "{dv_root}/data/common_modes.hjson",
- "{dv_root}/data/fusesoc.hjson",
- "{dv_root}/data/{tool}/{tool}.hjson"]
+ "{dv_root}/tools/dvsim/common_modes.hjson",
+ "{dv_root}/tools/dvsim/fusesoc.hjson",
+ "{dv_root}/tools/dvsim/{tool}.hjson"]
// Default directory structure for the output
build_dir: "{scratch_path}/{build_mode}"
@@ -37,11 +37,15 @@
tl_dbw: 4
// Default UVM verbosity settings
- n: UVM_NONE
- l: UVM_LOW
- m: UVM_MEDIUM
- h: UVM_HIGH
- d: UVM_DEBUG
+ expand_uvm_verbosity_n: UVM_NONE
+ expand_uvm_verbosity_l: UVM_LOW
+ expand_uvm_verbosity_m: UVM_MEDIUM
+ expand_uvm_verbosity_h: UVM_HIGH
+ expand_uvm_verbosity_d: UVM_DEBUG
+
+ // Default simulation verbosity (l => UVM_LOW). Can be overridden by
+ // the --verbosity command-line argument.
+ verbosity: l
// Path to the dut instance (this is used in a couple of places such as coverage cfg
// file, xprop cfg file etc. If this is different for your block, then override it with
@@ -63,43 +67,38 @@
"+define+UVM_REG_BYTENABLE_WIDTH={tl_dbw}"]
run_opts: ["+UVM_NO_RELNOTES",
- "+UVM_VERBOSITY={verbosity}"]
+ "+UVM_VERBOSITY={expand_uvm_verbosity_{verbosity}}"]
// Default list of things to export to shell
exports: [
- TOOL_SRCS_DIR: {tool_srcs_dir}
- EN_WAVES: {waves}
- DUMP_FMT: {dump_fmt}
- DUT_TOP: {dut}
- TB_TOP: {tb}
- dut_instance: {dut_instance}
+ { TOOL_SRCS_DIR: "{tool_srcs_dir}" },
+ { SIMULATOR: "{tool}" },
+ { WAVES: "{waves}" },
+ { DUT_TOP: "{dut}" },
+ { TB_TOP: "{tb}" },
+ { dut_instance: "{dut_instance}" }
]
// Build modes are collection of build_opts and run_opts
- // A test can enable a specific build mode by setting 'use_build_mode' key
- build_modes: [
- {
- name: foo
- build_opts: ["+define+bx",
- "+define+by",
- "+define+bz"]
- run_opts: ["+rx=1",
- "+ry=2",
- "+rz=3"]
- }
- {
- name: bar
- build_opts: ["+define+bbaru1",
- "+define+bbaru2",
- "+define+bbaru3"]
- run_opts: ["+rbar1u=1",
- "+rbar2u=2",
- "+rbar3u=3"]
- }
- {
- name: cover_reg_top
- }
- ]
+ //
+ // To define a build mode that overrides these flags, add something
+ // like the following to the IP block's configuration:
+ //
+ // build_modes: [
+ // {
+ // name: foo
+ // build_opts: ["+define+bx",
+ // "+define+by",
+ // "+define+bz"]
+ // run_opts: ["+rx=1",
+ // "+ry=2",
+ // "+rz=3"]
+ // }
+ // ]
+ //
+ // To use a build mode for a specific test, set the 'use_build_mode' key.
+ //
+ build_modes: []
// Regressions are tests that can be grouped together and run in one shot
// By default, two regressions are made available - "all" and "nightly". Both
@@ -110,7 +109,7 @@
// tests.
regressions: [
{
- name: sanity
+ name: smoke
tests: []
reseed: 1
}
@@ -133,7 +132,9 @@
// Add waves.tcl to the set of sources to be copied over to
// {tool_srcs_dir}. This can be sourced by the tool-specific TCL
// script to set up wave dumping.
- tool_srcs: ["{dv_root}/tools/waves.tcl"],
+ tool_srcs: ["{dv_root}/tools/sim.tcl",
+ "{dv_root}/tools/common.tcl",
+ "{dv_root}/tools/waves.tcl"]
// Project defaults for VCS
vcs_cov_cfg_file: "{{build_mode}_vcs_cov_cfg_file}"
diff --git a/vendor/lowrisc_ip/dv/data/dsim/dsim.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson
similarity index 78%
rename from vendor/lowrisc_ip/dv/data/dsim/dsim.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson
index 54fb0a6f..f5a61a36 100644
--- a/vendor/lowrisc_ip/dv/data/dsim/dsim.hjson
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/dsim.hjson
@@ -5,8 +5,13 @@
build_cmd: "{job_prefix} dsim"
run_cmd: "{job_prefix} dsim"
- build_opts: [
- "-work {build_dir}/dsim_out",
+ // Indicate the tool specific helper sources - these are copied over to the
+ // {tool_srcs_dir} before running the simulation.
+ // TODO, there is no dsim tool file, point to vcs for now to avoid error from script
+ // tool_srcs: ["{dv_root}/tools/dsim/*"]
+
+
+ build_opts: ["-work {build_dir}/dsim_out",
"-genimage image",
"-sv",
// Set parallel compilation jobs limit
@@ -21,24 +26,23 @@
"+incdir+{build_dir}",
// Suppress following DSim errors and warnings:
// EnumMustBePositive - UVM 1.2 violates this
- "-suppress EnumMustBePositive"
- ]
+ "-suppress EnumMustBePositive"]
- run_opts: [
- "-work {build_dir}/dsim_out",
+ run_opts: ["-work {build_dir}/dsim_out",
"-image image",
// UVM DPI
"-sv_lib {DSIM_HOME}/lib/libuvm_dpi.so",
"-sv_seed {seed}",
- // tell DSim to write line-buffered std output (lines will be written in proper order)
+ // tell DSim to write line-buffered stdout (lines will be written in proper order)
"-linebuf",
"+UVM_TESTNAME={uvm_test}",
"+UVM_TEST_SEQ={uvm_test_seq}"]
- // Indicate the tool specific helper sources - these are copied over to the
- // {tool_srcs_dir} before running the simulation.
- // TODO, there is no dsim tool file, point to vcs for now to avoid error from script
- tool_srcs: ["{dv_root}/tools/vcs/*"]
+ // Supported wave dumping formats (in order of preference).
+ supported_wave_formats: ["vcd", "fst"]
+
+ // Default tcl script used when running the sim. Override if needed.
+ run_script: ""
// TODO: refactor coverage configuration for DSim.
@@ -56,13 +60,12 @@
cov_merge_dir: "{scratch_base_path}/cov_merge"
cov_merge_db_dir: "{cov_merge_dir}/merged.vdb"
cov_merge_cmd: "{job_prefix} urg"
- cov_merge_opts: [
- ]
+ cov_merge_opts: []
+
// Generate coverage reports in text as well as html.
cov_report_dir: "{scratch_base_path}/cov_report"
cov_report_cmd: "{job_prefix} urg"
- cov_report_opts: [
- ]
+ cov_report_opts: []
cov_report_txt: "{cov_report_dir}/dashboard.txt"
cov_report_page: "dashboard.html"
@@ -72,40 +75,36 @@
cov_analyze_cmd: "{job_prefix} verdi"
cov_analyze_opts: ["-cov",
"-covdir {cov_merge_db_dir}",
- "-line nocasedef"
+ "-line nocasedef",
"-elfile {vcs_cov_excl_files}"]
// Vars that need to exported to the env.
exports: [
+ { PATH: "{DSIM_HOME}:{PATH}" }
+ { LD_LIBRARY_PATH: "{DSIM_HOME}/lib:{DSIM_HOME}/system_lib:{LD_LIBRARY_PATH}" }
]
// Defaults for DSim
// TODO: there is currently no equivalent of "all" coverage metrics in DSim
- cov_metrics: ""
+ cov_metrics: ""
// pass and fail patterns
build_fail_patterns: ["^Error-.*$"]
run_fail_patterns: ["^Error-.*$"] // Null pointer error
// waveform
- wave_type: "vcd"
- wave_file: "dsim_wave.{wave_type}"
- probe_file: "dsim.probe"
+ probe_file: "dsim.probe"
build_modes: [
{
name: dsim_waves
is_sim_mode: 1
- build_opts: [
- "+acc+b"
- ]
- run_opts: [
- "-waves {wave_file}",
- // dsim.probe is currently undefined
- //"-wave-scope-specs {probe_file}",
- // Dump unpacked structs and arrays.
- "-dump-agg"
- ]
+ build_opts: ["+acc+b"]
+ run_opts: ["-waves waves.{waves}",
+ // dsim.probe is currently undefined
+ //"-wave-scope-specs {probe_file}",
+ // Dump unpacked structs and arrays.
+ "-dump-agg"]
}
// TODO: support coverage mode
// Note: no specific build or run options are required for dsim to produce functional
diff --git a/vendor/lowrisc_ip/dv/data/fusesoc.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/fusesoc.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/fusesoc.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/fusesoc.hjson
diff --git a/vendor/lowrisc_ip/dv/data/riviera/riviera.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/riviera.hjson
similarity index 88%
rename from vendor/lowrisc_ip/dv/data/riviera/riviera.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/riviera.hjson
index e8628974..6be02dcd 100644
--- a/vendor/lowrisc_ip/dv/data/riviera/riviera.hjson
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/riviera.hjson
@@ -7,7 +7,7 @@
// Indicate the tool specific helper sources - these are copied over to the
// {tool_srcs_dir} before running the simulation.
- tool_srcs: ["{proj_root}/dv/uvm/data/riviera/*"]
+ tool_srcs: ["{dv_root}/tools/riviera/*"]
build_opts: ["-sv",
"-timescale 1ns/1ps",
@@ -20,7 +20,14 @@
"-lib {sv_flist_gen_dir}/work",
"+UVM_TESTNAME={uvm_test}",
"+UVM_TEST_SEQ={uvm_test_seq}",
- "-do {tool_srcs_dir}/riviera_run.do"]
+ "-do {run_script}"]
+
+
+ // Supported wave dumping formats (in order of preference).
+ supported_wave_formats: []
+
+ // Default tcl script used when running the sim. Override if needed.
+ run_script: "{tool_srcs_dir}/riviera_run.do"
// Coverage related.
// TODO: These options have to be filled in.
diff --git a/vendor/lowrisc_ip/dv/data/sim.mk b/vendor/lowrisc_ip/dv/tools/dvsim/sim.mk
similarity index 79%
rename from vendor/lowrisc_ip/dv/data/sim.mk
rename to vendor/lowrisc_ip/dv/tools/dvsim/sim.mk
index 72d111a4..702f3e6f 100644
--- a/vendor/lowrisc_ip/dv/data/sim.mk
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/sim.mk
@@ -19,11 +19,11 @@ prep_tool_srcs:
mkdir -p ${tool_srcs_dir}
${LOCK_TOOL_SRCS_DIR} "cp -Ru ${tool_srcs} ${tool_srcs_dir}/."
-pre_compile:
+pre_compile: prep_tool_srcs
@echo "[make]: pre_compile"
mkdir -p ${build_dir}
-gen_sv_flist: pre_compile prep_tool_srcs
+gen_sv_flist: pre_compile
@echo "[make]: gen_sv_flist"
cd ${build_dir} && ${sv_flist_gen_cmd} ${sv_flist_gen_opts}
@@ -39,7 +39,7 @@ compile_result: post_compile
run: run_result
-pre_run:
+pre_run: prep_tool_srcs
@echo "[make]: pre_run"
mkdir -p ${run_dir}
ifneq (${sw_test},)
@@ -62,11 +62,13 @@ ifneq (${sw_test},)
-n "rom" -o "${run_dir}"
# Copy over the boot rom image to the run_dir.
cp ${sw_build_dir}/build-out/sw/device/boot_rom/boot_rom_${sw_build_device}.32.vmem \
- ${run_dir}/rom.32.vmem
+ ${run_dir}/rom.vmem
+ cp ${sw_build_dir}/build-out/sw/device/boot_rom/boot_rom_${sw_build_device}.elf \
+ ${run_dir}/rom.elf
ifeq (${sw_test_is_prebuilt},1)
# Copy over the sw test image and related sources to the run_dir.
- cp ${proj_root}/${sw_test}.64.vmem ${run_dir}/sw.64.vmem
+ cp ${proj_root}/${sw_test}.64.vmem ${run_dir}/sw.vmem
# Optionally, assume that ${sw_test}_logs.txt exists and copy over to the run_dir.
# Ignore copy error if it actually doesn't exist. Likewise for ${sw_test}_rodata.txt.
-cp ${proj_root}/${sw_test}_logs.txt ${run_dir}/sw_logs.txt
@@ -76,14 +78,23 @@ else
# Compile the sw test code and generate the image.
${LOCK_SW_BUILD} "ninja -C ${sw_build_dir}/build-out \
${sw_test}_export_${sw_build_device}"
+ # Convert sw image to frame format
+ # TODO only needed for loading sw image through SPI. Can enhance this later
+ ${LOCK_SW_BUILD} "ninja -C ${sw_build_dir}/build-out sw/host/spiflash/spiflash_export"
+ ${LOCK_SW_BUILD} "${sw_build_dir}/build-bin/sw/host/spiflash/spiflash --input \
+ ${sw_build_dir}/build-bin/${sw_test}_${sw_build_device}.bin \
+ --dump-frames=${run_dir}/sw.frames.bin"
+ ${LOCK_SW_BUILD} "srec_cat ${run_dir}/sw.frames.bin --binary \
+ --offset 0x0 --byte-swap 4 --fill 0xff -within ${run_dir}/sw.frames.bin -binary -range-pad 4 \
+ --output ${run_dir}/sw.frames.vmem --vmem"
# Extract the sw test logs.
${proj_root}/util/device_sw_utils/extract_sw_logs.py \
-e "${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.elf" \
-f .logs.fields -r .rodata \
-n "sw" -o "${run_dir}"
# Copy over the sw test image to the run_dir.
- cp ${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.64.vmem \
- ${run_dir}/sw.64.vmem
+ cp ${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.64.vmem ${run_dir}/sw.vmem
+ cp ${sw_build_dir}/build-out/${sw_test}_${sw_build_device}.elf ${run_dir}/sw.elf
endif
endif
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/csr_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/csr_testplan.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/tools/testplans/csr_testplan.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/testplans/csr_testplan.hjson
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/enable_reg_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/enable_reg_testplan.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/tools/testplans/enable_reg_testplan.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/testplans/enable_reg_testplan.hjson
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/fpv_csr_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/fpv_csr_testplan.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/tools/testplans/fpv_csr_testplan.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/testplans/fpv_csr_testplan.hjson
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/intr_test_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/intr_test_testplan.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/tools/testplans/intr_test_testplan.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/testplans/intr_test_testplan.hjson
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/mem_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/mem_testplan.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/tools/testplans/mem_testplan.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/testplans/mem_testplan.hjson
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/shadow_reg_errors_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/shadow_reg_errors_testplan.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/tools/testplans/shadow_reg_errors_testplan.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/testplans/shadow_reg_errors_testplan.hjson
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/stress_all_with_reset_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/tools/testplans/stress_all_with_reset_testplan.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/testplans/stress_all_with_reset_testplan.hjson
diff --git a/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson
new file mode 100644
index 00000000..4e78edae
--- /dev/null
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/testplans/tl_device_access_types_testplan.hjson
@@ -0,0 +1,57 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+ entries: [
+ {
+ name: tl_d_oob_addr_access
+ desc: "Access out of bounds address and verify correctness of response / behavior"
+ milestone: V2
+ tests: ["{name}_tl_errors"]
+ }
+ {
+ name: tl_d_illegal_access
+ desc: '''Drive unsupported requests via TL interface and verify correctness of response
+ / behavior. Below error cases are tested
+ - TL-UL protocol error cases
+ - Unsupported opcode. e.g a_opcode isn't Get, PutPartialData or PutFullData
+ - Mask isn't all active if opcode = PutFullData
+ - Mask isn't in enabled lanes, e.g. a_address = 0x00, a_size = 0, a_mask = 'b0010
+ - Mask doesn't align with address, e.g. a_address = 0x01, a_mask = 'b0001
+ - Address and size aren't aligned, e.g. a_address = 0x01, a_size != 0
+ - Size is over 2.
+ - OpenTitan defined error cases
+ - Access unmapped address, return d_error = 1 when devmode_i == 1
+ - Write CSR with unaligned address, e.g. a_address[1:0] != 0
+ - Write CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
+ - Write a memory without enabling all lanes (a_mask = '1) if memory doesn't support
+ byte enabled write
+ - Read a WO (write-only) memory'''
+ milestone: V2
+ tests: ["{name}_tl_errors"]
+ }
+ {
+ name: tl_d_outstanding_access
+ desc: '''Drive back-to-back requests without waiting for response to ensure there is one
+ transaction outstanding within the TL device. Also, verify one outstanding when back-
+ to-back accesses are made to the same address.'''
+ milestone: V2
+ tests: ["{name}_csr_hw_reset",
+ "{name}_csr_rw",
+ "{name}_csr_aliasing",
+ "{name}_same_csr_outstanding"]
+ }
+ {
+ name: tl_d_partial_access
+ desc: '''Access CSR with one or more bytes of data
+ For read, expect to return all word value of the CSR
+ For write, enabling bytes should cover all CSR valid fields'''
+ milestone: V2
+ tests: ["{name}_csr_hw_reset",
+ "{name}_csr_rw",
+ "{name}_csr_aliasing",
+ "{name}_same_csr_outstanding"]
+ }
+ ]
+}
+
diff --git a/vendor/lowrisc_ip/dv/data/tests/csr_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/csr_tests.hjson
similarity index 98%
rename from vendor/lowrisc_ip/dv/data/tests/csr_tests.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/tests/csr_tests.hjson
index b936ed57..93e6e010 100644
--- a/vendor/lowrisc_ip/dv/data/tests/csr_tests.hjson
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/tests/csr_tests.hjson
@@ -62,7 +62,7 @@
regressions: [
{
- name: sanity
+ name: smoke
tests: ["{name}_csr_hw_reset", "{name}_csr_rw"]
}
diff --git a/vendor/lowrisc_ip/dv/data/tests/intr_test.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/intr_test.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/tests/intr_test.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/tests/intr_test.hjson
diff --git a/vendor/lowrisc_ip/dv/data/tests/mem_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/mem_tests.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/tests/mem_tests.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/tests/mem_tests.hjson
diff --git a/vendor/lowrisc_ip/dv/data/tests/shadow_reg_errors_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/shadow_reg_errors_tests.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/tests/shadow_reg_errors_tests.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/tests/shadow_reg_errors_tests.hjson
diff --git a/vendor/lowrisc_ip/dv/data/tests/stress_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/stress_tests.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/tests/stress_tests.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/tests/stress_tests.hjson
diff --git a/vendor/lowrisc_ip/dv/data/tests/tl_access_tests.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/tests/tl_access_tests.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/tests/tl_access_tests.hjson
diff --git a/vendor/lowrisc_ip/dv/data/vcs/vcs.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson
similarity index 70%
rename from vendor/lowrisc_ip/dv/data/vcs/vcs.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson
index 4ec6397f..812eae84 100644
--- a/vendor/lowrisc_ip/dv/data/vcs/vcs.hjson
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/vcs.hjson
@@ -25,22 +25,74 @@
// and final value of the selection input at the end of a simulation timestep.
// See https://github.com/lowRISC/ibex/issues/845.
"-xlrm uniq_prior_final",
- // Force DPI-C compilation in C99 mode
- "-CFLAGS \"--std=c99\"",
+ // Force DPI-C compilation in C99 mode. The -fno-extended-identifiers flag tells g++
+ // not to worry about unicode. For some bizarre reason, the VCS DPI code contains
+ // preprocessor macros with smart quotes, which causes GCC 10.2 and later to choke
+ // otherwise. The double-escaped quotes are because this needs to go inside a string
+ // that gets passed to Make (stripping one level of quotes), and then needs to
+ // result in an argument with an embedded space (the other one).
+ "-CFLAGS \\\"--std=c99 -fno-extended-identifiers\\\"",
+ // C++11 standard is enforced for all sources, including the DPI-C constructs.
+ // TODO, may need to update to c++14 to meet our requirements
+ // Refer to https://docs.opentitan.org/doc/ug/install_instructions
+ "-CFLAGS --std=c++11",
// Without this magic LDFLAGS argument below, we get compile time errors with
// VCS on Google Linux machines that look like this:
// .../libvcsnew.so: undefined reference to `snpsReallocFunc'
// .../libvcsnew.so: undefined reference to `snpsCheckStrdupFunc'
// .../libvcsnew.so: undefined reference to `snpsGetMemBytes'
- "-LDFLAGS \"-Wl,--no-as-needed\"",
+ "-LDFLAGS -Wl,--no-as-needed",
// This option enables the following: (needed for uvm_hdl_*)
// - Read capability on registers, variables, and nets
// - Write (deposit) capability on registers and variables
// - Force capability on registers, variables, and nets
- "-debug_access+f"]
+ "-debug_access+f",
+ // Use this to conditionally compile for VCS (example: LRM interpretations differ
+ // across tools).
+ "+define+VCS",
+ // Upgrade below warnings to errors to make VCS more strict on syntax to avoid
+ // having issue with other simulators but passing with VCS
+ //
+ // Identifier previously declared
+ "-error=IPDW",
+ // Input Supply Port with no driver
+ "-error=UPF_ISPND",
+ // Invalid generic or parameter assignment
+ "-error=IGPA",
+ // Class scope used outside of class
+ "-error=PCSRMIO",
+ // Attempt to override undefined parameter
+ "-error=AOUP",
+ // Unbound component
+ "-error=ELW_UNBOUND",
+ // Illegal Use of Wildcard Index
+ "-error=IUWI",
+ // Index into non-array variable
+ "-error=INAV",
+ // Illegal Static Cast
+ "-error=SV-ISC",
+ // Obsolete System Verilog feature
+ "-error=OSVF-NPVIUFPI",
+ // Duplicate port in module instantiation
+ "-error=DPIMI",
+ // Identifier in ANSI port declaration
+ "-error=IPDASP",
+ // File not found
+ "-error=CM-HIER-FNF",
+ // Concatenations with unsized constants
+ "-error=CWUC",
+ // More arguments than needed
+ "-error=MATN",
+ // Specifying negative delays is invalid
+ "-error=STASKW_NDTAZ1",
+ // Too many parameter overrides
+ "-error=TMPO",
+ // Class objects must not hide other class members due to same name
+ "-error=SV-OHCM",
+ ]
run_opts: ["-licqueue",
- "-ucli -do {tool_srcs_dir}/vcs.tcl",
+ "-ucli -do {run_script}",
"+ntb_random_seed={seed}",
// Disable the display of the SystemVerilog assert and cover statement summary
// at the end of simulation. This summary is list of assertions that started but
@@ -51,6 +103,12 @@
"+UVM_TESTNAME={uvm_test}",
"+UVM_TEST_SEQ={uvm_test_seq}"]
+ // Supported wave dumping formats (in order of preference).
+ supported_wave_formats: ["fsdb", "vpd"]
+
+ // Default tcl script used when running the sim. Override if needed.
+ run_script: "{tool_srcs_dir}/sim.tcl"
+
// Coverage related.
cov_db_dir: "{scratch_path}/coverage/{build_mode}.vdb"
@@ -105,8 +163,8 @@
// Vars that need to exported to the env.
exports: [
- VCS_ARCH_OVERRIDE: linux
- VCS_LIC_EXPIRE_WARNING: 1
+ { VCS_ARCH_OVERRIDE: "linux" },
+ { VCS_LIC_EXPIRE_WARNING: 1 }
]
// Defaults for VCS
diff --git a/vendor/lowrisc_ip/dv/data/xcelium/xcelium.hjson b/vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson
similarity index 80%
rename from vendor/lowrisc_ip/dv/data/xcelium/xcelium.hjson
rename to vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson
index a8161926..b859f816 100644
--- a/vendor/lowrisc_ip/dv/data/xcelium/xcelium.hjson
+++ b/vendor/lowrisc_ip/dv/tools/dvsim/xcelium.hjson
@@ -20,33 +20,51 @@
"-xmlibdirname {build_dir}/xcelium.d",
// for uvm_hdl_* used by csr backdoor
"-access +rw",
+ // Use this to conditionally compile for Xcelium (example: LRM interpretations differ
+ // across tools).
"+define+XCELIUM",
+ // Ignore "timescale is not specified for the package" warning
+ "-nowarn TSNSPK",
+ // Ignore "IEEE 1800-2009 SystemVerilog simulation semantics" warning
+ "-nowarn DSEMEL",
+ // Ignore hierarchial ref warnings in interfaces
+ "-nowarn CUVIHR",
]
- run_opts: ["-input {tool_srcs_dir}/xcelium.tcl",
+ run_opts: ["-input {run_script}",
"-licqueue",
"-64bit -xmlibdirname {build_dir}/xcelium.d -R",
"+SVSEED={seed}",
"+UVM_TESTNAME={uvm_test}",
- "+UVM_TEST_SEQ={uvm_test_seq}"]
+ "+UVM_TEST_SEQ={uvm_test_seq}",
+ // Ignore "IEEE 1800-2009 SystemVerilog simulation semantics" warning
+ "-nowarn DSEM2009",
+ ]
// Vars that need to exported to the env.
exports: [
// Poll for an available license in all servers.
- CDS_LIC_QUEUE_POLL: 1
+ { CDS_LIC_QUEUE_POLL: 1 },
+
// Poll for an available license every 1 min.
- CDS_LIC_QUEUE_POLL_INT: 60
+ { CDS_LIC_QUEUE_POLL_INT: 60 },
// X-prop related: these were suggested by Xcelium as warnings during the build time.
// These enable array corruption when the index is out of range or invalid.
- VL_ENABLE_INVALID_IDX_XPROP: 1
- VL_ENABLE_OUTOFRANGE_IDX_XPROP: 1
+ { VL_ENABLE_INVALID_IDX_XPROP: 1 },
+ { VL_ENABLE_OUTOFRANGE_IDX_XPROP: 1 },
// Export the cov_report path so that the tcl file can read these as env vars.
- cov_merge_db_dir: "{cov_merge_db_dir}"
- cov_report_dir: "{cov_report_dir}"
+ { cov_merge_db_dir: "{cov_merge_db_dir}" },
+ { cov_report_dir: "{cov_report_dir}" }
]
+ // Supported wave dumping formats (in order of preference).
+ supported_wave_formats: ["shm", "fsdb", "vcd"]
+
+ // Default tcl script used when running the sim. Override if needed.
+ run_script: "{tool_srcs_dir}/sim.tcl"
+
// Coverage related.
// By default, collect all coverage metrics: block:expr:fsm:toggle:functional.
cov_metrics: all
@@ -57,7 +75,7 @@
// Supply the cov refinement files.
// Note that this needs to be set as -load_refinement .
- xcelium_cov_refine_files: [""]
+ xcelium_cov_refine_files: []
// Set the coverage directories.
cov_work_dir: "{scratch_path}/coverage"
diff --git a/vendor/lowrisc_ip/dv/tools/fail_patterns b/vendor/lowrisc_ip/dv/tools/fail_patterns
deleted file mode 100644
index 298ab7ed..00000000
--- a/vendor/lowrisc_ip/dv/tools/fail_patterns
+++ /dev/null
@@ -1,3 +0,0 @@
-^TEST FAILED (UVM_)?CHECKS$
-^UVM_ERROR\s[^:].*$
-^\s*Offending '.*'
diff --git a/vendor/lowrisc_ip/dv/tools/fusesoc.mk b/vendor/lowrisc_ip/dv/tools/fusesoc.mk
deleted file mode 100644
index 85bc44b3..00000000
--- a/vendor/lowrisc_ip/dv/tools/fusesoc.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-# Make variables specific to FUSESOC tool used for generating the filelist
-# The following Make variables are to be set in the Test Makefile
-# FUSESOC_CORE: the top level fusesoc core file developed for the ip/top level testbench
-# see hw/ip/uart/dv/uart_sim.core as an example
-# Rest of the Make variables added here are intermeditiate ones used in the flow
-
-# fusesoc tool and options
-SV_FLIST_GEN_TOOL ?= fusesoc
-SV_FLIST_GEN_OPTS += --cores-root ${PROJ_ROOT} --cores-root ${RAL_MODEL_DIR} \
- run --target=sim --setup ${FUSESOC_CORE}
-FUSESOC_CORE_ = $(shell echo "${FUSESOC_CORE}" | tr ':' '_')
-SV_FLIST_GEN_DIR = ${BUILD_DIR}/build/${FUSESOC_CORE_}/sim-vcs
-SV_FLIST := ${SV_FLIST_GEN_DIR}/${FUSESOC_CORE_}.scr
diff --git a/vendor/lowrisc_ip/dv/tools/modes.mk b/vendor/lowrisc_ip/dv/tools/modes.mk
deleted file mode 100644
index 3c187b7f..00000000
--- a/vendor/lowrisc_ip/dv/tools/modes.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-# Makefile option groups that can be enabled by test Makefile / command line.
-# These are generic set of option groups that apply to all testbenches.
-# These are meant to be simulator agnostic
-# Please add tool specific options with appropriate ifeq's
-
-# Distinguish UVM TB and the other environments for Verilator, FPGA etc
-BUILD_OPTS += +define+UVM
-# uvm specific - set default widths
-BUILD_OPTS += +define+UVM_NO_DEPRECATED
-BUILD_OPTS += +define+UVM_REGEX_NO_DPI
-BUILD_OPTS += +define+UVM_REG_ADDR_WIDTH=${TL_AW}
-BUILD_OPTS += +define+UVM_REG_DATA_WIDTH=${TL_DW}
-BUILD_OPTS += +define+UVM_REG_BYTENABLE_WIDTH=${TL_DBW}
-
-# Enable UVM trace options
-UVM_TRACE ?= 0
-ifeq (${UVM_TRACE},1)
- RUN_OPTS += +UVM_PHASE_TRACE
- RUN_OPTS += +UVM_CONFIG_DB_TRACE
- RUN_OPTS += +UVM_OBJECTION_TRACE
-endif
-
-# Options for generating waves / debugging.
-WAVES ?= 0
-DUMP ?= fsdb
-DUMP_FILE ?= waves.${DUMP}
-export WAVES
-export DUMP
-export DUMP_FILE
-
-ifeq (${WAVES},1)
- ifeq (${SIMULATOR},vcs)
- VCS_WAVES = 1
- endif
-endif
-
-# Enable simulation profiling
-SIMPROFILE ?= 0
-ifeq (${SIMPROFILE},1)
- ifeq (${SIMULATOR},vcs)
- VCS_SIMPROFILE = 1
- else ifeq (${SIMULATOR},xcelium)
- XCELIUM_SIMPROFILE = 1
- endif
-endif
-
-# Enable coverage
-COV ?= 0
-ifeq (${COV},1)
- ifeq (${SIMULATOR},vcs)
- VCS_COV = 1
- else ifeq (${SIMULATOR},xcelium)
- XCELIUM_COV = 1
- endif
-endif
diff --git a/vendor/lowrisc_ip/dv/tools/pass_fail b/vendor/lowrisc_ip/dv/tools/pass_fail
deleted file mode 100644
index ee6b5cff..00000000
--- a/vendor/lowrisc_ip/dv/tools/pass_fail
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-# this script checks for test pass/fail signatures at the end of the test
-
-usage="Error: usage: $0 [path-to-run-log] [path-to-pass-patterns] [path-to-fail-patterns]";
-# need exactly 1 args (path to the run.log)
-if [[ $# -ne 3 || ! -f $1 || ! -f $2 || ! -f $3 ]]; then
- echo $usage
- exit 1;
-fi
-
-# none of the fail patterns should be present
-while ifs= read -r fail_pattern; do
- grep_cmd="grep -c -m 1 -E '$fail_pattern' $1";
- fail_pattern_found=$(eval $grep_cmd);
- if [[ $fail_pattern_found -ne 0 ]]; then
- echo "Fail pattern '$fail_pattern' found"
- exit 1
- fi
-done < $3
-
-# all pass patterns should be present
-while ifs= read -r pass_pattern; do
- grep_cmd="grep -c -m 1 -E '$pass_pattern' $1";
- pass_pattern_found=$(eval $grep_cmd);
- if [[ $pass_pattern_found -eq 0 ]]; then
- echo "Pass pattern '$pass_pattern' not found"
- exit 1
- fi
-done < $2
diff --git a/vendor/lowrisc_ip/dv/tools/pass_patterns b/vendor/lowrisc_ip/dv/tools/pass_patterns
deleted file mode 100644
index 4e5b5050..00000000
--- a/vendor/lowrisc_ip/dv/tools/pass_patterns
+++ /dev/null
@@ -1 +0,0 @@
-^TEST PASSED (UVM_)?CHECKS$
diff --git a/vendor/lowrisc_ip/dv/data/riviera/riviera_run.do b/vendor/lowrisc_ip/dv/tools/riviera/riviera_run.do
similarity index 100%
rename from vendor/lowrisc_ip/dv/data/riviera/riviera_run.do
rename to vendor/lowrisc_ip/dv/tools/riviera/riviera_run.do
diff --git a/vendor/lowrisc_ip/dv/tools/rules.mk b/vendor/lowrisc_ip/dv/tools/rules.mk
deleted file mode 100644
index 5736971c..00000000
--- a/vendor/lowrisc_ip/dv/tools/rules.mk
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-.DEFAULT_GOAL := all
-
-all: build run
-
-###############################
-## sim build and run targets ##
-###############################
-build: compile_result
-
-pre_compile:
- mkdir -p ${BUILD_DIR} && \
- env > ${BUILD_DIR}/env_vars
-
-gen_sv_flist: pre_compile
- cd ${BUILD_DIR} && ${SV_FLIST_GEN_TOOL} ${SV_FLIST_GEN_OPTS}
-
-compile: gen_sv_flist
- cd ${SV_FLIST_GEN_DIR} && $(BUILD_JOB_OPTS) ${SIMCC} ${BUILD_OPTS} ${CL_BUILD_OPTS}
-
-post_compile: compile
-
-compile_result: post_compile
-
-run: run_result
-
-pre_run:
- rm -rf ${RUN_PATH}/latest
- mkdir -p ${RUN_DIR}
- ln -s ${RUN_DIR} ${RUN_PATH}/latest
- /bin/bash ${MAKE_ROOT}/run_dir_limiter ${RUN_PATH} ${RUN_DIR_LIMIT}
- env > ${RUN_DIR}/env_vars
-
-sw_build: pre_run
-ifneq (${SW_NAME},)
- # NOTE: Pass -f, since we're going to be re-building everything every time,
- # anyways.
- cd $(PROJ_ROOT) && \
- BUILD_ROOT=$(SW_BUILD_DIR) $(PROJ_ROOT)/meson_init.sh -f
- # NOTE: We're using the fpga platform for now, because there is no
- # such thing as a DV platform yet (nor does any code do anything
- # special for DV yet).
- ninja -C $(SW_BUILD_DIR)/build-out sw/device/boot_rom/boot_rom_export_$(SW_BUILD_DEVICE)
- ninja -C $(SW_BUILD_DIR)/build-out sw/device/$(SW_DIR)/$(SW_NAME)_export_$(SW_BUILD_DEVICE)
-
- mkdir -p $(SW_BUILD_DIR)/sw $(SW_BUILD_DIR)/rom
- cp $(SW_BUILD_DIR)/build-out/sw/device/boot_rom/boot_rom_$(SW_BUILD_DEVICE).vmem \
- $(SW_BUILD_DIR)/rom/rom.vmem
- cp $(SW_BUILD_DIR)/build-out/sw/device/$(SW_DIR)/$(SW_NAME)_$(SW_BUILD_DEVICE).vmem \
- $(SW_BUILD_DIR)/sw/sw.vmem
-endif
-
-simulate: sw_build
- cd ${RUN_DIR} && $(RUN_JOB_OPTS) ${SIMX} ${RUN_OPTS} ${CL_RUN_OPTS}
-
-post_run: simulate
-
-run_result: post_run
- /bin/bash ${MAKE_ROOT}/pass_fail ${RUN_LOG} ${MAKE_ROOT}/pass_patterns ${MAKE_ROOT}/fail_patterns
-
-############################
-## coverage rated targets ##
-############################
-cov_merge:
- # TODO: add script to merge coverage in scratch scope
-
-# open coverage tool to review and create report or exclusion file
-cov_analyze:
- cd ${SCRATCH_PATH} && ${COV_ANALYZE_TOOL} ${COV_ANALYZE_OPTS}
-
-# generate coverage report directly
-cov_report:
- cd ${SCRATCH_PATH} && ${COV_REPORT_TOOL} ${COV_REPORT_OPTS}
-
-clean:
- rm -rf ${SCRATCH_PATH}/*
-
-.PHONY: ral \
- build \
- run \
- reg \
- pre_compile \
- compile \
- post_compile \
- compile_result \
- sw_build \
- pre_run \
- simulate \
- post_run \
- run_result \
- cov_merge \
- cov_analyze \
- cov_report \
- clean
diff --git a/vendor/lowrisc_ip/dv/tools/run_dir_limiter b/vendor/lowrisc_ip/dv/tools/run_dir_limiter
deleted file mode 100755
index 52d34dd8..00000000
--- a/vendor/lowrisc_ip/dv/tools/run_dir_limiter
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-# run_dir_limiter does what the name suggests - often, when tests are run multiple times, the number
-# of run directories created might end up getting large. This script finds and deletes N oldest
-# directories in a given path P, where P and N are the arguments to this script respedtively
-
-usage="Error: usage: $0 [path-to-run-dir] [num-run-dir-limit]";
-# need exactly 2 args, 1st needs to be the path and 2nd, a number
-if [[ $# -ne 2 || ! -d $1 || ! $2 =~ ^[0-9]+$ ]]; then
- echo $usage
- exit 1;
-fi
-
-run_dir=$(realpath $1);
-find_cmd="find $run_dir -mindepth 1 -maxdepth 1 -type d";
-num_dirs=$($find_cmd | wc -l);
-num_rm_dirs=$(($num_dirs - $2));
-if [[ $num_rm_dirs -gt 0 ]]; then
- dirs=$($find_cmd -printf '%T+ %p\n' | sort | head -n $num_rm_dirs | awk '{print $2}');
- for dir in $dirs; do
- echo "Removing $dir..."
- /bin/rm -rf $dir
- done
-fi
diff --git a/vendor/lowrisc_ip/dv/tools/sim.tcl b/vendor/lowrisc_ip/dv/tools/sim.tcl
new file mode 100644
index 00000000..9bad7493
--- /dev/null
+++ b/vendor/lowrisc_ip/dv/tools/sim.tcl
@@ -0,0 +1,21 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+# Common TCL script invoked at run-time by the simulator.
+# VCS syntax: -ucli -do
+# Xcelium syntax: -input
+
+set tool_srcs_dir ""
+if {[info exists ::env(TOOL_SRCS_DIR)]} {
+ set tool_srcs_dir "$::env(TOOL_SRCS_DIR)"
+} else {
+ puts "ERROR: Script run without TOOL_SRCS_DIR environment variable."
+ quit
+}
+
+source "${tool_srcs_dir}/common.tcl"
+source "${tool_srcs_dir}/waves.tcl"
+
+run
+quit
diff --git a/vendor/lowrisc_ip/dv/tools/testplans/tl_device_access_types_testplan.hjson b/vendor/lowrisc_ip/dv/tools/testplans/tl_device_access_types_testplan.hjson
deleted file mode 100644
index 97c5267f..00000000
--- a/vendor/lowrisc_ip/dv/tools/testplans/tl_device_access_types_testplan.hjson
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-{
- entries: [
- {
- name: oob_addr_access
- desc: "Access out of bounds address and verify correctness of response / behavior"
- milestone: V2
- tests: ["{name}_tl_errors"]
- }
- {
- name: illegal_access
- desc: '''Drive unsupported requests via TL interface and verify correctness of response
- / behavior '''
- milestone: V2
- tests: ["{name}_tl_errors"]
- }
- {
- name: outstanding_access
- desc: '''Drive back-to-back requests without waiting for response to ensure there is one
- transaction outstanding within the TL device. Also, verify one outstanding when back-
- to-back accesses are made to the same address.'''
- milestone: V2
- tests: ["{name}{intf}_csr_hw_reset",
- "{name}{intf}_csr_rw",
- "{name}{intf}_csr_aliasing",
- "{name}{intf}_same_csr_outstanding"]
- }
- {
- name: partial_access
- desc: '''Do partial accesses.'''
- milestone: V2
- tests: ["{name}{intf}_csr_hw_reset",
- "{name}{intf}_csr_rw",
- "{name}{intf}_csr_aliasing"]
- }
- ]
-}
-
diff --git a/vendor/lowrisc_ip/dv/tools/vcs/vcs.mk b/vendor/lowrisc_ip/dv/tools/vcs/vcs.mk
deleted file mode 100644
index 94b6b43b..00000000
--- a/vendor/lowrisc_ip/dv/tools/vcs/vcs.mk
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-# Makefile option groups that can be enabled by test Makefile / command line.
-# These are generic set of option groups that apply to all testbenches.
-# Simulator too specific options
-# Mandatory items to set (these are used by rules.mk):
-# SIMCC - Simulator compiler used to build / elaborate the bench
-# SIMX - Simulator executable used to run the tests
-
-SIMCC := vcs
-SIMX ?= ${BUILD_DIR}/simv
-SIM_SETUP ?= ${MAKE_ROOT}/vcs/vcs.tcl
-
-# set standard build options
-BUILD_OPTS += -sverilog -full64 -licqueue -timescale=1ns/1ps -kdb
-BUILD_OPTS += -ntb_opts uvm-1.2
-BUILD_OPTS += -Mdir=${BUILD_DIR}/simv.csrc
-BUILD_OPTS += -l ${BUILD_LOG}
-BUILD_OPTS += -o ${SIMX}
-BUILD_OPTS += -f ${SV_FLIST}
-BUILD_OPTS += +incdir+${BUILD_DIR}
-BUILD_OPTS += -debug_access+pp
-BUILD_OPTS += +warn=noUII-L
-# turn on warnings if functions are called with return value ignored
-BUILD_OPTS += +warn=SV-NFIVC
-# option below is required for $error / $fatal system calls
-BUILD_OPTS += -assert svaext
-# Force DPI-C compilation in C99 mode
-BUILD_OPTS += -CFLAGS "--std=c99"
-# Without this magic LDFLAGS argument below, we get compile time errors with
-# VCS on Google Linux machines that look like this:
-# .../libvcsnew.so: undefined reference to `snpsReallocFunc'
-# .../libvcsnew.so: undefined reference to `snpsCheckStrdupFunc'
-# .../libvcsnew.so: undefined reference to `snpsGetMemBytes'
-BUILD_OPTS += -LDFLAGS "-Wl,--no-as-needed"
-
-# set standard run options
-RUN_OPTS += -licqueue
-RUN_OPTS += -ucli -do ${SIM_SETUP}
-RUN_OPTS += +ntb_random_seed=${SEED}
-RUN_OPTS += +UVM_VERBOSITY=${UVM_VERBOSITY}
-RUN_OPTS += +UVM_TESTNAME=${UVM_TEST}
-RUN_OPTS += +UVM_TEST_SEQ=${UVM_TEST_SEQ}
-RUN_OPTS += -l ${RUN_LOG}
-
-#########################
-## Tool Specific Modes ##
-#########################
-
-# Enable simulation profiling
-VCS_SIMPROFILE ?= 0
-ifeq (${VCS_SIMPROFILE},1)
- BUILD_OPTS += -simprofile
- RUN_OPTS += -simprofile time
-endif
-
-# Enable waves
-VCS_WAVES ?= 0
-ifeq (${VCS_WAVES},1)
- BUILD_OPTS += -debug_access+all
-endif
-
-# Enable coverage
-VCS_COV ?= 0
-ifeq (${VCS_COV},1)
- COV_METRICS ?= line+cond+fsm+tgl+branch+assert
- BUILD_OPTS += -cm ${COV_METRICS}
- CM_HIER ?= ${MAKE_ROOT}/vcs/cover.cfg
- # Add -cm_hier switch if ${CM_HIER} file exists
- BUILD_OPTS += $(shell if [ -f ${CM_HIER} ]; then echo "-cm_hier ${CM_HIER}"; fi)
- # Cover all continuous assignments
- BUILD_OPTS += -cm_line contassign
- # Dump toggle coverage on mdas, array of structs and on ports only
- BUILD_OPTS += -cm_tgl mda+structarr+portsonly
- # Ignore initial blocks for coverage
- BUILD_OPTS += -cm_report noinitial
- # Filter unreachable/statically constant blocks
- BUILD_OPTS += -cm_noconst
- # Don't count coverage that's coming from zero-time glitches
- BUILD_OPTS += -cm_glitch 0
- # Ignore warnings about not applying cm_glitch to path and FSM
- BUILD_OPTS += "+warn=noVCM-OPTIGN"
- # Coverage database output location
- BUILD_OPTS += -cm_dir ${BUILD_DIR}/cov.vdb
-
- RUN_OPTS += -cm ${COV_METRICS}
- # Same directory as build
- RUN_OPTS += -cm_dir ${BUILD_DIR}/cov.vdb
- # Don't output cm.log which can be quite large
- RUN_OPTS += -cm_log /dev/null
- # Provide a name to the coverage collected for this test
- RUN_OPTS += -cm_name ${TEST_NAME}_${RUN_LOC}_${SEED}
- # Don't dump all the coverage assertion attempts at the end of simulation
- RUN_OPTS += -assert nopostproc
-endif
-
-# Enable XPROP
-XPROP ?= 1
-ifeq (${XPROP},1)
- VCS_XPROP_CFG_FILE ?= ${MAKE_ROOT}/vcs/xprop.cfg
- BUILD_OPTS += -xprop=${VCS_XPROP_CFG_FILE}
-endif
-
-# Coverage analyze/report options
-COV_COMMON_EXCL ?= ${MAKE_ROOT}/vcs/common_cov_excl.el
-COV_EXCL += ${COV_COMMON_EXCL} ${COV_DUT_EXCL}
-COV_ANALYZE_TOOL ?= verdi
-COV_REPORT_TOOL ?= urg
-COV_DIR ?= ${BUILD_DIR}/cov.vdb
-COV_EXCL_OPTS ?= -line nocasedef -elfile ${COV_EXCL}
-COV_ANALYZE_OPTS ?= -cov -covdir ${COV_DIR} ${COV_EXCL_OPTS}
-COV_REPORT_OPTS ?= -dir ${COV_DIR} ${COV_EXCL_OPTS} -report ${COV_REPORT_DIR}
-
-# env variables to be exported for VCS
-export VCS_ARCH_OVERRIDE := linux
-export VCS_LIC_EXPIRE_WARNING := 1
diff --git a/vendor/lowrisc_ip/dv/tools/vcs/vcs.tcl b/vendor/lowrisc_ip/dv/tools/vcs/vcs.tcl
deleted file mode 100644
index aca0eed8..00000000
--- a/vendor/lowrisc_ip/dv/tools/vcs/vcs.tcl
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-# TCL file invoked from VCS's simv at run-time using this: -ucli -do
-
-# Get some environment variables we need.
-set en_waves 0
-set tool_srcs_dir ""
-if {[info exists ::env(EN_WAVES)]} {
- set en_waves "$::env(EN_WAVES)"
-}
-if {[info exists ::env(TOOL_SRCS_DIR)]} {
- set tool_srcs_dir "$::env(TOOL_SRCS_DIR)"
-} else {
- puts "ERROR: tool script run without TOOL_SRCS_DIR environment variable."
- quit
-}
-
-# If wave dumping is enabled, run waves.tcl
-if {"$en_waves" == 1} {
- source "${tool_srcs_dir}/waves.tcl"
-}
-
-run
-quit
diff --git a/vendor/lowrisc_ip/dv/tools/vcs/xprop.cfg b/vendor/lowrisc_ip/dv/tools/vcs/xprop.cfg
index 552a1849..022d2baf 100644
--- a/vendor/lowrisc_ip/dv/tools/vcs/xprop.cfg
+++ b/vendor/lowrisc_ip/dv/tools/vcs/xprop.cfg
@@ -1,3 +1,7 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
merge = xmerge;
// Turn on xprop for dut only
diff --git a/vendor/lowrisc_ip/dv/tools/waves.tcl b/vendor/lowrisc_ip/dv/tools/waves.tcl
index 87d64ad9..23f806a8 100644
--- a/vendor/lowrisc_ip/dv/tools/waves.tcl
+++ b/vendor/lowrisc_ip/dv/tools/waves.tcl
@@ -2,44 +2,157 @@
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
-#
-# Generic TCL included by tool-specific scripts when wave dumping is
-# enabled.
-#
-# This is used by all supported simulators, and the driver scripts
+# This is sourced by all supported simulators. The driver scripts
# (dvsim.py) need to make sure that we don't ask for an unsupported
-# dumping format (SHM with VCS, for example).
+# dumping format (SHM with VCS, for example). The adjoining common.tcl
+# must be sourced prior to sourcing this file.
+
+if {[info proc setDefault] ne "setDefault"} {
+ puts "ERROR: Please ensure that common.tcl is sourced."
+ quit
+}
+
+global simulator
+global waves
+global tb_top
+
+set wavedump_db "waves.$waves"
+
+# TODO: convert this to a proc?
+switch $waves {
+ "none" {
+ puts "INFO: Dumping waves is not enabled."
+ }
+
+ "fsdb" {
+ if {$simulator eq "xcelium"} {
+ call fsdbDumpfile $wavedump_db
+ } else {
+ fsdbDumpfile $wavedump_db
+ }
+ }
+
+ "shm" {
+ checkEq simulator "xcelium"
+ database -open $wavedump_db -default -shm
+ }
+
+ "vpd" {
+ checkEq simulator "vcs"
+ dump -file $wavedump_db -type VPD
+ }
+
+ "vcd" {
+ if {$simulator eq "xcelium"} {
+ database -open $wavedump_db -default -vcd
+ } else {
+ puts "ERROR: Simulator $simulator does not support dumping waves in VCD."
+ quit
+ }
+ }
+
+ "evcd" {
+ if {$simulator eq "xcelium"} {
+ database -open $wavedump_db -default -evcd
+ } else {
+ puts "ERROR: Simulator $simulator does not support dumping waves in EVCD."
+ quit
+ }
+ }
+
+ default {
+ puts "ERROR: Unknown wave format: ${waves}."
+ quit
+ }
+}
+
+if {$waves ne "none"} {
+ puts "INFO: Dumping waves in [string toupper $waves] format to $wavedump_db."
+}
+
+# Provides wave-format-agnostic way to set a scope (design heirarchy).
#
+# In large designs, dumping waves on the entire hierarchy can significantly slow down the
+# simulation. It is useful in that case to only dump the relevant scopes of interest during debug.
+#
+# scope : Design / testbench hierarchy to dump waves. Defaults to $tb_top.
+# depth : Levels in the hierarchy to dump waves. Defaults to 0 (dump all levels).
+# fsdb_flags : Additional string flags passed to fsdbDumpVars. Defaults to "+all".
+# probe_flags : Additional string flags passed to probe command (Xcelium). Defaults to "-all".
+# dump_flags : Additional string flags passed to dump command (VCS). Defaults to "-aggregates".
+#
+# Depending on the need, more such technlogy specific flags can be added in future.
+proc wavedumpScope {scope {depth 0} {fsdb_flags "+all"} {probe_flags "-all"}
+ {dump_flags "-aggregates"}} {
+ global simulator
+ global waves
+ global wavedump_db
-set dump_fmt none
-if {[info exists ::env(DUMP_FMT)]} {
- set dump_fmt "$::env(DUMP_FMT)"
-} else {
- puts "ERROR: No DUMP_FMT specified for wave dumping."
- quit
+ switch $waves {
+ "none" {
+ }
+
+ "fsdb" {
+ # The fsdbDumpvars +all command dumps everything: memories, MDAs,
+ # structs, unions, power, packed structs. In addition, also dump SVAs.
+ if {$simulator eq "xcelium"} {
+ call fsdbDumpvars $depth $scope $fsdb_flags
+ call fsdbDumpSVA $depth $scope
+ } else {
+ fsdbDumpvars $depth $scope $fsdb_flags
+ fsdbDumpSVA $depth $scope
+ }
+ }
+
+ "shm" {
+ if {$depth == 0} {
+ set depth "all"
+ }
+ probe "$scope" $probe_flags -depth $depth -shm
+ }
+
+ "vpd" {
+ # The dump command switch -aggregates enables dumping of structs &
+ # arrays.
+ dump -add "$scope" -depth $depth $dump_flags
+ }
+
+ "vcd" {
+ if {$simulator eq "xcelium"} {
+ if {$depth == 0} {
+ set depth "all"
+ }
+ probe "$scope" $probe_flags -depth $depth -vcd
+ }
+ }
+
+ "evcd" {
+ if {$simulator eq "xcelium"} {
+ if {$depth == 0} {
+ set depth "all"
+ }
+ probe "$scope" $probe_flags -depth $depth -evcd
+ }
+ }
+
+ default {
+ puts "ERROR: Unknown wave format: ${waves}."
+ quit
+ }
+ }
+ puts "INFO: Dumping waves in scope \"$scope:$depth\"."
}
-set tb_top "tb"
-if {[info exists ::(TB_TOP)]} {
- set tb_top "$::env(TB_TOP)"
-}
+# Decide whether to dump the entire testbench hierarchy by default.
+#
+# If this variable is not set externally, it is set to 1 by default here. When set to 1, it adds the
+# entire top-level testbench instance for dumping waves. For larger designs, this may slow down the
+# simulation. The user can if needed, set it to 0 in the external tcl script that sources this
+# script and manually add the hierarchies of interest in there, using the wavedumpScope proc.
+setDefault dump_tb_top 1
-if {"$dump_fmt" == "fsdb"} {
- # The fsdbDumpvars +all command dumps everything: memories,
- # MDA signals, structs, unions, power and packed structs.
- puts "Dumping waves with VERDI to waves.fsdb"
- fsdbDumpfile "waves.fsdb"
- fsdbDumpvars 0 $tb_top +all
- fsdbDumpSVA 0 $tb_top
-} elseif {"$dump_fmt" == "shm"} {
- puts "Dumping waves in SHM format to waves.shm"
- database -open -default -shm "waves.shm"
- probe "$tb_top" -all -depth all -shm
-} elseif {"$dump_fmt" == "vpd"} {
- puts "Dumping waves in VCD+ format to waves.vpd"
- dump -file "waves.vpd"
- dump -add "$tb_top" -depth 0 -aggregates -scope "."
-} else {
- puts "ERROR: Unknown dump format: ${dump_fmt}"
- quit
+
+# By default, add the full test bench scope for wavedump.
+if {$dump_tb_top == 1} {
+ wavedumpScope $tb_top
}
diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.mk b/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.mk
deleted file mode 100644
index 45fec21b..00000000
--- a/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-# Makefile option groups that can be enabled by test Makefile / command line.
-# These are generic set of option groups that apply to all testbenches.
-# Simulator too specific options
-# Mandatory items to set (these are used by rules.mk):
-# SIMCC - Simulator compiler used to build / elaborate the bench
-# SIMX - Simulator executable used to run the tests
-
-SIMCC := xrun -elaborate
-SIMX ?= xrun
-SIM_SETUP ?= ${MAKE_ROOT}/xcelium/xcelium.tcl
-
-# max number of errors before the run is stopped
-ERROR_MAX := 50
-# set standard build options
-BUILD_OPTS += -l ${BUILD_LOG}
-BUILD_OPTS += -access +r
-BUILD_OPTS += -messages
-BUILD_OPTS += -errormax ${ERROR_MAX}
-BUILD_OPTS += -sv
-BUILD_OPTS += -timescale 1ns/1ps
-BUILD_OPTS += -uvmhome ${UVM_HOME}
-BUILD_OPTS += -xmlibdirname ${SV_FLIST_GEN_DIR}/xcelium.d
-BUILD_OPTS += -f ${SV_FLIST}
-BUILD_OPTS += -64bit
-BUILD_OPTS += -xprop F # -xverbose << add to see which modules does not have xprop enabled
-
-
-# set standard run options
-RUN_OPTS += -input ${SIM_SETUP}
-RUN_OPTS += +SVSEED=${SEED}
-RUN_OPTS += +UVM_VERBOSITY=${UVM_VERBOSITY}
-RUN_OPTS += +UVM_TESTNAME=${UVM_TEST}
-RUN_OPTS += +UVM_TEST_SEQ=${UVM_TEST_SEQ}
-RUN_OPTS += -l ${RUN_LOG}
-RUN_OPTS += -xmlibdirname ${SV_FLIST_GEN_DIR}/xcelium.d -R
-RUN_OPTS += -64bit
-
-
-#########################
-## Tool Specific Modes ##
-#########################
-# Enable simulation profiling
-XCELIUM_SIMPROFILE ?= 0
-ifeq (${XCELIUM_SIMPROFILE},1)
-endif
-
-# Enable coverage
-XCELIUM_COV ?= 0
-ifeq (${XCELIUM_COV},1)
-endif
diff --git a/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.tcl b/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.tcl
deleted file mode 100644
index bd328c72..00000000
--- a/vendor/lowrisc_ip/dv/tools/xcelium/xcelium.tcl
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-# TCL file invoked from xcelium simulations at run-time using this: -input
-
-# Get some environment variables we need.
-set en_waves 0
-set tool_srcs_dir ""
-if {[info exists ::env(EN_WAVES)]} {
- set en_waves "$::env(EN_WAVES)"
-}
-if {[info exists ::env(TOOL_SRCS_DIR)]} {
- set tool_srcs_dir "$::env(TOOL_SRCS_DIR)"
-} else {
- puts "ERROR: tool script run without TOOL_SRCS_DIR environment variable."
- quit
-}
-
-# If wave dumping is enabled, run waves.tcl
-if {"$en_waves" == 1} {
- source "${tool_srcs_dir}/waves.tcl"
-}
-
-run
-quit
diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc
new file mode 100644
index 00000000..7e348af8
--- /dev/null
+++ b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.cc
@@ -0,0 +1,666 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "dpi_memutil.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "sv_scoped.h"
+
+// DPI Exports
+extern "C" {
+
+/**
+ * Write |file| to a memory
+ *
+ * @param file path to a SystemVerilog $readmemh()-compatible file (VMEM file)
+ */
+extern void simutil_memload(const char *file);
+
+/**
+ * Write a 32 bit word |val| to memory at index |index|
+ *
+ * @return 1 if successful, 0 otherwise
+ */
+extern int simutil_set_mem(int index, const svBitVecVal *val);
+}
+
+namespace {
+// Convenience class for runtime errors when loading an ELF file
+class ElfError : public std::exception {
+ public:
+ ElfError(const std::string &path, const std::string &msg) {
+ std::ostringstream oss;
+ oss << "Failed to load ELF file at `" << path << "': " << msg;
+ msg_ = oss.str();
+ }
+
+ const char *what() const noexcept override { return msg_.c_str(); }
+
+ private:
+ std::string msg_;
+};
+
+// Class wrapping an open ELF file
+class ElfFile {
+ public:
+ ElfFile(const std::string &path) : path_(path) {
+ (void)elf_errno();
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ throw std::runtime_error(elf_errmsg(-1));
+ }
+
+ fd_ = open(path.c_str(), O_RDONLY, 0);
+ if (fd_ < 0) {
+ throw ElfError(path, "could not open file.");
+ }
+
+ ptr_ = elf_begin(fd_, ELF_C_READ, NULL);
+ if (!ptr_) {
+ close(fd_);
+ throw ElfError(path, elf_errmsg(-1));
+ }
+
+ if (elf_kind(ptr_) != ELF_K_ELF) {
+ elf_end(ptr_);
+ close(fd_);
+ throw ElfError(path, "not an ELF file.");
+ }
+ }
+
+ ~ElfFile() {
+ elf_end(ptr_);
+ close(fd_);
+ }
+
+ size_t GetPhdrNum() {
+ size_t phnum;
+ if (elf_getphdrnum(ptr_, &phnum) != 0) {
+ throw ElfError(path_, elf_errmsg(-1));
+ }
+ return phnum;
+ }
+
+ const Elf32_Phdr *GetPhdrs() {
+ const Elf32_Phdr *phdrs = elf32_getphdr(ptr_);
+ if (!phdrs)
+ throw ElfError(path_, elf_errmsg(-1));
+ return phdrs;
+ }
+
+ std::string path_;
+ int fd_;
+ Elf *ptr_;
+};
+} // namespace
+
+// Convert a string to a MemImageType, throwing a std::runtime_error
+// if it's not a known name.
+static MemImageType GetMemImageTypeByName(const std::string &name) {
+ if (name == "elf")
+ return kMemImageElf;
+ if (name == "vmem")
+ return kMemImageVmem;
+
+ std::ostringstream oss;
+ oss << "Unknown image type: `" << name << "'.";
+ throw std::runtime_error(oss.str());
+}
+
+// Return a MemImageType for the file at filepath or throw a std::runtime_error.
+// Never returns kMemImageUnknown.
+static MemImageType DetectMemImageType(const std::string &filepath) {
+ size_t ext_pos = filepath.find_last_of(".");
+ if (ext_pos == std::string::npos) {
+ // Assume ELF files if no file extension is given.
+ // TODO: Make this more robust by actually checking the file contents.
+ return kMemImageElf;
+ }
+
+ std::string ext = filepath.substr(ext_pos + 1);
+ MemImageType image_type = GetMemImageTypeByName(ext);
+ if (image_type == kMemImageUnknown) {
+ std::ostringstream oss;
+ oss << "Cannot auto-detect file type for `" << filepath << "'.";
+ throw std::runtime_error(oss.str());
+ }
+
+ return image_type;
+}
+
+// Generate a single array of bytes representing the contents of PT_LOAD
+// segments of the ELF file. Like objcopy, this generates a single "giant
+// segment" whose first byte corresponds to the first byte of the lowest
+// addressed segment and whose last byte corresponds to the last byte of the
+// highest address.
+static std::vector FlattenElfFile(const std::string &filepath) {
+ ElfFile elf(filepath);
+
+ size_t phnum = elf.GetPhdrNum();
+ const Elf32_Phdr *phdrs = elf.GetPhdrs();
+
+ // To mimic what objcopy does (that is, the binary target of BFD), we need to
+ // iterate over all loadable program headers, find the lowest address, and
+ // then copy in our loadable data based on their offset with respect to the
+ // found base address.
+
+ bool any = false;
+ Elf32_Addr low = 0, high = 0;
+ for (size_t i = 0; i < phnum; i++) {
+ const Elf32_Phdr &phdr = phdrs[i];
+
+ if (phdr.p_type != PT_LOAD) {
+ std::cout << "Program header number " << i << " in `" << filepath
+ << "' is not of type PT_LOAD; ignoring." << std::endl;
+ continue;
+ }
+
+ if (phdr.p_memsz == 0) {
+ continue;
+ }
+
+ if (!any || phdr.p_paddr < low) {
+ low = phdr.p_paddr;
+ }
+
+ Elf32_Addr seg_top = phdr.p_paddr + (phdr.p_memsz - 1);
+ if (seg_top < phdr.p_paddr) {
+ std::ostringstream oss;
+ oss << "phdr for segment " << i << " has start 0x" << std::hex
+ << phdr.p_paddr << " and size 0x" << phdr.p_memsz
+ << ", which overflows the address space.";
+ throw ElfError(filepath, oss.str());
+ }
+
+ if (!any || seg_top > high) {
+ high = seg_top;
+ }
+
+ any = true;
+ }
+
+ // If any is false, there were no segments that contributed to the
+ // file. Return nothing.
+ if (!any)
+ return std::vector();
+
+ // Otherwise, we know every valid byte of data has an address in the
+ // range [low, high] (inclusive).
+ assert(low <= high);
+
+ size_t file_size;
+ const char *file_data = elf_rawfile(elf.ptr_, &file_size);
+ assert(file_data);
+
+ StagedMem ret;
+
+ for (size_t i = 0; i < phnum; i++) {
+ const Elf32_Phdr &phdr = phdrs[i];
+
+ if (phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ // Check the segment actually fits in the file
+ if (file_size < phdr.p_offset + phdr.p_filesz) {
+ std::ostringstream oss;
+ oss << "phdr for segment " << i << " claims to end at offset 0x"
+ << std::hex << phdr.p_offset + phdr.p_filesz
+ << ", but the file only has size 0x" << file_size << ".";
+ throw ElfError(filepath, oss.str());
+ }
+
+ uint32_t off = phdr.p_paddr - low;
+ uint32_t dst_len = phdr.p_memsz;
+ uint32_t src_len = std::min(phdr.p_filesz, dst_len);
+
+ if (!dst_len)
+ continue;
+
+ std::vector seg(dst_len, 0);
+ memcpy(&seg[0], file_data + phdr.p_offset, src_len);
+ ret.AddSegment(off, std::move(seg));
+ }
+
+ return ret.GetFlat();
+}
+
+// Write a "segment" of data to the given memory area.
+static void WriteSegment(const MemArea &m, uint32_t offset,
+ const std::vector &data) {
+ assert(m.width_byte <= 32);
+ assert(m.addr_loc.size == 0 || offset + data.size() <= m.addr_loc.size);
+ assert((offset % m.width_byte) == 0);
+
+ // If this fails to set scope, it will throw an error which should
+ // be caught at this function's callsite.
+ SVScoped scoped(m.location.data());
+
+ // This "mini buffer" is used to transfer each write to SystemVerilog. It's
+ // not massively efficient, but doing so ensures that we pass 256 bits (32
+ // bytes) of initialised data each time. This is for simutil_set_mem (defined
+ // in prim_util_memload.svh), whose "val" argument has SystemVerilog type bit
+ // [255:0].
+ uint8_t minibuf[32];
+ memset(minibuf, 0, sizeof minibuf);
+ assert(m.width_byte <= sizeof minibuf);
+
+ uint32_t all_words = (data.size() + m.width_byte - 1) / m.width_byte;
+ uint32_t full_data_words = data.size() / m.width_byte;
+ uint32_t part_data_word_len = data.size() % m.width_byte;
+ bool has_part_data_word = part_data_word_len != 0;
+
+ uint32_t word_offset = offset / m.width_byte;
+
+ // Copy the full data words
+ for (uint32_t i = 0; i < full_data_words; ++i) {
+ uint32_t dst_word = word_offset + i;
+ uint32_t src_byte = i * m.width_byte;
+ memcpy(minibuf, &data[src_byte], m.width_byte);
+ if (!simutil_set_mem(dst_word, (svBitVecVal *)minibuf)) {
+ std::ostringstream oss;
+ oss << "Could not set `" << m.name << "' memory at byte offset 0x"
+ << std::hex << dst_word * m.width_byte << ".";
+ throw std::runtime_error(oss.str());
+ }
+ }
+
+ // Copy any partial data, zeroing minibuf first to ensure that the latter
+ // bytes in the word are zero.
+ if (has_part_data_word) {
+ memset(minibuf, 0, sizeof minibuf);
+ uint32_t dst_word = word_offset + full_data_words;
+ uint32_t src_byte = full_data_words * m.width_byte;
+ memcpy(minibuf, &data[src_byte], part_data_word_len);
+ if (!simutil_set_mem(dst_word, (svBitVecVal *)minibuf)) {
+ std::ostringstream oss;
+ oss << "Could not set `" << m.name << "' memory at byte offset 0x"
+ << std::hex << dst_word * m.width_byte << " (partial data word).";
+ throw std::runtime_error(oss.str());
+ }
+ }
+}
+
+static void WriteElfToMem(const MemArea &m, const std::string &filepath) {
+ WriteSegment(m, 0, FlattenElfFile(filepath));
+}
+
+static void WriteVmemToMem(const MemArea &m, const std::string &filepath) {
+ SVScoped scoped(m.location.data());
+ // TODO: Add error handling.
+ simutil_memload(filepath.data());
+}
+
+// Merge seg0 and seg1, overwriting any overlapping data in seg0 with
+// that from seg1. rng0/rng1 is the base and top address of seg0/seg1,
+// respectively.
+static std::vector MergeSegments(const AddrRange &rng0,
+ std::vector &&seg0,
+ const AddrRange &rng1,
+ std::vector &&seg1) {
+ // First, deal with the special case where seg1 completely contains
+ // seg0 (since there's no copying needed at all).
+ if (rng1.lo <= rng0.lo && rng0.hi <= rng1.hi) {
+ return std::move(seg1);
+ }
+
+ uint32_t new_bot = std::min(rng0.lo, rng1.lo);
+ uint32_t new_top = std::max(rng0.hi, rng1.hi);
+ assert(new_bot <= new_top);
+ size_t new_len = 1 + (size_t)(new_top - new_bot);
+ assert(seg0.size() <= new_len);
+ assert(seg1.size() <= new_len);
+
+ // We want to avoid copying if possible. The next most efficient
+ // case (after just returning seg1) is when seg0 doesn't stick out
+ // the left hand end. In this case, we can extend seg1 to the right
+ // (which might not cause a copy) and then copy just the bytes we
+ // need from seg0.
+ if (rng1.lo <= rng0.lo) {
+ assert(rng1.hi < rng0.hi);
+ assert(new_len == seg1.size() + (rng0.hi - rng1.hi));
+
+ size_t old_len = seg1.size();
+ std::vector ret = std::move(seg1);
+ ret.resize(new_len);
+
+ // We know that that rng0 isn't completely contained in rng1 and
+ // that rng0 doesn't stick out of the left hand end. That means it
+ // must stick out of the right (so rng1.hi < rng0.hi). However, we
+ // also know that the two ranges overlap, so rng0.lo <= rng1.hi.
+ assert(rng0.lo <= rng1.hi);
+
+ // src_off is the index of the first byte that needs copying from
+ // seg0. Note that this is always at least 1 (because there is an
+ // actual overlap).
+ uint32_t src_off = 1 + (rng1.hi - rng0.lo);
+
+ assert(seg0.size() == src_off + (rng0.hi - rng1.hi));
+
+ memcpy(&ret[old_len], &seg0[src_off], rng0.hi - rng1.hi);
+ return ret;
+ }
+
+ // In this final case, seg0 sticks out the left hand end. That means
+ // we'll have to copy seg1 whatever happens (because we have to
+ // shuffle its elements to the right). Work by resizing seg0 and
+ // then writing seg1 where it's needed.
+ std::vector ret = std::move(seg0);
+ ret.resize(new_len);
+
+ uint32_t off = rng1.lo - rng0.lo;
+ memcpy(&ret[off], &seg1[0], seg1.size());
+ return ret;
+}
+
+void StagedMem::AddSegment(uint32_t offset, std::vector &&seg) {
+ if (seg.empty())
+ return;
+
+ uint32_t seg_top = offset + seg.size() - 1;
+ assert(seg_top >= offset);
+
+ min_addr_ = std::min(min_addr_, offset);
+ max_addr_ = std::max(max_addr_, seg_top);
+ segs_.Emplace(offset, seg_top, std::move(seg), MergeSegments);
+}
+
+std::vector StagedMem::GetFlat() const {
+ // Since max_addr_ and min_addr_ are inclusive, the size to allocate
+ // is 1+(max-min). We cast to size_t to make sure the +1 doesn't
+ // overflow.
+ size_t len = (size_t)1 + (max_addr_ - min_addr_);
+ std::vector ret(len, 0);
+
+ for (const auto &pr : segs_) {
+ const AddrRange &rng = pr.first;
+ const std::vector &seg = pr.second;
+ assert(seg.size() == 1 + (rng.hi - rng.lo));
+ assert(min_addr_ <= rng.lo);
+
+ uint32_t off = rng.lo - min_addr_;
+ assert(off + seg.size() <= ret.size());
+
+ memcpy(&ret[off], &seg[0], seg.size());
+ }
+ return ret;
+}
+
+bool DpiMemUtil::RegisterMemoryArea(const std::string name,
+ const std::string location) {
+ // Default to 32bit width and no address
+ return RegisterMemoryArea(name, location, 32, nullptr);
+}
+
+bool DpiMemUtil::RegisterMemoryArea(const std::string name,
+ const std::string location,
+ size_t width_bit,
+ const MemAreaLoc *addr_loc) {
+ assert((width_bit <= 256) &&
+ "TODO: Memory loading only supported up to 256 bits.");
+ assert(width_bit % 8 == 0);
+
+ // First, create and register the memory by name
+ MemArea mem = {.name = name,
+ .location = location,
+ .width_byte = (uint32_t)width_bit / 8,
+ .addr_loc = {.base = 0, .size = 0}};
+ auto ret = name_to_mem_.emplace(name, mem);
+ if (ret.second == false) {
+ std::cerr << "ERROR: Can not register \"" << name << "\" at: \"" << location
+ << "\" (Previously defined at: \"" << ret.first->second.location
+ << "\")" << std::endl;
+ return false;
+ }
+
+ MemArea *stored_mem_area = &ret.first->second;
+
+ // If we have no address information, there's nothing more to do. However, if
+ // we do have address information, we should add an entry to addr_to_mem_.
+ if (!addr_loc) {
+ return true;
+ }
+
+ // Check that the size of the new area is positive, and that we don't overflow
+ // the address space.
+ if (addr_loc->size == 0) {
+ std::cerr << "ERROR: Can not register '" << name
+ << "' because it has zero size.\n";
+ return false;
+ }
+ uint32_t addr_top = addr_loc->base + (addr_loc->size - 1);
+ if (addr_top < addr_loc->base) {
+ std::cerr << "ERROR: Can not register '" << name
+ << "' because it overflows the top of the address space.\n";
+ return false;
+ }
+
+ auto clash = addr_to_mem_.EmplaceDisjoint(addr_loc->base, addr_top,
+ std::move(stored_mem_area));
+ if (clash) {
+ assert(*clash);
+ std::cerr << "ERROR: Can not register '" << name
+ << "' because its address range overlaps the existing area `"
+ << (*clash)->name << "'.\n";
+ return false;
+ }
+ stored_mem_area->addr_loc = *addr_loc;
+ return true;
+}
+
+MemImageType DpiMemUtil::GetMemImageType(const std::string &path,
+ const char *type) {
+ return type ? GetMemImageTypeByName(type) : DetectMemImageType(path);
+}
+
+void DpiMemUtil::PrintMemRegions() const {
+ std::cout << "Registered memory regions:" << std::endl;
+ for (const auto &pr : name_to_mem_) {
+ const MemArea &m = pr.second;
+ std::cout << "\t'" << m.name << "' (" << m.width_byte * 8
+ << "bits) at location: '" << m.location << "'";
+ if (m.addr_loc.size) {
+ uint32_t low = m.addr_loc.base;
+ uint32_t high = m.addr_loc.base + m.addr_loc.size - 1;
+ std::cout << " (LMA range [0x" << std::hex << low << ", 0x" << high
+ << "])" << std::dec;
+ }
+ std::cout << std::endl;
+ }
+}
+
+void DpiMemUtil::LoadFileToNamedMem(bool verbose, const std::string &name,
+ const std::string &filepath,
+ MemImageType type) {
+ // If the image type isn't specified, try to figure it out from the file name
+ if (type == kMemImageUnknown) {
+ type = DetectMemImageType(filepath);
+ }
+ assert(type != kMemImageUnknown);
+
+ // Search for corresponding registered memory based on the name
+ auto it = name_to_mem_.find(name);
+ if (it == name_to_mem_.end()) {
+ std::ostringstream oss;
+ oss << "`" << name
+ << ("' is not the name of a known memory region. "
+ "Run with --meminit=list to get a list.");
+ throw std::runtime_error(oss.str());
+ }
+
+ if (verbose) {
+ std::cout << "Loading data from file `" << filepath << "' into memory `"
+ << name << "'." << std::endl;
+ }
+
+ const MemArea &m = it->second;
+
+ try {
+ switch (type) {
+ case kMemImageElf:
+ WriteElfToMem(m, filepath);
+ break;
+ case kMemImageVmem:
+ WriteVmemToMem(m, filepath);
+ break;
+ default:
+ assert(0);
+ }
+ } catch (const SVScoped::Error &err) {
+ std::ostringstream oss;
+ oss << "No memory found at `" << err.scope_name_
+ << "' (the scope associated with region `" << m.name << "').";
+ throw std::runtime_error(oss.str());
+ }
+}
+
+void DpiMemUtil::LoadElfToMemories(bool verbose, const std::string &filepath) {
+ // Load the contents of the ELF file into the staging area
+ StageElf(verbose, filepath);
+
+ for (const auto &pr : staging_area_) {
+ const std::string &mem_name = pr.first;
+ const StagedMem &staged_mem = pr.second;
+
+ auto mem_area_it = name_to_mem_.find(mem_name);
+ assert(mem_area_it != name_to_mem_.end());
+
+ const MemArea &mem_area = mem_area_it->second;
+
+ for (const auto seg_pr : staged_mem.GetSegs()) {
+ const AddrRange &seg_rng = seg_pr.first;
+ const std::vector &seg_data = seg_pr.second;
+ try {
+ WriteSegment(mem_area, seg_rng.lo, seg_data);
+ } catch (const SVScoped::Error &err) {
+ std::ostringstream oss;
+ oss << "No memory found at `" << err.scope_name_
+ << "' (the scope associated with region `" << mem_area.name
+ << "', used by a segment that starts at LMA 0x" << std::hex
+ << mem_area.addr_loc.base + seg_rng.lo << ").";
+ throw std::runtime_error(oss.str());
+ }
+ }
+ }
+}
+
+void DpiMemUtil::StageElf(bool verbose, const std::string &path) {
+ // Clear out anything that was in the staging area before
+ staging_area_.clear();
+
+ ElfFile elf(path);
+
+ size_t file_size;
+ const char *file_data = elf_rawfile(elf.ptr_, &file_size);
+ assert(file_data);
+
+ size_t phnum = elf.GetPhdrNum();
+ const Elf32_Phdr *phdrs = elf.GetPhdrs();
+
+ for (size_t i = 0; i < phnum; ++i) {
+ const Elf32_Phdr &phdr = phdrs[i];
+ if (phdr.p_type != PT_LOAD)
+ continue;
+
+ if (phdr.p_memsz == 0)
+ continue;
+
+ const MemArea &mem_area =
+ GetRegionForSegment(path, i, phdr.p_paddr, phdr.p_memsz);
+
+ // Check that the segment is aligned correctly for the memory
+ uint32_t local_base = phdr.p_paddr - mem_area.addr_loc.base;
+ if (local_base % mem_area.width_byte) {
+ std::ostringstream oss;
+ oss << "Segment " << i << " has LMA 0x" << std::hex << phdr.p_paddr
+ << ", which starts at offset 0x" << local_base
+ << " in the memory region `" << mem_area.name
+ << "'. This offset is not aligned to the region's word width of "
+ << std::dec << 8 * mem_area.width_byte << " bits.";
+ throw ElfError(path, oss.str());
+ }
+
+ // Where does the segment finish in the file image? We don't need
+ // to worry about overflow here, because we're adding two
+ // uint32_t's into a size_t. But we do need to check the segment
+ // actually fits in the file
+ size_t off_end = (size_t)phdr.p_offset + phdr.p_filesz;
+ if (file_size < off_end) {
+ std::ostringstream oss;
+ oss << "phdr for segment " << i << " claims to end at offset 0x"
+ << std::hex << off_end - 1 << ", but the file only has size 0x"
+ << file_size << ".";
+ throw ElfError(path, oss.str());
+ }
+
+ if (verbose) {
+ std::cout << "Loading segment " << i << " from ELF file `" << path
+ << "' into memory `" << mem_area.name << "'." << std::endl;
+ }
+
+ // Get the StagedMem object associated with this memory area. If
+ // there isn't one, make a new empty one.
+ StagedMem &staged_mem = staging_area_[mem_area.name];
+
+ const char *seg_data = file_data + phdr.p_offset;
+ std::vector vec(phdr.p_memsz, 0);
+ memcpy(&vec[0], seg_data, std::min(phdr.p_filesz, phdr.p_memsz));
+
+ staged_mem.AddSegment(local_base, std::move(vec));
+ }
+}
+
+const StagedMem &DpiMemUtil::GetMemoryData(const std::string &mem_name) const {
+ auto it = staging_area_.find(mem_name);
+ return (it == staging_area_.end()) ? empty_ : it->second;
+}
+
+const MemArea &DpiMemUtil::GetRegionForSegment(const std::string &path,
+ int seg_idx, uint32_t lma,
+ uint32_t mem_sz) const {
+ assert(mem_sz > 0);
+
+ auto mem_area_it = addr_to_mem_.find(lma);
+ if (mem_area_it == addr_to_mem_.end()) {
+ std::ostringstream oss;
+ oss << "No memory region is registered that contains the address 0x"
+ << std::hex << lma << " (the base address of segment " << seg_idx
+ << ").";
+ throw ElfError(path, oss.str());
+ }
+ const MemArea *mem_area = mem_area_it->second;
+ assert(mem_area);
+ assert(mem_area->addr_loc.base <= lma);
+
+ uint32_t lma_top = lma + (mem_sz - 1);
+ if (lma_top < lma) {
+ std::ostringstream oss;
+ oss << "Integer overflow for top address of segment " << seg_idx << ".";
+ throw ElfError(path, oss.str());
+ }
+
+ uint32_t local_base = lma - mem_area->addr_loc.base;
+ uint32_t local_top = lma_top - mem_area->addr_loc.base;
+
+ if (mem_area->addr_loc.size <= local_top) {
+ std::ostringstream oss;
+ oss << "Segment " << seg_idx << " has size 0x" << std::hex << mem_sz
+ << " bytes. Its LMA of 0x" << lma << " is at offset 0x" << local_base
+ << " in the memory region `" << mem_area->name
+ << "', so the segment finishes at offset 0x" << local_top
+ << ", but the memory region is only 0x" << mem_area->addr_loc.size
+ << " bytes long.";
+ throw ElfError(path, oss.str());
+ }
+
+ return *mem_area;
+}
diff --git a/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h
new file mode 100644
index 00000000..f37ef97c
--- /dev/null
+++ b/vendor/lowrisc_ip/dv/verilator/cpp/dpi_memutil.h
@@ -0,0 +1,166 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include