mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 22:07:43 -04:00
Update paths for vendored DV code
This commit amends some paths in the vendoring hjson file (and updates config files to use things at the new paths). Finally it re-runs the vendoring tool: Update code from upstream repository https://github.com/lowRISC/opentitan to revision 92e9242424c72c59008e267dd3779e2af5ec8e83 which just ends up with a load of file renames. Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
This commit is contained in:
parent
77d8010015
commit
690f8af65e
316 changed files with 35 additions and 35 deletions
29
vendor/lowrisc_ip/dv/sv/common_ifs/clk_if.sv
vendored
Normal file
29
vendor/lowrisc_ip/dv/sv/common_ifs/clk_if.sv
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//----------------------------- DESCRIPTION ------------------------------------
|
||||
//
|
||||
// Generic clock interface for clock events in various utilities
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
interface clk_if(input logic clk);
|
||||
|
||||
clocking cb @(posedge clk);
|
||||
endclocking
|
||||
|
||||
clocking cbn @(negedge clk);
|
||||
endclocking
|
||||
|
||||
// Wait for 'n' clocks based of postive clock edge
|
||||
task automatic wait_clks(int num_clks);
|
||||
repeat (num_clks) @cb;
|
||||
endtask
|
||||
|
||||
// Wait for 'n' clocks based of negative clock edge
|
||||
task automatic wait_n_clks(int num_clks);
|
||||
repeat (num_clks) @cbn;
|
||||
endtask
|
||||
|
||||
endinterface
|
269
vendor/lowrisc_ip/dv/sv/common_ifs/clk_rst_if.sv
vendored
Normal file
269
vendor/lowrisc_ip/dv/sv/common_ifs/clk_rst_if.sv
vendored
Normal file
|
@ -0,0 +1,269 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//
|
||||
// Interface: clk_rst_if
|
||||
// Generic clock and reset interface for clock events in various utilities
|
||||
// It also generates o_clk and o_rst_n signals for driving clk and rst_n in the tb. The advantage is
|
||||
// clk and rst_n can be completely controlled in course of the simulation.
|
||||
// This interface provides methods to set freq/period, wait for clk/rst_n, apply rst_n among other
|
||||
// things. See individual method descriptions below.
|
||||
// inout clk
|
||||
// inout rst_n
|
||||
|
||||
interface clk_rst_if #(
|
||||
parameter string IfName = "main"
|
||||
) (
|
||||
inout clk,
|
||||
inout rst_n
|
||||
);
|
||||
|
||||
`ifndef VERILATOR
|
||||
// include macros and import pkgs
|
||||
`include "dv_macros.svh"
|
||||
`include "uvm_macros.svh"
|
||||
import uvm_pkg::*;
|
||||
`endif
|
||||
|
||||
bit drive_clk; // enable clk generation
|
||||
logic o_clk; // output clk
|
||||
|
||||
bit drive_rst_n; // enable rst_n generation
|
||||
logic o_rst_n; // output rst_n
|
||||
|
||||
// clk params
|
||||
bit clk_gate = 1'b0; // clk gate signal
|
||||
int clk_period_ps = 20_000; // 50MHz default
|
||||
real clk_freq_mhz = 50; // 50MHz default
|
||||
int duty_cycle = 50; // 50% default
|
||||
int max_jitter_ps = 1000; // 1ns default
|
||||
bit recompute = 1'b1; // compute half periods when period/freq/duty are changed
|
||||
int clk_hi_ps; // half period hi in ps
|
||||
int clk_lo_ps; // half period lo in ps
|
||||
int jitter_chance_pc = 0; // jitter chance in percentage on clock edge - disabled by default
|
||||
bit sole_clock = 1'b0; // if true, this is the only clock in the system
|
||||
|
||||
// use IfName as a part of msgs to indicate which clk_rst_vif instance
|
||||
string msg_id = {"clk_rst_if::", IfName};
|
||||
|
||||
clocking cb @(posedge clk);
|
||||
endclocking
|
||||
|
||||
clocking cbn @(negedge clk);
|
||||
endclocking
|
||||
|
||||
// Wait for 'n' clocks based of postive clock edge
|
||||
task automatic wait_clks(int num_clks);
|
||||
repeat (num_clks) @cb;
|
||||
endtask
|
||||
|
||||
// Wait for 'n' clocks based of negative clock edge
|
||||
task automatic wait_n_clks(int num_clks);
|
||||
repeat (num_clks) @cbn;
|
||||
endtask
|
||||
|
||||
// wait for rst_n to assert and then deassert
|
||||
task automatic wait_for_reset(bit wait_negedge = 1'b1, bit wait_posedge = 1'b1);
|
||||
if (wait_negedge && ($isunknown(rst_n) || rst_n === 1'b1)) @(negedge rst_n);
|
||||
if (wait_posedge && (rst_n === 1'b0)) @(posedge rst_n);
|
||||
endtask
|
||||
|
||||
// set the clk frequency in khz
|
||||
function automatic void set_freq_khz(int freq_khz);
|
||||
clk_freq_mhz = $itor(freq_khz) / 1000;
|
||||
clk_period_ps = 1000_000 / clk_freq_mhz;
|
||||
recompute = 1'b1;
|
||||
endfunction
|
||||
|
||||
// set the clk frequency in mhz
|
||||
function automatic void set_freq_mhz(int freq_mhz);
|
||||
set_freq_khz(freq_mhz * 1000);
|
||||
endfunction
|
||||
|
||||
// call this function at t=0 (from tb top) to enable clk and rst_n to be driven
|
||||
function automatic void set_active(bit drive_clk_val = 1'b1, bit drive_rst_n_val = 1'b1);
|
||||
time t = $time;
|
||||
if (t == 0) begin
|
||||
drive_clk = drive_clk_val;
|
||||
drive_rst_n = drive_rst_n_val;
|
||||
end
|
||||
else begin
|
||||
`ifdef VERILATOR
|
||||
$error({msg_id, "this function can only be called at t=0"});
|
||||
`else
|
||||
`uvm_fatal(msg_id, "this function can only be called at t=0")
|
||||
`endif
|
||||
end
|
||||
endfunction
|
||||
|
||||
// set the clk period in ns
|
||||
function automatic void set_period_ns(int period_ps);
|
||||
clk_period_ps = period_ps;
|
||||
clk_freq_mhz = 1000_000 / clk_period_ps;
|
||||
recompute = 1'b1;
|
||||
endfunction
|
||||
|
||||
// set the duty cycle (1-99)
|
||||
function automatic void set_duty_cycle(int duty);
|
||||
if (!(duty inside {[1:99]})) begin
|
||||
`ifdef VERILATOR
|
||||
$error({msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty)});
|
||||
`else
|
||||
`uvm_fatal(msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty))
|
||||
`endif
|
||||
end
|
||||
duty_cycle = duty;
|
||||
recompute = 1'b1;
|
||||
endfunction
|
||||
|
||||
// set maximum jitter in ps
|
||||
function automatic void set_max_jitter_ps(int jitter_ps);
|
||||
max_jitter_ps = jitter_ps;
|
||||
endfunction
|
||||
|
||||
// set jitter chance in percentage (0 - 100)
|
||||
// 0 - dont add any jitter; 100 - add jitter on every clock edge
|
||||
function automatic void set_jitter_chance_pc(int jitter_chance);
|
||||
if (!(jitter_chance inside {[0:100]})) begin
|
||||
`ifdef VERILATOR
|
||||
$error({msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance)});
|
||||
`else
|
||||
`uvm_fatal(msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance))
|
||||
`endif
|
||||
end
|
||||
jitter_chance_pc = jitter_chance;
|
||||
endfunction
|
||||
|
||||
// Set whether this is the only clock in the system. If true, various bits of timing randomisation
|
||||
// are disabled. If there's no other clock to (de)synchronise with, this should not weaken the
|
||||
// test at all.
|
||||
function automatic void set_sole_clock(bit is_sole = 1'b1);
|
||||
sole_clock = is_sole;
|
||||
endfunction
|
||||
|
||||
// start / ungate the clk
|
||||
task automatic start_clk(bit wait_for_posedge = 1'b0);
|
||||
clk_gate = 1'b0;
|
||||
if (wait_for_posedge) wait_clks(1);
|
||||
endtask
|
||||
|
||||
// stop / gate the clk
|
||||
function automatic void stop_clk();
|
||||
clk_gate = 1'b1;
|
||||
endfunction
|
||||
|
||||
// add jitter to clk_hi and clk_lo half periods based on jitter_chance_pc
|
||||
function automatic void add_jitter();
|
||||
int jitter_ps;
|
||||
if ($urandom_range(1, 100) <= jitter_chance_pc) begin
|
||||
`ifndef VERILATOR
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
|
||||
jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
|
||||
`endif
|
||||
clk_hi_ps += jitter_ps;
|
||||
end
|
||||
if ($urandom_range(1, 100) <= jitter_chance_pc) begin
|
||||
`ifndef VERILATOR
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
|
||||
jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
|
||||
`endif
|
||||
clk_lo_ps += jitter_ps;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// can be used to override clk/rst pins, e.g. at the beginning of the simulation
|
||||
task automatic drive_rst_pin(logic val = 1'b0);
|
||||
o_rst_n = val;
|
||||
endtask
|
||||
|
||||
// apply reset with specified scheme
|
||||
// TODO make this enum?
|
||||
// rst_n_scheme
|
||||
// 0 - fullly synchronous reset - it is asserted and deasserted on clock edges
|
||||
// 1 - async assert, sync dessert (default)
|
||||
// 2 - async assert, async dessert
|
||||
// 3 - clk gated when reset asserted
|
||||
// Note: for power on reset, please ensure pre_reset_dly_clks is set to 0
|
||||
// TODO #2338 issue workaround - $urandom call moved from default argument value to function body
|
||||
task automatic apply_reset(int pre_reset_dly_clks = 0,
|
||||
integer reset_width_clks = 'x,
|
||||
int post_reset_dly_clks = 0,
|
||||
int rst_n_scheme = 1);
|
||||
int dly_ps;
|
||||
if ($isunknown(reset_width_clks)) reset_width_clks = $urandom_range(4, 20);
|
||||
dly_ps = $urandom_range(0, clk_period_ps);
|
||||
wait_clks(pre_reset_dly_clks);
|
||||
case (rst_n_scheme)
|
||||
0: begin : sync_assert_deassert
|
||||
o_rst_n <= 1'b0;
|
||||
wait_clks(reset_width_clks);
|
||||
o_rst_n <= 1'b1;
|
||||
end
|
||||
1: begin : async_assert_sync_deassert
|
||||
#(dly_ps * 1ps);
|
||||
o_rst_n <= 1'b0;
|
||||
wait_clks(reset_width_clks);
|
||||
o_rst_n <= 1'b1;
|
||||
end
|
||||
2: begin : async_assert_async_deassert
|
||||
#(dly_ps * 1ps);
|
||||
o_rst_n <= 1'b0;
|
||||
wait_clks(reset_width_clks);
|
||||
dly_ps = $urandom_range(0, clk_period_ps);
|
||||
#(dly_ps * 1ps);
|
||||
o_rst_n <= 1'b1;
|
||||
end
|
||||
default: begin
|
||||
`ifdef VERILATOR
|
||||
$error({msg_id, $sformatf("rst_n_scheme %0d not supported", rst_n_scheme)});
|
||||
`else
|
||||
`uvm_fatal(msg_id, $sformatf("rst_n_scheme %0d not supported", rst_n_scheme))
|
||||
`endif
|
||||
end
|
||||
endcase
|
||||
wait_clks(post_reset_dly_clks);
|
||||
endtask
|
||||
|
||||
// clk gen
|
||||
initial begin
|
||||
// start driving clk only after the first por reset assertion. The fork/join means that we'll
|
||||
// wait a whole number of clock periods, which means it's possible for the clock to synchronise
|
||||
// with the "expected" timestamps.
|
||||
bit done;
|
||||
fork
|
||||
begin
|
||||
wait_for_reset(.wait_posedge(1'b0));
|
||||
|
||||
// Wait a short time after reset before starting to drive the clock.
|
||||
#1ps;
|
||||
o_clk = 1'b0;
|
||||
|
||||
done = 1'b1;
|
||||
end
|
||||
while (!done) #(clk_period_ps * 1ps);
|
||||
join
|
||||
|
||||
// If there might be multiple clocks in the system, wait another (randomised) short time to
|
||||
// desynchronise.
|
||||
if (!sole_clock) #($urandom_range(0, clk_period_ps) * 1ps);
|
||||
|
||||
forever begin
|
||||
if (recompute) begin
|
||||
clk_hi_ps = clk_period_ps * duty_cycle / 100;
|
||||
clk_lo_ps = clk_period_ps - clk_hi_ps;
|
||||
recompute = 1'b0;
|
||||
end
|
||||
if (jitter_chance_pc != 0) add_jitter();
|
||||
#(clk_lo_ps * 1ps);
|
||||
// wiggle output clk if not gated
|
||||
if (!clk_gate) o_clk = 1'b1;
|
||||
#(clk_hi_ps * 1ps);
|
||||
o_clk = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
assign clk = drive_clk ? o_clk : 1'bz;
|
||||
assign rst_n = drive_rst_n ? o_rst_n : 1'bz;
|
||||
|
||||
endinterface
|
20
vendor/lowrisc_ip/dv/sv/common_ifs/common_ifs.core
vendored
Normal file
20
vendor/lowrisc_ip/dv/sv/common_ifs/common_ifs.core
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
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:common_ifs"
|
||||
description: "Common interfaces used in DV"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:pins_if
|
||||
files:
|
||||
- clk_if.sv
|
||||
- clk_rst_if.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
81
vendor/lowrisc_ip/dv/sv/common_ifs/index.md
vendored
Normal file
81
vendor/lowrisc_ip/dv/sv/common_ifs/index.md
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
# Common interfaces
|
||||
|
||||
|
||||
## Overview
|
||||
In this directory, we provide commonly used interfaces used to construct
|
||||
testbenches for DV. These interfaces are instantiated inside `tb` module for
|
||||
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:
|
||||
* `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
|
||||
* `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
|
||||
|
||||
### `pins_if`
|
||||
This paramterized interface provides the ability to drive / sample any signal
|
||||
in the DUT.
|
||||
```systemverilog
|
||||
interface pins_if #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
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
|
||||

|
||||
|
||||
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`
|
96
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv
vendored
Normal file
96
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
// Interface: pins_if
|
||||
// Description: Pin interface for driving and sampling individual pins such as interrupts, alerts
|
||||
// and gpios.
|
||||
`ifndef SYNTHESIS
|
||||
|
||||
interface pins_if #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
inout [Width-1:0] pins
|
||||
);
|
||||
|
||||
|
||||
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
|
||||
|
||||
// function to set pin output enable for specific pin (useful for single pin interface)
|
||||
function automatic void drive_en_pin(int idx = 0, bit val);
|
||||
pins_oe[idx] = val;
|
||||
endfunction
|
||||
|
||||
// function to set pin output enable for all pins
|
||||
function automatic void drive_en(bit [Width-1:0] val);
|
||||
pins_oe = val;
|
||||
endfunction
|
||||
|
||||
// function to drive a specific pin with a value (useful for single pin interface)
|
||||
function automatic void drive_pin(int idx = 0, logic val);
|
||||
pins_oe[idx] = 1'b1;
|
||||
pins_o[idx] = val;
|
||||
endfunction // drive_pin
|
||||
|
||||
// function to drive all pins
|
||||
function automatic void drive(logic [Width-1:0] val);
|
||||
pins_oe = {Width{1'b1}};
|
||||
pins_o = val;
|
||||
endfunction // drive
|
||||
|
||||
// function to drive all pull down values
|
||||
function automatic void set_pulldown_en(bit [Width-1:0] val);
|
||||
pins_pd = val;
|
||||
endfunction // set_pulldown_en
|
||||
|
||||
// function to drive all pull up values
|
||||
function automatic void set_pullup_en(bit [Width-1:0] val);
|
||||
pins_pu = val;
|
||||
endfunction // set_pullup_en
|
||||
|
||||
// function to drive the pull down value on a specific pin
|
||||
function automatic void set_pulldown_en_pin(int idx = 0, bit val);
|
||||
pins_pd[idx] = val;
|
||||
endfunction // set_pulldown_en_pin
|
||||
|
||||
// function to drive the pull up value on a specific pin
|
||||
function automatic void set_pullup_en_pin(int idx = 0, bit val);
|
||||
pins_pu[idx] = val;
|
||||
endfunction // set_pullup_en_pin
|
||||
|
||||
// function to sample a specific pin (useful for single pin interface)
|
||||
function automatic logic sample_pin(int idx = 0);
|
||||
return pins[idx];
|
||||
endfunction
|
||||
|
||||
// function to sample all pins
|
||||
function automatic logic [Width-1:0] sample();
|
||||
return pins;
|
||||
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;
|
||||
`ifdef VERILATOR
|
||||
assign pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
|
||||
`else
|
||||
assign (pull0, pull1) pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
|
||||
`endif
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endinterface
|
||||
`endif
|
1
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg
vendored
Normal file
1
vendor/lowrisc_ip/dv/sv/common_ifs/pins_if.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 56 KiB |
17
vendor/lowrisc_ip/dv/sv/common_ifs/pins_ifs.core
vendored
Normal file
17
vendor/lowrisc_ip/dv/sv/common_ifs/pins_ifs.core
vendored
Normal file
|
@ -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:pins_if"
|
||||
description: "Common interfaces used in DV"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
files:
|
||||
- pins_if.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
159
vendor/lowrisc_ip/dv/sv/csr_utils/README.md
vendored
Normal file
159
vendor/lowrisc_ip/dv/sv/csr_utils/README.md
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
# CSR utilities
|
||||
|
||||
|
||||
This csr_utils folder intends to implement CSR related methods and test sequences for DV
|
||||
to share across all testbenches.
|
||||
|
||||
### CSR utility package
|
||||
`csr_utils_pkg` provides common methods and properties to support and manage CSR accesses
|
||||
and CSR related test sequences.
|
||||
|
||||
#### Global types and variables
|
||||
All common types and variables are defined at this package level. Examples are:
|
||||
```systemverilog
|
||||
uint outstanding_accesses = 0;
|
||||
uint default_timeout_ns = 1_000_000;
|
||||
```
|
||||
|
||||
##### Outstanding_accesses
|
||||
`csr_utils_pkg` used an internal variable to store the number of accesses
|
||||
(read or write) that have not yet completed. This variable is shared among all methods of
|
||||
register reading and writing. Directly accessing this variable is discouraged. Instead,
|
||||
the following methods are used to control this variable to keep track of non-blocking
|
||||
accesses made in the testbench:
|
||||
```systemverilog
|
||||
function automatic void increment_outstanding_access();
|
||||
outstanding_accesses++;
|
||||
endfunction
|
||||
|
||||
function automatic void decrement_outstanding_access();
|
||||
outstanding_accesses--;
|
||||
endfunction
|
||||
|
||||
task automatic wait_no_outstanding_access();
|
||||
wait(outstanding_accesses == 0);
|
||||
endtask
|
||||
|
||||
function automatic void clear_outstanding_access();
|
||||
outstanding_accesses = 0;
|
||||
endfunction
|
||||
```
|
||||
|
||||
##### CSR spinwait
|
||||
One of the commonly used tasks in `csr_utils_pkg` is `csr_spinwait`. This task
|
||||
can poll a CSR or CSR field continuously or periodically until it reads out the
|
||||
expected value. This task also has a timeout check in case due to DUT or testbench
|
||||
issue, the CSR or CSR field never returns the expected value.
|
||||
Example below uses the `csr_spinwait` to wait until the CSR `fifo_status` field
|
||||
`fifo_full` reaches value bit 1:
|
||||
```systemverilog
|
||||
csr_spinwait(.ptr(ral.status.fifo_full), .exp_data(1'b0));
|
||||
```
|
||||
|
||||
##### Read and check all CSRs
|
||||
The purpose of the `read_and_check_all_csrs` task is to read all valid CSRs from
|
||||
the given `uvm_reg_block` and check against their expected values from RAL. This
|
||||
task is primarily implemented to use after reset, to make sure all the CSRs are
|
||||
being reset to the default value.
|
||||
|
||||
##### Under_reset
|
||||
Due to `csr_utils_pkg` is not connected to any interface, methods inside
|
||||
this package are not able to get reset information. Current the `under_reset`
|
||||
bit is declared with two functions:
|
||||
```systemverilog
|
||||
function automatic void reset_asserted();
|
||||
under_reset = 1;
|
||||
endfunction
|
||||
|
||||
function automatic void reset_deasserted();
|
||||
under_reset = 0;
|
||||
endfunction
|
||||
```
|
||||
This reset information is updated in `dv_lib/dv_base_vseq.sv`. When the
|
||||
`apply_reset` task is triggered, it will set and reset the `under_reset` bit
|
||||
via the functions above.
|
||||
|
||||
#### Global CSR util methods
|
||||
##### Global methods for CSR and MEM attributes
|
||||
This package provides methods to access CSR or Memory attributes, such as address,
|
||||
value, etc. Examples are:
|
||||
* `get_csr_addrs`
|
||||
* `get_mem_addr_ranges`
|
||||
* `decode_csr_or_field`
|
||||
|
||||
##### Global methods for CSR access
|
||||
The CSR access methods are based on `uvm_reg` methods, such as `uvm_reg::read()`,
|
||||
`uvm_reg::write()`, `uvm_reg::update()`. For all CSR methods, user can
|
||||
pass either a register or a field handle. Examples are:
|
||||
* `csr_rd_check`: Given the uvm_reg or uvm_reg_field object, this method will
|
||||
compare the CSR value with the expected value (given as an input) or with
|
||||
the RAL mirrored value
|
||||
* `csr_update`: Given the uvm_reg object, this method will update the value of the
|
||||
register in DUT to match the desired value
|
||||
|
||||
To enhance the usability, these methods support CSR blocking, non-blocking
|
||||
read/write, and a timeout checking.
|
||||
* A blocking thread will not execute the next sequence until the current CSR
|
||||
access is finished
|
||||
* A non-blocking thread allows multiple CSR accesses to be issued back-to-back
|
||||
without waiting for the response
|
||||
* A timeout check will discard the ongoing CSR access by disabling the forked
|
||||
thread and will throw a UVM_ERROR once the process exceeds the max timeout setting
|
||||
|
||||
### CSR sequence library
|
||||
`csr_seq_lib.sv` provides common CSR related test sequences to share across all testbenches.
|
||||
These test sequences are based off the standard sequences provided in UVM1.2 RAL.
|
||||
The parent class (DUT-specific test or sequence class) that creates them needs to provide them
|
||||
with the DUT RAL model. The list of CSRs are then extracted from the RAL model to performs the checks.
|
||||
In addition, the test sequences provide an ability to exclude a CSR from writes or reads (or both)
|
||||
depending on the behavior of the CSR in the design. This is explained more in the
|
||||
[CSR exclusion methodology](#csr-exclusion-methodology) section below.
|
||||
All CSR accesses in these sequences are made non-blocking to ensure back-to-back scenarios
|
||||
are exercised.
|
||||
Supported CSR test sequences are:
|
||||
* `csr_hw_reset`: Write all CSRs with random values and then reset the DUT.
|
||||
After reset, read all CSRs and compare with expected values
|
||||
* `csr_rw`: Write a randomly selected CSRs, then read out the updated
|
||||
CSR or CSR field and compare with expected value
|
||||
* `csr_bit_bash`: Randomly select a CSR and write 1's and 0's to
|
||||
every bit, then read the CSR to compare with expected value
|
||||
* `csr_aliasing`: Randomly write a CSR, then read all CSRs to
|
||||
verify that only the CSR that was written was updated
|
||||
* `mem_walk`: Write and read all valid addresses in the memory. Compare
|
||||
the read results with the expected values
|
||||
|
||||
### CSR exclusion methodology
|
||||
The CSR test sequences listed above intend to perform a sanity 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.
|
||||
`csr_excl_item` is a class that supports adding exclusions to CSR test sequences.
|
||||
Examples of useful functions in this class are:
|
||||
* `add_excl`: Add exclusions to the CSR test sequences. This function has two inputs:
|
||||
- Exclusion scope: A hierarchical path name at all levels including block,
|
||||
CSR, and field. This input supports * and ? wildcards for glob style matching
|
||||
- CSR_exclude type: An enumeration defined as below:
|
||||
```systemverilog
|
||||
typedef enum bit[2:0] {
|
||||
CsrNoExcl = 3'b000, // no exclusions
|
||||
CsrExclInitCheck = 3'b001, // exclude csr from init val check
|
||||
CsrExclWriteCheck = 3'b010, // exclude csr from write-read check
|
||||
CsrExclCheck = 3'b011, // exclude csr from init or write-read check
|
||||
CsrExclWrite = 3'b100, // exclude csr from write
|
||||
CsrExclAll = 3'b111 // exclude csr from init or write or write-read check
|
||||
} csr_excl_type_e;
|
||||
```
|
||||
|
||||
One example to use this function in HMAC to exclude all CSRs or fields with
|
||||
names starting with "key":
|
||||
```systemverilog
|
||||
csr_excl.add_excl({scope, ".", "key?"}, CsrExclWrite);
|
||||
```
|
||||
|
||||
* `has_excl`: Check if the CSR has a match in the existing exclusions loopup,
|
||||
and is not intended to use externally
|
||||
|
||||
### CSR sequence framework
|
||||
The [cip_lib]({{< relref "hw/dv/sv/cip_lib/doc" >}}) includes a virtual sequence named `cip_base_vseq`,
|
||||
that provides a common framework for all testbenchs to run these CSR test sequences and
|
||||
add exclusions.
|
500
vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
vendored
Normal file
500
vendor/lowrisc_ip/dv/sv/csr_utils/csr_seq_lib.sv
vendored
Normal file
|
@ -0,0 +1,500 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// CSR suite of sequences that do writes and reads to csrs
|
||||
// includes hw_reset, rw, bit_bash and aliasing tests for csrs, and mem_walk for uvm_mems
|
||||
// TODO: when mem backdoor is implemented, add uvm_mem_access_seq for backdoor rd
|
||||
// The sequences perform csr writes and reads and follow the standard csr test suite. If external
|
||||
// checker is enabled, then the external entity is required to update the mirrored value on
|
||||
// writes. If not enabled, the sequences themselves call predict function to update the mirrored
|
||||
// value. Consequently, the read values are checked against the mirrored value and not the
|
||||
// previously written value. This approach is better since it takes care of special
|
||||
// register and field access policies. Also, we use csr_rd_check task instead of csr_mirror to take
|
||||
// field exclusions into account.
|
||||
//
|
||||
// Csrs to be tested is accumulated and shuffled from the supplied reg models.
|
||||
// What / how many csrs to test can be further controlled in 3 ways -
|
||||
// 1. Externally add specific csrs to test_csrs queue (highest prio)
|
||||
// 2. Set num_test_csrs test a randomly picked set of csrs from the supplied models
|
||||
// 3. Set / pass via plusarg, num_csr_chunks / test_csr_chunk
|
||||
//
|
||||
// Exclusions are to be provided using the csr_excl_item item (see class for more details).
|
||||
class csr_base_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));
|
||||
`uvm_object_utils(csr_base_seq)
|
||||
|
||||
uvm_reg_block models[$];
|
||||
uvm_reg all_csrs[$];
|
||||
uvm_reg test_csrs[$];
|
||||
csr_excl_item m_csr_excl_item;
|
||||
|
||||
// By default, assume external checker (example, scoreboard) is turned off. If that is the case,
|
||||
// then writes are followed by call to predict function to update the mirrored value. Reads are
|
||||
// then checked against the mirrored value using csr_rd_check task. If external checker is
|
||||
// enabled, then we let the external checker do the predict and compare.
|
||||
// In either case, we should be able to do completely non-blocking writes and reads.
|
||||
bit external_checker = 1'b0;
|
||||
|
||||
// either use num_test_csrs or {test_csr_chunk, num_csr_chunks} to test slice of all csrs
|
||||
int num_test_csrs = 0;
|
||||
int test_csr_chunk = 1;
|
||||
int num_csr_chunks = 1;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
// pre_start
|
||||
virtual task pre_start();
|
||||
super.pre_start();
|
||||
|
||||
// create test_csrs list only if its empty
|
||||
if (test_csrs.size() == 0) set_csr_test_range();
|
||||
|
||||
// create dummy m_csr_excl_item if not supplied
|
||||
if (m_csr_excl_item == null) begin
|
||||
`uvm_info(`gtn, "m_csr_excl_item is null, creating a dummy one locally", UVM_LOW)
|
||||
m_csr_excl_item = csr_excl_item::type_id::create("m_csr_excl_item");
|
||||
end
|
||||
endtask
|
||||
|
||||
// post_start
|
||||
virtual task post_start();
|
||||
super.post_start();
|
||||
wait_no_outstanding_access();
|
||||
test_csrs.delete();
|
||||
endtask
|
||||
|
||||
function void set_csr_excl_item(csr_excl_item item);
|
||||
this.m_csr_excl_item = item;
|
||||
endfunction
|
||||
|
||||
// extract csrs and split and prune to a specified test_csr_chunk
|
||||
virtual function void set_csr_test_range();
|
||||
int start_idx;
|
||||
int end_idx;
|
||||
int chunk_size;
|
||||
|
||||
// extract all csrs from the model
|
||||
// TODO: add and use function here instead that allows pre filtering csrs
|
||||
all_csrs.delete();
|
||||
foreach (models[i]) begin
|
||||
models[i].get_registers(all_csrs);
|
||||
end
|
||||
|
||||
if (num_test_csrs != 0) begin
|
||||
num_csr_chunks = all_csrs.size / num_test_csrs + 1;
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(test_csr_chunk,
|
||||
test_csr_chunk inside {[1:num_csr_chunks]};)
|
||||
end
|
||||
else begin
|
||||
// extract test_csr_chunk, num_csr_chunks from plusargs
|
||||
void'($value$plusargs("test_csr_chunk=%0d", test_csr_chunk));
|
||||
void'($value$plusargs("num_csr_chunks=%0d", num_csr_chunks));
|
||||
end
|
||||
|
||||
if (!(test_csr_chunk inside {[1:num_csr_chunks]})) begin
|
||||
`uvm_fatal(`gtn, $sformatf({{"invalid opt +test_csr_chunk=%0d, +num_csr_chunks=%0d "},
|
||||
{"(1 <= test_csr_chunk <= num_csr_chunks)"}},
|
||||
test_csr_chunk, num_csr_chunks))
|
||||
end
|
||||
chunk_size = (num_test_csrs != 0) ? num_test_csrs : (all_csrs.size / num_csr_chunks + 1);
|
||||
start_idx = (test_csr_chunk - 1) * chunk_size;
|
||||
end_idx = test_csr_chunk * chunk_size;
|
||||
if (end_idx >= all_csrs.size())
|
||||
end_idx = all_csrs.size() - 1;
|
||||
|
||||
test_csrs = all_csrs[start_idx:end_idx];
|
||||
`uvm_info(`gtn, $sformatf("testing %0d csrs [%0d - %0d] in all supplied models",
|
||||
test_csrs.size(), start_idx, end_idx), UVM_MEDIUM)
|
||||
foreach (test_csrs[i]) begin
|
||||
`uvm_info(`gtn, $sformatf("test_csrs list: %0s, reset: 0x%0x", test_csrs[i].get_full_name(),
|
||||
test_csrs[i].get_mirrored_value()), UVM_HIGH)
|
||||
end
|
||||
test_csrs.shuffle();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_hw_reset_seq
|
||||
// Brief Description: This sequence reads all CSRs and checks it against the reset value provided
|
||||
// in the RAL specification. Note that this does not sufficiently qualify as the CSR HW reset test.
|
||||
// The 'full' CSR HW reset test is constructed externally by running the csr_write_seq below first,
|
||||
// issuing reset and only then running this sequence.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_hw_reset_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_hw_reset_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
foreach (test_csrs[i]) begin
|
||||
uvm_reg_data_t compare_mask;
|
||||
|
||||
// check if parent block or register is excluded from init check
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclInitCheck, CsrHwResetTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclInitCheck exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Verifying reset value of register %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclInitCheck, CsrHwResetTest,
|
||||
m_csr_excl_item);
|
||||
csr_rd_check(.ptr (test_csrs[i]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (compare_mask));
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_write_seq
|
||||
// Brief Description: This sequence writes a random value to all CSRs. It does not perform any
|
||||
// checks. It is run as the first step of the CSR HW reset test.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_write_seq extends csr_base_seq;
|
||||
static bit test_backdoor_path_done; // only run once
|
||||
bit en_rand_backdoor_write;
|
||||
`uvm_object_utils(csr_write_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
uvm_reg_data_t wdata;
|
||||
|
||||
// check all hdl paths are valid
|
||||
if (!test_backdoor_path_done) begin
|
||||
uvm_reg_mem_hdl_paths_seq hdl_check_seq;
|
||||
hdl_check_seq = uvm_reg_mem_hdl_paths_seq::type_id::create("hdl_check_seq");
|
||||
foreach (models[i]) begin
|
||||
hdl_check_seq.model = models[i];
|
||||
hdl_check_seq.start(null);
|
||||
end
|
||||
test_backdoor_path_done = 1;
|
||||
end
|
||||
|
||||
foreach (test_csrs[i]) begin
|
||||
dv_base_reg dv_csr;
|
||||
bit backdoor;
|
||||
// check if parent block or register is excluded from write
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrHwResetTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Writing random data to register %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
|
||||
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrHwResetTest, m_csr_excl_item);
|
||||
|
||||
`downcast(dv_csr, test_csrs[i])
|
||||
if (en_rand_backdoor_write && !dv_csr.get_is_ext_reg()) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(backdoor,
|
||||
backdoor dist {0 :/ 7, 1 :/ 3};)
|
||||
end
|
||||
|
||||
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .backdoor(backdoor));
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_rw_seq
|
||||
// Brief Description: This seq writes a random value to a CSR and reads it back. The read value
|
||||
// is checked for correctness while adhering to its access policies. A random choice is made between
|
||||
// reading back the CSR as a whole or reading fields individually, so that partial accesses are made
|
||||
// into the DUT as well.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_rw_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_rw_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;
|
||||
uvm_reg_data_t compare_mask;
|
||||
uvm_reg_field test_fields[$];
|
||||
|
||||
// check if parent block or register is excluded from write
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrRwTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`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);
|
||||
|
||||
// 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
|
||||
// the pre-predict also needs to happen after the register is being written, to make sure the
|
||||
// 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
|
||||
|
||||
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
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_bit_bash_seq
|
||||
// Brief Description: This sequence walks a 1 through each CSR by writing one bit at a time and
|
||||
// reading the CSR back. The read value is checked for correctness while adhering to its access
|
||||
// policies. This verifies that there is no aliasing within the fields / bits of a CSR.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_bit_bash_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_bit_bash_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
foreach (test_csrs[i]) begin
|
||||
// check if parent block or register is excluded from write
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrBitBashTest) ||
|
||||
m_csr_excl_item.is_excl(test_csrs[i], CsrExclWriteCheck, CsrBitBashTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite/WriteCheck exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Verifying register bit bash for %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
begin
|
||||
uvm_reg_field fields[$];
|
||||
string mode[`UVM_REG_DATA_WIDTH];
|
||||
uvm_reg_data_t dc_mask; // dont write or read
|
||||
uvm_reg_data_t cmp_mask; // read but dont compare
|
||||
int n_bits;
|
||||
string field_access;
|
||||
int next_lsb;
|
||||
|
||||
n_bits = test_csrs[i].get_n_bytes() * 8;
|
||||
|
||||
// Let's see what kind of bits we have...
|
||||
test_csrs[i].get_fields(fields);
|
||||
|
||||
next_lsb = 0;
|
||||
dc_mask = 0;
|
||||
cmp_mask = 0;
|
||||
|
||||
foreach (fields[j]) begin
|
||||
int lsb, w, dc, cmp;
|
||||
|
||||
field_access = fields[j].get_access(test_csrs[i].get_default_map());
|
||||
cmp = (fields[j].get_compare() == UVM_NO_CHECK);
|
||||
lsb = fields[j].get_lsb_pos();
|
||||
w = fields[j].get_n_bits();
|
||||
|
||||
// Exclude write-only fields from compare because you are not supposed to read them
|
||||
case (field_access)
|
||||
"WO", "WOC", "WOS", "WO1", "NOACCESS", "": cmp = 1;
|
||||
endcase
|
||||
|
||||
// skip fields that are wr-excluded
|
||||
if (m_csr_excl_item.is_excl(fields[j], CsrExclWrite, CsrBitBashTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping field %0s due to CsrExclWrite exclusion",
|
||||
fields[j].get_full_name()), UVM_MEDIUM)
|
||||
dc = 1;
|
||||
end
|
||||
|
||||
// ignore fields that are init or rd-excluded
|
||||
cmp = m_csr_excl_item.is_excl(fields[j], CsrExclInitCheck, CsrBitBashTest) ||
|
||||
m_csr_excl_item.is_excl(fields[j], CsrExclWriteCheck, CsrBitBashTest) ;
|
||||
|
||||
// Any unused bits on the right side of the LSB?
|
||||
while (next_lsb < lsb) mode[next_lsb++] = "RO";
|
||||
|
||||
repeat (w) begin
|
||||
mode[next_lsb] = field_access;
|
||||
dc_mask[next_lsb] = dc;
|
||||
cmp_mask[next_lsb] = cmp;
|
||||
next_lsb++;
|
||||
end
|
||||
end
|
||||
|
||||
// Any unused bits on the left side of the MSB?
|
||||
while (next_lsb < `UVM_REG_DATA_WIDTH)
|
||||
mode[next_lsb++] = "RO";
|
||||
|
||||
// Bash the kth bit
|
||||
for (int k = 0; k < n_bits; k++) begin
|
||||
// Cannot test unpredictable bit behavior
|
||||
if (dc_mask[k]) continue;
|
||||
bash_kth_bit(test_csrs[i], k, mode[k], cmp_mask);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endtask
|
||||
|
||||
task bash_kth_bit(uvm_reg rg,
|
||||
int k,
|
||||
string mode,
|
||||
uvm_reg_data_t mask);
|
||||
|
||||
uvm_reg_data_t val;
|
||||
string err_msg;
|
||||
|
||||
`uvm_info(`gtn, $sformatf("bashing %0s bit #%0d", mode, k), UVM_HIGH)
|
||||
repeat (2) begin
|
||||
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
|
||||
|
||||
// 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
|
||||
csr_rd_check(.ptr (rg),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (~mask),
|
||||
.err_msg (err_msg));
|
||||
end
|
||||
endtask: bash_kth_bit
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_aliasing_seq
|
||||
// Brief Description: For each CSR, this sequence writes a random value to it and reads ALL CSRs
|
||||
// back. The read value of the CSR that was written is checked for correctness while adhering to its
|
||||
// access policies. The read value of all other CSRs are compared against their previous values.
|
||||
// This verifies that there is no aliasing across the address bits within the valid CSR space.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_aliasing_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_aliasing_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
foreach(test_csrs[i]) begin
|
||||
uvm_reg_data_t wdata;
|
||||
|
||||
// check if parent block or register is excluded
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrAliasingTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Verifying register aliasing for %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
`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
|
||||
|
||||
all_csrs.shuffle();
|
||||
foreach (all_csrs[j]) begin
|
||||
uvm_reg_data_t compare_mask;
|
||||
|
||||
// check if parent block or register is excluded
|
||||
if (m_csr_excl_item.is_excl(all_csrs[j], CsrExclInitCheck, CsrAliasingTest) ||
|
||||
m_csr_excl_item.is_excl(all_csrs[j], CsrExclWriteCheck, CsrAliasingTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclInit/WriteCheck exclusion",
|
||||
all_csrs[j].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
compare_mask = get_mask_excl_fields(all_csrs[j], CsrExclWriteCheck, CsrAliasingTest,
|
||||
m_csr_excl_item);
|
||||
csr_rd_check(.ptr (all_csrs[j]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (compare_mask));
|
||||
end
|
||||
wait_no_outstanding_access();
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_mem_walk_seq
|
||||
// Brief Description: This seq walks through each address of the memory by running the default
|
||||
// UVM mem walk sequence.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_mem_walk_seq extends csr_base_seq;
|
||||
uvm_mem_walk_seq mem_walk_seq;
|
||||
|
||||
`uvm_object_utils(csr_mem_walk_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
mem_walk_seq = uvm_mem_walk_seq::type_id::create("mem_walk_seq");
|
||||
foreach (models[i]) begin
|
||||
mem_walk_seq.model = models[i];
|
||||
mem_walk_seq.start(null);
|
||||
end
|
||||
endtask : body
|
||||
|
||||
endclass
|
21
vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils.core
vendored
Normal file
21
vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils.core
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
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:csr_utils"
|
||||
description: "CSR utilities"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_utils
|
||||
- lowrisc:dv:dv_base_reg
|
||||
files:
|
||||
- csr_utils_pkg.sv
|
||||
- csr_seq_lib.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
657
vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv
vendored
Normal file
657
vendor/lowrisc_ip/dv/sv/csr_utils/csr_utils_pkg.sv
vendored
Normal file
|
@ -0,0 +1,657 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package csr_utils_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import dv_utils_pkg::*;
|
||||
import dv_base_reg_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`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;
|
||||
|
||||
// csr field struct - hold field specific params
|
||||
typedef struct {
|
||||
uvm_reg csr;
|
||||
uvm_reg_field field;
|
||||
uvm_reg_data_t mask;
|
||||
uint shift;
|
||||
} csr_field_t;
|
||||
|
||||
function automatic void increment_outstanding_access();
|
||||
outstanding_accesses++;
|
||||
endfunction
|
||||
|
||||
function automatic void decrement_outstanding_access();
|
||||
outstanding_accesses--;
|
||||
endfunction
|
||||
|
||||
task automatic wait_no_outstanding_access();
|
||||
wait(outstanding_accesses == 0);
|
||||
endtask
|
||||
|
||||
function automatic void clear_outstanding_access();
|
||||
outstanding_accesses = 0;
|
||||
endfunction
|
||||
|
||||
// timeout may happen if we issue too many non-blocking accesses at once
|
||||
// limit the nonblocking items to be up to max outstanding
|
||||
task automatic wait_if_max_outstanding_accesses_reached(int max = max_outstanding_accesses);
|
||||
wait(outstanding_accesses <= max);
|
||||
endtask
|
||||
|
||||
function automatic void reset_asserted();
|
||||
under_reset = 1;
|
||||
endfunction
|
||||
|
||||
function automatic void reset_deasserted();
|
||||
under_reset = 0;
|
||||
endfunction
|
||||
|
||||
// Get all valid csr addrs - useful to check if incoming addr falls in the csr range.
|
||||
function automatic void get_csr_addrs(input uvm_reg_block ral, ref uvm_reg_addr_t csr_addrs[$]);
|
||||
uvm_reg csrs[$];
|
||||
ral.get_registers(csrs);
|
||||
foreach (csrs[i]) begin
|
||||
csr_addrs.push_back(csrs[i].get_address());
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Get all valid mem addr ranges - useful to check if incoming addr falls in the mem range.
|
||||
function automatic void get_mem_addr_ranges(uvm_reg_block ral, ref addr_range_t mem_ranges[$]);
|
||||
uvm_mem mems[$];
|
||||
ral.get_memories(mems);
|
||||
foreach (mems[i]) begin
|
||||
addr_range_t mem_range;
|
||||
mem_range.start_addr = mems[i].get_address();
|
||||
mem_range.end_addr = mem_range.start_addr +
|
||||
mems[i].get_size() * mems[i].get_n_bytes() - 1;
|
||||
mem_ranges.push_back(mem_range);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// get mem object from address
|
||||
function automatic uvm_mem get_mem_by_addr(uvm_reg_block ral, uvm_reg_addr_t addr);
|
||||
uvm_mem mem;
|
||||
addr[1:0] = 0;
|
||||
mem = ral.default_map.get_mem_by_offset(addr);
|
||||
`DV_CHECK_NE_FATAL(mem, null, $sformatf("Can't find any mem with addr 0x%0h", addr), msg_id)
|
||||
return mem;
|
||||
endfunction
|
||||
|
||||
// get mem access like RW, RO
|
||||
function automatic string get_mem_access_by_addr(uvm_reg_block ral, uvm_reg_addr_t addr);
|
||||
uvm_mem mem = get_mem_by_addr(ral, addr);
|
||||
return mem.get_access();
|
||||
endfunction
|
||||
|
||||
// This fucntion return mirrored value of reg/field of given RAL
|
||||
function automatic uvm_reg_data_t get_reg_fld_mirror_value(uvm_reg_block ral,
|
||||
string reg_name,
|
||||
string field_name = "");
|
||||
uvm_reg csr;
|
||||
uvm_reg_field fld;
|
||||
uvm_reg_data_t result;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::get_reg_fld_mirror_value"};
|
||||
csr = ral.get_reg_by_name(reg_name);
|
||||
`DV_CHECK_NE_FATAL(csr, null, "", msg_id)
|
||||
// return field mirror value if field_name is passed, else return reg mirror value
|
||||
if (field_name != "") begin
|
||||
fld = csr.get_field_by_name(field_name);
|
||||
`DV_CHECK_NE_FATAL(fld, null, "", msg_id)
|
||||
result = fld.get_mirrored_value();
|
||||
end
|
||||
else begin
|
||||
result = csr.get_mirrored_value();
|
||||
end
|
||||
return result;
|
||||
endfunction : get_reg_fld_mirror_value
|
||||
|
||||
// This function attempts to cast a given uvm_object ptr into uvm_reg or uvm_reg_field. If cast
|
||||
// is successful on either, then set the appropriate csr_field_t return values.
|
||||
function automatic csr_field_t decode_csr_or_field(input uvm_object ptr);
|
||||
uvm_reg csr;
|
||||
uvm_reg_field fld;
|
||||
csr_field_t result;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::decode_csr_or_field"};
|
||||
|
||||
if ($cast(csr, ptr)) begin
|
||||
// return csr object with null field; set the mask to all 1s and shift to 0
|
||||
result.csr = csr;
|
||||
result.mask = '1;
|
||||
result.shift = 0;
|
||||
end
|
||||
else if ($cast(fld, ptr)) begin
|
||||
// return csr field object; return the appropriate mask and shift values
|
||||
result.csr = fld.get_parent();
|
||||
result.field = fld;
|
||||
result.mask = (1 << fld.get_n_bits()) - 1;
|
||||
result.shift = fld.get_lsb_pos();
|
||||
end
|
||||
else begin
|
||||
`uvm_fatal(msg_id, $sformatf("ptr %0s is not of type uvm_reg or uvm_reg_field",
|
||||
ptr.get_full_name()))
|
||||
end
|
||||
return result;
|
||||
endfunction : decode_csr_or_field
|
||||
|
||||
// mask and shift data to extract the value specific to that supplied field
|
||||
function automatic uvm_reg_data_t get_field_val(uvm_reg_field field,
|
||||
uvm_reg_data_t value);
|
||||
uvm_reg_data_t mask = (1 << field.get_n_bits()) - 1;
|
||||
uint shift = field.get_lsb_pos();
|
||||
get_field_val = (value >> shift) & mask;
|
||||
endfunction
|
||||
|
||||
// get updated reg value by using new specific field value
|
||||
function automatic uvm_reg_data_t get_csr_val_with_updated_field(uvm_reg_field field,
|
||||
uvm_reg_data_t csr_value,
|
||||
uvm_reg_data_t field_value);
|
||||
uvm_reg_data_t mask = (1 << field.get_n_bits()) - 1;
|
||||
uint shift = field.get_lsb_pos();
|
||||
csr_value = csr_value & ~(mask << shift) | ((mask & field_value) << shift);
|
||||
return csr_value;
|
||||
endfunction
|
||||
|
||||
// wait until current csr op is complete
|
||||
task automatic csr_wait(input uvm_reg csr);
|
||||
`uvm_info(msg_id, $sformatf("%0s: wait_busy: %0b",
|
||||
csr.get_full_name(), csr.m_is_busy), UVM_HIGH)
|
||||
wait(csr.m_is_busy == 1'b0);
|
||||
`uvm_info(msg_id, $sformatf("%0s: done wait_busy: %0b",
|
||||
csr.get_full_name(), csr.m_is_busy), UVM_HIGH)
|
||||
endtask
|
||||
|
||||
task automatic csr_update(input uvm_reg csr,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null,
|
||||
input bit en_shadow_wr = 1);
|
||||
if (blocking) begin
|
||||
csr_update_sub(csr, check, path, timeout_ns, map, en_shadow_wr);
|
||||
end else begin
|
||||
fork
|
||||
csr_update_sub(csr, check, path, timeout_ns, map, en_shadow_wr);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// 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_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null,
|
||||
input bit en_shadow_wr = 1);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_update"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
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
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_update %0s (addr=0x%0h)",
|
||||
csr.get_full_name(), csr.get_address()));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
task automatic csr_wr(input uvm_reg csr,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_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 bit predict = 0,
|
||||
input uvm_reg_map map = null,
|
||||
input bit en_shadow_wr = 1);
|
||||
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)));
|
||||
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)));
|
||||
end
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// 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_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null,
|
||||
input bit en_shadow_wr = 1);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_wr"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
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
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_wr %0s (addr=0x%0h)",
|
||||
csr.get_full_name(), csr.get_address()));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
task automatic csr_pre_write_sub(ref uvm_reg csr, bit en_shadow_wr);
|
||||
dv_base_reg dv_reg;
|
||||
`downcast(dv_reg, csr, "", fatal, msg_id)
|
||||
if (dv_reg.get_is_shadowed()) begin
|
||||
if (en_shadow_wr) increment_outstanding_access();
|
||||
dv_reg.atomic_en_shadow_wr.get(1);
|
||||
dv_reg.set_en_shadow_wr(en_shadow_wr);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic csr_post_write_sub(ref uvm_reg csr, bit en_shadow_wr);
|
||||
dv_base_reg dv_reg;
|
||||
`downcast(dv_reg, csr, "", fatal, msg_id)
|
||||
if (dv_reg.get_is_shadowed()) begin
|
||||
// try setting en_shadow_wr back to default value 1, this function will only work if the
|
||||
// shadow reg finished both writes
|
||||
dv_reg.set_en_shadow_wr(1);
|
||||
dv_reg.atomic_en_shadow_wr.put(1);
|
||||
if (en_shadow_wr) begin
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
end
|
||||
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);
|
||||
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));
|
||||
if (check == UVM_CHECK && status != UVM_IS_OK) begin
|
||||
string str;
|
||||
uvm_hdl_path_concat paths[$];
|
||||
csr.get_full_hdl_path(paths);
|
||||
foreach (paths[0].slices[i]) str = $sformatf("%0s\n%0s", str, paths[0].slices[i].path);
|
||||
`uvm_fatal(msg_id, $sformatf("poke failed for %0s, check below paths %0s",
|
||||
csr.get_full_name(), str))
|
||||
end
|
||||
// poke always updates predict value, if predict == 0, revert back to old mirrored value
|
||||
if (!predict) 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_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);
|
||||
end else begin
|
||||
fork
|
||||
csr_rd_sub(ptr, value, check, path, timeout_ns, map);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// 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,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
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
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
csr_or_fld = decode_csr_or_field(ptr);
|
||||
if (csr_or_fld.field != null) begin
|
||||
csr_or_fld.field.read(.status(status), .value(value), .path(path), .map(map),
|
||||
.prior(100));
|
||||
end else begin
|
||||
csr_or_fld.csr.read(.status(status), .value(value), .path(path), .map(map),
|
||||
.prior(100));
|
||||
end
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_rd %0s (addr=0x%0h)",
|
||||
ptr.get_full_name(), csr_or_fld.csr.get_address()));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
// backdoor read csr
|
||||
// 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);
|
||||
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
|
||||
uvm_hdl_path_concat paths[$];
|
||||
|
||||
csr.get_full_hdl_path(paths);
|
||||
foreach (paths[0].slices[i]) begin
|
||||
uvm_reg_data_t field_val;
|
||||
if (uvm_hdl_read(paths[0].slices[i].path, field_val)) begin
|
||||
if (check == UVM_CHECK) `DV_CHECK_EQ($isunknown(value), 0, "", error, msg_id)
|
||||
value |= field_val << paths[0].slices[i].offset;
|
||||
end else begin
|
||||
`uvm_fatal(msg_id, $sformatf("uvm_hdl_read failed for %0s", csr.get_full_name()))
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
`uvm_fatal(msg_id, $sformatf("No backdoor defined for %0s", csr.get_full_name()))
|
||||
end
|
||||
|
||||
// if it's field, only return field value
|
||||
if (csr_or_fld.field != null) value = get_field_val(csr_or_fld.field, value);
|
||||
endtask
|
||||
|
||||
task automatic csr_rd_check(input uvm_object ptr,
|
||||
input uvm_check_e check = UVM_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 bit compare = 1'b1,
|
||||
input bit compare_vs_ral = 1'b0,
|
||||
input uvm_reg_data_t compare_mask = '1,
|
||||
input uvm_reg_data_t compare_value = 0,
|
||||
input string err_msg = "",
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
fork
|
||||
begin
|
||||
csr_field_t csr_or_fld;
|
||||
uvm_status_e status;
|
||||
uvm_reg_data_t obs;
|
||||
uvm_reg_data_t exp;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_rd_check"};
|
||||
|
||||
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));
|
||||
|
||||
// get mirrored value after read to make sure the read reg access is updated
|
||||
if (csr_or_fld.field != null) begin
|
||||
exp = csr_or_fld.field.get_mirrored_value();
|
||||
end else begin
|
||||
exp = csr_or_fld.csr.get_mirrored_value();
|
||||
end
|
||||
if (compare && !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},
|
||||
error, msg_id)
|
||||
end
|
||||
end
|
||||
join_none
|
||||
if (blocking) wait fork;
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
else #0;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
// task to read all csrs and check against ral expected value. Mainly used after reset
|
||||
task automatic read_and_check_all_csrs(input uvm_reg_block ral);
|
||||
uvm_reg ral_csrs[$];
|
||||
ral.get_registers(ral_csrs);
|
||||
ral_csrs.shuffle();
|
||||
|
||||
foreach (ral_csrs[i]) csr_rd_check(.ptr(ral_csrs[i]), .compare_vs_ral(1));
|
||||
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_path_e path = UVM_DEFAULT_PATH,
|
||||
input uvm_reg_map map = null,
|
||||
input uint spinwait_delay_ns = 0,
|
||||
input uint timeout_ns = default_spinwait_timeout_ns,
|
||||
input compare_op_e compare_op = CompareOpEq,
|
||||
input bit backdoor = 0,
|
||||
input uvm_verbosity verbosity = UVM_HIGH);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
csr_field_t csr_or_fld;
|
||||
uvm_reg_data_t read_data;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_spinwait"};
|
||||
|
||||
csr_or_fld = decode_csr_or_field(ptr);
|
||||
if (backdoor && spinwait_delay_ns == 0) spinwait_delay_ns = 1;
|
||||
fork
|
||||
while (!under_reset) begin
|
||||
if (spinwait_delay_ns) #(spinwait_delay_ns * 1ns);
|
||||
csr_rd(.ptr(ptr), .value(read_data), .check(check), .path(path),
|
||||
.blocking(1), .map(map), .backdoor(backdoor));
|
||||
`uvm_info(msg_id, $sformatf("ptr %0s == 0x%0h",
|
||||
ptr.get_full_name(), read_data), verbosity)
|
||||
case (compare_op)
|
||||
CompareOpEq: if (read_data == exp_data) break;
|
||||
CompareOpCaseEq: if (read_data === exp_data) break;
|
||||
CompareOpNe: if (read_data != exp_data) break;
|
||||
CompareOpCaseNe: if (read_data !== exp_data) break;
|
||||
CompareOpGt: if (read_data > exp_data) break;
|
||||
CompareOpGe: if (read_data >= exp_data) break;
|
||||
CompareOpLt: if (read_data < exp_data) break;
|
||||
CompareOpLe: if (read_data <= exp_data) break;
|
||||
default: begin
|
||||
`uvm_fatal(ptr.get_full_name(), $sformatf("invalid operator:%0s", compare_op))
|
||||
end
|
||||
endcase
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id, $sformatf("timeout %0s (addr=0x%0h) == 0x%0h",
|
||||
ptr.get_full_name(), csr_or_fld.csr.get_address(), exp_data));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
task automatic mem_rd(input uvm_mem ptr,
|
||||
input int offset,
|
||||
output bit[31:0] data,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
if (blocking) begin
|
||||
mem_rd_sub(ptr, offset, data, check, timeout_ns, map);
|
||||
end else begin
|
||||
fork
|
||||
mem_rd_sub(ptr, offset, data, check, timeout_ns, map);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask : mem_rd
|
||||
|
||||
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 uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolating_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::mem_rd"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
ptr.read(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin : mem_rd_timeout
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_rd %0s (addr=0x%0h)",
|
||||
ptr.get_full_name(), offset));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolating_fork
|
||||
join
|
||||
endtask : mem_rd_sub
|
||||
|
||||
task automatic mem_wr(input uvm_mem ptr,
|
||||
input int offset,
|
||||
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_reg_map map = null);
|
||||
if (blocking) begin
|
||||
mem_wr_sub(ptr, offset, data, timeout_ns, check, map);
|
||||
end else begin
|
||||
fork
|
||||
mem_wr_sub(ptr, offset, data, timeout_ns, check, map);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask : mem_wr
|
||||
|
||||
task automatic mem_wr_sub(input uvm_mem ptr,
|
||||
input int offset,
|
||||
input bit[31:0] data,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::mem_wr"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
ptr.write(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_wr %0s (addr=0x%0h)",
|
||||
ptr.get_full_name(), offset));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask : mem_wr_sub
|
||||
|
||||
// Fields could be excluded from writes & reads - This function zeros out the excluded fields
|
||||
function automatic uvm_reg_data_t get_mask_excl_fields(uvm_reg csr,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type,
|
||||
csr_excl_item m_csr_excl_item);
|
||||
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);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
// sources
|
||||
`include "csr_seq_lib.sv"
|
||||
|
||||
endpackage
|
111
vendor/lowrisc_ip/dv/sv/dv_base_reg/csr_excl_item.sv
vendored
Normal file
111
vendor/lowrisc_ip/dv/sv/dv_base_reg/csr_excl_item.sv
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Class: csr_excl_item
|
||||
// Description: CSR exclusion item that holds exclusions applied for a given set of blocks /
|
||||
// registers / fields provided and maintained as strings.
|
||||
class csr_excl_item extends uvm_object;
|
||||
`uvm_object_utils(csr_excl_item)
|
||||
|
||||
typedef struct {
|
||||
int csr_test_type;
|
||||
csr_excl_type_e csr_excl_type;
|
||||
} csr_excl_s;
|
||||
local csr_excl_s exclusions[string];
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
// add exclusion for an individual block, csr or field
|
||||
// arg obj: this is the hierarchical path name to the block, csr or field - passing * and ?
|
||||
// wildcards for glob style matching is allowed. User needs to take care that wildcards does not
|
||||
// end up inadvertently matching more that what was desired. Examples:
|
||||
// To exclude ral.ctrl.tx field from writes, obj can be "ral.ctrl.tx" or "*.ctrl.tx"; passing
|
||||
// "*.tx" might be too generic
|
||||
virtual function void add_excl(string obj,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type = CsrAllTests);
|
||||
bit [2:0] val = CsrNoExcl;
|
||||
bit [NUM_CSR_TESTS-1:0] test = CsrInvalidTest;
|
||||
csr_excl_s csr_excl_item;
|
||||
if (csr_test_type == CsrInvalidTest) begin
|
||||
`uvm_fatal(`gfn, $sformatf("add %s exclusion without a test", obj))
|
||||
end
|
||||
val = csr_excl_type | exclusions[obj].csr_excl_type;
|
||||
test = csr_test_type | exclusions[obj].csr_test_type;
|
||||
exclusions[obj].csr_excl_type = csr_excl_type_e'(val);
|
||||
exclusions[obj].csr_test_type = test;
|
||||
endfunction
|
||||
|
||||
// function to check if given blk / csr or field AND its parent has been excluded with the
|
||||
// supplied exclusion type
|
||||
// arg uvm_object obj: given blk, csr or field
|
||||
// arg csr_excl_type_e csr_excl_type: exclusion type
|
||||
function bit is_excl(uvm_object obj,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type);
|
||||
uvm_reg_block blk;
|
||||
uvm_reg csr;
|
||||
|
||||
// if supplied obj is a uvm_reg_block or uvm_reg, then its parent is a uvm_reg_block
|
||||
// check if obj's parent is excluded
|
||||
if ($cast(blk, obj)) begin
|
||||
if (blk.get_parent() != null) begin
|
||||
blk = blk.get_parent();
|
||||
if (has_excl(blk.`gfn, csr_excl_type, csr_test_type)) return 1'b1;
|
||||
end
|
||||
end
|
||||
if ($cast(csr, obj)) begin
|
||||
blk = csr.get_parent();
|
||||
if (has_excl(blk.`gfn, csr_excl_type, csr_test_type)) return 1'b1;
|
||||
end
|
||||
// TODO: check if any parent in the hierarchy above is excluded
|
||||
// check if obj is excluded
|
||||
return (has_excl(obj.`gfn, csr_excl_type, csr_test_type));
|
||||
endfunction
|
||||
|
||||
// check if applied string obj has a match in existing exclusions lookup in defined csr_test_type
|
||||
// function is to not be called externally
|
||||
local function bit has_excl(string obj,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type);
|
||||
// check if obj exists verbatim
|
||||
if (exclusions.exists(obj)) begin
|
||||
`uvm_info(`gfn, $sformatf("has_excl: found exact excl match for %0s: %0s",
|
||||
obj, exclusions[obj].csr_excl_type.name()), UVM_DEBUG)
|
||||
// check if bit(s) corresponding to csr_excl_type are set in defined csr_test_type
|
||||
if ((exclusions[obj].csr_test_type & csr_test_type) != CsrInvalidTest) begin
|
||||
if ((exclusions[obj].csr_excl_type & csr_excl_type) != CsrNoExcl) return 1'b1;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// attempt glob style matching
|
||||
foreach (exclusions[str]) begin
|
||||
if (!uvm_re_match(str, obj)) begin
|
||||
`uvm_info(`gfn, $sformatf("has_excl: found glob excl match for %0s(%0s): %0s",
|
||||
obj, str, exclusions[str].csr_excl_type.name()), UVM_DEBUG)
|
||||
// check if bit(s) corresponding to csr_excl_type are set in defined csr_test_type
|
||||
if ((exclusions[str].csr_test_type & csr_test_type) != CsrInvalidTest) begin
|
||||
if ((exclusions[str].csr_excl_type & csr_excl_type) != CsrNoExcl) return 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return 1'b0;
|
||||
endfunction
|
||||
|
||||
// print all exclusions for ease of debug (call this ideally after adding all exclusions)
|
||||
virtual function void print_exclusions(uvm_verbosity verbosity = UVM_HIGH);
|
||||
string test_names;
|
||||
for (int i = NUM_CSR_TESTS - 1; i >= 0; i--) begin
|
||||
csr_test_type_e csr_test = csr_test_type_e'(1 << i);
|
||||
test_names = {test_names, csr_test.name(), (i > 0) ? " " : ""};
|
||||
end
|
||||
foreach (exclusions[item]) begin
|
||||
`uvm_info(`gfn, $sformatf("CSR/field [%0s] excluded with %0s in csr_tests: {%s} = {%0b}",
|
||||
item, exclusions[item].csr_excl_type.name(), test_names,
|
||||
exclusions[item].csr_test_type), verbosity)
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
61
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_mem.sv
vendored
Normal file
61
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_mem.sv
vendored
Normal file
|
@ -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
|
||||
//
|
||||
// base register reg class which will be used to generate the reg mem
|
||||
class dv_base_mem extends uvm_mem;
|
||||
|
||||
// uvm_mem::m_access is local variable. Create it again in order to use "access" in current class
|
||||
local string m_access;
|
||||
|
||||
// if mem doesn't support partial write, doing that will result d_error = 1
|
||||
local bit mem_partial_write_support;
|
||||
|
||||
function new(string name,
|
||||
longint unsigned size,
|
||||
int unsigned n_bits,
|
||||
string access = "RW",
|
||||
int has_coverage = UVM_NO_COVERAGE);
|
||||
super.new(name, size, n_bits, access, has_coverage);
|
||||
m_access = access;
|
||||
endfunction : new
|
||||
|
||||
function void set_mem_partial_write_support(bit enable);
|
||||
mem_partial_write_support = enable;
|
||||
endfunction : set_mem_partial_write_support
|
||||
|
||||
function bit get_mem_partial_write_support();
|
||||
return mem_partial_write_support;
|
||||
endfunction : get_mem_partial_write_support
|
||||
|
||||
// rewrite this function to support "WO" access type for mem
|
||||
function void configure(uvm_reg_block parent,
|
||||
string hdl_path="");
|
||||
if (parent == null)
|
||||
`uvm_fatal("REG/NULL_PARENT","configure: parent argument is null")
|
||||
|
||||
set_parent(parent);
|
||||
|
||||
if (!(m_access inside {"RW", "RO", "WO"})) begin
|
||||
`uvm_error("RegModel", {"Memory '",get_full_name(),"' can only be RW, RO or WO"})
|
||||
end
|
||||
|
||||
begin
|
||||
uvm_mem_mam_cfg cfg = new;
|
||||
|
||||
cfg.n_bytes = ((get_n_bits() - 1) / 8) + 1;
|
||||
cfg.start_offset = 0;
|
||||
cfg.end_offset = get_size() - 1;
|
||||
|
||||
cfg.mode = uvm_mem_mam::GREEDY;
|
||||
cfg.locality = uvm_mem_mam::BROAD;
|
||||
|
||||
mam = new(get_full_name(), cfg, this);
|
||||
end
|
||||
|
||||
parent.add_mem(this);
|
||||
|
||||
if (hdl_path != "") add_hdl_path_slice(hdl_path, -1, -1);
|
||||
endfunction: configure
|
||||
|
||||
endclass
|
25
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.core
vendored
Normal file
25
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.core
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
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_base_reg"
|
||||
description: "DV base reg/mem library"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_utils
|
||||
files:
|
||||
- dv_base_reg_pkg.sv
|
||||
- csr_excl_item.sv: {is_include_file: true}
|
||||
- dv_base_reg_field.sv: {is_include_file: true}
|
||||
- dv_base_reg.sv: {is_include_file: true}
|
||||
- dv_base_mem.sv: {is_include_file: true}
|
||||
- dv_base_reg_block.sv: {is_include_file: true}
|
||||
- dv_base_reg_map.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
212
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv
vendored
Normal file
212
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg.sv
vendored
Normal file
|
@ -0,0 +1,212 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// base register class which will be used to generate the reg
|
||||
class dv_base_reg extends uvm_reg;
|
||||
// external reg doesn't have storage in reg module, which may connect to some combinational logic
|
||||
// hence, backdoor write isn't available
|
||||
local bit is_ext_reg;
|
||||
|
||||
local dv_base_reg locked_regs[$];
|
||||
local uvm_reg_data_t staged_shadow_val;
|
||||
local bit is_shadowed;
|
||||
local bit shadow_wr_staged; // stage the first shadow reg write
|
||||
local bit shadow_update_err;
|
||||
local bit en_shadow_wr = 1;
|
||||
|
||||
// atomic_shadow_wr: semaphore to guarantee atomicity of the two writes for shadowed registers.
|
||||
// In case a parallel thread writing a different value to the same reg causing an update_err
|
||||
semaphore atomic_shadow_wr;
|
||||
|
||||
// atomic_en_shadow_wr: semaphore to guarantee setting or resetting en_shadow_wr is unchanged
|
||||
// through the 1st/2nd (or both) writes
|
||||
semaphore atomic_en_shadow_wr;
|
||||
|
||||
function new(string name = "",
|
||||
int unsigned n_bits,
|
||||
int has_coverage);
|
||||
super.new(name, n_bits, has_coverage);
|
||||
atomic_en_shadow_wr = new(1);
|
||||
atomic_shadow_wr = new(1);
|
||||
endfunction : new
|
||||
|
||||
function void get_dv_base_reg_fields(ref dv_base_reg_field dv_fields[$]);
|
||||
uvm_reg_field ral_fields[$];
|
||||
get_fields(ral_fields);
|
||||
foreach (ral_fields[i]) `downcast(dv_fields[i], ral_fields[i])
|
||||
endfunction
|
||||
|
||||
// get_n_bits will return number of all the bits in the csr
|
||||
// while this function will return actual number of bits used in reg field
|
||||
function uint get_n_used_bits();
|
||||
uvm_reg_field fields[$];
|
||||
get_fields(fields);
|
||||
foreach (fields[i]) get_n_used_bits += fields[i].get_n_bits();
|
||||
endfunction
|
||||
|
||||
// loop all the fields to find the msb position of this reg
|
||||
function uint get_msb_pos();
|
||||
uvm_reg_field fields[$];
|
||||
get_fields(fields);
|
||||
foreach (fields[i]) begin
|
||||
uint field_msb_pos = fields[i].get_lsb_pos() + fields[i].get_n_bits() - 1;
|
||||
if (field_msb_pos > get_msb_pos) get_msb_pos = field_msb_pos;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// if the register is an enable reg, it will add controlled registers in the queue
|
||||
function void add_locked_reg(dv_base_reg locked_reg);
|
||||
locked_regs.push_back(locked_reg);
|
||||
endfunction
|
||||
|
||||
function bit is_enable_reg();
|
||||
return (locked_regs.size() > 0);
|
||||
endfunction
|
||||
|
||||
function bit is_staged();
|
||||
return shadow_wr_staged;
|
||||
endfunction
|
||||
|
||||
// if enable register is set to 1, the locked registers will be set to RO access
|
||||
// once enable register is reset to 0, the locked registers will be set back to original access
|
||||
function void set_locked_regs_access(string access = "original_access");
|
||||
foreach (locked_regs[i]) begin
|
||||
dv_base_reg_field locked_fields[$];
|
||||
locked_regs[i].get_dv_base_reg_fields(locked_fields);
|
||||
foreach (locked_fields[i]) locked_fields[i].set_locked_fields_access(access);
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void get_locked_regs(ref dv_base_reg locked_regs_q[$]);
|
||||
locked_regs_q = locked_regs;
|
||||
endfunction
|
||||
|
||||
// is_shadowed bit is only one-time programmable
|
||||
// once this function is called in RAL auto-generated class, it cannot be changed
|
||||
function void set_is_shadowed();
|
||||
is_shadowed = 1;
|
||||
endfunction
|
||||
|
||||
function uvm_reg_data_t get_staged_shadow_val();
|
||||
return staged_shadow_val;
|
||||
endfunction
|
||||
|
||||
function void set_en_shadow_wr(bit val);
|
||||
// do not update en_shadow_wr if shadow register write is in process
|
||||
if ((en_shadow_wr ^ val) && shadow_wr_staged) begin
|
||||
`uvm_info(`gfn,
|
||||
$sformatf("unable to %0s en_shadow_wr because register already completed first write",
|
||||
val ? "set" : "clear"), UVM_HIGH)
|
||||
return;
|
||||
end
|
||||
en_shadow_wr = val;
|
||||
endfunction
|
||||
|
||||
function bit get_is_shadowed();
|
||||
return is_shadowed;
|
||||
endfunction
|
||||
|
||||
function bit get_shadow_update_err();
|
||||
return shadow_update_err;
|
||||
endfunction
|
||||
|
||||
virtual function void clear_shadow_update_err();
|
||||
shadow_update_err = 0;
|
||||
endfunction
|
||||
|
||||
// post_write callback to handle special regs:
|
||||
// - shadow register, enable reg won't be updated until the second write has no error
|
||||
// - enable register, if enable_reg is disabled, change access policy to all the locked_regs
|
||||
// TODO: create an `enable_field_access_policy` variable and set the template code during
|
||||
// automation.
|
||||
virtual task post_write(uvm_reg_item rw);
|
||||
dv_base_reg_field fields[$];
|
||||
string field_access;
|
||||
if (is_shadowed) begin
|
||||
// first write
|
||||
if (!shadow_wr_staged) begin
|
||||
shadow_wr_staged = 1;
|
||||
staged_shadow_val = rw.value[0];
|
||||
return;
|
||||
end begin
|
||||
// second write
|
||||
shadow_wr_staged = 0;
|
||||
if (staged_shadow_val != rw.value[0]) begin
|
||||
shadow_update_err = 1;
|
||||
return;
|
||||
end
|
||||
end
|
||||
end
|
||||
if (is_enable_reg()) begin
|
||||
get_dv_base_reg_fields(fields);
|
||||
field_access = fields[0].get_access();
|
||||
case (field_access)
|
||||
// rw.value is a dynamic array
|
||||
"W1C": if (rw.value[0][0] == 1'b1) set_locked_regs_access("RO");
|
||||
"W0C": if (rw.value[0][0] == 1'b0) set_locked_regs_access("RO");
|
||||
"RO": ; // if RO, it's updated by design, need to predict in scb
|
||||
default:`uvm_fatal(`gfn, $sformatf("enable register invalid access %s", field_access))
|
||||
endcase
|
||||
end
|
||||
endtask
|
||||
|
||||
// shadow register read will clear its phase tracker
|
||||
virtual task post_read(uvm_reg_item rw);
|
||||
if (is_shadowed) shadow_wr_staged = 0;
|
||||
endtask
|
||||
|
||||
virtual function void set_is_ext_reg(bit is_ext);
|
||||
is_ext_reg = is_ext;
|
||||
endfunction
|
||||
|
||||
virtual function bit get_is_ext_reg();
|
||||
return is_ext_reg;
|
||||
endfunction
|
||||
|
||||
// if it is a shadowed register, and is enabled to write it twice, this task will write the
|
||||
// register twice with the same value and address.
|
||||
virtual task write(output uvm_status_e status,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uvm_reg_map map = null,
|
||||
input uvm_sequence_base parent=null,
|
||||
input int prior = -1,
|
||||
input uvm_object extension = null,
|
||||
input string fname = "",
|
||||
input int lineno = 0);
|
||||
if (is_shadowed) atomic_shadow_wr.get(1);
|
||||
super.write(status, value, path, map, parent, prior, extension, fname, lineno);
|
||||
if (is_shadowed && en_shadow_wr) begin
|
||||
super.write(status, value, path, map, parent, prior, extension, fname, lineno);
|
||||
end
|
||||
if (is_shadowed) atomic_shadow_wr.put(1);
|
||||
endtask
|
||||
|
||||
// override do_predict function to support shadow_reg:
|
||||
// skip predict if it is shadow_reg's first write, or second write with an update_err
|
||||
virtual function void do_predict (uvm_reg_item rw,
|
||||
uvm_predict_e kind = UVM_PREDICT_DIRECT,
|
||||
uvm_reg_byte_en_t be = -1);
|
||||
if (is_shadowed && (shadow_wr_staged || shadow_update_err) && kind != UVM_PREDICT_READ) begin
|
||||
`uvm_info(`gfn,
|
||||
$sformatf("skip predict csr %s: due to shadow_reg_first_wr=%0b or update_err=%0b",
|
||||
get_name(), shadow_wr_staged, shadow_update_err), UVM_HIGH)
|
||||
return;
|
||||
end
|
||||
super.do_predict(rw, kind, be);
|
||||
endfunction
|
||||
|
||||
virtual function void reset(string kind = "HARD");
|
||||
super.reset(kind);
|
||||
if (is_shadowed) begin
|
||||
shadow_update_err = 0;
|
||||
shadow_wr_staged = 0;
|
||||
// in case reset is issued during shadowed writes
|
||||
void'(atomic_shadow_wr.try_get(1));
|
||||
void'(atomic_en_shadow_wr.try_get(1));
|
||||
atomic_shadow_wr.put(1);
|
||||
atomic_en_shadow_wr.put(1);
|
||||
end
|
||||
endfunction
|
||||
endclass
|
57
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv
vendored
Normal file
57
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_block.sv
vendored
Normal file
|
@ -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
|
||||
//
|
||||
// base register block class which will be used to generate the reg blocks
|
||||
class dv_base_reg_block extends uvm_reg_block;
|
||||
`uvm_object_utils(dv_base_reg_block)
|
||||
|
||||
csr_excl_item csr_excl;
|
||||
|
||||
function new (string name = "", int has_coverage = UVM_NO_COVERAGE);
|
||||
super.new(name, has_coverage);
|
||||
endfunction
|
||||
|
||||
// provide build function to supply base addr
|
||||
virtual function void build(uvm_reg_addr_t base_addr,
|
||||
csr_excl_item csr_excl = null);
|
||||
`uvm_fatal(`gfn, "this method is not supposed to be called directly!")
|
||||
endfunction
|
||||
|
||||
function void get_dv_base_reg_blocks(ref dv_base_reg_block blks[$]);
|
||||
uvm_reg_block uvm_blks[$];
|
||||
this.get_blocks(uvm_blks);
|
||||
foreach (uvm_blks[i]) `downcast(blks[i], uvm_blks[i])
|
||||
endfunction
|
||||
|
||||
function void get_dv_base_regs(ref dv_base_reg dv_regs[$]);
|
||||
uvm_reg ral_regs[$];
|
||||
this.get_registers(ral_regs);
|
||||
foreach (ral_regs[i]) `downcast(dv_regs[i], ral_regs[i])
|
||||
endfunction
|
||||
|
||||
function void get_enable_regs(ref dv_base_reg enable_regs[$]);
|
||||
dv_base_reg_block blks[$];
|
||||
this.get_dv_base_reg_blocks(blks);
|
||||
if (blks.size() == 0) begin
|
||||
dv_base_reg all_regs[$];
|
||||
this.get_dv_base_regs(all_regs);
|
||||
foreach (all_regs[i]) begin
|
||||
if (all_regs[i].is_enable_reg()) enable_regs.push_back(all_regs[i]);
|
||||
end
|
||||
return;
|
||||
end else begin
|
||||
foreach (blks[i]) blks[i].get_enable_regs(enable_regs);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// override RAL's reset function to support enable registers
|
||||
// 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[$];
|
||||
super.reset(kind);
|
||||
get_enable_regs(enable_regs);
|
||||
foreach (enable_regs[i]) enable_regs[i].set_locked_regs_access();
|
||||
endfunction
|
||||
|
||||
endclass
|
42
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_field.sv
vendored
Normal file
42
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_field.sv
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// base register reg class which will be used to generate the reg field
|
||||
class dv_base_reg_field extends uvm_reg_field;
|
||||
local string m_original_access;
|
||||
|
||||
`uvm_object_utils(dv_base_reg_field)
|
||||
`uvm_object_new
|
||||
|
||||
// when use UVM_PREDICT_WRITE and the CSR access is WO, this function will return the default
|
||||
// val of the register, rather than the written value
|
||||
virtual function uvm_reg_data_t XpredictX(uvm_reg_data_t cur_val,
|
||||
uvm_reg_data_t wr_val,
|
||||
uvm_reg_map map);
|
||||
|
||||
if (get_access(map) == "WO") return get_reset();
|
||||
else return super.XpredictX(cur_val, wr_val, map);
|
||||
endfunction
|
||||
|
||||
virtual function string get_original_access();
|
||||
return m_original_access;
|
||||
endfunction
|
||||
|
||||
virtual function void set_original_access(string access);
|
||||
if (m_original_access == "") begin
|
||||
m_original_access = access;
|
||||
end else begin
|
||||
`uvm_fatal(`gfn, "register original access can only be written once")
|
||||
end
|
||||
endfunction
|
||||
|
||||
virtual function void set_locked_fields_access(string access = "original_access");
|
||||
case (access)
|
||||
"RO": void'(this.set_access(access));
|
||||
"original_access": void'(this.set_access(m_original_access));
|
||||
default: `uvm_fatal(`gfn, $sformatf("attempt to set access to %s", access))
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
endclass
|
9
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_map.sv
vendored
Normal file
9
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_map.sv
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// base register reg class which will be used to generate the reg map
|
||||
class dv_base_reg_map extends uvm_reg_map;
|
||||
`uvm_object_utils(dv_base_reg_map)
|
||||
`uvm_object_new
|
||||
endclass
|
49
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv
vendored
Normal file
49
vendor/lowrisc_ip/dv/sv/dv_base_reg/dv_base_reg_pkg.sv
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package dv_base_reg_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import dv_utils_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
||||
// global paramters for number of csr tests (including memory test)
|
||||
parameter uint NUM_CSR_TESTS = 4;
|
||||
|
||||
// csr exclusion indications
|
||||
typedef enum bit [2:0] {
|
||||
CsrNoExcl = 3'b000, // no exclusions
|
||||
CsrExclInitCheck = 3'b001, // exclude csr from init val check
|
||||
CsrExclWriteCheck = 3'b010, // exclude csr from write-read check
|
||||
CsrExclCheck = 3'b011, // exclude csr from init or write-read check
|
||||
CsrExclWrite = 3'b100, // exclude csr from write
|
||||
CsrExclAll = 3'b111 // exclude csr from init or write or write-read check
|
||||
} csr_excl_type_e;
|
||||
|
||||
// csr test types
|
||||
typedef enum bit [NUM_CSR_TESTS-1:0] {
|
||||
CsrInvalidTest = 4'h0,
|
||||
// elementary test types
|
||||
CsrHwResetTest = 4'h1,
|
||||
CsrRwTest = 4'h2,
|
||||
CsrBitBashTest = 4'h4,
|
||||
CsrAliasingTest = 4'h8,
|
||||
// combinational test types (combinations of the above), used for exclusion tagging
|
||||
CsrNonInitTests = 4'he, // all but HwReset test
|
||||
CsrAllTests = 4'hf // all tests
|
||||
} csr_test_type_e;
|
||||
|
||||
// package sources
|
||||
// base ral
|
||||
`include "csr_excl_item.sv"
|
||||
`include "dv_base_reg_field.sv"
|
||||
`include "dv_base_reg.sv"
|
||||
`include "dv_base_mem.sv"
|
||||
`include "dv_base_reg_block.sv"
|
||||
`include "dv_base_reg_map.sv"
|
||||
|
||||
endpackage
|
56
vendor/lowrisc_ip/dv/sv/dv_lib/README.md
vendored
Normal file
56
vendor/lowrisc_ip/dv/sv/dv_lib/README.md
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: "DV Library Classes"
|
||||
---
|
||||
|
||||
# DV library classes
|
||||
|
||||
## Overview
|
||||
The DV library classes form the base layer / framework for constructing UVM
|
||||
testbenches. These classes provide features (settings, methods, hooks and other
|
||||
constructs used in verification) that are generic enough to be reused across
|
||||
all testbenches.
|
||||
|
||||
In this doc, we will capture some of the most salient / frequently used features
|
||||
in extended classes. These classes are being updated frequently. So, for a more
|
||||
detailed understanding, please read the class definitions directly.
|
||||
|
||||
The DV library classes fall into 3 categories - UVM RAL (register abstraction
|
||||
layer), UVM agent, and UVM environment extensions.
|
||||
|
||||
### UVM RAL extensions
|
||||
The RAL model generated using the [reggen]({{< relref "util/reggen/README.md" >}}) tool
|
||||
extend from these classes. These themselves extend from the corresponding RAL
|
||||
classes provided in UVM.
|
||||
|
||||
#### `dv_base_reg_field`
|
||||
Currently, this class does not provide any additional features. One of the
|
||||
features planned for future is setting exclusion tags at the field level for the
|
||||
CSR suite of tests that will be extracted automatically from the Hjson-based
|
||||
IP CSR specification.
|
||||
|
||||
#### `dv_base_reg`
|
||||
This class provides the following functions to support verification:
|
||||
* `gen_n_used_bits()`: This function returns the actual number of bits used in
|
||||
the CSR (sum of all available field widths).
|
||||
* `get_msb_pos()`: This function returns the MSB bit position of all available
|
||||
fields. CSR either ends at this bit (`BUS_DW` - 1) or has reserved / invalid
|
||||
bits beyond this bit.
|
||||
|
||||
#### `dv_base_reg_block`
|
||||
* ` build(uvm_reg_addr_t base_addr)`: This function is implemented as a pseudo
|
||||
pure virtual function (returns a fatal error if called directly). It is used
|
||||
for building the complete RAL model. For a polymorphic approach, the DV user
|
||||
can use this class handle to create the extended (IP specific) class instance
|
||||
and call this function to build the actual RAL model. This is exactly how it
|
||||
is done in [dv_base_env_cfg](#dv_base_env_cfg).
|
||||
|
||||
#### `dv_base_reg_map`
|
||||
Currently, this class does not provide any additional features. Having this
|
||||
extension provides an opportunity to add common features in future.
|
||||
|
||||
### UVM Agent extensions
|
||||
TODO
|
||||
|
||||
### UVM Environment extensions
|
||||
TODO
|
||||
|
60
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_agent.sv
vendored
Normal file
60
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_agent.sv
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_agent #(type CFG_T = dv_base_agent_cfg,
|
||||
type DRIVER_T = dv_base_driver,
|
||||
type HOST_DRIVER_T = DRIVER_T,
|
||||
type DEVICE_DRIVER_T = DRIVER_T,
|
||||
type SEQUENCER_T = dv_base_sequencer,
|
||||
type MONITOR_T = dv_base_monitor,
|
||||
type COV_T = dv_base_agent_cov) extends uvm_agent;
|
||||
|
||||
`uvm_component_param_utils(dv_base_agent #(CFG_T, DRIVER_T, HOST_DRIVER_T, DEVICE_DRIVER_T,
|
||||
SEQUENCER_T, MONITOR_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
COV_T cov;
|
||||
DRIVER_T driver;
|
||||
SEQUENCER_T sequencer;
|
||||
MONITOR_T monitor;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
// get CFG_T object from uvm_config_db
|
||||
if (!uvm_config_db#(CFG_T)::get(this, "", "cfg", cfg)) begin
|
||||
`uvm_fatal(`gfn, $sformatf("failed to get %s from uvm_config_db", cfg.get_type_name()))
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("\n%0s", cfg.sprint()), UVM_HIGH)
|
||||
|
||||
// create components
|
||||
if (cfg.en_cov) begin
|
||||
cov = COV_T ::type_id::create("cov", this);
|
||||
cov.cfg = cfg;
|
||||
end
|
||||
|
||||
monitor = MONITOR_T::type_id::create("monitor", this);
|
||||
monitor.cfg = cfg;
|
||||
monitor.cov = cov;
|
||||
|
||||
if (cfg.is_active) begin
|
||||
sequencer = SEQUENCER_T::type_id::create("sequencer", this);
|
||||
sequencer.cfg = cfg;
|
||||
|
||||
if (cfg.if_mode == Host) driver = HOST_DRIVER_T::type_id::create("driver", this);
|
||||
else driver = DEVICE_DRIVER_T::type_id::create("driver", this);
|
||||
driver.cfg = cfg;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void connect_phase(uvm_phase phase);
|
||||
super.connect_phase(phase);
|
||||
if (cfg.is_active) begin
|
||||
driver.seq_item_port.connect(sequencer.seq_item_export);
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
23
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_agent_cfg.sv
vendored
Normal file
23
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_agent_cfg.sv
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_agent_cfg extends uvm_object;
|
||||
|
||||
// agent cfg knobs
|
||||
bit is_active = 1'b1; // active driver or passive monitor
|
||||
bit en_cov = 1'b1; // enable coverage
|
||||
if_mode_e if_mode; // interface mode - Host or Device
|
||||
|
||||
// use for phase_ready_to_end to add additional delay after ok_to_end is set
|
||||
int ok_to_end_delay_ns = 1000;
|
||||
|
||||
`uvm_object_utils_begin(dv_base_agent_cfg)
|
||||
`uvm_field_int (is_active, UVM_DEFAULT)
|
||||
`uvm_field_int (en_cov, UVM_DEFAULT)
|
||||
`uvm_field_enum(if_mode_e, if_mode, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
12
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_agent_cov.sv
vendored
Normal file
12
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_agent_cov.sv
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_agent_cov #(type CFG_T = dv_base_agent_cfg) extends uvm_component;
|
||||
`uvm_component_param_utils(dv_base_agent_cov #(CFG_T))
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
37
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_driver.sv
vendored
Normal file
37
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_driver.sv
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_driver #(type ITEM_T = uvm_sequence_item,
|
||||
type CFG_T = dv_base_agent_cfg,
|
||||
type RSP_ITEM_T = ITEM_T)
|
||||
extends uvm_driver #(.REQ(ITEM_T), .RSP(RSP_ITEM_T));
|
||||
|
||||
`uvm_component_param_utils(dv_base_driver #(.ITEM_T (ITEM_T),
|
||||
.CFG_T (CFG_T),
|
||||
.RSP_ITEM_T (RSP_ITEM_T)))
|
||||
|
||||
bit under_reset;
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
fork
|
||||
reset_signals();
|
||||
get_and_drive();
|
||||
join
|
||||
endtask
|
||||
|
||||
// reset signals
|
||||
virtual task reset_signals();
|
||||
`uvm_fatal(`gfn, "this is implemented as pure virtual task - please extend")
|
||||
endtask
|
||||
|
||||
// drive trans received from sequencer
|
||||
virtual task get_and_drive();
|
||||
`uvm_fatal(`gfn, "this is implemented as pure virtual task - please extend")
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
60
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv
vendored
Normal file
60
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env.sv
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_env #(type CFG_T = dv_base_env_cfg,
|
||||
type VIRTUAL_SEQUENCER_T = dv_base_virtual_sequencer,
|
||||
type SCOREBOARD_T = dv_base_scoreboard,
|
||||
type COV_T = dv_base_env_cov) extends uvm_env;
|
||||
`uvm_component_param_utils(dv_base_env #(CFG_T, VIRTUAL_SEQUENCER_T, SCOREBOARD_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
VIRTUAL_SEQUENCER_T virtual_sequencer;
|
||||
SCOREBOARD_T scoreboard;
|
||||
COV_T cov;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
// get dv_base_env_cfg object from uvm_config_db
|
||||
if (!uvm_config_db#(CFG_T)::get(this, "", "cfg", cfg)) begin
|
||||
`uvm_fatal(`gfn, $sformatf("failed to get %s from uvm_config_db", cfg.get_type_name()))
|
||||
end
|
||||
|
||||
// get vifs
|
||||
if (!uvm_config_db#(virtual clk_rst_if)::get(this, "", "clk_rst_vif", cfg.clk_rst_vif)) begin
|
||||
`uvm_fatal(get_full_name(), "failed to get clk_rst_if from uvm_config_db")
|
||||
end
|
||||
cfg.clk_rst_vif.set_freq_mhz(cfg.clk_freq_mhz);
|
||||
|
||||
// create components
|
||||
if (cfg.en_cov) begin
|
||||
cov = COV_T::type_id::create("cov", this);
|
||||
cov.cfg = cfg;
|
||||
end
|
||||
|
||||
if (cfg.is_active) begin
|
||||
virtual_sequencer = VIRTUAL_SEQUENCER_T::type_id::create("virtual_sequencer", this);
|
||||
virtual_sequencer.cfg = cfg;
|
||||
virtual_sequencer.cov = cov;
|
||||
end
|
||||
|
||||
// scb also monitors the reset and call cfg.reset_asserted/reset_deasserted for reset
|
||||
scoreboard = SCOREBOARD_T::type_id::create("scoreboard", this);
|
||||
scoreboard.cfg = 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
|
96
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv
vendored
Normal file
96
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cfg.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
||||
|
||||
bit is_active = 1;
|
||||
bit en_scb = 1; // can be changed at run-time
|
||||
bit en_scb_tl_err_chk = 1;
|
||||
bit en_scb_mem_chk = 1;
|
||||
bit en_cov = 1;
|
||||
bit has_ral = 1;
|
||||
bit under_reset = 0;
|
||||
|
||||
// bit to configure all uvcs with zero delays to create high bw test
|
||||
rand bit zero_delays;
|
||||
|
||||
// set zero_delays 40% of the time
|
||||
constraint zero_delays_c {
|
||||
zero_delays dist {1'b0 := 6, 1'b1 := 4};
|
||||
}
|
||||
|
||||
// reg model & q of valid csr addresses
|
||||
RAL_T ral;
|
||||
dv_base_reg_block ral_models[$];
|
||||
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;
|
||||
|
||||
`uvm_object_param_utils_begin(dv_base_env_cfg #(RAL_T))
|
||||
`uvm_field_int (is_active, UVM_DEFAULT)
|
||||
`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
|
||||
ral = RAL_T::type_id::create("ral");
|
||||
ral.build(this.csr_base_addr, null);
|
||||
apply_ral_fixes();
|
||||
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();
|
||||
// fix access policies & reset values
|
||||
endfunction
|
||||
|
||||
virtual function void reset_asserted();
|
||||
this.under_reset = 1;
|
||||
csr_utils_pkg::reset_asserted();
|
||||
endfunction
|
||||
|
||||
virtual function void reset_deasserted();
|
||||
this.under_reset = 0;
|
||||
csr_utils_pkg::reset_deasserted();
|
||||
endfunction
|
||||
endclass
|
42
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cov.sv
vendored
Normal file
42
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_env_cov.sv
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// 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;
|
||||
|
||||
// Covergroup: bit_toggle_cg
|
||||
// Generic covergroup definition
|
||||
covergroup bit_toggle_cg(string name, bit toggle_cov_en = 1) with function sample(bit value);
|
||||
option.per_instance = 1;
|
||||
option.name = name;
|
||||
cp_value: coverpoint value;
|
||||
cp_transitions: coverpoint value {
|
||||
option.weight = toggle_cov_en;
|
||||
bins rising = (0 => 1);
|
||||
bins falling = (1 => 0);
|
||||
}
|
||||
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);
|
||||
endfunction : new
|
||||
|
||||
// Function: sample
|
||||
function void sample(bit value);
|
||||
bit_toggle_cg.sample(value);
|
||||
endfunction : sample
|
||||
|
||||
endclass : dv_base_generic_cov_obj
|
||||
|
||||
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))
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
113
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_monitor.sv
vendored
Normal file
113
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_monitor.sv
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_monitor #(type ITEM_T = uvm_sequence_item,
|
||||
type CFG_T = dv_base_agent_cfg,
|
||||
type COV_T = dv_base_agent_cov) extends uvm_monitor;
|
||||
`uvm_component_param_utils(dv_base_monitor #(ITEM_T, CFG_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
COV_T cov;
|
||||
|
||||
// Indicates activity on the interface, driven only within the `monitor_ready_to_end()` task.
|
||||
protected bit ok_to_end = 1;
|
||||
|
||||
// Used to ensure we run the watchdog exactly once at the end of the run phase. Cleared at start
|
||||
// of run phase. Set once watchdog_ok_to_end has been started at the end of the run_phase.
|
||||
protected bit phase_ready_to_end_invoked = 0;
|
||||
|
||||
// Analysis port for the collected transfer.
|
||||
uvm_analysis_port #(ITEM_T) analysis_port;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
analysis_port = new("analysis_port", this);
|
||||
endfunction
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
fork
|
||||
collect_trans(phase);
|
||||
join
|
||||
endtask
|
||||
|
||||
// collect transactions forever
|
||||
virtual protected task collect_trans(uvm_phase phase);
|
||||
`uvm_fatal(`gfn, "this method is not supposed to be called directly!")
|
||||
endtask
|
||||
|
||||
// UVM callback which is invoked during phase sequencing.
|
||||
virtual function void phase_ready_to_end(uvm_phase phase);
|
||||
if (!phase.is(uvm_run_phase::get())) return;
|
||||
if (phase_ready_to_end_invoked) return;
|
||||
|
||||
phase_ready_to_end_invoked = 1;
|
||||
fork
|
||||
monitor_ready_to_end();
|
||||
watchdog_ok_to_end(phase);
|
||||
join_none
|
||||
endfunction
|
||||
|
||||
// Ensures that ok_to_end when asserted, stays asserted for 1 ok_to_end_delay_ns period.
|
||||
//
|
||||
// If ok_to_end de-asserts before the watchdog expires, it waits for it to assert again
|
||||
// and restarts the timer. This ensures that there is sufficient drain time to allow the
|
||||
// simulation to end gracefully. It raises and drops the objection at the appropriate times.
|
||||
virtual task watchdog_ok_to_end(uvm_phase run_phase);
|
||||
bit objection_raised;
|
||||
bit watchdog_done;
|
||||
uint watchdog_restart_count = 1;
|
||||
|
||||
forever begin
|
||||
if (!objection_raised) begin
|
||||
`uvm_info(`gfn, "watchdog_ok_to_end: raising objection", UVM_MEDIUM)
|
||||
run_phase.raise_objection(this, {`gfn, " objection raised"});
|
||||
objection_raised = 1'b1;
|
||||
end
|
||||
|
||||
// Start the timer only when ok_to_end is asserted.
|
||||
wait(ok_to_end);
|
||||
`uvm_info(`gfn, $sformatf("watchdog_ok_to_end: starting the timer (count: %0d)",
|
||||
watchdog_restart_count++), UVM_MEDIUM)
|
||||
fork
|
||||
begin: isolation_fork
|
||||
fork
|
||||
begin
|
||||
watchdog_done = 1'b0;
|
||||
#(cfg.ok_to_end_delay_ns * 1ns);
|
||||
watchdog_done = 1'b1;
|
||||
end
|
||||
@(ok_to_end);
|
||||
join_any
|
||||
disable fork;
|
||||
end: isolation_fork
|
||||
join
|
||||
|
||||
// The #0 delay ensures that we sample the stabilized value of ok_to_end in the condition
|
||||
// below in case it toggles more than once in the same simulation time-step.
|
||||
#0;
|
||||
|
||||
// If ok_to_end stayed high throughout the watchdog timer expiry, then drop the objection.
|
||||
if (ok_to_end && watchdog_done) begin
|
||||
`uvm_info(`gfn, "watchdog_ok_to_end: dropping objection", UVM_MEDIUM)
|
||||
run_phase.drop_objection(this, {`gfn, " objection dropped"});
|
||||
objection_raised = 1'b0;
|
||||
|
||||
// Wait for ok_to_end to de-assert again in future.
|
||||
wait(!ok_to_end);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
// Asserts/de-asserts ok_to_end to indicate bus activity.
|
||||
//
|
||||
// This task is invoked in a forked thread within `phase_ready_to_end()`, which is callback
|
||||
// invoked by UVM at the end of the phase. The forked thread does not join. Hence, the extended
|
||||
// monitor needs to override this function and assert ok_to_end based on the activity on the bus
|
||||
// (assert it when idle, de-assert when its not) in a forever loop.
|
||||
virtual task monitor_ready_to_end();
|
||||
endtask
|
||||
|
||||
endclass
|
68
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv
vendored
Normal file
68
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_scoreboard.sv
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
|
||||
type CFG_T = dv_base_env_cfg,
|
||||
type COV_T = dv_base_env_cov) extends uvm_component;
|
||||
`uvm_component_param_utils(dv_base_scoreboard #(RAL_T, CFG_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
RAL_T ral;
|
||||
COV_T cov;
|
||||
|
||||
bit obj_raised = 1'b0;
|
||||
bit under_pre_abort = 1'b0;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
ral = cfg.ral;
|
||||
endfunction
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
super.run_phase(phase);
|
||||
fork
|
||||
monitor_reset();
|
||||
join_none
|
||||
endtask
|
||||
|
||||
virtual task monitor_reset();
|
||||
forever begin
|
||||
if (!cfg.clk_rst_vif.rst_n) begin
|
||||
`uvm_info(`gfn, "reset occurred", UVM_HIGH)
|
||||
cfg.reset_asserted();
|
||||
@(posedge cfg.clk_rst_vif.rst_n);
|
||||
reset();
|
||||
cfg.reset_deasserted();
|
||||
`uvm_info(`gfn, "out of reset", UVM_HIGH)
|
||||
end
|
||||
else begin
|
||||
// wait for a change to rst_n
|
||||
@(cfg.clk_rst_vif.rst_n);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
virtual function void reset(string kind = "HARD");
|
||||
// reset the ral model
|
||||
if (cfg.has_ral) ral.reset(kind);
|
||||
endfunction
|
||||
|
||||
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
|
||||
under_pre_abort = 1;
|
||||
check_phase(m_current_phase);
|
||||
under_pre_abort = 0;
|
||||
end
|
||||
endfunction : pre_abort
|
||||
|
||||
endclass
|
||||
|
25
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_seq.sv
vendored
Normal file
25
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_seq.sv
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_seq #(type REQ = uvm_sequence_item,
|
||||
type RSP = REQ,
|
||||
type CFG_T = dv_base_agent_cfg,
|
||||
type SEQUENCER_T = dv_base_sequencer) extends uvm_sequence#(REQ, RSP);
|
||||
`uvm_object_param_utils(dv_base_seq #(REQ, RSP, CFG_T, SEQUENCER_T))
|
||||
`uvm_declare_p_sequencer(SEQUENCER_T)
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
task pre_start();
|
||||
super.pre_start();
|
||||
cfg = p_sequencer.cfg;
|
||||
endtask
|
||||
|
||||
task body();
|
||||
`uvm_fatal(`gtn, "Need to override this when you extend from this class!")
|
||||
endtask : body
|
||||
|
||||
endclass
|
18
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_sequencer.sv
vendored
Normal file
18
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_sequencer.sv
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_sequencer #(type ITEM_T = uvm_sequence_item,
|
||||
type CFG_T = dv_base_agent_cfg,
|
||||
type RSP_ITEM_T = ITEM_T)
|
||||
extends uvm_sequencer #(.REQ(ITEM_T), .RSP(RSP_ITEM_T));
|
||||
|
||||
`uvm_component_param_utils(dv_base_sequencer #(.ITEM_T (ITEM_T),
|
||||
.CFG_T (CFG_T),
|
||||
.RSP_ITEM_T (RSP_ITEM_T)))
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
77
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_test.sv
vendored
Normal file
77
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_test.sv
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_test #(type CFG_T = dv_base_env_cfg,
|
||||
type ENV_T = dv_base_env) extends uvm_test;
|
||||
`uvm_component_param_utils(dv_base_test #(CFG_T, ENV_T))
|
||||
|
||||
ENV_T env;
|
||||
CFG_T cfg;
|
||||
bit run_test_seq = 1'b1;
|
||||
string test_seq_s;
|
||||
|
||||
uint max_quit_count = 1;
|
||||
uint64 test_timeout_ns = 200_000_000; // 200ms
|
||||
uint drain_time_ns = 2_000; // 2us
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
dv_report_server m_dv_report_server = new();
|
||||
uvm_report_server::set_server(m_dv_report_server);
|
||||
|
||||
super.build_phase(phase);
|
||||
|
||||
env = ENV_T::type_id::create("env", this);
|
||||
cfg = CFG_T::type_id::create("cfg", this);
|
||||
// don't add args for initialize. Use default value instead
|
||||
cfg.initialize();
|
||||
`DV_CHECK_RANDOMIZE_FATAL(cfg)
|
||||
uvm_config_db#(CFG_T)::set(this, "env", "cfg", cfg);
|
||||
|
||||
// knob to en/dis scb (enabled by default)
|
||||
void'($value$plusargs("en_scb=%0b", cfg.en_scb));
|
||||
void'($value$plusargs("en_scb_tl_err_chk=%0b", cfg.en_scb_tl_err_chk));
|
||||
void'($value$plusargs("en_scb_mem_chk=%0b", cfg.en_scb_mem_chk));
|
||||
// knob to cfg all agents with zero delays
|
||||
void'($value$plusargs("zero_delays=%0b", cfg.zero_delays));
|
||||
endfunction : build_phase
|
||||
|
||||
virtual function void end_of_elaboration_phase(uvm_phase phase);
|
||||
super.end_of_elaboration_phase(phase);
|
||||
void'($value$plusargs("max_quit_count=%0d", max_quit_count));
|
||||
set_max_quit_count(max_quit_count);
|
||||
void'($value$plusargs("test_timeout_ns=%0d", test_timeout_ns));
|
||||
uvm_top.set_timeout((test_timeout_ns * 1ns));
|
||||
endfunction : end_of_elaboration_phase
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
void'($value$plusargs("drain_time_ns=%0d", drain_time_ns));
|
||||
phase.phase_done.set_drain_time(this, (drain_time_ns * 1ns));
|
||||
void'($value$plusargs("UVM_TEST_SEQ=%0s", test_seq_s));
|
||||
if (run_test_seq) begin
|
||||
run_seq(test_seq_s, phase);
|
||||
end
|
||||
// TODO: add hook for end of test checking
|
||||
endtask : run_phase
|
||||
|
||||
virtual task run_seq(string test_seq_s, uvm_phase phase);
|
||||
uvm_sequence test_seq = create_seq_by_name(test_seq_s);
|
||||
|
||||
// provide virtual_sequencer earlier, so we may use the p_sequencer in constraint
|
||||
test_seq.set_sequencer(env.virtual_sequencer);
|
||||
`DV_CHECK_RANDOMIZE_FATAL(test_seq)
|
||||
|
||||
`uvm_info(`gfn, {"Starting test sequence ", test_seq_s}, UVM_MEDIUM)
|
||||
phase.raise_objection(this, $sformatf("%s objection raised", `gn));
|
||||
test_seq.start(env.virtual_sequencer);
|
||||
phase.drop_objection(this, $sformatf("%s objection dropped", `gn));
|
||||
`uvm_info(`gfn, {"Finished test sequence ", test_seq_s}, UVM_MEDIUM)
|
||||
endtask
|
||||
|
||||
// TODO: add default report_phase implementation
|
||||
|
||||
endclass : dv_base_test
|
||||
|
||||
|
14
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_virtual_sequencer.sv
vendored
Normal file
14
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_virtual_sequencer.sv
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_virtual_sequencer #(type CFG_T = dv_base_env_cfg,
|
||||
type COV_T = dv_base_env_cov) extends uvm_sequencer;
|
||||
`uvm_component_param_utils(dv_base_virtual_sequencer #(CFG_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
COV_T cov;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
215
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
Normal file
215
vendor/lowrisc_ip/dv/sv/dv_lib/dv_base_vseq.sv
vendored
Normal file
|
@ -0,0 +1,215 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
||||
type CFG_T = dv_base_env_cfg,
|
||||
type COV_T = dv_base_env_cov,
|
||||
type VIRTUAL_SEQUENCER_T = dv_base_virtual_sequencer) extends uvm_sequence;
|
||||
`uvm_object_param_utils(dv_base_vseq #(RAL_T, CFG_T, COV_T, VIRTUAL_SEQUENCER_T))
|
||||
`uvm_declare_p_sequencer(VIRTUAL_SEQUENCER_T)
|
||||
|
||||
// number of iterations to run the test seq - please override constraint in extended vseq
|
||||
// randomization for this is disabled in pre_start since we don't want to re-randomize it again
|
||||
rand uint num_trans;
|
||||
|
||||
constraint num_trans_c {
|
||||
num_trans inside {[1:20]};
|
||||
}
|
||||
|
||||
// handles for ease of op
|
||||
CFG_T cfg;
|
||||
RAL_T ral;
|
||||
COV_T cov;
|
||||
|
||||
// knobs to enable pre_start routines
|
||||
bit do_dut_init = 1'b1;
|
||||
bit do_apply_reset = 1'b1;
|
||||
bit do_wait_for_reset = 1'b1;
|
||||
|
||||
// knobs to enable post_start routines
|
||||
bit do_dut_shutdown = 1'b1;
|
||||
|
||||
// various knobs to enable certain routines
|
||||
// this knob allows user to disable assertions in csr_hw_reset before random write sequence,
|
||||
// the assertions will turn back on after the hw reset deasserted
|
||||
bit enable_asserts_in_hw_reset_rand_wr = 1'b1;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual function void set_handles();
|
||||
`DV_CHECK_NE_FATAL(p_sequencer, null, "Did you forget to call `set_sequencer()`?")
|
||||
cfg = p_sequencer.cfg;
|
||||
cov = p_sequencer.cov;
|
||||
ral = cfg.ral;
|
||||
endfunction
|
||||
|
||||
// This function is invoked in pre_randomize(). Override it in the extended classes to configure
|
||||
// / control the randomization of this sequence.
|
||||
virtual function void configure_vseq();
|
||||
endfunction
|
||||
|
||||
function void pre_randomize();
|
||||
// Set the handles in pre_randomize(), so that the knobs in cfg are available during sequence
|
||||
// randomization. This forces `p_sequencer` handle to be set before the randomization - users
|
||||
// are required to call `set_sequencer()` right after creating the sequence and before
|
||||
// randomizing it.
|
||||
if (cfg == null) set_handles();
|
||||
configure_vseq();
|
||||
endfunction
|
||||
|
||||
task pre_start();
|
||||
super.pre_start();
|
||||
if (cfg == null) set_handles();
|
||||
if (do_dut_init) dut_init("HARD");
|
||||
num_trans.rand_mode(0);
|
||||
endtask
|
||||
|
||||
task body();
|
||||
`uvm_fatal(`gtn, "Need to override this when you extend from this class!")
|
||||
endtask : body
|
||||
|
||||
task post_start();
|
||||
super.post_start();
|
||||
if (do_dut_shutdown) dut_shutdown();
|
||||
endtask
|
||||
|
||||
/*
|
||||
* startup, reset and shutdown related tasks
|
||||
*/
|
||||
virtual task dut_init(string reset_kind = "HARD");
|
||||
if (do_apply_reset) apply_reset(reset_kind);
|
||||
else if (do_wait_for_reset) wait_for_reset(reset_kind);
|
||||
// delay after reset for tl agent check seq_item_port empty
|
||||
#1ps;
|
||||
endtask
|
||||
|
||||
virtual task apply_reset(string kind = "HARD");
|
||||
if (kind == "HARD") begin
|
||||
csr_utils_pkg::reset_asserted();
|
||||
cfg.clk_rst_vif.apply_reset();
|
||||
csr_utils_pkg::reset_deasserted();
|
||||
end
|
||||
if (cfg.has_ral) begin
|
||||
foreach (cfg.ral_models[i]) cfg.ral_models[i].reset(kind);
|
||||
end
|
||||
endtask
|
||||
|
||||
virtual task wait_for_reset(string reset_kind = "HARD",
|
||||
bit wait_for_assert = 1,
|
||||
bit wait_for_deassert = 1);
|
||||
if (wait_for_assert) begin
|
||||
`uvm_info(`gfn, "waiting for rst_n assertion...", UVM_MEDIUM)
|
||||
@(negedge cfg.clk_rst_vif.rst_n);
|
||||
end
|
||||
if (wait_for_deassert) begin
|
||||
`uvm_info(`gfn, "waiting for rst_n de-assertion...", UVM_MEDIUM)
|
||||
@(posedge cfg.clk_rst_vif.rst_n);
|
||||
end
|
||||
`uvm_info(`gfn, "wait_for_reset done", UVM_HIGH)
|
||||
endtask
|
||||
|
||||
// dut shutdown - this is called in post_start if do_dut_shutdown bit is set
|
||||
virtual task dut_shutdown();
|
||||
csr_utils_pkg::wait_no_outstanding_access();
|
||||
endtask
|
||||
|
||||
// function to add csr exclusions of the given type using the csr_excl_item item
|
||||
// arg csr_test_type: this the the type of csr test run - we may want additional exclusions
|
||||
// depending on what test seq we are running
|
||||
// arg csr_excl: this is the csr exclusion object that maintains the list of exclusions
|
||||
// the same object handle is to be passed to csr sequences in csr_seq_lib so that they can query
|
||||
// those exclusions
|
||||
virtual function void add_csr_exclusions(string csr_test_type,
|
||||
csr_excl_item csr_excl,
|
||||
string scope = "ral");
|
||||
`uvm_info(`gfn, "no exclusion item added from this function", UVM_DEBUG)
|
||||
endfunction
|
||||
|
||||
// TODO: temp support, can delete this once all IPs update their exclusion in hjson
|
||||
virtual function csr_excl_item add_and_return_csr_excl(string csr_test_type);
|
||||
add_csr_exclusions(csr_test_type, ral.csr_excl);
|
||||
ral.csr_excl.print_exclusions();
|
||||
return ral.csr_excl;
|
||||
endfunction
|
||||
|
||||
// wrapper task around run_csr_vseq - the purpose is to be able to call this directly for actual
|
||||
// csr tests (as opposed to higher level stress test that could also run csr seq as a fork by
|
||||
// calling run_csr_vseq(..) task)
|
||||
virtual task run_csr_vseq_wrapper(int num_times = 1);
|
||||
string csr_test_type;
|
||||
csr_excl_item csr_excl;
|
||||
|
||||
// env needs to have a ral instance
|
||||
`DV_CHECK_EQ_FATAL(cfg.has_ral, 1'b1)
|
||||
|
||||
// get csr_test_type from plusarg
|
||||
void'($value$plusargs("csr_%0s", csr_test_type));
|
||||
|
||||
// create csr exclusions before running the csr seq
|
||||
csr_excl = add_and_return_csr_excl(csr_test_type);
|
||||
|
||||
// run the csr seq
|
||||
for (int i = 1; i <= num_times; i++) begin
|
||||
`uvm_info(`gfn, $sformatf("running csr %0s vseq iteration %0d/%0d",
|
||||
csr_test_type, i, num_times), UVM_LOW)
|
||||
run_csr_vseq(.csr_test_type(csr_test_type), .csr_excl(csr_excl));
|
||||
end
|
||||
endtask
|
||||
|
||||
// capture the entire csr seq as a task that can be overridden if desired
|
||||
// arg csr_test_type: what csr test to run {hw_reset, rw, bit_bash, aliasing}
|
||||
// arg csr_excl: csr exclusion object - needs to be created and exclusions set before call
|
||||
// arg num_test_csrs:instead of testing the entire ral model or passing test chunk info via
|
||||
// plusarg, provide ability to set a random number of csrs to test from higher level sequence
|
||||
virtual task run_csr_vseq(string csr_test_type = "",
|
||||
csr_excl_item csr_excl = null,
|
||||
int num_test_csrs = 0,
|
||||
bit do_rand_wr_and_reset = 1);
|
||||
csr_base_seq m_csr_seq;
|
||||
|
||||
// env needs to have a ral instance
|
||||
`DV_CHECK_EQ_FATAL(cfg.has_ral, 1'b1)
|
||||
|
||||
// check which csr test type
|
||||
case (csr_test_type)
|
||||
"hw_reset": csr_base_seq::type_id::set_type_override(csr_hw_reset_seq::get_type());
|
||||
"rw" : csr_base_seq::type_id::set_type_override(csr_rw_seq::get_type());
|
||||
"bit_bash": csr_base_seq::type_id::set_type_override(csr_bit_bash_seq::get_type());
|
||||
"aliasing": csr_base_seq::type_id::set_type_override(csr_aliasing_seq::get_type());
|
||||
"mem_walk": csr_base_seq::type_id::set_type_override(csr_mem_walk_seq::get_type());
|
||||
default : `uvm_fatal(`gfn, $sformatf("specified opt is invalid: +csr_%0s", csr_test_type))
|
||||
endcase
|
||||
|
||||
// if hw_reset test, then write all CSRs first and reset the whole dut
|
||||
if (csr_test_type == "hw_reset" && do_rand_wr_and_reset) begin
|
||||
string reset_type = "HARD";
|
||||
csr_write_seq m_csr_write_seq;
|
||||
|
||||
// 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.start(null);
|
||||
|
||||
// run dut_shutdown before asserting reset
|
||||
dut_shutdown();
|
||||
// issue reset
|
||||
void'($value$plusargs("do_reset=%0s", reset_type));
|
||||
dut_init(reset_type);
|
||||
if (!enable_asserts_in_hw_reset_rand_wr) $asserton;
|
||||
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.start(null);
|
||||
endtask
|
||||
|
||||
endclass
|
38
vendor/lowrisc_ip/dv/sv/dv_lib/dv_lib.core
vendored
Normal file
38
vendor/lowrisc_ip/dv/sv/dv_lib/dv_lib.core
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
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_lib"
|
||||
description: "DV base class UVM library"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_utils
|
||||
- lowrisc:dv:csr_utils
|
||||
- lowrisc:dv:dv_base_reg
|
||||
- lowrisc:ibex:bus_params_pkg
|
||||
files:
|
||||
- dv_lib_pkg.sv
|
||||
|
||||
- dv_base_agent_cfg.sv: {is_include_file: true}
|
||||
- dv_base_agent_cov.sv: {is_include_file: true}
|
||||
- dv_base_monitor.sv: {is_include_file: true}
|
||||
- dv_base_sequencer.sv: {is_include_file: true}
|
||||
- dv_base_driver.sv: {is_include_file: true}
|
||||
- dv_base_agent.sv: {is_include_file: true}
|
||||
- dv_base_seq.sv: {is_include_file: true}
|
||||
|
||||
- dv_base_env_cfg.sv: {is_include_file: true}
|
||||
- dv_base_env_cov.sv: {is_include_file: true}
|
||||
- dv_base_virtual_sequencer.sv: {is_include_file: true}
|
||||
- dv_base_scoreboard.sv: {is_include_file: true}
|
||||
- dv_base_env.sv: {is_include_file: true}
|
||||
- dv_base_vseq.sv: {is_include_file: true}
|
||||
- dv_base_test.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
45
vendor/lowrisc_ip/dv/sv/dv_lib/dv_lib_pkg.sv
vendored
Normal file
45
vendor/lowrisc_ip/dv/sv/dv_lib/dv_lib_pkg.sv
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package dv_lib_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import bus_params_pkg::*;
|
||||
import dv_utils_pkg::*;
|
||||
import csr_utils_pkg::*;
|
||||
import dv_base_reg_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
||||
// package variables
|
||||
string msg_id = "dv_lib_pkg";
|
||||
|
||||
// package sources
|
||||
// base agent
|
||||
`include "dv_base_agent_cfg.sv"
|
||||
`include "dv_base_agent_cov.sv"
|
||||
`include "dv_base_monitor.sv"
|
||||
`include "dv_base_sequencer.sv"
|
||||
`include "dv_base_driver.sv"
|
||||
`include "dv_base_agent.sv"
|
||||
|
||||
// base seq
|
||||
`include "dv_base_seq.sv"
|
||||
|
||||
// base env
|
||||
`include "dv_base_env_cfg.sv"
|
||||
`include "dv_base_env_cov.sv"
|
||||
`include "dv_base_virtual_sequencer.sv"
|
||||
`include "dv_base_scoreboard.sv"
|
||||
`include "dv_base_env.sv"
|
||||
|
||||
// base test vseq
|
||||
`include "dv_base_vseq.sv"
|
||||
|
||||
// base test
|
||||
`include "dv_base_test.sv"
|
||||
|
||||
endpackage
|
0
vendor/lowrisc_ip/dv/sv/dv_utils/README.md
vendored
Normal file
0
vendor/lowrisc_ip/dv/sv/dv_utils/README.md
vendored
Normal file
322
vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh
vendored
Normal file
322
vendor/lowrisc_ip/dv/sv/dv_utils/dv_macros.svh
vendored
Normal file
|
@ -0,0 +1,322 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// UVM speficic macros
|
||||
`ifndef gfn
|
||||
`define gfn get_full_name()
|
||||
`endif
|
||||
|
||||
`ifndef gtn
|
||||
`define gtn get_type_name()
|
||||
`endif
|
||||
|
||||
`ifndef gn
|
||||
`define gn get_name()
|
||||
`endif
|
||||
|
||||
`ifndef gmv
|
||||
`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
|
||||
`define downcast(EXT_, BASE_, MSG_="", SEV_=fatal, ID_=`gfn) \
|
||||
begin \
|
||||
if (!$cast(EXT_, BASE_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf({"Cast failed: base class variable %0s ", \
|
||||
"does not hold extended class %0s handle %s"}, \
|
||||
`"BASE_`", `"EXT_`", MSG_)) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// Note, UVM provides a macro `uvm_new_func -- which only applies to uvm_components
|
||||
`ifndef uvm_object_new
|
||||
`define uvm_object_new \
|
||||
function new (string name=""); \
|
||||
super.new(name); \
|
||||
endfunction : new
|
||||
`endif
|
||||
|
||||
`ifndef uvm_create_obj
|
||||
`define uvm_create_obj(_type_name_, _inst_name_) \
|
||||
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`");
|
||||
`endif
|
||||
|
||||
`ifndef uvm_component_new
|
||||
`define uvm_component_new \
|
||||
function new (string name="", uvm_component parent=null); \
|
||||
super.new(name, parent); \
|
||||
endfunction : new
|
||||
`endif
|
||||
|
||||
`ifndef uvm_create_comp
|
||||
`define uvm_create_comp(_type_name_, _inst_name_) \
|
||||
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`", this);
|
||||
`endif
|
||||
|
||||
// Common check macros used by DV_CHECK error and fatal macros.
|
||||
// Note: Should not be called by user code
|
||||
`ifndef DV_CHECK
|
||||
`define DV_CHECK(T_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
if (!(T_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed (%s) %s ", `"T_`", MSG_)) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_EQ
|
||||
`define DV_CHECK_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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_NE
|
||||
`define DV_CHECK_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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_CASE_EQ
|
||||
`define DV_CHECK_CASE_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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 \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_CASE_NE
|
||||
`define DV_CHECK_CASE_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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_LT
|
||||
`define DV_CHECK_LT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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_GT
|
||||
`define DV_CHECK_GT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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_LE
|
||||
`define DV_CHECK_LE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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_GE
|
||||
`define DV_CHECK_GE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
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
|
||||
|
||||
// Fatal version of the checks
|
||||
`ifndef DV_CHECK_FATAL
|
||||
`define DV_CHECK_FATAL(T_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK(T_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_EQ_FATAL
|
||||
`define DV_CHECK_EQ_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_EQ(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_NE_FATAL
|
||||
`define DV_CHECK_NE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_NE(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_LT_FATAL
|
||||
`define DV_CHECK_LT_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_LT(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_GT_FATAL
|
||||
`define DV_CHECK_GT_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_GT(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_LE_FATAL
|
||||
`define DV_CHECK_LE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_LE(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_GE_FATAL
|
||||
`define DV_CHECK_GE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_GE(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) \
|
||||
`DV_CHECK_FATAL(VAR_.randomize(), MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common foo.randomize() with { } + fatal check
|
||||
`ifndef DV_CHECK_RANDOMIZE_WITH_FATAL
|
||||
`define DV_CHECK_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(VAR_.randomize() with {WITH_C_}, MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common std::randomize(foo) + fatal check
|
||||
`ifndef DV_CHECK_STD_RANDOMIZE_FATAL
|
||||
`define DV_CHECK_STD_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(std::randomize(VAR_), MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common std::randomize(foo) with { } + fatal check
|
||||
`ifndef DV_CHECK_STD_RANDOMIZE_WITH_FATAL
|
||||
`define DV_CHECK_STD_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_, MSG_="Randomization failed!",ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(std::randomize(VAR_) with {WITH_C_}, MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common this.randomize(foo) + fatal check
|
||||
`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_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common this.randomize(foo) with { } + fatal check
|
||||
`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_)
|
||||
`endif
|
||||
|
||||
// print static/dynamic 1d array or queue
|
||||
`ifndef DV_PRINT_ARR_CONTENTS
|
||||
`define DV_PRINT_ARR_CONTENTS(ARR_, V_=UVM_MEDIUM, ID_=`gfn) \
|
||||
begin \
|
||||
foreach (ARR_[i]) begin \
|
||||
`uvm_info(ID_, $sformatf("%s[%0d] = 0x%0d[0x%0h]", `"ARR_`", i, ARR_[i], ARR_[i]), V_) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_TLM_FIFO_CONTENTS
|
||||
`define DV_EOT_PRINT_TLM_FIFO_CONTENTS(TYP_, FIFO_, SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
while (!FIFO_.is_empty()) begin \
|
||||
TYP_ item; \
|
||||
void'(FIFO_.try_get(item)); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"FIFO_`", item.sprint())) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_TLM_FIFO_ARR_CONTENTS
|
||||
`define DV_EOT_PRINT_TLM_FIFO_ARR_CONTENTS(TYP_, FIFO_, SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
foreach (FIFO_[i]) begin \
|
||||
while (!FIFO_[i].is_empty()) begin \
|
||||
TYP_ item; \
|
||||
void'(FIFO_[i].try_get(item)); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s[%0d] item uncompared:\n%s", `"FIFO_`", i, item.sprint())) \
|
||||
end \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_Q_CONTENTS
|
||||
`define DV_EOT_PRINT_Q_CONTENTS(TYP_, Q_, SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
while (Q_.size() != 0) begin \
|
||||
TYP_ item = Q_.pop_front(); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"Q_`", item.sprint())) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_Q_ARR_CONTENTS
|
||||
`define DV_EOT_PRINT_Q_ARR_CONTENTS(TYP_, Q_, SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
foreach (Q_[i]) begin \
|
||||
while (Q_[i].size() != 0) begin \
|
||||
TYP_ item = Q_[i].pop_front(); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s[%0d] item uncompared:\n%s", `"Q_`", i, item.sprint())) \
|
||||
end \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// check for non-empty mailbox and print items that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_MAILBOX_CONTENTS
|
||||
`define DV_EOT_PRINT_MAILBOX_CONTENTS(TYP_, MAILBOX_, SEV_=error, ID_=`gfn) \
|
||||
begin \
|
||||
while (MAILBOX_.num() != 0) begin \
|
||||
TYP_ item; \
|
||||
void'(MAILBOX_.try_get(item)); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"MAILBOX_`", item.sprint())) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// get parity - implemented as a macro so that it can be invoked in constraints as well
|
||||
`ifndef GET_PARITY
|
||||
`define GET_PARITY(val, odd=0) (^val ^ odd)
|
||||
`endif
|
||||
|
||||
// wait a task or statement with timer watchdog
|
||||
// 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) \
|
||||
begin \
|
||||
fork begin \
|
||||
fork \
|
||||
begin \
|
||||
WAIT_ \
|
||||
end \
|
||||
begin \
|
||||
wait_timeout(TIMEOUT_NS_, ID_, MSG_); \
|
||||
end \
|
||||
join_any \
|
||||
disable fork; \
|
||||
end join \
|
||||
end
|
||||
`endif
|
87
vendor/lowrisc_ip/dv/sv/dv_utils/dv_report_server.sv
vendored
Normal file
87
vendor/lowrisc_ip/dv/sv/dv_utils/dv_report_server.sv
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Standardize look & feel of report phase and uvm logging messages.
|
||||
class dv_report_server extends uvm_default_report_server;
|
||||
|
||||
bit show_file_line = 1'b1;
|
||||
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("use_default_uvm_report_message_format=%0b",
|
||||
use_default_uvm_report_message_format));
|
||||
endfunction
|
||||
|
||||
function void report_summarize(UVM_FILE file = 0);
|
||||
int num_uvm_warning;
|
||||
int num_uvm_error;
|
||||
int num_uvm_fatal;
|
||||
|
||||
num_uvm_warning = get_severity_count(UVM_WARNING);
|
||||
num_uvm_error = get_severity_count(UVM_ERROR);
|
||||
num_uvm_fatal = get_severity_count(UVM_FATAL);
|
||||
|
||||
// Print default summary report
|
||||
super.report_summarize(file);
|
||||
|
||||
// Print final test pass-fail - external tool can use this signature for test status
|
||||
// Treat UVM_WARNINGs as a sign of test failure since it could silently result in false pass
|
||||
if ((num_uvm_warning + num_uvm_error + num_uvm_fatal) == 0) begin
|
||||
$display("\nTEST PASSED CHECKS");
|
||||
$display(" _____ _ _ _ ");
|
||||
$display("|_ _|__ ___| |_ _ __ __ _ ___ ___ ___ __| | |");
|
||||
$display(" | |/ _ \\/ __| __| | '_ \\ / _` / __/ __|/ _ \\/ _` | |");
|
||||
$display(" | | __/\\__ \\ |_ | |_) | (_| \\__ \\__ \\ __/ (_| |_|");
|
||||
$display(" |_|\\___||___/\\__| | .__/ \\__,_|___/___/\\___|\\__,_(_)");
|
||||
$display(" |_| \n");
|
||||
end
|
||||
else begin
|
||||
$display("\nTEST FAILED CHECKS");
|
||||
$display(" _____ _ __ _ _ _ _ ");
|
||||
$display("|_ _|__ ___| |_ / _| __ _(_) | ___ __| | |");
|
||||
$display(" | |/ _ \\/ __| __| | |_ / _` | | |/ _ \\/ _` | |");
|
||||
$display(" | | __/\\__ \\ |_ | _| (_| | | | __/ (_| |_|");
|
||||
$display(" |_|\\___||___/\\__| |_| \\__,_|_|_|\\___|\\__,_(_)\n");
|
||||
end
|
||||
|
||||
endfunction
|
||||
|
||||
// Override default messaging format to standard "pretty" format for all testbenches
|
||||
virtual function string compose_report_message(uvm_report_message report_message,
|
||||
string report_object_name = "");
|
||||
|
||||
if (use_default_uvm_report_message_format) begin
|
||||
return (super.compose_report_message(report_message, report_object_name));
|
||||
end else begin
|
||||
uvm_severity severity = report_message.get_severity();
|
||||
string filename = report_message.get_filename();
|
||||
int line = report_message.get_line();
|
||||
string obj_name = report_message.get_report_object().get_full_name();
|
||||
string id = report_message.get_id();
|
||||
string message = report_message.get_message();
|
||||
string file_line;
|
||||
|
||||
if (show_file_line && filename != "") begin
|
||||
filename = get_no_hier_filename(filename);
|
||||
file_line = $sformatf("(%0s:%0d) ", filename, line);
|
||||
end
|
||||
obj_name = {obj_name, ((obj_name != "") ? " " : "")};
|
||||
compose_report_message = $sformatf({"%0s @ %t: ", file_line, obj_name, "[%0s] %0s"},
|
||||
severity.name(), $realtime, id, message);
|
||||
return compose_report_message;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// get we don't really want the full path to the filename
|
||||
// this should be reasonably lightweight
|
||||
local function string get_no_hier_filename(string filename);
|
||||
int idx;
|
||||
for (idx = filename.len() - 1; idx >= 0; idx--) if (filename[idx] == "/") break;
|
||||
return (filename.substr(idx + 1, filename.len() - 1));
|
||||
endfunction
|
||||
|
||||
endclass
|
23
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils.core
vendored
Normal file
23
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils.core
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
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_utils"
|
||||
description: "DV utilities"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- 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}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
156
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
vendored
Normal file
156
vendor/lowrisc_ip/dv/sv/dv_utils/dv_utils_pkg.sv
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package dv_utils_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import bus_params_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "dv_macros.svh"
|
||||
`include "uvm_macros.svh"
|
||||
|
||||
// common parameters used across all benches
|
||||
parameter int NUM_MAX_INTERRUPTS = 32;
|
||||
parameter int NUM_MAX_ALERTS = 32;
|
||||
|
||||
// types & variables
|
||||
typedef bit [31:0] uint;
|
||||
typedef bit [7:0] uint8;
|
||||
typedef bit [15:0] uint16;
|
||||
typedef bit [31:0] uint32;
|
||||
typedef bit [63:0] uint64;
|
||||
|
||||
// typedef parameterized pins_if for ease of implementation for interrupts and alerts
|
||||
typedef virtual pins_if #(NUM_MAX_INTERRUPTS) intr_vif;
|
||||
typedef virtual pins_if #(1) devmode_vif;
|
||||
|
||||
// interface direction / mode - Host or Device
|
||||
typedef enum bit {
|
||||
Host,
|
||||
Device
|
||||
} if_mode_e;
|
||||
|
||||
// speed for the clock
|
||||
typedef enum int {
|
||||
ClkFreq24Mhz = 24,
|
||||
ClkFreq25Mhz = 25,
|
||||
ClkFreq48Mhz = 48,
|
||||
ClkFreq50Mhz = 50,
|
||||
ClkFreq100Mhz = 100
|
||||
} clk_freq_mhz_e;
|
||||
|
||||
// compare operator types
|
||||
typedef enum {
|
||||
CompareOpEq,
|
||||
CompareOpCaseEq,
|
||||
CompareOpNe,
|
||||
CompareOpCaseNe,
|
||||
CompareOpGt,
|
||||
CompareOpGe,
|
||||
CompareOpLt,
|
||||
CompareOpLe
|
||||
} compare_op_e;
|
||||
|
||||
// mem address struct
|
||||
typedef struct {
|
||||
uvm_reg_addr_t start_addr;
|
||||
uvm_reg_addr_t end_addr;
|
||||
} addr_range_t;
|
||||
|
||||
// Enum representing a bus operation type - read or write.
|
||||
typedef enum bit {
|
||||
BusOpWrite = 1'b0,
|
||||
BusOpRead = 1'b1
|
||||
} bus_op_e;
|
||||
|
||||
// Enum representing a type of host requests - read only, write only or random read & write
|
||||
typedef enum int {
|
||||
HostReqNone = 0,
|
||||
HostReqReadOnly = 1,
|
||||
HostReqWriteOnly = 2,
|
||||
HostReqReadWrite = 3
|
||||
} host_req_type_e;
|
||||
|
||||
string msg_id = "dv_utils_pkg";
|
||||
|
||||
// return the smaller value of 2 inputs
|
||||
function automatic int min2(int a, int b);
|
||||
return (a < b) ? a : b;
|
||||
endfunction
|
||||
|
||||
// return the bigger value of 2 inputs
|
||||
function automatic int max2(int a, int b);
|
||||
return (a > b) ? a : b;
|
||||
endfunction
|
||||
|
||||
// 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();
|
||||
report_server.set_max_quit_count(n);
|
||||
endfunction
|
||||
|
||||
// return if uvm_fatal occurred
|
||||
function automatic bit has_uvm_fatal_occurred();
|
||||
uvm_report_server report_server = uvm_report_server::get_server();
|
||||
return report_server.get_severity_count(UVM_FATAL) > 0;
|
||||
endfunction
|
||||
|
||||
// task that waits for the specfied timeout
|
||||
task automatic wait_timeout(input uint timeout_ns,
|
||||
input string error_msg_id = msg_id,
|
||||
input string error_msg = "timeout occurred!",
|
||||
input bit report_fatal = 1);
|
||||
#(timeout_ns * 1ns);
|
||||
if (report_fatal) `uvm_fatal(error_msg_id, error_msg)
|
||||
else `uvm_error(error_msg_id, error_msg)
|
||||
endtask : wait_timeout
|
||||
|
||||
// get masked data based on provided byte mask; if csr reg handle is provided (optional) then
|
||||
// masked bytes from csr's mirrored value are returned, else masked bytes are 0's
|
||||
function automatic bit [bus_params_pkg::BUS_DW-1:0]
|
||||
get_masked_data(bit [bus_params_pkg::BUS_DW-1:0] data,
|
||||
bit [bus_params_pkg::BUS_DBW-1:0] mask,
|
||||
uvm_reg csr = null);
|
||||
bit [bus_params_pkg::BUS_DW-1:0] csr_data;
|
||||
csr_data = (csr != null) ? csr.get_mirrored_value() : '0;
|
||||
get_masked_data = data;
|
||||
foreach (mask[i]) begin
|
||||
if (~mask[i]) get_masked_data[i * 8 +: 8] = csr_data[i * 8 +: 8];
|
||||
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;
|
||||
uvm_factory factory;
|
||||
uvm_sequence seq;
|
||||
|
||||
factory = uvm_factory::get();
|
||||
obj = factory.create_object_by_name(seq_name, "", seq_name);
|
||||
if (obj == null) begin
|
||||
// print factory overrides to help debug
|
||||
factory.print(1);
|
||||
`uvm_fatal(msg_id, $sformatf("could not create %0s seq", seq_name))
|
||||
end
|
||||
if (!$cast(seq, obj)) begin
|
||||
`uvm_fatal(msg_id, $sformatf("cast failed - %0s is not a uvm_sequence", seq_name))
|
||||
end
|
||||
return seq;
|
||||
endfunction
|
||||
|
||||
// sources
|
||||
`include "dv_report_server.sv"
|
||||
|
||||
endpackage
|
26
vendor/lowrisc_ip/dv/sv/mem_model/README.md
vendored
Normal file
26
vendor/lowrisc_ip/dv/sv/mem_model/README.md
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
## Memory Model
|
||||
The memory model UVC models a memory device which any host interface can read
|
||||
from or write to. It is implemented as a `uvm_object`, and instantiates an
|
||||
associative array of bytes `system_memory`. This class is paramterized by both
|
||||
the address width and the data width, and creates two `typedefs` to represent
|
||||
both, `mem_addr_t` and `mem_data_t`.
|
||||
The `mem_model` class has four main functions, which are detailed below.
|
||||
|
||||
### `read_byte(mem_addr_t addr)`
|
||||
This function looks up the byte of data corresponding to the memory address
|
||||
passed in, and returns it. If the address does not exist in `system_memory`, it
|
||||
will randomize the returned data and throw a `UVM_ERROR`.
|
||||
|
||||
### `write_byte(mem_addr_t addr, bit [7:0] data)`
|
||||
This function simply assigns the given data to the specified memory address
|
||||
location in `system_memory`.
|
||||
|
||||
### `write(input mem_addr_t addr, mem_data_t data)`
|
||||
This function writes a full memory word of width `mem_data_t` to the specified
|
||||
address, breaking it down into a series of back-to-back calls to `write_byte()`
|
||||
to correctly byte-address the memory.
|
||||
|
||||
### `read(mem_addr_t addr)`
|
||||
This function reads a full memory word of width `mem_data_t` from the specified
|
||||
address, breaking it down into a series of back-to-back calls to `read_byte()`
|
||||
to correctly byte-address the memory.
|
20
vendor/lowrisc_ip/dv/sv/mem_model/mem_model.core
vendored
Normal file
20
vendor/lowrisc_ip/dv/sv/mem_model/mem_model.core
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
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:mem_model"
|
||||
description: "DV Memory Model"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:opentitan:bus_params_pkg
|
||||
files:
|
||||
- mem_model_pkg.sv
|
||||
- mem_model.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
85
vendor/lowrisc_ip/dv/sv/mem_model/mem_model.sv
vendored
Normal file
85
vendor/lowrisc_ip/dv/sv/mem_model/mem_model.sv
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class mem_model #(int AddrWidth = bus_params_pkg::BUS_AW,
|
||||
int DataWidth = bus_params_pkg::BUS_DW,
|
||||
int MaskWidth = bus_params_pkg::BUS_DBW) extends uvm_object;
|
||||
|
||||
typedef bit [AddrWidth-1:0] mem_addr_t;
|
||||
typedef bit [DataWidth-1:0] mem_data_t;
|
||||
typedef bit [MaskWidth-1:0] mem_mask_t;
|
||||
|
||||
bit [7:0] system_memory[mem_addr_t];
|
||||
|
||||
`uvm_object_param_utils(mem_model#(AddrWidth, DataWidth))
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
function int get_written_bytes();
|
||||
return system_memory.size();
|
||||
endfunction
|
||||
|
||||
function bit [7:0] read_byte(mem_addr_t addr);
|
||||
bit [7:0] data;
|
||||
if (system_memory.exists(addr)) begin
|
||||
data = system_memory[addr];
|
||||
`uvm_info(`gfn, $sformatf("Read Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
|
||||
end else begin
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(data)
|
||||
`uvm_error(`gfn, $sformatf("read to uninitialzed addr 0x%0h", addr))
|
||||
end
|
||||
return data;
|
||||
endfunction
|
||||
|
||||
function void write_byte(mem_addr_t addr, bit [7:0] data);
|
||||
`uvm_info(`gfn, $sformatf("Write Mem : Addr[0x%0h], Data[0x%0h]", addr, data), UVM_HIGH)
|
||||
system_memory[addr] = data;
|
||||
endfunction
|
||||
|
||||
function void compare_byte(mem_addr_t addr, bit [7:0] act_data);
|
||||
`uvm_info(`gfn, $sformatf("Compare Mem : Addr[0x%0h], Act Data[0x%0h], Exp Data[0x%0h]",
|
||||
addr, act_data, system_memory[addr]), UVM_HIGH)
|
||||
system_memory[addr] = act_data;
|
||||
`DV_CHECK_EQ(act_data, system_memory[addr], $sformatf("addr 0x%0h read out mismatch", addr))
|
||||
endfunction
|
||||
|
||||
function void write(input mem_addr_t addr, mem_data_t data, mem_mask_t mask = '1);
|
||||
bit [7:0] byte_data;
|
||||
for (int i = 0; i < DataWidth / 8; i++) begin
|
||||
if (mask[0]) begin
|
||||
byte_data = data[7:0];
|
||||
write_byte(addr + i, byte_data);
|
||||
end
|
||||
data = data >> 8;
|
||||
mask = mask >> 1;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function mem_data_t read(mem_addr_t addr, mem_mask_t mask = '1);
|
||||
mem_data_t data;
|
||||
for (int i = DataWidth / 8 - 1; i >= 0; i--) begin
|
||||
data = data << 8;
|
||||
if (mask[MaskWidth - 1]) data[7:0] = read_byte(addr + i);
|
||||
else data[7:0] = 0;
|
||||
mask = mask << 1;
|
||||
end
|
||||
return data;
|
||||
endfunction
|
||||
|
||||
function void compare(mem_addr_t addr, mem_data_t act_data, mem_mask_t mask = '1);
|
||||
bit [7:0] byte_data;
|
||||
for (int i = 0; i < DataWidth / 8; i++) begin
|
||||
byte_data = act_data[7:0];
|
||||
if (mask[0]) begin
|
||||
compare_byte(addr + i, byte_data);
|
||||
end else begin
|
||||
`DV_CHECK_EQ(byte_data, 0,
|
||||
$sformatf("addr 0x%0h masked data aren't 0, mask 0x%0h", addr, mask))
|
||||
end
|
||||
act_data = act_data>> 8;
|
||||
mask = mask >> 1;
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
12
vendor/lowrisc_ip/dv/sv/mem_model/mem_model_pkg.sv
vendored
Normal file
12
vendor/lowrisc_ip/dv/sv/mem_model/mem_model_pkg.sv
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package mem_model_pkg;
|
||||
|
||||
import uvm_pkg::*;
|
||||
|
||||
`include "uvm_macros.svh"
|
||||
`include "mem_model.sv"
|
||||
|
||||
endpackage
|
13
vendor/lowrisc_ip/dv/verilator/README.md
vendored
Normal file
13
vendor/lowrisc_ip/dv/verilator/README.md
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Verilator memory loading support
|
||||
|
||||
## ELF support
|
||||
|
||||
### BSS sections
|
||||
|
||||
When loading ELF files into the memory, only the data stored in the file is loaded into the memory.
|
||||
Data specified by the memory size is not set.
|
||||
This is the case for BSS sections for which only the size information is stored in the ELF file.
|
||||
The zero-ing of this sections is the responsibility of the executed code.
|
||||
This is typically achieved by setting symbols for the start and end of the BSS section in the linker script and zero-ing the intermediate addresses by the startup routine.
|
||||
|
||||
**Requirement: BSS zero-ing must be implemented by the executed software.**
|
472
vendor/lowrisc_ip/dv/verilator/cpp/verilator_memutil.cc
vendored
Normal file
472
vendor/lowrisc_ip/dv/verilator/cpp/verilator_memutil.cc
vendored
Normal file
|
@ -0,0 +1,472 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "verilator_memutil.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h>
|
||||
#include <getopt.h>
|
||||
#include <libelf.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
// DPI Exports
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* Write |file| to a memory
|
||||
*
|
||||
* @param file path to a SystemVerilog $readmemh()-compatible file (VMEM file)
|
||||
*/
|
||||
extern void simutil_verilator_memload(const char *file);
|
||||
|
||||
/**
|
||||
* Write a 32 bit word |val| to memory at index |index|
|
||||
*
|
||||
* @return 1 if successful, 0 otherwise
|
||||
*/
|
||||
extern int simutil_verilator_set_mem(int index, const svBitVecVal *val);
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::RegisterMemoryArea(const std::string name,
|
||||
const std::string location) {
|
||||
// Default to 32bit width
|
||||
return RegisterMemoryArea(name, location, 32);
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::RegisterMemoryArea(const std::string name,
|
||||
const std::string location,
|
||||
size_t width_bit) {
|
||||
MemArea mem = {.name = name, .location = location, .width_bit = width_bit};
|
||||
|
||||
assert((width_bit <= 256) &&
|
||||
"TODO: Memory loading only supported up to 256 bits.");
|
||||
|
||||
auto ret = mem_register_.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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::ParseCLIArguments(int argc, char **argv,
|
||||
bool &exit_app) {
|
||||
const struct option long_options[] = {
|
||||
{"rominit", required_argument, nullptr, 'r'},
|
||||
{"raminit", required_argument, nullptr, 'm'},
|
||||
{"flashinit", required_argument, nullptr, 'f'},
|
||||
{"meminit", required_argument, nullptr, 'l'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{nullptr, no_argument, nullptr, 0}};
|
||||
|
||||
// Reset the command parsing index in-case other utils have already parsed
|
||||
// some arguments
|
||||
optind = 1;
|
||||
while (1) {
|
||||
int c = getopt_long(argc, argv, ":r:m:f:l:h", long_options, nullptr);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Disable error reporting by getopt
|
||||
opterr = 0;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
break;
|
||||
case 'r':
|
||||
if (!MemWrite("rom", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (!MemWrite("ram", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (!MemWrite("flash", optarg)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'l': {
|
||||
if (strcasecmp(optarg, "list") == 0) {
|
||||
PrintMemRegions();
|
||||
exit_app = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::string filepath;
|
||||
MemImageType type;
|
||||
if (!ParseMemArg(optarg, name, filepath, type)) {
|
||||
std::cerr << "ERROR: Unable to parse meminit arguments." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MemWrite(name, filepath, type)) {
|
||||
std::cerr << "ERROR: Unable to initialize memory." << std::endl;
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case 'h':
|
||||
PrintHelp();
|
||||
return true;
|
||||
case ':': // missing argument
|
||||
std::cerr << "ERROR: Missing argument." << std::endl << std::endl;
|
||||
return false;
|
||||
case '?':
|
||||
default:;
|
||||
// Ignore unrecognized options since they might be consumed by
|
||||
// other utils
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VerilatorMemUtil::PrintMemRegions() const {
|
||||
std::cout << "Registered memory regions:" << std::endl;
|
||||
for (const auto &m : mem_register_) {
|
||||
std::cout << "\t'" << m.second.name << "' (" << m.second.width_bit
|
||||
<< "bits) at location: '" << m.second.location << "'"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatorMemUtil::PrintHelp() const {
|
||||
std::cout << "Simulation memory utilities:\n\n"
|
||||
"-r|--rominit=FILE\n"
|
||||
" Initialize the ROM with FILE (elf/vmem)\n\n"
|
||||
"-m|--raminit=FILE\n"
|
||||
" Initialize the RAM with FILE (elf/vmem)\n\n"
|
||||
"-f|--flashinit=FILE\n"
|
||||
" Initialize the FLASH with FILE (elf/vmem)\n\n"
|
||||
"-l|--meminit=NAME,FILE[,TYPE]\n"
|
||||
" Initialize memory region NAME with FILE [of TYPE]\n"
|
||||
" TYPE is either 'elf' or 'vmem'\n\n"
|
||||
"-l list|--meminit=list\n"
|
||||
" Print registered memory regions\n\n"
|
||||
"-h|--help\n"
|
||||
" Show help\n\n";
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::ParseMemArg(std::string mem_argument, std::string &name,
|
||||
std::string &filepath, MemImageType &type) {
|
||||
std::array<std::string, 3> args;
|
||||
size_t pos = 0;
|
||||
size_t end_pos = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
end_pos = mem_argument.find(",", pos);
|
||||
// Check for possible exit conditions
|
||||
if (pos == end_pos) {
|
||||
std::cerr << "ERROR: empty field in: " << mem_argument << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (end_pos == std::string::npos) {
|
||||
args[i] = mem_argument.substr(pos);
|
||||
break;
|
||||
}
|
||||
args[i] = mem_argument.substr(pos, end_pos - pos);
|
||||
pos = end_pos + 1;
|
||||
}
|
||||
// mem_argument is not empty as getopt requires an argument,
|
||||
// but not a valid argument for memory initialization
|
||||
if (i == 0) {
|
||||
std::cerr << "ERROR: meminit must be in \"name,file[,type]\""
|
||||
<< " got: " << mem_argument << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
name = args[0];
|
||||
filepath = args[1];
|
||||
|
||||
if (i == 1) {
|
||||
// Type not set explicitly
|
||||
type = DetectMemImageType(filepath);
|
||||
} else {
|
||||
type = GetMemImageTypeByName(args[2]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MemImageType VerilatorMemUtil::DetectMemImageType(const std::string filepath) {
|
||||
size_t ext_pos = filepath.find_last_of(".");
|
||||
std::string ext = filepath.substr(ext_pos + 1);
|
||||
|
||||
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;
|
||||
}
|
||||
return GetMemImageTypeByName(ext);
|
||||
}
|
||||
|
||||
MemImageType VerilatorMemUtil::GetMemImageTypeByName(const std::string name) {
|
||||
if (name.compare("elf") == 0) {
|
||||
return kMemImageElf;
|
||||
}
|
||||
if (name.compare("vmem") == 0) {
|
||||
return kMemImageVmem;
|
||||
}
|
||||
return kMemImageUnknown;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::IsFileReadable(std::string filepath) const {
|
||||
struct stat statbuf;
|
||||
return stat(filepath.data(), &statbuf) == 0;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::ElfFileToBinary(const std::string &filepath,
|
||||
uint8_t **data,
|
||||
size_t &len_bytes) const {
|
||||
uint8_t *buf;
|
||||
bool retval, any = false;
|
||||
GElf_Phdr phdr;
|
||||
GElf_Addr high = 0;
|
||||
GElf_Addr low = (GElf_Addr)-1;
|
||||
Elf_Data *elf_data;
|
||||
size_t i;
|
||||
|
||||
(void)elf_errno();
|
||||
len_bytes = 0;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
std::cerr << elf_errmsg(-1) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = open(filepath.c_str(), O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
std::cerr << "Could not open file: " << filepath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
Elf *elf_desc;
|
||||
elf_desc = elf_begin(fd, ELF_C_READ, NULL);
|
||||
if (elf_desc == NULL) {
|
||||
std::cerr << elf_errmsg(-1) << " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_fd_end;
|
||||
}
|
||||
if (elf_kind(elf_desc) != ELF_K_ELF) {
|
||||
std::cerr << "Not a ELF file: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
// TODO: add support for ELFCLASS64
|
||||
if (gelf_getclass(elf_desc) != ELFCLASS32) {
|
||||
std::cerr << "Not a 32-bit ELF file: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
size_t phnum;
|
||||
if (elf_getphdrnum(elf_desc, &phnum) != 0) {
|
||||
std::cerr << elf_errmsg(-1) << " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
//
|
||||
// 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 sections based on their offset with respect to
|
||||
// the found base address.
|
||||
//
|
||||
for (i = 0; i < phnum; i++) {
|
||||
if (gelf_getphdr(elf_desc, i, &phdr) == NULL) {
|
||||
std::cerr << elf_errmsg(-1) << " segment number: " << i
|
||||
<< " in: " << filepath << std::endl;
|
||||
retval = false;
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
std::cout << "Program header number " << i << " is not of type PT_LOAD; "
|
||||
<< "ignoring." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr.p_filesz == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!any || phdr.p_paddr < low) {
|
||||
low = phdr.p_paddr;
|
||||
}
|
||||
|
||||
if (!any || phdr.p_paddr + phdr.p_filesz > high) {
|
||||
high = phdr.p_paddr + phdr.p_filesz;
|
||||
}
|
||||
|
||||
any = true;
|
||||
}
|
||||
|
||||
len_bytes = high - low;
|
||||
buf = (uint8_t *)malloc(len_bytes);
|
||||
assert(buf != NULL);
|
||||
|
||||
for (i = 0; i < phnum; i++) {
|
||||
(void)gelf_getphdr(elf_desc, i, &phdr);
|
||||
|
||||
if (phdr.p_type != PT_LOAD || phdr.p_filesz == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
elf_data = elf_getdata_rawchunk(elf_desc, phdr.p_offset, phdr.p_filesz,
|
||||
ELF_T_BYTE);
|
||||
|
||||
if (elf_data == NULL) {
|
||||
retval = false;
|
||||
free(buf);
|
||||
goto return_elf_end;
|
||||
}
|
||||
|
||||
memcpy(&buf[phdr.p_paddr - low], (uint8_t *)elf_data->d_buf,
|
||||
elf_data->d_size);
|
||||
}
|
||||
|
||||
*data = buf;
|
||||
retval = true;
|
||||
|
||||
return_elf_end:
|
||||
elf_end(elf_desc);
|
||||
return_fd_end:
|
||||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::MemWrite(const std::string &name,
|
||||
const std::string &filepath) {
|
||||
MemImageType type = DetectMemImageType(filepath);
|
||||
if (type == kMemImageUnknown) {
|
||||
std::cerr << "ERROR: Unable to detect file type for: " << filepath
|
||||
<< std::endl;
|
||||
// Continuing for more error messages
|
||||
}
|
||||
return MemWrite(name, filepath, type);
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::MemWrite(const std::string &name,
|
||||
const std::string &filepath,
|
||||
MemImageType type) {
|
||||
// Search for corresponding registered memory based on the name
|
||||
auto it = mem_register_.find(name);
|
||||
if (it == mem_register_.end()) {
|
||||
std::cerr << "ERROR: Memory location not set for: '" << name << "'"
|
||||
<< std::endl;
|
||||
PrintMemRegions();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MemWrite(it->second, filepath, type)) {
|
||||
std::cerr << "ERROR: Setting memory '" << name << "' failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::MemWrite(const MemArea &m, const std::string &filepath,
|
||||
MemImageType type) {
|
||||
if (!IsFileReadable(filepath)) {
|
||||
std::cerr << "ERROR: Memory initialization file "
|
||||
<< "'" << filepath << "'"
|
||||
<< " is not readable." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
svScope scope = svGetScopeFromName(m.location.data());
|
||||
if (!scope) {
|
||||
std::cerr << "ERROR: No memory found at " << m.location << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m.width_bit % 8) != 0) {
|
||||
std::cerr << "ERROR: width for: " << m.name
|
||||
<< "must be a multiple of 8 (was : " << m.width_bit << ")"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
size_t size_byte = m.width_bit / 8;
|
||||
|
||||
switch (type) {
|
||||
case kMemImageElf:
|
||||
if (!WriteElfToMem(scope, filepath, size_byte)) {
|
||||
std::cerr << "ERROR: Writing ELF file to memory \"" << m.name << "\" ("
|
||||
<< m.location << ") failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case kMemImageVmem:
|
||||
if (!WriteVmemToMem(scope, filepath)) {
|
||||
std::cerr << "ERROR: Writing VMEM file to memory \"" << m.name << "\" ("
|
||||
<< m.location << ") failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case kMemImageUnknown:
|
||||
default:
|
||||
std::cerr << "ERROR: Unknown file type for " << m.name << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::WriteElfToMem(const svScope &scope,
|
||||
const std::string &filepath,
|
||||
size_t size_byte) {
|
||||
bool retcode;
|
||||
svScope prev_scope = svSetScope(scope);
|
||||
|
||||
uint8_t *buf = nullptr;
|
||||
size_t len_bytes;
|
||||
|
||||
if (!ElfFileToBinary(filepath, &buf, len_bytes)) {
|
||||
std::cerr << "ERROR: Could not load: " << filepath << std::endl;
|
||||
retcode = false;
|
||||
goto ret;
|
||||
}
|
||||
for (int i = 0; i < (len_bytes + size_byte - 1) / size_byte; ++i) {
|
||||
if (!simutil_verilator_set_mem(i, (svBitVecVal *)&buf[size_byte * i])) {
|
||||
std::cerr << "ERROR: Could not set memory byte: " << i * size_byte << "/"
|
||||
<< len_bytes << "" << std::endl;
|
||||
|
||||
retcode = false;
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
|
||||
retcode = true;
|
||||
|
||||
ret:
|
||||
svSetScope(prev_scope);
|
||||
free(buf);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
bool VerilatorMemUtil::WriteVmemToMem(const svScope &scope,
|
||||
const std::string &filepath) {
|
||||
svScope prev_scope = svSetScope(scope);
|
||||
|
||||
// TODO: Add error handling.
|
||||
simutil_verilator_memload(filepath.data());
|
||||
|
||||
svSetScope(prev_scope);
|
||||
return true;
|
||||
}
|
115
vendor/lowrisc_ip/dv/verilator/cpp/verilator_memutil.h
vendored
Normal file
115
vendor/lowrisc_ip/dv/verilator/cpp/verilator_memutil.h
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_VERILATOR_MEMUTIL_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_CPP_VERILATOR_MEMUTIL_H_
|
||||
|
||||
#include "sim_ctrl_extension.h"
|
||||
|
||||
#include <vltstd/svdpi.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
enum MemImageType {
|
||||
kMemImageUnknown = 0,
|
||||
kMemImageElf,
|
||||
kMemImageVmem,
|
||||
};
|
||||
|
||||
struct MemArea {
|
||||
std::string name; // Unique identifier
|
||||
std::string location; // Design scope location
|
||||
size_t width_bit; // Memory width
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide various memory loading utilities for Verilator simulations
|
||||
*
|
||||
* These utilities require the corresponding DPI functions:
|
||||
* simutil_verilator_memload()
|
||||
* simutil_verilator_set_mem()
|
||||
* to be defined somewhere as SystemVerilog functions.
|
||||
*/
|
||||
class VerilatorMemUtil : public SimCtrlExtension {
|
||||
public:
|
||||
/**
|
||||
* Register a memory as instantiated by generic ram
|
||||
*
|
||||
* The |name| must be a unique identifier. The function will return false
|
||||
* if |name| is already used. |location| is the path to the scope of the
|
||||
* instantiated memory, which needs to support the DPI-C interfaces
|
||||
* 'simutil_verilator_memload' and 'simutil_verilator_set_mem' used for
|
||||
* 'vmem' and 'elf' files, respectively.
|
||||
* The |width_bit| argument specifies the with in bits of the target memory
|
||||
* instance (used for packing data).
|
||||
*
|
||||
* Memories must be registered before command arguments are parsed by
|
||||
* ParseCommandArgs() in order for them to be known.
|
||||
*/
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location,
|
||||
size_t width_bit);
|
||||
|
||||
/**
|
||||
* Register a memory with default width (32bits)
|
||||
*/
|
||||
bool RegisterMemoryArea(const std::string name, const std::string location);
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*
|
||||
* Process all recognized command-line arguments from argc/argv.
|
||||
*
|
||||
* @param argc, argv Standard C command line arguments
|
||||
* @param exit_app Indicate that program should terminate
|
||||
* @return Return code, true == success
|
||||
*/
|
||||
virtual bool ParseCLIArguments(int argc, char **argv, bool &exit_app);
|
||||
|
||||
private:
|
||||
std::map<std::string, MemArea> mem_register_;
|
||||
|
||||
/**
|
||||
* Print a list of all registered memory regions
|
||||
*
|
||||
* @see RegisterMemoryArea()
|
||||
*/
|
||||
void PrintMemRegions() const;
|
||||
|
||||
/**
|
||||
* Print help how to use this tool
|
||||
*/
|
||||
void PrintHelp() const;
|
||||
|
||||
/**
|
||||
* Parse argument section specific to memory initialization.
|
||||
*
|
||||
* Must be in the form of: name,file[,type].
|
||||
*/
|
||||
bool ParseMemArg(std::string mem_argument, std::string &name,
|
||||
std::string &filepath, MemImageType &type);
|
||||
|
||||
MemImageType DetectMemImageType(const std::string filepath);
|
||||
|
||||
MemImageType GetMemImageTypeByName(const std::string name);
|
||||
|
||||
bool IsFileReadable(std::string filepath) const;
|
||||
|
||||
/**
|
||||
* Dump an ELF file into a raw binary
|
||||
*/
|
||||
bool ElfFileToBinary(const std::string &filepath, uint8_t **data,
|
||||
size_t &len_bytes) const;
|
||||
|
||||
bool MemWrite(const std::string &name, const std::string &filepath);
|
||||
bool MemWrite(const std::string &name, const std::string &filepath,
|
||||
MemImageType type);
|
||||
bool MemWrite(const MemArea &m, const std::string &filepath,
|
||||
MemImageType type);
|
||||
bool WriteElfToMem(const svScope &scope, const std::string &filepath,
|
||||
size_t size_byte);
|
||||
bool WriteVmemToMem(const svScope &scope, const std::string &filepath);
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_CPP_VERILATOR_MEMUTIL_H_
|
20
vendor/lowrisc_ip/dv/verilator/memutil_verilator.core
vendored
Normal file
20
vendor/lowrisc_ip/dv/verilator/memutil_verilator.core
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
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_verilator:memutil_verilator"
|
||||
description: "Verilator memory utilities"
|
||||
filesets:
|
||||
files_cpp:
|
||||
depend:
|
||||
- lowrisc:dv_verilator:simutil_verilator
|
||||
files:
|
||||
- cpp/verilator_memutil.cc
|
||||
- cpp/verilator_memutil.h: { is_include_file: true }
|
||||
file_type: cppSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_cpp
|
41
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/sim_ctrl_extension.h
vendored
Normal file
41
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/sim_ctrl_extension.h
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_SIM_CTRL_EXTENSION_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_SIM_CTRL_EXTENSION_H_
|
||||
|
||||
class SimCtrlExtension {
|
||||
public:
|
||||
virtual ~SimCtrlExtension() = default;
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*
|
||||
* Process all recognized command-line arguments from argc/argv.
|
||||
*
|
||||
* @param argc, argv Standard C command line arguments
|
||||
* @param exit_app Indicate that program should terminate
|
||||
* @return Return code, true == success
|
||||
*/
|
||||
virtual bool ParseCLIArguments(int argc, char **argv, bool &exit_app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to be called prior to executing the simulation
|
||||
*/
|
||||
virtual void PreExec() {}
|
||||
|
||||
/**
|
||||
* Function to be called every clock cycle
|
||||
*/
|
||||
virtual void OnClock(unsigned long sim_time) {}
|
||||
|
||||
/**
|
||||
* Function to be called after executing the simulation
|
||||
*/
|
||||
virtual void PostExec() {}
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_SIM_CTRL_EXTENSION_H_
|
12
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilated_toplevel.cc
vendored
Normal file
12
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilated_toplevel.cc
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "verilated_toplevel.h"
|
||||
|
||||
TOPLEVEL_NAME &VerilatedToplevel::dut() {
|
||||
// The static_cast below is generally unsafe, but we know the types involved.
|
||||
// It's safe for these.
|
||||
TOPLEVEL_NAME &dut = static_cast<TOPLEVEL_NAME &>(*this);
|
||||
return dut;
|
||||
}
|
155
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilated_toplevel.h
vendored
Normal file
155
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilated_toplevel.h
vendored
Normal file
|
@ -0,0 +1,155 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_VERILATED_TOPLEVEL_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_VERILATED_TOPLEVEL_H_
|
||||
|
||||
#ifndef TOPLEVEL_NAME
|
||||
#error "TOPLEVEL_NAME must be set to the name of the toplevel."
|
||||
#endif
|
||||
|
||||
#include <verilated.h>
|
||||
|
||||
#define STR(s) #s
|
||||
#define STR_AND_EXPAND(s) STR(s)
|
||||
|
||||
// Include Verilator-generated toplevel
|
||||
#define VERILATED_TOPLEVEL_HEADER_STR2(s) STR(V##s)
|
||||
#define VERILATED_TOPLEVEL_HEADER_STR(s) VERILATED_TOPLEVEL_HEADER_STR2(s)
|
||||
|
||||
#include VERILATED_TOPLEVEL_HEADER_STR(TOPLEVEL_NAME.h)
|
||||
|
||||
// Name of the Verilated class
|
||||
#define VERILATED_TOPLEVEL_NAME3(s) V##s
|
||||
#define VERILATED_TOPLEVEL_NAME2(s) VERILATED_TOPLEVEL_NAME3(s)
|
||||
#define VERILATED_TOPLEVEL_NAME VERILATED_TOPLEVEL_NAME2(TOPLEVEL_NAME)
|
||||
|
||||
// VM_TRACE is defined by Verilator and passed through the command line as 1 or
|
||||
// 0. The code below serves as safety net only.
|
||||
#ifndef VM_TRACE
|
||||
#define VM_TRACE 0
|
||||
#endif
|
||||
|
||||
// VM_TRACE_FMT_FST must be set by the user when calling Verilator with
|
||||
// --trace-fst. VM_TRACE is set by Verilator itself.
|
||||
#if VM_TRACE == 1
|
||||
#ifdef VM_TRACE_FMT_FST
|
||||
#include "verilated_fst_c.h"
|
||||
#define VM_TRACE_CLASS_NAME VerilatedFstC
|
||||
#else
|
||||
#include "verilated_vcd_c.h"
|
||||
#define VM_TRACE_CLASS_NAME VerilatedVcdC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if VM_TRACE == 1
|
||||
/**
|
||||
* "Base" for all tracers in Verilator with common functionality
|
||||
*
|
||||
* This class is (like the VerilatedToplevel class) a workaround for the
|
||||
* insufficient class hierarchy in Verilator-generated C++ code.
|
||||
*
|
||||
* Once Verilator is improved to support this functionality natively this class
|
||||
* should go away.
|
||||
*/
|
||||
class VerilatedTracer {
|
||||
public:
|
||||
VerilatedTracer() : impl_(nullptr) { impl_ = new VM_TRACE_CLASS_NAME(); };
|
||||
|
||||
~VerilatedTracer() { delete impl_; }
|
||||
|
||||
bool isOpen() const { return impl_->isOpen(); };
|
||||
|
||||
void open(const char *filename) { impl_->open(filename); };
|
||||
|
||||
void close() { impl_->close(); };
|
||||
|
||||
void dump(vluint64_t timeui) { impl_->dump(timeui); }
|
||||
|
||||
operator VM_TRACE_CLASS_NAME *() const {
|
||||
assert(impl_);
|
||||
return impl_;
|
||||
}
|
||||
|
||||
private:
|
||||
VM_TRACE_CLASS_NAME *impl_;
|
||||
};
|
||||
#else
|
||||
/**
|
||||
* No-op tracer interface
|
||||
*/
|
||||
class VerilatedTracer {
|
||||
public:
|
||||
VerilatedTracer(){};
|
||||
~VerilatedTracer() {}
|
||||
bool isOpen() const { return false; };
|
||||
void open(const char *filename){};
|
||||
void close(){};
|
||||
void dump(vluint64_t timeui) {}
|
||||
};
|
||||
#endif // VM_TRACE == 1
|
||||
|
||||
// Forward-declare for use in VerilatedToplevel
|
||||
class TOPLEVEL_NAME;
|
||||
|
||||
/**
|
||||
* Abstract class for verilated toplevel modules
|
||||
*
|
||||
* Verilator-produced toplevel modules do not have a common base class defining
|
||||
* the methods such as eval(); instead, they are only inheriting from the
|
||||
* generic VerilatedModule class, which doesn't have toplevel-specific
|
||||
* functionality. This makes it impossible to write code which accepts any
|
||||
* toplevel module as input by specifying the common "toplevel base class".
|
||||
*
|
||||
* This class, VerilatedToplevel, fills this gap by defining an abstract base
|
||||
* class for verilated toplevel modules. This class should be used together with
|
||||
* the VERILATED_TOPLEVEL macro.
|
||||
*
|
||||
* Note that this function is a workaround until Verilator gains this
|
||||
* functionality natively.
|
||||
*
|
||||
* To support the different tracing implementations (VCD, FST or no tracing),
|
||||
* the trace() function is modified to take a VerilatedTracer argument instead
|
||||
* of the tracer-specific class.
|
||||
*/
|
||||
class VerilatedToplevel {
|
||||
public:
|
||||
VerilatedToplevel(){};
|
||||
virtual ~VerilatedToplevel(){};
|
||||
|
||||
virtual void eval() = 0;
|
||||
virtual void final() = 0;
|
||||
virtual const char *name() const = 0;
|
||||
virtual void trace(VerilatedTracer &tfp, int levels, int options) = 0;
|
||||
|
||||
/**
|
||||
* Get the Verilator-generated device under test
|
||||
*
|
||||
* Use this method to access all public signals of the DUT:
|
||||
*
|
||||
* VerilatedToplevel ⊤
|
||||
* int clk_value = top->dut().IO_CLK;
|
||||
* top->dut().IO_CLK = 1;
|
||||
*/
|
||||
TOPLEVEL_NAME &dut();
|
||||
};
|
||||
|
||||
class TOPLEVEL_NAME : public VERILATED_TOPLEVEL_NAME, public VerilatedToplevel {
|
||||
public:
|
||||
TOPLEVEL_NAME(const char *name = "TOP")
|
||||
: VERILATED_TOPLEVEL_NAME(name), VerilatedToplevel() {}
|
||||
const char *name() const { return STR_AND_EXPAND(TOPLEVEL_NAME); }
|
||||
void eval() { VERILATED_TOPLEVEL_NAME::eval(); }
|
||||
void final() { VERILATED_TOPLEVEL_NAME::final(); }
|
||||
void trace(VerilatedTracer &tfp, int levels, int options = 0) {
|
||||
#if VM_TRACE == 1
|
||||
VERILATED_TOPLEVEL_NAME::trace(static_cast<VM_TRACE_CLASS_NAME *>(tfp),
|
||||
levels, options);
|
||||
#else
|
||||
assert(0 && "Tracing not enabled.");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_VERILATED_TOPLEVEL_H_
|
394
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc
vendored
Normal file
394
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.cc
vendored
Normal file
|
@ -0,0 +1,394 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "verilator_sim_ctrl.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <verilated.h>
|
||||
|
||||
// This is defined by Verilator and passed through the command line
|
||||
#ifndef VM_TRACE
|
||||
#define VM_TRACE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the current simulation time
|
||||
*
|
||||
* Called by $time in Verilog, converts to double, to match what SystemC does
|
||||
*/
|
||||
double sc_time_stamp() { return VerilatorSimCtrl::GetInstance().GetTime(); }
|
||||
|
||||
VerilatorSimCtrl &VerilatorSimCtrl::GetInstance() {
|
||||
static VerilatorSimCtrl instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::SetTop(VerilatedToplevel *top, CData *sig_clk,
|
||||
CData *sig_rst, VerilatorSimCtrlFlags flags) {
|
||||
top_ = top;
|
||||
sig_clk_ = sig_clk;
|
||||
sig_rst_ = sig_rst;
|
||||
flags_ = flags;
|
||||
}
|
||||
|
||||
int VerilatorSimCtrl::Exec(int argc, char **argv) {
|
||||
bool exit_app = false;
|
||||
if (!ParseCommandArgs(argc, argv, exit_app)) {
|
||||
return 1;
|
||||
}
|
||||
if (exit_app) {
|
||||
// Successful exit requested by command argument parsing
|
||||
return 0;
|
||||
}
|
||||
|
||||
RunSimulation();
|
||||
|
||||
if (!WasSimulationSuccessful()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::ParseCommandArgs(int argc, char **argv, bool &exit_app) {
|
||||
const struct option long_options[] = {
|
||||
{"term-after-cycles", required_argument, nullptr, 'c'},
|
||||
{"trace", no_argument, nullptr, 't'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{nullptr, no_argument, nullptr, 0}};
|
||||
|
||||
while (1) {
|
||||
int c = getopt_long(argc, argv, ":c:th", long_options, nullptr);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Disable error reporting by getopt
|
||||
opterr = 0;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
break;
|
||||
case 't':
|
||||
if (!tracing_possible_) {
|
||||
std::cerr << "ERROR: Tracing has not been enabled at compile time."
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
TraceOn();
|
||||
break;
|
||||
case 'c':
|
||||
term_after_cycles_ = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
PrintHelp();
|
||||
exit_app = true;
|
||||
break;
|
||||
case ':': // missing argument
|
||||
std::cerr << "ERROR: Missing argument." << std::endl << std::endl;
|
||||
return false;
|
||||
case '?':
|
||||
default:;
|
||||
// Ignore unrecognized options since they might be consumed by
|
||||
// Verilator's built-in parsing below.
|
||||
}
|
||||
}
|
||||
|
||||
// Pass args to verilator
|
||||
Verilated::commandArgs(argc, argv);
|
||||
|
||||
// Parse arguments for all registered extensions
|
||||
for (auto it = extension_array_.begin(); it != extension_array_.end(); ++it) {
|
||||
if (!(*it)->ParseCLIArguments(argc, argv, exit_app)) {
|
||||
return false;
|
||||
if (exit_app) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::RunSimulation() {
|
||||
RegisterSignalHandler();
|
||||
|
||||
// Print helper message for tracing
|
||||
if (TracingPossible()) {
|
||||
std::cout << "Tracing can be toggled by sending SIGUSR1 to this process:"
|
||||
<< std::endl
|
||||
<< "$ kill -USR1 " << getpid() << std::endl;
|
||||
}
|
||||
// Call all extension pre-exec methods
|
||||
for (auto it = extension_array_.begin(); it != extension_array_.end(); ++it) {
|
||||
(*it)->PreExec();
|
||||
}
|
||||
// Run the simulation
|
||||
Run();
|
||||
// Call all extension post-exec methods
|
||||
for (auto it = extension_array_.begin(); it != extension_array_.end(); ++it) {
|
||||
(*it)->PostExec();
|
||||
}
|
||||
// Print simulation speed info
|
||||
PrintStatistics();
|
||||
// Print helper message for tracing
|
||||
if (TracingEverEnabled()) {
|
||||
std::cout << std::endl
|
||||
<< "You can view the simulation traces by calling" << std::endl
|
||||
<< "$ gtkwave " << GetTraceFileName() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::SetInitialResetDelay(unsigned int cycles) {
|
||||
initial_reset_delay_cycles_ = cycles;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::SetResetDuration(unsigned int cycles) {
|
||||
reset_duration_cycles_ = cycles;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::RequestStop(bool simulation_success) {
|
||||
request_stop_ = true;
|
||||
simulation_success_ &= simulation_success;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::RegisterExtension(SimCtrlExtension *ext) {
|
||||
extension_array_.push_back(ext);
|
||||
}
|
||||
|
||||
VerilatorSimCtrl::VerilatorSimCtrl()
|
||||
: top_(nullptr),
|
||||
time_(0),
|
||||
tracing_enabled_(false),
|
||||
tracing_enabled_changed_(false),
|
||||
tracing_ever_enabled_(false),
|
||||
tracing_possible_(VM_TRACE),
|
||||
initial_reset_delay_cycles_(2),
|
||||
reset_duration_cycles_(2),
|
||||
request_stop_(false),
|
||||
simulation_success_(true),
|
||||
tracer_(VerilatedTracer()),
|
||||
term_after_cycles_(0) {}
|
||||
|
||||
void VerilatorSimCtrl::RegisterSignalHandler() {
|
||||
struct sigaction sigIntHandler;
|
||||
|
||||
sigIntHandler.sa_handler = SignalHandler;
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
sigaction(SIGUSR1, &sigIntHandler, NULL);
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::SignalHandler(int sig) {
|
||||
VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance();
|
||||
|
||||
switch (sig) {
|
||||
case SIGINT:
|
||||
simctrl.RequestStop(true);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
if (simctrl.TracingEnabled()) {
|
||||
simctrl.TraceOff();
|
||||
} else {
|
||||
simctrl.TraceOn();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::PrintHelp() const {
|
||||
std::cout << "Execute a simulation model for " << GetName() << "\n\n";
|
||||
if (tracing_possible_) {
|
||||
std::cout << "-t|--trace\n"
|
||||
" Write a trace file from the start\n\n";
|
||||
}
|
||||
std::cout << "-c|--term-after-cycles=N\n"
|
||||
" Terminate simulation after N cycles\n\n"
|
||||
"-h|--help\n"
|
||||
" Show help\n\n"
|
||||
"All arguments are passed to the design and can be used "
|
||||
"in the design, e.g. by DPI modules.\n\n";
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::TraceOn() {
|
||||
bool old_tracing_enabled = tracing_enabled_;
|
||||
|
||||
tracing_enabled_ = tracing_possible_;
|
||||
tracing_ever_enabled_ = tracing_enabled_;
|
||||
|
||||
if (old_tracing_enabled != tracing_enabled_) {
|
||||
tracing_enabled_changed_ = true;
|
||||
}
|
||||
return tracing_enabled_;
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::TraceOff() {
|
||||
if (tracing_enabled_) {
|
||||
tracing_enabled_changed_ = true;
|
||||
}
|
||||
tracing_enabled_ = false;
|
||||
return tracing_enabled_;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::PrintStatistics() const {
|
||||
double speed_hz = time_ / 2 / (GetExecutionTimeMs() / 1000.0);
|
||||
double speed_khz = speed_hz / 1000.0;
|
||||
|
||||
std::cout << std::endl
|
||||
<< "Simulation statistics" << std::endl
|
||||
<< "=====================" << std::endl
|
||||
<< "Executed cycles: " << time_ / 2 << std::endl
|
||||
<< "Wallclock time: " << GetExecutionTimeMs() / 1000.0 << " s"
|
||||
<< std::endl
|
||||
<< "Simulation speed: " << speed_hz << " cycles/s "
|
||||
<< "(" << speed_khz << " kHz)" << std::endl;
|
||||
|
||||
int trace_size_byte;
|
||||
if (tracing_enabled_ && FileSize(GetTraceFileName(), trace_size_byte)) {
|
||||
std::cout << "Trace file size: " << trace_size_byte << " B" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
const char *VerilatorSimCtrl::GetTraceFileName() const {
|
||||
#ifdef VM_TRACE_FMT_FST
|
||||
return "sim.fst";
|
||||
#else
|
||||
return "sim.vcd";
|
||||
#endif
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::Run() {
|
||||
assert(top_ && "Use SetTop() first.");
|
||||
|
||||
// We always need to enable this as tracing can be enabled at runtime
|
||||
if (tracing_possible_) {
|
||||
Verilated::traceEverOn(true);
|
||||
top_->trace(tracer_, 99, 0);
|
||||
}
|
||||
|
||||
// Evaluate all initial blocks, including the DPI setup routines
|
||||
top_->eval();
|
||||
|
||||
std::cout << std::endl
|
||||
<< "Simulation running, end by pressing CTRL-c." << std::endl;
|
||||
|
||||
time_begin_ = std::chrono::steady_clock::now();
|
||||
UnsetReset();
|
||||
Trace();
|
||||
while (1) {
|
||||
if (time_ / 2 >= initial_reset_delay_cycles_) {
|
||||
SetReset();
|
||||
}
|
||||
if (time_ / 2 >= reset_duration_cycles_ + initial_reset_delay_cycles_) {
|
||||
UnsetReset();
|
||||
}
|
||||
|
||||
*sig_clk_ = !*sig_clk_;
|
||||
|
||||
// Call all extension on-clock methods
|
||||
if (*sig_clk_) {
|
||||
for (auto it = extension_array_.begin(); it != extension_array_.end();
|
||||
++it) {
|
||||
(*it)->OnClock(time_);
|
||||
}
|
||||
}
|
||||
|
||||
top_->eval();
|
||||
time_++;
|
||||
|
||||
Trace();
|
||||
|
||||
if (request_stop_) {
|
||||
std::cout << "Received stop request, shutting down simulation."
|
||||
<< std::endl;
|
||||
break;
|
||||
}
|
||||
if (Verilated::gotFinish()) {
|
||||
std::cout << "Received $finish() from Verilog, shutting down simulation."
|
||||
<< std::endl;
|
||||
break;
|
||||
}
|
||||
if (term_after_cycles_ && (time_ / 2 >= term_after_cycles_)) {
|
||||
std::cout << "Simulation timeout of " << term_after_cycles_
|
||||
<< " cycles reached, shutting down simulation." << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
top_->final();
|
||||
time_end_ = std::chrono::steady_clock::now();
|
||||
|
||||
if (TracingEverEnabled()) {
|
||||
tracer_.close();
|
||||
}
|
||||
}
|
||||
|
||||
std::string VerilatorSimCtrl::GetName() const {
|
||||
if (top_) {
|
||||
return top_->name();
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
unsigned int VerilatorSimCtrl::GetExecutionTimeMs() const {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(time_end_ -
|
||||
time_begin_)
|
||||
.count();
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::SetReset() {
|
||||
if (flags_ & ResetPolarityNegative) {
|
||||
*sig_rst_ = 0;
|
||||
} else {
|
||||
*sig_rst_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::UnsetReset() {
|
||||
if (flags_ & ResetPolarityNegative) {
|
||||
*sig_rst_ = 1;
|
||||
} else {
|
||||
*sig_rst_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool VerilatorSimCtrl::FileSize(std::string filepath, int &size_byte) const {
|
||||
struct stat statbuf;
|
||||
if (stat(filepath.data(), &statbuf) != 0) {
|
||||
size_byte = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_byte = statbuf.st_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VerilatorSimCtrl::Trace() {
|
||||
// We cannot output a message when calling TraceOn()/TraceOff() as these
|
||||
// functions can be called from a signal handler. Instead we print the message
|
||||
// here from the main loop.
|
||||
if (tracing_enabled_changed_) {
|
||||
if (TracingEnabled()) {
|
||||
std::cout << "Tracing enabled." << std::endl;
|
||||
} else {
|
||||
std::cout << "Tracing disabled." << std::endl;
|
||||
}
|
||||
tracing_enabled_changed_ = false;
|
||||
}
|
||||
|
||||
if (!TracingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tracer_.isOpen()) {
|
||||
tracer_.open(GetTraceFileName());
|
||||
std::cout << "Writing simulation traces to " << GetTraceFileName()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
tracer_.dump(GetTime());
|
||||
}
|
240
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h
vendored
Normal file
240
vendor/lowrisc_ip/dv/verilator/simutil_verilator/cpp/verilator_sim_ctrl.h
vendored
Normal file
|
@ -0,0 +1,240 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#ifndef OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_VERILATOR_SIM_CTRL_H_
|
||||
#define OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_VERILATOR_SIM_CTRL_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "sim_ctrl_extension.h"
|
||||
#include "verilated_toplevel.h"
|
||||
|
||||
enum VerilatorSimCtrlFlags {
|
||||
Defaults = 0,
|
||||
ResetPolarityNegative = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Simulation controller for verilated simulations
|
||||
*/
|
||||
class VerilatorSimCtrl {
|
||||
public:
|
||||
/**
|
||||
* Get the simulation controller instance
|
||||
*
|
||||
* @see SetTop()
|
||||
*/
|
||||
static VerilatorSimCtrl &GetInstance();
|
||||
|
||||
VerilatorSimCtrl(VerilatorSimCtrl const &) = delete;
|
||||
void operator=(VerilatorSimCtrl const &) = delete;
|
||||
|
||||
/**
|
||||
* Set the top-level design
|
||||
*/
|
||||
void SetTop(VerilatedToplevel *top, CData *sig_clk, CData *sig_rst,
|
||||
VerilatorSimCtrlFlags flags = Defaults);
|
||||
|
||||
/**
|
||||
* Setup and run the simulation (all in one)
|
||||
*
|
||||
* Use this function as high-level entry point, suitable for most use cases.
|
||||
*
|
||||
* SetTop() must be called before this function.
|
||||
*
|
||||
* This function performs the following tasks:
|
||||
* 1. Parses a C-style set of command line arguments (see ParseCommandArgs())
|
||||
* 2. Runs the simulation (see RunSimulation())
|
||||
*
|
||||
* @return a main()-compatible process exit code: 0 for success, 1 in case
|
||||
* of an error.
|
||||
*/
|
||||
int Exec(int argc, char **argv);
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*
|
||||
* Process all recognized command-line arguments from argc/argv.
|
||||
*
|
||||
* @param argc, argv Standard C command line arguments
|
||||
* @param exit_app Indicate that program should terminate
|
||||
* @return Return code, true == success
|
||||
*/
|
||||
bool ParseCommandArgs(int argc, char **argv, bool &exit_app);
|
||||
|
||||
/**
|
||||
* A helper function to execute a standard set of run commands.
|
||||
*
|
||||
* This function performs the following tasks:
|
||||
* 1. Sets up a signal handler to enable tracing to be turned on/off during
|
||||
* a run by sending SIGUSR1 to the process
|
||||
* 2. Prints some tracer-related helper messages
|
||||
* 3. Runs the simulation
|
||||
* 4. Prints some further helper messages and statistics once the simulation
|
||||
* has run to completion
|
||||
*/
|
||||
void RunSimulation();
|
||||
|
||||
/**
|
||||
* Get the simulation result
|
||||
*/
|
||||
bool WasSimulationSuccessful() const { return simulation_success_; }
|
||||
|
||||
/**
|
||||
* Set the number of clock cycles (periods) before the reset signal is
|
||||
* activated
|
||||
*/
|
||||
void SetInitialResetDelay(unsigned int cycles);
|
||||
|
||||
/**
|
||||
* Set the number of clock cycles (periods) the reset signal is activated
|
||||
*/
|
||||
void SetResetDuration(unsigned int cycles);
|
||||
|
||||
/**
|
||||
* Request the simulation to stop
|
||||
*/
|
||||
void RequestStop(bool simulation_success);
|
||||
|
||||
/**
|
||||
* Register an extension to be called automatically
|
||||
*/
|
||||
void RegisterExtension(SimCtrlExtension *ext);
|
||||
|
||||
/**
|
||||
* Get the current time in ticks
|
||||
*/
|
||||
unsigned long GetTime() const { return time_; }
|
||||
|
||||
private:
|
||||
VerilatedToplevel *top_;
|
||||
CData *sig_clk_;
|
||||
CData *sig_rst_;
|
||||
VerilatorSimCtrlFlags flags_;
|
||||
unsigned long time_;
|
||||
bool tracing_enabled_;
|
||||
bool tracing_enabled_changed_;
|
||||
bool tracing_ever_enabled_;
|
||||
bool tracing_possible_;
|
||||
unsigned int initial_reset_delay_cycles_;
|
||||
unsigned int reset_duration_cycles_;
|
||||
volatile unsigned int request_stop_;
|
||||
volatile bool simulation_success_;
|
||||
std::chrono::steady_clock::time_point time_begin_;
|
||||
std::chrono::steady_clock::time_point time_end_;
|
||||
VerilatedTracer tracer_;
|
||||
int term_after_cycles_;
|
||||
std::vector<SimCtrlExtension *> extension_array_;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* Use GetInstance() instead.
|
||||
*/
|
||||
VerilatorSimCtrl();
|
||||
|
||||
/**
|
||||
* Register the signal handler
|
||||
*/
|
||||
void RegisterSignalHandler();
|
||||
|
||||
/**
|
||||
* Signal handler callback
|
||||
*
|
||||
* Use RegisterSignalHandler() to setup.
|
||||
*/
|
||||
static void SignalHandler(int sig);
|
||||
|
||||
/**
|
||||
* Print help how to use this tool
|
||||
*/
|
||||
void PrintHelp() const;
|
||||
|
||||
/**
|
||||
* Enable tracing (if possible)
|
||||
*
|
||||
* Enabling tracing can fail if no tracing support has been compiled into the
|
||||
* simulation.
|
||||
*
|
||||
* @return Is tracing enabled?
|
||||
*/
|
||||
bool TraceOn();
|
||||
|
||||
/**
|
||||
* Disable tracing
|
||||
*
|
||||
* @return Is tracing enabled?
|
||||
*/
|
||||
bool TraceOff();
|
||||
|
||||
/**
|
||||
* Is tracing currently enabled?
|
||||
*/
|
||||
bool TracingEnabled() const { return tracing_enabled_; }
|
||||
|
||||
/**
|
||||
* Has tracing been ever enabled during the run?
|
||||
*
|
||||
* Tracing can be enabled and disabled at runtime.
|
||||
*/
|
||||
bool TracingEverEnabled() const { return tracing_ever_enabled_; }
|
||||
|
||||
/**
|
||||
* Is tracing support compiled into the simulation?
|
||||
*/
|
||||
bool TracingPossible() const { return tracing_possible_; }
|
||||
|
||||
/**
|
||||
* Print statistics about the simulation run
|
||||
*/
|
||||
void PrintStatistics() const;
|
||||
|
||||
/**
|
||||
* Get the file name of the trace file
|
||||
*/
|
||||
const char *GetTraceFileName() const;
|
||||
|
||||
/**
|
||||
* Run the main loop of the simulation
|
||||
*
|
||||
* This function blocks until the simulation finishes.
|
||||
*/
|
||||
void Run();
|
||||
|
||||
/**
|
||||
* Get a name for this simulation
|
||||
*
|
||||
* This name is typically the name of the top-level.
|
||||
*/
|
||||
std::string GetName() const;
|
||||
|
||||
/**
|
||||
* Get the wallclock execution time in ms
|
||||
*/
|
||||
unsigned int GetExecutionTimeMs() const;
|
||||
|
||||
/**
|
||||
* Assert the reset signal
|
||||
*/
|
||||
void SetReset();
|
||||
|
||||
/**
|
||||
* Deassert the reset signal
|
||||
*/
|
||||
void UnsetReset();
|
||||
|
||||
/**
|
||||
* Return the size of a file
|
||||
*/
|
||||
bool FileSize(std::string filepath, int &size_byte) const;
|
||||
|
||||
/**
|
||||
* Perform tracing in Verilator if required
|
||||
*/
|
||||
void Trace();
|
||||
};
|
||||
|
||||
#endif // OPENTITAN_HW_DV_VERILATOR_SIMUTIL_VERILATOR_CPP_VERILATOR_SIM_CTRL_H_
|
21
vendor/lowrisc_ip/dv/verilator/simutil_verilator/simutil_verilator.core
vendored
Normal file
21
vendor/lowrisc_ip/dv/verilator/simutil_verilator/simutil_verilator.core
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
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_verilator:simutil_verilator"
|
||||
description: "Verilator simulator support"
|
||||
filesets:
|
||||
files_cpp:
|
||||
files:
|
||||
- cpp/verilator_sim_ctrl.cc
|
||||
- cpp/verilated_toplevel.cc
|
||||
- cpp/verilator_sim_ctrl.h: { is_include_file: true }
|
||||
- cpp/verilated_toplevel.h: { is_include_file: true }
|
||||
- cpp/sim_ctrl_extension.h: { is_include_file: true }
|
||||
file_type: cppSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_cpp
|
Loading…
Add table
Add a link
Reference in a new issue