[rv_dm, dv] Fixes for sberror=2,7 support
Previously, if the SBA TL access returned an error response,
it would have not impact (i.e. it would not show up) in the
DMI SBCS status register, and the debugger would have no way
of knowing an erroneous response occurred. Likewise, if the
SBA TL response had a data integrity error, it would also not
show up.
The recent changes to the PULP debug module added support for
indicating device error response as sberror=2 and also allowed
TL device intg error to be indicated as other error (sberror=7).
This commit updates the sba_access_utils_pkg (monitor and the
utility tasks) and the RV_DM testbench (sequences and the
scoreboard with fixes needed to support this design change.
The sba access monitor can only predict sberror=3,4. It now
wires the actual sberror value seen when polling the sbcs for
status into the predicted SBA access item that is written to the
analysis port. The rv_dm scoreboard which retrieves the item
checks the correctness of sberror=2,7 based on the monitored
TL transaction.
The sba access util tasks have minor flow related enhancements
to ensure the accesses to SBA registers in the DMI space are
sequenced corrected to properly facilitate a SBA TL access.
The rv_dm sba test sequences have similar fixes to properly
sequence events to avoid unexpected bahaviors. The injection of
SBA TL device error response and SBA TL device intg error is
now disbled in the base sequence class, which now does only
does clean accesses. These are constrained-randomized in the
extended vseq classes instead, since they have a real impact
on the behavior of the PULP debug module's SBA logic.
Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/hw/dv/sv/jtag_dmi_agent/sba_access_item.sv b/hw/dv/sv/jtag_dmi_agent/sba_access_item.sv
index e8ea9bb..d8f5afa 100644
--- a/hw/dv/sv/jtag_dmi_agent/sba_access_item.sv
+++ b/hw/dv/sv/jtag_dmi_agent/sba_access_item.sv
@@ -7,28 +7,38 @@
rand bus_op_e bus_op;
rand sba_access_size_e size;
rand logic [BUS_AW-1:0] addr;
- rand logic [BUS_DW-1:0] wdata;
+ rand logic [BUS_DW-1:0] wdata[$];
// Special stimulus configuration knobs.
rand logic readonaddr;
rand logic readondata;
- rand logic autoincrement;
+
+ // Have the design autoincrement the address.
+ //
+ // If set to 0, the autoincrement feature is disabled and a single access is made. If set to N (N
+ // > 0), N + 1 accesses are made by the sba_access_utils_pkg::sba_acess() task. This is only used
+ // during the generation of stimulus. It is not used by the sba_access_monitor.
+ uint autoincrement;
// Response side signals.
- rand logic [BUS_DW-1:0] rdata;
- rand sba_access_err_e is_err;
- rand logic is_busy_err;
- rand logic timed_out;
+ logic [BUS_DW-1:0] rdata[$];
+ sba_access_err_e is_err;
+ logic is_busy_err;
+ logic timed_out;
+
+ constraint wdata_size_c {
+ wdata.size() == (autoincrement + 1);
+ }
`uvm_object_utils_begin(sba_access_item)
`uvm_field_enum(bus_op_e, bus_op, UVM_DEFAULT)
`uvm_field_enum(sba_access_size_e, size, UVM_DEFAULT)
`uvm_field_int (addr, UVM_DEFAULT)
- `uvm_field_int (wdata, UVM_DEFAULT)
+ `uvm_field_queue_int(wdata, UVM_DEFAULT)
`uvm_field_int (readonaddr, UVM_DEFAULT)
`uvm_field_int (readondata, UVM_DEFAULT)
`uvm_field_int (autoincrement, UVM_DEFAULT)
- `uvm_field_int (rdata, UVM_DEFAULT)
+ `uvm_field_queue_int(rdata, UVM_DEFAULT)
`uvm_field_int (is_busy_err, UVM_DEFAULT)
`uvm_field_enum(sba_access_err_e, is_err, UVM_DEFAULT)
`uvm_field_int (timed_out, UVM_DEFAULT)
@@ -36,21 +46,4 @@
`uvm_object_new
- virtual function void disable_req_randomization();
- bus_op.rand_mode(0);
- addr.rand_mode(0);
- size.rand_mode(0);
- wdata.rand_mode(0);
- readonaddr.rand_mode(0);
- readondata.rand_mode(0);
- autoincrement.rand_mode(0);
- endfunction
-
- virtual function void disable_rsp_randomization();
- rdata.rand_mode(0);
- is_err.rand_mode(0);
- is_busy_err.rand_mode(0);
- timed_out.rand_mode(0);
- endfunction
-
endclass
diff --git a/hw/dv/sv/jtag_dmi_agent/sba_access_monitor.sv b/hw/dv/sv/jtag_dmi_agent/sba_access_monitor.sv
index 5eef3c1..f36cb56 100644
--- a/hw/dv/sv/jtag_dmi_agent/sba_access_monitor.sv
+++ b/hw/dv/sv/jtag_dmi_agent/sba_access_monitor.sv
@@ -111,6 +111,10 @@
end
endtask
+ // Predict what is expected to happen if one of the SBA registers is read.
+ //
+ // If the sbcs register is read, and sbbusy bit drops on a pending write, we consider the
+ // transaction to have completed - we write the predicted SBA transaction to the analysis port.
virtual protected function void process_sba_csr_read(uvm_reg csr, jtag_dmi_item dmi_item);
case (csr.get_name())
"sbcs": begin
@@ -118,22 +122,37 @@
uvm_reg_data_t sbbusyerror = get_field_val(jtag_dmi_ral.sbcs.sbbusyerror, dmi_item.rdata);
uvm_reg_data_t sberror = get_field_val(jtag_dmi_ral.sbcs.sberror, dmi_item.rdata);
- // We should have predicted an SBA access if sbbusy got set.
- if (sbbusy) `DV_CHECK(sba_req_q.size(), "sbbusy indicated, but no SBA access was predicted")
+ // We should have predicted an SBA access if any of the status bits got set.
+ if (sbbusy || sbbusyerror) begin
+ `DV_CHECK(sba_req_q.size(),
+ $sformatf({"One of these bits is set, but no SBA access was predicted: ",
+ "sbbusy=%0b, sbbusyerror=%0b"}, sbbusy, sbbusyerror))
+ end
- // Check if our error predictions were correct.
+ // Check if we correctly predicted busy error.
`DV_CHECK_EQ(sbbusyerror, jtag_dmi_ral.sbcs.sbbusyerror.get_mirrored_value())
- `DV_CHECK_EQ(sberror, jtag_dmi_ral.sbcs.sberror.get_mirrored_value())
+ if (sbbusyerror) sba_req_q[0].is_busy_err = 1'b1;
+
+ // Check if we correctly predicted the malformed SBA access request errors.
+ //
+ // We can only predict SbaErrBadAlignment and SbaErrBadSize errors. For the externally
+ // indicated errors SbaErrTimeout, SbaErrBadAddr and SbaErrOther, we pass the actually seen
+ // sberror to the sba_access_item that is written to the analysis port. The external entity
+ // reading from this port is expected to verify the correctness of these errors.
+ if (sberror inside {SbaErrNone, SbaErrBadAlignment, SbaErrBadSize}) begin
+ `DV_CHECK_EQ(sberror, jtag_dmi_ral.sbcs.sberror.get_mirrored_value())
+ end
if (sba_req_q.size() > 0) begin
+ if (sberror) sba_req_q[0].is_err = sba_access_err_e'(sberror);
if (!sbbusy) begin
- predict_autoincr_sba_addr();
- if (sba_req_q[0].bus_op == BusOpWrite) begin
- // Mark the write access as complete if sbbusy deasserted.
+ // Write the predicted SBA transaction to the analysis port when sbbusy de-asserts in
+ // the following scenarios - a bus write, a bus read when readondata is set.
+ if (sba_req_q[0].bus_op == BusOpWrite ||
+ (sba_req_q[0].bus_op == BusOpRead && `gmv(jtag_dmi_ral.sbcs.sbreadondata))) begin
ITEM_T tr = sba_req_q.pop_front();
- tr.is_err = SbaErrNone;
- tr.is_busy_err = 0;
analysis_port.write(tr);
+ predict_autoincr_sba_addr();
end
end
end
@@ -146,12 +165,18 @@
if (sba_req_q.size() > 0) begin
// If SBA read access completed, then return the data read from this register. We count on
// stimulus to have read the sbcs register before to ensure the access actually completed.
+ // The external scoreboard is expected to verify the correctness of externally indicated
+ // errors SbaErrTimeout, SbaErrBadAddr and SbaErrOther, when the stimulus reads the sbcs
+ // register during a pending SBA read transaction.
+ //
+ // The stimulus (in sba_access_utils_pkg::sba_access()) terminates SBA access on sbdata0
+ // read when readonaddr is set and readondata is not set. If readondata is set, then
+ // accesses terminate at sbcs read. The monitor bases its predictions on this behavior.
if (sba_req_q[0].bus_op == BusOpRead &&
- !jtag_dmi_ral.sbcs.sbbusy.get_mirrored_value()) begin
+ !jtag_dmi_ral.sbcs.sbbusy.get_mirrored_value() &&
+ (`gmv(jtag_dmi_ral.sbcs.sbreadonaddr) && !`gmv(jtag_dmi_ral.sbcs.sbreadondata))) begin
ITEM_T tr = sba_req_q.pop_front();
- tr.rdata = dmi_item.rdata;
- tr.is_err = SbaErrNone;
- tr.is_busy_err = 0;
+ tr.rdata[0] = dmi_item.rdata;
analysis_port.write(tr);
end
end
@@ -167,6 +192,7 @@
endcase
endfunction
+ // Predict what is expected to happen if one of the SBA registers is written.
virtual protected function void process_sba_csr_write(uvm_reg csr);
case (csr.get_name())
"sbcs": begin
@@ -253,9 +279,12 @@
item.is_err = SbaErrNone;
item.is_busy_err = 0;
item.timed_out = 0;
- if (bus_op == BusOpWrite) item.wdata = data;
-
- `DV_CHECK_EQ(sba_req_q.size(), 0)
+ if (bus_op == BusOpWrite) item.wdata[0] = data;
+ `uvm_info(`gfn, $sformatf("Predicted new SBA req: %0s",
+ item.sprint(uvm_default_line_printer)), UVM_MEDIUM)
+ `DV_CHECK_EQ(sba_req_q.size(), 0,
+ $sformatf("Predicted new SBA req before previous req %0s was popped.",
+ sba_req_q[$].sprint(uvm_default_line_printer)))
sba_req_q.push_back(item);
req_analysis_port.write(item);
void'(jtag_dmi_ral.sbcs.sbbusy.predict(.value(1), .kind(UVM_PREDICT_DIRECT)));
diff --git a/hw/dv/sv/jtag_dmi_agent/sba_access_utils_pkg.sv b/hw/dv/sv/jtag_dmi_agent/sba_access_utils_pkg.sv
index ee3a810..c7db43e 100644
--- a/hw/dv/sv/jtag_dmi_agent/sba_access_utils_pkg.sv
+++ b/hw/dv/sv/jtag_dmi_agent/sba_access_utils_pkg.sv
@@ -50,7 +50,6 @@
input bit sba_access_err_clear = 1'b1);
uvm_reg_data_t rdata, wdata;
- logic is_busy;
// Update sbcs for the new transaction.
wdata = jtag_dmi_ral.sbcs.get_mirrored_value();
@@ -64,45 +63,94 @@
wdata = get_csr_val_with_updated_field(jtag_dmi_ral.sbcs.sbreadondata, wdata, 0);
end
wdata = get_csr_val_with_updated_field(jtag_dmi_ral.sbcs.sbautoincrement, wdata,
- req.autoincrement);
+ (req.autoincrement > 0));
if (wdata != jtag_dmi_ral.sbcs.sbaccess.get_mirrored_value()) begin
csr_wr(.ptr(jtag_dmi_ral.sbcs), .value(wdata), .predict(1));
end
+ // Update the sbaddress0.
csr_wr(.ptr(jtag_dmi_ral.sbaddress0), .value(req.addr), .predict(1));
+
+ // These steps are run in several branches in the following code. Macroize them.
+`define BUSYWAIT_AND_EXIT_ON_ERR \
+ sba_access_busy_wait_and_clear_error(.jtag_dmi_ral(jtag_dmi_ral), .cfg(cfg), .req(req), \
+ .sba_access_err_clear(sba_access_err_clear)); \
+ \
+ // We don't know what to do if any of the following is true - return to the caller: \
+ // \
+ // - The request timed out or returned is_busy_err \
+ // - The access reported non-0 sberror and sba_access_err_clear is set to 0 \
+ // - Request was malformed (An SBA access is not made in the case) \
+ // - The access returned SbaErrOther error response (The DUT may need special handling) \
+ if (req.timed_out || \
+ req.is_busy_err || \
+ (req.is_err != SbaErrNone && !sba_access_err_clear) || \
+ req.is_err inside {SbaErrBadAlignment, SbaErrBadSize, SbaErrOther}) begin \
+ return; \
+ end \
+
+ // Write / read sbdata, wait for transaction to complete.
if (req.bus_op == BusOpRead) begin
- // Writing to addr with readonaddr would have already trigger an SBA read.
- if (req.readondata && !req.readonaddr) begin
- csr_rd(.ptr(jtag_dmi_ral.sbdata0), .value(rdata));
- end
- if (!req.readonaddr && !req.readondata) begin
- `uvm_info(MsgId, {"readonaddr and readondata are not set. ",
- "Read request will not be triggered. Returning."}, UVM_MEDIUM)
- return;
- end
+ case ({req.readondata, req.readonaddr})
+ 2'b00: begin
+ `uvm_info(MsgId, {"readonaddr and readondata are not set. ",
+ "Read request will not be triggered. Returning."}, UVM_MEDIUM)
+ end
+ 2'b01: begin
+ // SBA read already triggered on write to sbaddress0. Read sbdata0 to fetch the response
+ // data. If autoincrement is set, then return to the caller - it is not a valid usecase.
+ `BUSYWAIT_AND_EXIT_ON_ERR
+ if (!cfg.in_reset) begin
+ csr_rd(.ptr(jtag_dmi_ral.sbdata0), .value(rdata));
+ req.rdata[0] = rdata;
+ end
+ end
+ 2'b10, 2'b11: begin
+ if (!req.readonaddr) begin
+ // The first read to sbdata returns stale data. Discard it.
+ if (!cfg.in_reset) begin
+ csr_rd(.ptr(jtag_dmi_ral.sbdata0), .value(rdata));
+ end
+ end
+ // The previous step triggered an SBA read. Wait for it to complete.
+ `BUSYWAIT_AND_EXIT_ON_ERR
+
+ // Read sbdata req.autoincrement+1 number of times.
+ //
+ // Randomly also inject reads to sbaddress0 to verify address is correctly incremented
+ // (done externally by the scoreboard).
+ for (int i = 0; i < req.autoincrement + 1; i++) begin
+ if (!cfg.in_reset) begin
+ csr_rd(.ptr(jtag_dmi_ral.sbdata0), .value(rdata));
+ req.rdata[i] = rdata;
+ end
+ `BUSYWAIT_AND_EXIT_ON_ERR
+ if (i > 0 && !cfg.in_reset && !$urandom_range(0, 3)) begin //25%
+ csr_rd(.ptr(jtag_dmi_ral.sbaddress0), .value(rdata));
+ end
+ end
+ end
+ default: begin
+ `uvm_fatal(MsgId, "Unreachable!")
+ end
+ endcase
end else begin
- csr_wr(.ptr(jtag_dmi_ral.sbdata0), .value(req.wdata), .predict(1));
- end
-
- // Wait for the access to complete.
- sba_access_busy_wait(jtag_dmi_ral, cfg, req, is_busy);
-
- // If the access returns with an error, then the request was not made - return back to the
- // caller.
- if (req.is_busy_err || req.is_err != SbaErrNone) begin
- if (!cfg.in_reset && sba_access_err_clear) begin
- sba_access_error_clear(jtag_dmi_ral, req);
+ // Write sbdata req.autoincrement+1 number of times.
+ //
+ // Randomly also inject reads to sbaddress0 to verify address is correctly incremented
+ // (done externally by the scoreboard).
+ for (int i = 0; i < req.autoincrement + 1; i++) begin
+ if (!cfg.in_reset) begin
+ csr_wr(.ptr(jtag_dmi_ral.sbdata0), .value(req.wdata[i]), .predict(1));
+ end
+ `BUSYWAIT_AND_EXIT_ON_ERR
+ if (i > 0 && !cfg.in_reset && !$urandom_range(0, 3)) begin //25%
+ csr_rd(.ptr(jtag_dmi_ral.sbaddress0), .value(rdata));
+ end
end
- return;
end
- // Return the data on reads.
- if (req.bus_op == BusOpRead && !cfg.in_reset && !is_busy) begin
- csr_rd(.ptr(jtag_dmi_ral.sbdata0), .value(req.rdata));
- end
-
- // If readondata is set, the read above will trigger another SBA read, which needs to be handled
- // by the caller.
+`undef BUSYWAIT_AND_EXIT_ON_ERR
endtask
// Read the SBA access status.
@@ -114,15 +162,17 @@
is_busy = get_field_val(jtag_dmi_ral.sbcs.sbbusy, data);
req.is_busy_err = get_field_val(jtag_dmi_ral.sbcs.sbbusyerror, data);
req.is_err = sba_access_err_e'(get_field_val(jtag_dmi_ral.sbcs.sberror, data));
- `uvm_info(MsgId, $sformatf("SBA req status: %0s", req.sprint(uvm_default_line_printer)),
- UVM_HIGH)
+ if (!is_busy) begin
+ `uvm_info(MsgId, $sformatf("SBA req completed: %0s", req.sprint(uvm_default_line_printer)),
+ UVM_HIGH)
+ end
endtask
// Reads sbcs register to poll and wait for access to complete.
task automatic sba_access_busy_wait(input jtag_dmi_reg_block jtag_dmi_ral,
input jtag_agent_cfg cfg,
- input sba_access_item req,
- output logic is_busy);
+ input sba_access_item req);
+ logic is_busy;
`DV_SPINWAIT_EXIT(
do begin
sba_access_status(jtag_dmi_ral, req, is_busy);
@@ -148,17 +198,31 @@
// Clear SBA access busy error and access error sticky bits if they are set.
//
// Note that the req argument will continue to reflect the error bits.
- task automatic sba_access_error_clear(jtag_dmi_reg_block jtag_dmi_ral, sba_access_item req);
+ task automatic sba_access_error_clear(jtag_dmi_reg_block jtag_dmi_ral, sba_access_item req,
+ bit clear_sbbusyerror = 1'b1, bit clear_sberror = 1'b1);
uvm_reg_data_t data = jtag_dmi_ral.sbcs.get_mirrored_value();
- if (req.is_busy_err) begin
+ if (clear_sbbusyerror && req.is_busy_err) begin
data = get_csr_val_with_updated_field(jtag_dmi_ral.sbcs.sbbusyerror, data, 1);
end
- if (req.is_err != SbaErrNone) begin
+ if (clear_sberror && req.is_err != SbaErrNone) begin
data = get_csr_val_with_updated_field(jtag_dmi_ral.sbcs.sberror, data, 1);
end
csr_wr(.ptr(jtag_dmi_ral.sbcs), .value(data), .predict(1));
endtask
+ // Wrapper task for busy wait followed by clearing of sberror if an error was reported.
+ task automatic sba_access_busy_wait_and_clear_error(input jtag_dmi_reg_block jtag_dmi_ral,
+ input jtag_agent_cfg cfg,
+ input sba_access_item req,
+ input bit sba_access_err_clear);
+ sba_access_busy_wait(jtag_dmi_ral, cfg, req);
+ // Only clear sberror - let the caller handle sbbusyerror.
+ if (!cfg.in_reset && sba_access_err_clear && req.is_err != SbaErrNone) begin
+ sba_access_error_clear(.jtag_dmi_ral(jtag_dmi_ral), .req(req), .clear_sbbusyerror(0));
+ end
+ endtask
+
+
`include "sba_access_item.sv"
`include "sba_access_monitor.sv"
diff --git a/hw/ip/rv_dm/dv/env/rv_dm_scoreboard.sv b/hw/ip/rv_dm/dv/env/rv_dm_scoreboard.sv
index faedabb..9667678 100644
--- a/hw/ip/rv_dm/dv/env/rv_dm_scoreboard.sv
+++ b/hw/ip/rv_dm/dv/env/rv_dm_scoreboard.sv
@@ -177,7 +177,13 @@
sba_access_fifo.get(item);
`uvm_info(`gfn, $sformatf("Received SBA access item:\n%0s",
item.sprint(uvm_default_line_printer)), UVM_HIGH)
- compare_sba_access(item, sba_tl_access_q.pop_front());
+ if (sba_tl_access_q.size() > 0) begin
+ compare_sba_access(item, sba_tl_access_q.pop_front());
+ end else begin
+ `uvm_error(`gfn, $sformatf({"Received predicted SBA access but no transaction was seen on ",
+ "the SBA TL host interface: %0s"},
+ item.sprint(uvm_default_line_printer)))
+ end
end
endtask
@@ -229,7 +235,7 @@
// SBA system shifts the read data based on transfer size. The higher order bits that are
// don't care are left alone. The RISCV debug spec does not mandate that they be masked.
data = sba_tl_item.d_data >> shift;
- `DV_CHECK_EQ(sba_item.rdata, data, msg)
+ `DV_CHECK_EQ(sba_item.rdata[0], data, msg)
// TLUL adapter host does full word reads with all byte lanes enabled.
`DV_CHECK_EQ(sba_tl_item.a_mask, '1, msg)
end else begin
@@ -248,13 +254,17 @@
word_mask[i*8 +: 8] = 8'hff;
byte_mask[i] = 1'b1;
end
- data = (sba_item.wdata & word_mask) << shift;
+ data = (sba_item.wdata[0] & word_mask) << shift;
`DV_CHECK_EQ(data, sba_tl_item.a_data, msg)
byte_mask <<= sba_item.addr[BUS_SZW-1:0];
`DV_CHECK_EQ(byte_mask, sba_tl_item.a_mask, msg)
end
- // The d_chan may have intg errors and response error (d_error = 1). None of those reflect back
- // in the SBA interface.
+ // d_chan intg error is reported as "other" error and takes precedence over transaction error.
+ if (!sba_tl_item.is_d_chan_intg_ok(.throw_error(0))) begin
+ `DV_CHECK_EQ(sba_item.is_err, sba_access_utils_pkg::SbaErrOther)
+ end else if (sba_tl_item.d_error) begin
+ `DV_CHECK_EQ(sba_item.is_err, sba_access_utils_pkg::SbaErrBadAddr)
+ end
`DV_CHECK_EQ(sba_tl_item.a_source, 0, msg)
endfunction
@@ -336,6 +346,7 @@
virtual function void reset(string kind = "HARD");
super.reset(kind);
+ sba_tl_access_q.delete();
jtag_non_dmi_dtm_fifo.flush();
jtag_non_sba_dmi_fifo.flush();
tl_sba_a_chan_fifo.flush();
diff --git a/hw/ip/rv_dm/dv/env/seq_lib/rv_dm_sba_tl_access_vseq_lib.sv b/hw/ip/rv_dm/dv/env/seq_lib/rv_dm_sba_tl_access_vseq_lib.sv
index cd59a45..b52b199 100644
--- a/hw/ip/rv_dm/dv/env/seq_lib/rv_dm_sba_tl_access_vseq_lib.sv
+++ b/hw/ip/rv_dm/dv/env/seq_lib/rv_dm_sba_tl_access_vseq_lib.sv
@@ -24,22 +24,17 @@
rand int num_times;
rand sba_access_item req;
- rand bit bad_access;
- rand bit autoincr;
- rand uint autoincr_length;
-
- constraint autoincr_length_c {
- autoincr_length dist { [0:24] :/ 4, [25:50] :/ 4, [51:200] :/ 2};
- }
+ rand bit bad_req;
+ rand uint autoincrement;
// Base class only does legal accesses.
- constraint bad_access_c {
- bad_access == 0;
+ constraint bad_req_c {
+ bad_req == 0;
}
// Base class only does single accesses.
- constraint autoincr_c {
- autoincr == 0;
+ constraint autoincrement_c {
+ autoincrement == 0;
}
// To generate reads to the same addr followed by previous writes.
@@ -60,14 +55,14 @@
max_rsp_delay inside {[min_rsp_delay:min_rsp_delay+100]};
}
+ // Base class does not set d_error=1 on SBA TL responses.
constraint d_error_pct_c {
- d_error_pct inside {[0:100]};
- d_error_pct dist { 0 :/ 6, [1:20] :/ 2, [21:99] :/ 1, 100 :/ 1};
+ d_error_pct == 0;
}
+ // Base class does not inject TL intg error on SBA TL responses.
constraint d_chan_intg_err_pct_c {
- d_chan_intg_err_pct inside {[0:100]};
- d_chan_intg_err_pct dist { 0 :/ 7, [1:20] :/ 2, [21:100] :/ 1};
+ d_chan_intg_err_pct == 0;
}
// TODO: Randomize these controls every num_times iteration.
@@ -114,62 +109,21 @@
num_trans.rand_mode(0);
for (int j = 1; j <= num_trans; j++) begin
req = sba_access_item::type_id::create("req");
- `DV_CHECK_MEMBER_RANDOMIZE_FATAL(autoincr)
- `DV_CHECK_MEMBER_RANDOMIZE_FATAL(bad_access)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(autoincrement)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(bad_req)
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(read_addr_after_write)
randomize_req(req);
`uvm_info(`gfn, $sformatf("Starting transaction %0d/%0d: %0s",
j, num_trans, req.sprint(uvm_default_line_printer)), UVM_MEDIUM)
sba_access(.jtag_dmi_ral(jtag_dmi_ral), .cfg(cfg.m_jtag_agent_cfg), .req(req));
- `DV_CHECK(!req.is_busy_err)
`DV_CHECK(!req.timed_out)
-
- if (bad_access) begin
- `DV_CHECK(req.is_err)
- continue;
+ `DV_CHECK(!req.is_busy_err)
+ if (bad_req) begin
+ `DV_CHECK(req.is_err inside {SbaErrBadAlignment, SbaErrBadSize})
end
-
- if (autoincr) begin
- logic [BUS_DW-1:0] rwdata;
- `DV_CHECK_MEMBER_RANDOMIZE_FATAL(autoincr_length)
- if (req.bus_op == BusOpWrite) begin
- repeat (autoincr_length) begin
- logic is_busy;
- `DV_CHECK_STD_RANDOMIZE_FATAL(rwdata)
- if (!cfg.m_jtag_agent_cfg.in_reset) begin
- csr_wr(.ptr(jtag_dmi_ral.sbdata0), .value(rwdata), .predict(1));
- end
- sba_access_busy_wait_wrap(req);
- if (!$urandom_range(0, 3)) begin //25%
- csr_rd(.ptr(jtag_dmi_ral.sbaddress0), .value(rwdata));
- end
- end
- end else begin
- if (req.readondata) begin
- sba_access_busy_wait_wrap(req);
- repeat (autoincr_length) begin
- logic is_busy;
- if (!cfg.m_jtag_agent_cfg.in_reset) begin
- csr_rd(.ptr(jtag_dmi_ral.sbdata0), .value(rwdata));
- end
- sba_access_busy_wait_wrap(req);
- if (!$urandom_range(0, 3)) begin //25%
- csr_rd(.ptr(jtag_dmi_ral.sbaddress0), .value(rwdata));
- end
- end
- end
- end
- end
-
- // If readondata is set, then the read on sbdata0 performed by sba_access invocation
- // above will trigger another SBA read. So we set readondata to 0 and do a busywait.
- if (req.bus_op == BusOpRead && req.readondata) begin
- bit is_busy;
- uvm_reg_data_t rwdata = jtag_dmi_ral.sbcs.get_mirrored_value();
- rwdata = get_csr_val_with_updated_field(jtag_dmi_ral.sbcs.sbreadondata, rwdata, 0);
- csr_wr(.ptr(jtag_dmi_ral.sbcs), .value(rwdata), .predict(1));
- sba_access_busy_wait_wrap(req);
- csr_rd(.ptr(jtag_dmi_ral.sbdata0), .value(rwdata));
+ if (req.is_err == SbaErrOther) begin
+ // Reset the DUT to clear the intg error.
+ dut_init();
end
end
sba_tl_device_seq_stop();
@@ -178,10 +132,9 @@
// Randomizes legal, valid requests.
virtual function void randomize_req(sba_access_item req);
- req.disable_rsp_randomization();
+ req.autoincrement = autoincrement;
`DV_CHECK_RANDOMIZE_WITH_FATAL(req,
- autoincrement == local::autoincr;
- if (bad_access) {
+ if (bad_req) {
// Create unsupported size or unaligned address error cases.
(size > SbaAccessSize32b) || ((addr % (1 << size)) != 0);
} else {
@@ -202,45 +155,54 @@
bus_op_prev = req.bus_op;
addr_prev = req.addr;
size_prev = req.size;
- // Since we override the addr, size & bus_op, the original bad_access value may need to be
+ // Since we override the addr, size & bus_op, the original bad_req value may need to be
// updated.
- bad_access = (req.size > SbaAccessSize32b) || ((req.addr % (1 << req.size)) != 0);
+ bad_req = (req.size > SbaAccessSize32b) || ((req.addr % (1 << req.size)) != 0);
endfunction
- // Wrapper task for busy wait.
- task sba_access_busy_wait_wrap(sba_access_item req);
- logic is_busy;
- sba_access_busy_wait(jtag_dmi_ral, cfg.m_jtag_agent_cfg, req, is_busy);
- `DV_CHECK(!req.is_err)
- `DV_CHECK(!req.is_busy_err)
- `DV_CHECK(!req.timed_out)
- endtask
-
endclass
// Drive random traffic out the SBA TL interface with more weightage on accesses that will result
-// in sberror begin flagged.
+// in sberror begin flagged, either due to a bad request or a bad response via d_error / d_chan
+// intg err.
class rv_dm_bad_sba_tl_access_vseq extends rv_dm_sba_tl_access_vseq;
`uvm_object_utils(rv_dm_bad_sba_tl_access_vseq)
`uvm_object_new
- constraint bad_access_c {
- bad_access dist {0 :/ 3, 1 :/ 7};
+ constraint bad_req_c {
+ bad_req dist {0 :/ 6, 1 :/ 4};
+ }
+
+ constraint d_error_pct_c {
+ d_error_pct dist { 0 :/ 1, [1:20] :/ 3, [21:99] :/ 5, 100 :/ 1 };
+ }
+
+ constraint d_chan_intg_err_pct_c {
+ d_chan_intg_err_pct dist { 0 :/ 2, [1:20] :/ 3, [21:99] :/ 4, 100 :/ 1 };
}
endclass
-// Enable address autoincrements on reads and writes. Also ocassionally send bad accesses.
+// Enable address autoincrements on reads and writes. Also ocassionally send bad requests and
+// responses.
class rv_dm_autoincr_sba_tl_access_vseq extends rv_dm_sba_tl_access_vseq;
`uvm_object_utils(rv_dm_autoincr_sba_tl_access_vseq)
`uvm_object_new
- constraint bad_access_c {
- bad_access dist {0 :/ 7, 1 :/ 3};
+ constraint bad_req_c {
+ bad_req dist {0 :/ 7, 1 :/ 3};
}
- constraint autoincr_c {
- autoincr dist {0 :/ 4, 1 :/ 6};
+ constraint d_error_pct_c {
+ d_error_pct dist { 0 :/ 1, [1:20] :/ 3, [21:99] :/ 5, 100 :/ 1 };
+ }
+
+ constraint d_chan_intg_err_pct_c {
+ d_chan_intg_err_pct dist { 0 :/ 3, [1:20] :/ 3, [21:99] :/ 3, 100 :/ 1 };
+ }
+
+ constraint autoincrement_c {
+ autoincrement dist { 0 :/ 4, [1:24] :/ 2, [25:50] :/ 2, [51:200] :/ 2};
}
endclass