Skip to content

Commit

Permalink
[i2c,dv] Update predictions for transfers where a the DUT-Controller …
Browse files Browse the repository at this point in the history
…sees NACKs.

This is the start of the modelling process, which is still largely incomplete.
It's not yet very granular at handling things like FIFO queues, resets and interrupts.

Signed-off-by: Harry Callahan <[email protected]>
  • Loading branch information
hcallahan-lowrisc committed Jul 5, 2024
1 parent 1d9f15f commit 6c39ade
Showing 1 changed file with 90 additions and 16 deletions.
106 changes: 90 additions & 16 deletions hw/ip/i2c/dv/env/i2c_reference_model.sv
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,25 @@ class i2c_reference_model extends uvm_component;
local i2c_item in_progress_wr_transfer;
local i2c_item in_progress_rd_transfer;

// These queues are used to capture predictions of transfers before pushing them out to the
// scoreboard at the appropriate times.
local i2c_item exp_controller_mode_txn_q[$];
// This queue stores predicted Controller-Mode transfers based upon CSR writes to the DUT.
// As we can potentially enque multiple transfers by writing to the FMTFIFO before
// any transfer has even started, we need to keep a track of the expected ordering of transfers.
// Once we have predicted the transfer to have actually been driven and any data read back, we
// can drop the item from this queue.
local i2c_item prev_item[$];

// DUT-Controller
//
// The following local seq_items are used to construct larger transactions.
// We update the local items as different events occur, then push the item
// into a queue for checking after the end of the transaction has been detected.
// See the header comment for more details.
//
local i2c_item exp_wr_item, prev_wr_item;
local i2c_item exp_rd_item, prev_rd_item, rd_pending_item;
local i2c_item exp_wr_item;
local i2c_item exp_rd_item, rd_pending_item;
local i2c_item rd_pending_q[$]; // Helper-queue : holds partial read transactions
local uint rdata_cnt = 0; // Count data-bytes read in a single transfer (DUT-Controller)

Expand Down Expand Up @@ -165,6 +175,7 @@ class i2c_reference_model extends uvm_component;
super.run_phase(phase);
fork
forever create_target_exp();
forever create_controller_exp();
join_none
endtask: run_phase

Expand Down Expand Up @@ -317,7 +328,7 @@ class i2c_reference_model extends uvm_component;
// the current transfer ends (with an RSTART beginning a new transfer.)
exp_wr_item.rstart_back = 1'b1;
push_complete_write_txn(exp_wr_item);
prev_wr_item = exp_wr_item; // Keep track of the previously pushed item.
prev_item.push_back(exp_wr_item); // Keep track of the previously pushed item.
exp_wr_item = new("exp_wr_item");
end

Expand All @@ -329,8 +340,9 @@ class i2c_reference_model extends uvm_component;
i2c_pkg::READ: begin
exp_rd_item = new("exp_rd_item");
exp_rd_item.bus_op = BusOpRead;
exp_rd_item.dir = i2c_pkg::READ;
exp_rd_item.addr = fbyte[7:1];
if (prev_rd_item != null && prev_rd_item.rstart_back) begin
if (prev_item[$] != null && prev_item[$].rstart_back) begin
exp_rd_item.rstart_front = 1;
end else begin
exp_rd_item.start = 1;
Expand All @@ -340,8 +352,9 @@ class i2c_reference_model extends uvm_component;
i2c_pkg::WRITE: begin
exp_wr_item = new("exp_wr_item");
exp_wr_item.bus_op = BusOpWrite;
exp_wr_item.dir = i2c_pkg::WRITE;
exp_wr_item.addr = fbyte[7:1];
if (prev_wr_item != null && prev_wr_item.rstart_back) begin
if (prev_item[$] != null && prev_item[$].rstart_back) begin
exp_wr_item.rstart_front = 1;
end else begin
exp_wr_item.start = 1;
Expand Down Expand Up @@ -369,7 +382,7 @@ class i2c_reference_model extends uvm_component;
exp_wr_item.stop = stop;
if (stop) begin
push_complete_write_txn(exp_wr_item);
prev_wr_item = exp_wr_item; // Keep track of the previously pushed item.
prev_item.push_back(exp_wr_item); // Keep track of the previously pushed item.
exp_wr_item = new("exp_wr_item");
end
end
Expand Down Expand Up @@ -407,7 +420,7 @@ class i2c_reference_model extends uvm_component;
// reads from 'rdata'.
`downcast(tmp_rd_item, exp_rd_item.clone());
rd_pending_q.push_back(tmp_rd_item);
prev_rd_item = tmp_rd_item; // Keep track of the previously pushed item.
prev_item.push_back(tmp_rd_item); // Keep track of the previously pushed item.

// If this read also set the 'stop' bit to end the transaction, clear the
// temporary variable we use to accumulate chained reads.
Expand All @@ -431,10 +444,29 @@ class i2c_reference_model extends uvm_component;
begin
i2c_item temp_item;
`downcast(temp_item, wr_item.clone());
controller_mode_wr_port.write(temp_item);
exp_controller_mode_txn_q.push_back(temp_item);
end
endtask: push_complete_write_txn

// Push DUT-Controller read transfer items to the scoreboard once all data for the transfer
// has been received.
//
task push_complete_read_txn(i2c_item rd_item);
// If we wrote to the FMTFIFO while in-reset or host/controller-mode was
// inactive, break here so we don't push the expectation for checking.
if (cfg.under_reset || !(`gmv(ral.ctrl.enablehost))) return;

begin
i2c_item temp_item;
`downcast(temp_item, rd_item.clone());
exp_controller_mode_txn_q.push_back(temp_item);
end

// Zero-out the pending item, though we will soon drop it for a new
// item the next time we go around and pick up the new FMTFIFO write.
rd_pending_item.clear_all();
endtask: push_complete_read_txn

// Complete forming our expectations of DUT-Controller read items by capturing data read from the
// RXFIFO and adding it into the items created when we started a read xfer in the fmtfifo.
//
Expand Down Expand Up @@ -492,14 +524,7 @@ class i2c_reference_model extends uvm_component;
rdata_cnt = 0;
rd_pending_item.data_ack_q.push_back(i2c_pkg::NACK);

if (!cfg.under_reset) begin
i2c_item tmp_rd_item;
`downcast(tmp_rd_item, rd_pending_item.clone());
controller_mode_rd_port.write(tmp_rd_item);
// Zero-out the pending item, though we will soon drop it for a new
// item the next time we go around and pick up the new FMTFIFO write.
rd_pending_item.clear_all();
end
push_complete_read_txn(rd_pending_item);
end else begin
rd_pending_item.data_ack_q.push_back(i2c_pkg::ACK);
end
Expand All @@ -508,6 +533,55 @@ class i2c_reference_model extends uvm_component;

endtask: rdata_read

// Fuse data from both the i2c and tl_ul interface to create predictions for
// DUT-Controller transfers.
//
virtual task create_controller_exp();
i2c_item csr_pred_xfer;
i2c_item inp_xfer; // in_progress_xfer
i2c_pkg::acknack_e data_ack_q[$];

controller_mode_in_progress_fifo.get(inp_xfer);
`DV_CHECK(inp_xfer.state inside {StNone, StStarted})

// First, wait until we see the Target's ACK/NACK response to the address+dir byte.
// This tells us if our transfer was accepted by anyone.
wait(inp_xfer.state == StAddrByteAckRcvd);

`uvm_info(`gfn, $sformatf(
"create_controller_exp() got inp_xfer : (address=2'h%2x, rw=1'b%b (%0s))",
inp_xfer.addr, inp_xfer.dir, inp_xfer.dir.name()), UVM_HIGH)

// If the address byte was NACK'd, we may just end up sending a STOP immediately, depending on
// if the software chose to clear any FMTFIFO state in response to the NACK.
// If not, we loop to capture all the data-byte ACKs from in-progress transfers.
fork
wait(inp_xfer.state == StStopped);
forever begin
wait(inp_xfer.state == StDataByteRcvd);
wait(inp_xfer.state == StDataByteAckRcvd);
data_ack_q.push_back(i2c_pkg::acknack_e'(inp_xfer.data_ack_q[$]));
end
join_any

// Wait until we have made a prediction based on the CSR accesses only.
wait(exp_controller_mode_txn_q.size > 0);
csr_pred_xfer = exp_controller_mode_txn_q.pop_front();

// If we see a NACK from the Target, then update the prediction
if (inp_xfer.addr_ack == i2c_pkg::NACK) csr_pred_xfer.addr_ack = i2c_pkg::NACK;
// Update the data acks with our observations
foreach (csr_pred_xfer.data_ack_q[i]) csr_pred_xfer.data_ack_q[i] = bit'(data_ack_q[i]);

// Now that we have updated the predicted transfer, push it to the scoreboard for checking
case (csr_pred_xfer.bus_op)
BusOpRead: controller_mode_rd_port.write(csr_pred_xfer);
BusOpWrite: controller_mode_wr_port.write(csr_pred_xfer);
endcase
void'(prev_item.pop_front());

endtask : create_controller_exp

// This task generates DUT-Target transfer expectations
//
// Previously, these expectations were formed by taking the stimulus items directly from the
Expand Down

0 comments on commit 6c39ade

Please sign in to comment.