From 6c39ade80c2d28b2df053ed9b06864dbd555e2e9 Mon Sep 17 00:00:00 2001 From: Harry Callahan Date: Fri, 5 Jul 2024 15:32:00 +0100 Subject: [PATCH] [i2c,dv] Update predictions for transfers where a the DUT-Controller 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 --- hw/ip/i2c/dv/env/i2c_reference_model.sv | 106 ++++++++++++++++++++---- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/hw/ip/i2c/dv/env/i2c_reference_model.sv b/hw/ip/i2c/dv/env/i2c_reference_model.sv index 1b998280120b37..792ae7778fb38f 100644 --- a/hw/ip/i2c/dv/env/i2c_reference_model.sv +++ b/hw/ip/i2c/dv/env/i2c_reference_model.sv @@ -98,6 +98,16 @@ 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. @@ -105,8 +115,8 @@ class i2c_reference_model extends uvm_component; // 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) @@ -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 @@ -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 @@ -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; @@ -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; @@ -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 @@ -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. @@ -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. // @@ -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 @@ -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