[i2c, dv] Add i2c_fifo_overflow (v2) test for rx_fifo and fmt_fifo
The main point of this test is to create a sequence and specific
constraints that deterministicallys trigger overflow interrupts
which can be predictable and verified by DV
- Update i2c_testplan.hjson to replace rx_fifo_overflow and
fmt_fifo_overflow tests by a unified fifo_overflow test
- Add i2c_fifo_overflow_vseq to verify fmt_overflow and rx_overflow interrupt
- Update i2c_scoreboard to verify dropped data due to fifo overflow
- Minor refactor i2c_fifo_watermark for consistency
Signed-off-by: Tung Hoang <hoang.tung@wdc.com>
diff --git a/hw/dv/sv/i2c_agent/i2c_device_driver.sv b/hw/dv/sv/i2c_agent/i2c_device_driver.sv
index fc500d8..5ac642c 100644
--- a/hw/dv/sv/i2c_agent/i2c_device_driver.sv
+++ b/hw/dv/sv/i2c_agent/i2c_device_driver.sv
@@ -6,12 +6,16 @@
`uvm_component_utils(i2c_device_driver)
`uvm_component_new
- rand bit [7:0] rd_data;
+ rand bit [7:0] rd_data[256]; // max length of read transaction
- constraint rd_data_c { rd_data inside {[0 : 127]}; }
+ // get an array with unique read data
+ constraint rd_data_c {
+ unique { rd_data };
+ }
virtual task get_and_drive();
- int num_stretch_host_clks;
+ uint num_stretch_host_clks;
+ uint rd_data_cnt = 0;
i2c_item rsp_item;
@(posedge cfg.vif.rst_ni);
@@ -42,11 +46,12 @@
end
RdData: begin
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(rd_data)
+ rd_data_cnt++;
for (int i = 7; i >= 0; i--) begin
- cfg.vif.device_send_bit(cfg.timing_cfg, rd_data[i]);
+ cfg.vif.device_send_bit(cfg.timing_cfg, rd_data[rd_data_cnt][i]);
end
- `uvm_info(`gfn, $sformatf("driver, trans %0d, byte %0d %0b",
- rsp_item.tran_id, rsp_item.num_data+1, rd_data), UVM_DEBUG)
+ `uvm_info(`gfn, $sformatf("driver, trans %0d, byte %0d %0x",
+ rsp_item.tran_id, rsp_item.num_data+1, rd_data[rd_data_cnt]), UVM_DEBUG)
end
default: begin
`uvm_fatal(`gfn, $sformatf("driver, received invalid request from monitor/seq"))
diff --git a/hw/dv/sv/i2c_agent/i2c_item.sv b/hw/dv/sv/i2c_agent/i2c_item.sv
index f536cbe..dfabc9b 100644
--- a/hw/dv/sv/i2c_agent/i2c_item.sv
+++ b/hw/dv/sv/i2c_agent/i2c_item.sv
@@ -6,9 +6,9 @@
// transaction data part
bit [7:0] data_q[$];
- bit [9:0] addr; // enough to support both 7 & 10-bit target address
+ bit [9:0] addr; // enough to support both 7 & 10-bit target address
int tran_id;
- int num_data;
+ int num_data; // valid data
bus_op_e bus_op;
drv_type_e drv_type;
// transaction control part
@@ -16,6 +16,9 @@
bit ack;
bit rstart;
+ // queue dropped data due to fmt_overflow
+ bit [7:0] fmt_ovf_data_q[$];
+
// random flags
rand bit [7:0] fbyte;
rand bit nakok, rcont, read, stop, start;
@@ -31,13 +34,13 @@
`uvm_object_utils_begin(i2c_item)
`uvm_field_int(tran_id, UVM_DEFAULT)
- `uvm_field_enum(bus_op_e, bus_op, UVM_DEFAULT)
+ `uvm_field_enum(bus_op_e, bus_op, UVM_DEFAULT)
`uvm_field_int(addr, UVM_DEFAULT)
`uvm_field_int(num_data, UVM_DEFAULT)
- `uvm_field_queue_int(data_q, UVM_DEFAULT)
`uvm_field_int(start, UVM_DEFAULT)
`uvm_field_int(stop, UVM_DEFAULT)
- //------
+ `uvm_field_queue_int(data_q, UVM_DEFAULT)
+ `uvm_field_queue_int(fmt_ovf_data_q, UVM_DEFAULT | UVM_NOCOMPARE)
`uvm_field_int(rstart, UVM_DEFAULT | UVM_NOPRINT | UVM_NOCOMPARE)
`uvm_field_int(fbyte, UVM_DEFAULT | UVM_NOPRINT | UVM_NOCOMPARE)
`uvm_field_int(ack, UVM_DEFAULT | UVM_NOPRINT | UVM_NOCOMPARE)
@@ -55,6 +58,7 @@
addr = 0;
drv_type = None;
data_q.delete();
+ fmt_ovf_data_q.delete();
endfunction : clear_data
function void clear_flag();
diff --git a/hw/ip/i2c/data/i2c_testplan.hjson b/hw/ip/i2c/data/i2c_testplan.hjson
index 3224409..09ce54d 100644
--- a/hw/ip/i2c/data/i2c_testplan.hjson
+++ b/hw/ip/i2c/data/i2c_testplan.hjson
@@ -121,6 +121,22 @@
tests: ["i2c_fifo_watermark"]
}
{
+ name: fifo_overflow
+ desc: '''
+ Test the overflow interrupt of fmt_fifo and rx_fifo.
+
+ Stimulus:
+ - Keep sending a number of format byte higher than fmt_fifo and rx_fifo depth
+
+ Checking:
+ - Ensure excess format bytes are dropped
+ - Ensure fmt_overflow and rx_overflow interrupt are asserted
+ - Ensure receving correct number of fmt_fifo and rx_fifo watermark interrupts
+ '''
+ milestone: V2
+ tests: ["i2c_fifo_overflow"]
+ }
+ {
name: fmt_reset
desc: '''
Test fmt_fifo reset.
@@ -138,11 +154,11 @@
{
name: rx_reset
desc: '''
- Test rx_reset reset.
+ Test rx_fifo reset.
Stimulus:
- Fill up the rx fifo by sending data bytes over rx
- - Reset the rx_fifo randomly after a random number of bytes shows up on rx_fifo,
+ - Reset the rx_fifo randomly after a random number of bytes shows up on rx_fifo
Checking:
- Ensure that reads to rdata register yield 0s after rx_fifo is reset
@@ -151,36 +167,6 @@
tests: []
}
{
- name: fmt_overflow
- desc: '''
- Test fmt_overflow interrupt.
-
- Stimulus:
- - Keep sending a number of format byte higher than fmt_fifo depth, delay the target response
-
- Checking:
- - Ensure excess format bytes are dropped
- - Ensure intr_fmt_overflow interrupt is asserted
- '''
- milestone: V2
- tests: []
- }
- {
- name: rx_overflow
- desc: '''
- Test rx_overflow interrupt.
-
- Stimulus:
- - Request a number of bytes higher than rx_fifo depth, delay rx_fifo reading
-
- Checking:
- - Ensure excess data bytes are dropped
- - Ensure intr_rx_overflow interrupt is asserted
- '''
- milestone: V2
- tests: []
- }
- {
name: fmt_rx_fifo_full
desc: '''
Test fmt_fifo and rx_fifo in full states.
diff --git a/hw/ip/i2c/dv/env/i2c_env.core b/hw/ip/i2c/dv/env/i2c_env.core
index 5a0b685..110f3b0 100644
--- a/hw/ip/i2c/dv/env/i2c_env.core
+++ b/hw/ip/i2c/dv/env/i2c_env.core
@@ -25,6 +25,7 @@
- seq_lib/i2c_sanity_vseq.sv: {is_include_file: true}
- seq_lib/i2c_override_vseq.sv: {is_include_file: true}
- seq_lib/i2c_fifo_watermark_vseq.sv: {is_include_file: true}
+ - seq_lib/i2c_fifo_overflow_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource
generate:
diff --git a/hw/ip/i2c/dv/env/i2c_env_cfg.sv b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
index 5eac1d7..066e241 100644
--- a/hw/ip/i2c/dv/env/i2c_env_cfg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
@@ -5,8 +5,15 @@
class i2c_env_cfg extends cip_base_env_cfg #(.RAL_T(i2c_reg_block));
i2c_target_addr_mode_e target_addr_mode = Addr7BitMode;
+ uint ok_to_end_delay_ns = I2C_IDLE_TIME;
- uint ok_to_end_delay_ns = 5000;
+ // bits to control fifos access
+ // set en_fmt_overflow to ensure fmt_overflow irq is triggered
+ bit en_fmt_overflow = 1'b0;
+ // set en_rx_overflow to ensure ensure rx_overflow irq is triggered
+ bit en_rx_overflow = 1'b0;
+ // set en_rx_watermark to ensure rx_watermark irq is triggered
+ bit en_rx_watermark = 1'b0;
rand i2c_agent_cfg m_i2c_agent_cfg;
diff --git a/hw/ip/i2c/dv/env/i2c_env_pkg.sv b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
index 01354fd..e03c8cc 100644
--- a/hw/ip/i2c/dv/env/i2c_env_pkg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
@@ -33,6 +33,12 @@
NumI2cIntr = 10
} i2c_intr_e;
+ typedef enum int {
+ ReadOnly = 0,
+ WriteOnly = 1,
+ ReadWrite = 2
+ } tran_type_e;
+
// csr and mem total size for IP, TODO confirm below value with spec
parameter uint I2C_ADDR_MAP_SIZE = 128;
parameter uint I2C_FMT_FIFO_DEPTH = 32;
@@ -55,7 +61,7 @@
parameter uint I2C_MAX_TIMEOUT = 4;
parameter uint I2C_MAX_RXILVL = 7;
parameter uint I2C_MAX_FMTILVL = 3;
-
+ parameter uint I2C_IDLE_TIME = 5000;
// package sources
`include "i2c_env_cfg.sv"
`include "i2c_env_cov.sv"
diff --git a/hw/ip/i2c/dv/env/i2c_scoreboard.sv b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
index 22dc898..ce9e8cf 100644
--- a/hw/ip/i2c/dv/env/i2c_scoreboard.sv
+++ b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
@@ -18,7 +18,6 @@
local i2c_item exp_rd_q[$];
local i2c_item exp_wr_q[$];
local uint rd_wait;
-
local bit host_init = 1'b0;
local uint rdata_cnt = 0;
local uint tran_id = 0;
@@ -52,6 +51,7 @@
i2c_item sb_exp_wr_item;
i2c_item sb_exp_rd_item;
bit do_read_check = 1'b0;
+
bit write = item.is_write();
uvm_reg_addr_t csr_addr = get_normalized_addr(item.a_addr);
@@ -121,13 +121,20 @@
end else begin // transaction begins with started/rstarted
// write transaction
if (exp_wr_item.start && exp_wr_item.bus_op == BusOpWrite) begin
- exp_wr_item.data_q.push_back(fbyte);
- exp_wr_item.num_data++;
- exp_wr_item.stop = stop;
- if (exp_wr_item.stop) begin
- // get a complete write
- `downcast(sb_exp_wr_item, exp_wr_item.clone());
- exp_wr_item.clear_all();
+ cfg.clk_rst_vif.wait_clks(1); // irq appears with one cycle latency
+ if (cfg.intr_vif.pins[FmtOverflow]) begin
+ // fmt_fifo is overflow, capture dropped data
+ exp_wr_item.fmt_ovf_data_q.push_back(fbyte);
+ end else begin
+ // fmt_fifo is underflow then collect data, otherwise drop data
+ exp_wr_item.data_q.push_back(fbyte);
+ exp_wr_item.num_data++;
+ exp_wr_item.stop = stop;
+ if (exp_wr_item.stop) begin
+ // get a complete write
+ `downcast(sb_exp_wr_item, exp_wr_item.clone());
+ exp_wr_item.clear_all();
+ end
end
end
// read transaction
@@ -148,6 +155,8 @@
exp_rd_item.nakok = nakok;
exp_rd_item.nack = ~exp_rd_item.rcont;
exp_rd_item.rstart = (exp_rd_item.stop) ? 1'b0 : 1'b1;
+ // decrement since data is dropped by rx_overflow
+ if (cfg.en_rx_overflow) exp_rd_item.num_data--;
// if not a chained read (stop is issued)
if (exp_rd_item.stop) begin
`uvm_info(`gfn, $sformatf("\n\nscoreboard, exp_rd_item\n\%s",
@@ -188,7 +197,6 @@
`DV_CHECK_GE_FATAL(rd_pending_q.size(), 0)
rd_pending_item = rd_pending_q.pop_front();
end
- // collect read data byte
rd_pending_item.data_q.push_back(ral.rdata.get_mirrored_value());
rdata_cnt++;
`uvm_info(`gfn, $sformatf("\n\nscoreboard, rd_pending_item\n\%s",
@@ -210,28 +218,46 @@
endtask : process_tl_access
task compare_trans(bus_op_e dir = BusOpWrite);
- i2c_item exp_item;
+ i2c_item exp_trn;
i2c_item dut_trn;
forever begin
if (dir == BusOpWrite) begin
wr_item_fifo.get(dut_trn);
wait(exp_wr_q.size() > 0);
- exp_item = exp_wr_q.pop_front();
+ exp_trn = exp_wr_q.pop_front();
end else begin // BusOpRead
rd_item_fifo.get(dut_trn);
wait(exp_rd_q.size() > 0);
- exp_item = exp_rd_q.pop_front();
+ exp_trn = exp_rd_q.pop_front();
end
- if (!dut_trn.compare(exp_item)) begin
- `uvm_error(`gfn, $sformatf("%s item mismatch!\n--> EXP:\n%0s\--> DUT:\n%0s",
- (dir == BusOpWrite) ? "WRITE" : "READ", exp_item.sprint(), dut_trn.sprint()))
+
+ // when rx_fifo is overflow, dut_trn containts data which is dropped from exp_trn
+ if (cfg.en_rx_overflow) begin
+ dut_trn.data_q.pop_back();
+ dut_trn.num_data--;
+ end
+
+ if (!dut_trn.compare(exp_trn)) begin
+ if (!check_overflow_data_fmt_fifo(exp_trn, dut_trn)) begin // fmt_overflow transaction
+ `uvm_error(`gfn, $sformatf("%s item mismatch!\n--> EXP:\n%0s\--> DUT:\n%0s",
+ (dir == BusOpWrite) ? "WRITE" : "READ", exp_trn.sprint(), dut_trn.sprint()))
+ end
end else begin
`uvm_info(`gfn, $sformatf("direction %s item match!\n--> EXP:\n%0s\--> DUT:\n%0s",
- (dir == BusOpWrite) ? "WRITE" : "READ", exp_item.sprint(), dut_trn.sprint()), UVM_DEBUG)
+ (dir == BusOpWrite) ? "WRITE" : "READ", exp_trn.sprint(), dut_trn.sprint()), UVM_DEBUG)
end
end
endtask : compare_trans
+ function bit check_overflow_data_fmt_fifo(i2c_item exp_trn, i2c_item dut_trn);
+ // check overflow data is not captured
+ if (exp_trn.fmt_ovf_data_q.size() > 0) begin
+ bit [7:0] unique_q[$] = dut_trn.data_q.find with
+ ( item inside {exp_trn.fmt_ovf_data_q} );
+ return (unique_q.size() == 0);
+ end
+ endfunction : check_overflow_data_fmt_fifo
+
virtual function void reset(string kind = "HARD");
super.reset(kind);
// reset local fifos queues and variables
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv
index 327edeb..b1302a0 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv
@@ -15,14 +15,6 @@
bit under_program_regs = 1'b0;
bit program_incorrect_regs = 1'b0;
- // bits to control fifos access
- // delay_read_rx_until_full is unset delay reading rx_fifo until rx_fifo is full or
- // delay_read_rx_until_full is unset (used in fifo_watermark test)
- bit delay_read_rx_until_full = 1'b1;
- // avoid_write_fmt_overflow is set to prevent writing to fmt_fifo from overflow,
- // by checking fmt_fifo is not full (used in fifo_overflow)
- bit avoid_write_fmt_overflow = 1'b1;
-
local timing_cfg_t timing_cfg;
bit [7:0] rd_data;
i2c_item fmt_item;
@@ -35,8 +27,9 @@
rand uint num_trans;
rand uint num_wr_bytes;
rand uint num_rd_bytes;
+ rand uint num_data_ovf;
rand bit rw_bit;
- rand bit [7:0] wr_data;
+ rand bit [7:0] wr_data[$];
rand bit [9:0] addr; // support both 7-bit and 10-bit target address
rand bit [2:0] rxilvl;
rand bit [1:0] fmtilvl;
@@ -57,9 +50,18 @@
// constraints
constraint addr_c { addr inside {[I2C_MIN_ADDR : I2C_MAX_ADDR]}; }
- constraint wr_data_c { wr_data inside {[I2C_MIN_DATA : I2C_MAX_DATA]}; }
constraint fmtilvl_c { fmtilvl inside {[0 : I2C_MAX_FMTILVL]}; }
constraint num_trans_c { num_trans inside {[I2C_MIN_TRAN : I2C_MAX_TRAN]}; }
+ // get an array with unique write data
+ constraint wr_data_c {
+ solve num_wr_bytes before wr_data;
+ wr_data.size == num_wr_bytes;
+ unique { wr_data };
+ }
+
+ // number of extra data write written to fmt to trigger interrupts
+ // i.e. overflow, watermark
+ constraint num_data_ovf_c { num_data_ovf inside {[5 : 10]}; }
// create uniform assertion distributions of rx_watermark interrupt
constraint rxilvl_c {
@@ -81,6 +83,7 @@
};
}
constraint num_rd_bytes_c {
+ num_rd_bytes < 128;
num_rd_bytes dist {
1 :/ 17,
[2:4] :/ 17,
@@ -229,18 +232,30 @@
csr_update(ral.fifo_ctrl);
endtask : program_registers
- virtual task program_format_flag(i2c_item item, string msg="");
+ function automatic int get_byte_latency();
+ return 8*(timing_cfg.tClockLow + timing_cfg.tSetupBit +
+ timing_cfg.tClockPulse + timing_cfg.tHoldBit);
+ endfunction : get_byte_latency
+
+ virtual task program_format_flag(i2c_item item, string msg="");
+ bit fmtfull;
+
ral.fdata.nakok.set(item.nakok);
ral.fdata.rcont.set(item.rcont);
ral.fdata.read.set(item.read);
ral.fdata.stop.set(item.stop);
ral.fdata.start.set(item.start);
ral.fdata.fbyte.set(item.fbyte);
- // avoid_write_fmt_overflow is set, ensure fmt_fifo is not full before write
- // otherwise, fmt_fifo can be overflow written
- if (avoid_write_fmt_overflow) begin
+ // en_fmt_underflow is set to ensure no write data overflow with fmt_fifo
+ // regardless en_fmt_underflow, the last data (consist of STOP bit) must be
+ // pushed into fmt_fifo to safely complete transaction
+ if (!cfg.en_fmt_overflow || fmt_item.stop) begin
csr_spinwait(.ptr(ral.status.fmtfull), .exp_data(1'b0));
- end
+ end
+ // if fmt_overflow irq is triggered it must be cleared before new fmt data is program
+ // otherwise, scoreboard can drop this data while fmt_fifo is not full
+ wait(!cfg.intr_vif.pins[FmtOverflow]);
+ // program fmt_fifo
csr_update(.csr(ral.fdata));
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_fifo_access_dly)
cfg.clk_rst_vif.wait_clks(fmt_fifo_access_dly);
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_fifo_overflow_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_fifo_overflow_vseq.sv
new file mode 100644
index 0000000..3354c2a
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_fifo_overflow_vseq.sv
@@ -0,0 +1,102 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// basic fifo_overflow test vseq
+class i2c_fifo_overflow_vseq extends i2c_fifo_watermark_vseq;
+ `uvm_object_utils(i2c_fifo_overflow_vseq)
+
+ `uvm_object_new
+
+ // counting the number of received overflow interrupts
+ local uint num_fmt_overflow;
+ local uint num_rx_overflow;
+ rand uint num_data_rx_ovf;
+
+ // send more one data than rx_fifo depth to trigger rx_overflow
+ constraint num_rd_bytes_c {
+ num_rd_bytes == I2C_RX_FIFO_DEPTH + 1;
+ }
+
+ task body();
+ bit check_fmt_overflow;
+ bit check_rx_overflow;
+ bit rxempty = 1'b0;
+ device_init();
+ host_init();
+
+ // config fmt_overflow and rx_overflow tests
+ cfg.en_fmt_overflow = 1'b1;
+ cfg.en_rx_overflow = 1'b1;
+
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_trans)
+ for (int i = 0; i < num_trans; i++) begin
+ check_fmt_overflow = 1'b1; // set to gracefully stop check_fmt_overflow_intr
+ check_rx_overflow = 1'b1; // set to gracefully stop check_ex_overflow_intr
+ num_fmt_overflow = 0;
+ num_rx_overflow = 0;
+
+ fork
+ begin
+ //*** verify fmt_overflow irq:
+ // -> send write transaction -> pooling and counting fmt_overflow interrupt
+ // -> check write complete -> stop pooling fmt_overflow interrupt
+ // -> verify the number of received fmt_watermark interrupt
+ // -> verify fmt_data dropped is performed scoreboard
+ if (check_fmt_overflow) begin
+ host_send_trans(.num_trans(1), .trans_type(WriteOnly));
+ csr_spinwait(.ptr(ral.status.fmtempty), .exp_data(1'b1));
+ check_fmt_overflow = 1'b0;
+ // number of fmt_overflow received is at most num_data_rx_ovf
+ // since fmt_fifo can be drained thus decreasing num_fmt_overflow counter
+ `DV_CHECK_GT(num_fmt_overflow, 0)
+ `DV_CHECK_LE(num_fmt_overflow, num_data_ovf)
+ `uvm_info(`gfn, $sformatf("\nRun %0d, num_fmt_overflow %0d",
+ i, num_fmt_overflow), UVM_DEBUG)
+ end
+
+ //*** verify rx_overflow irq:
+ // -> send read transaction -> pooling and counting rx_overflow interrupt
+ // -> check write complete -> stop pooling rx_overflow interrupt
+ // -> verify the number of received rx_overflow interrupt
+ // TODO: -> verify the rx_data dropped should be performed in scoreboard
+ if (check_rx_overflow) begin
+ host_send_trans(.num_trans(1), .trans_type(ReadOnly));
+ csr_spinwait(.ptr(ral.status.rxempty), .exp_data(1'b1));
+ check_rx_overflow = 1'b0;
+ `DV_CHECK_EQ(num_rx_overflow, 1)
+ `uvm_info(`gfn, $sformatf("\nRun %0d, num_rx_overflow %d",
+ i, num_rx_overflow), UVM_LOW)
+ end
+ end
+ begin
+ while (check_fmt_overflow) check_fmt_overflow_intr();
+ end
+ begin
+ while (check_rx_overflow) check_rx_overflow_intr();
+ end
+ join
+ end
+ endtask : body
+
+ task check_fmt_overflow_intr();
+ bit fmt_overflow;
+
+ csr_rd(.ptr(ral.intr_state.fmt_overflow), .value(fmt_overflow));
+ if (fmt_overflow) begin
+ clear_interrupt(FmtOverflow);
+ num_fmt_overflow++;
+ end
+ endtask : check_fmt_overflow_intr
+
+ task check_rx_overflow_intr();
+ bit rx_overflow;
+
+ csr_rd(.ptr(ral.intr_state.rx_overflow), .value(rx_overflow));
+ if (rx_overflow) begin
+ clear_interrupt(RxOverflow);
+ num_rx_overflow++;
+ end
+ endtask : check_rx_overflow_intr
+
+endclass : i2c_fifo_overflow_vseq
\ No newline at end of file
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_fifo_watermark_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_fifo_watermark_vseq.sv
index 78bad9a..471fa63 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_fifo_watermark_vseq.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_fifo_watermark_vseq.sv
@@ -12,23 +12,20 @@
local uint num_fmt_watermark;
local uint num_rx_watermark;
- // the number of re-programming fmtilvl and rxilvl
- local rand uint num_reprog_ilvl;
- constraint num_reprog_ilvl_c { num_reprog_ilvl inside {[8 : 16]}; }
-
// fast write data to fmt_fifo to quickly trigger fmt_watermark interrupt
constraint fmt_fifo_access_dly_c { fmt_fifo_access_dly == 0;}
// fast read data from rd_fifo to quickly finish simulation (increasing sim. performance)
constraint rx_fifo_access_dly_c { rx_fifo_access_dly == 0;}
- // per each run, send a single read/write transaction
- constraint num_trans_c { num_trans == 1; }
-
- // transaction length is long enough (not less than fmt/rx_fifo depth)
- // in order to cross the watermark levels of rx_fifo and fmt_fifo
- constraint num_wr_bytes_c { num_wr_bytes == I2C_FMT_FIFO_DEPTH + 5; }
+ // write transaction length is more than fmt_fifo depth to cross fmtilvl
+ constraint num_wr_bytes_c {
+ solve num_data_ovf before num_wr_bytes;
+ num_wr_bytes == I2C_FMT_FIFO_DEPTH + num_data_ovf;
+ }
+ // read transaction length is equal to rx_fifo depth to cross rxilvl
constraint num_rd_bytes_c { num_rd_bytes == I2C_RX_FIFO_DEPTH; }
+ constraint num_trans_c { num_trans inside {[8 : 12]}; }
task body();
bit check_fmt_watermark, check_rx_watermark;
@@ -36,8 +33,11 @@
device_init();
host_init();
- `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_reprog_ilvl)
- for (int i = 0; i < num_reprog_ilvl; i++) begin
+ // config rx_watermark test (fmt_watermark test is auto configed)
+ cfg.en_rx_watermark = 1'b1;
+
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_trans)
+ for (int i = 0; i < num_trans; i++) begin
check_fmt_watermark = 1'b1;
check_rx_watermark = 1'b1;
num_fmt_watermark = 0;
@@ -45,32 +45,35 @@
fork
begin
- // verify fmt_watermark irq:
+ //*** verify fmt_watermark irq:
// -> send write transaction -> pooling and counting fmt_watermark interrupt
// -> check write complete -> stop pooling fmt_watermark interrupt
// -> verify the number of received fmt_watermark interrupt
if (check_fmt_watermark) begin
- host_send_trans(num_trans, "WriteOnly");
+ host_send_trans(.num_trans(1), .trans_type(WriteOnly));
csr_spinwait(.ptr(ral.status.fmtempty), .exp_data(1'b1));
- check_fmt_watermark = 1'b0;
- // depending the programmed fmtivl, the num_fmt_watermark could be 1 or 2
+ check_fmt_watermark = 1'b0; // gracefully stop check_fmt_watermark_intr
+ // depending the programmed fmtivl and the DUT configuration
+ // (timing regsisters), num_fmt_watermark could be
+ // 1: fmtilvl is crossed one when data drains from fmt_fifo
+ // 2: fmtilvl is crossed twice when data fills up or drains from fmt_fifo
`DV_CHECK_GT(num_fmt_watermark, 0)
`DV_CHECK_LE(num_fmt_watermark, 2)
`uvm_info(`gfn, $sformatf("\nRun %0d, num_fmt_watermark %0d",
- i, num_fmt_watermark), UVM_LOW)
+ i, num_fmt_watermark), UVM_DEBUG)
end
- // verify rx_watermark irq:
+ //*** verify rx_watermark irq:
// -> send read transaction -> pooling and counting rx_watermark interrupt
// -> check read complete -> stop pooling rx_watermark interrupt
// -> verify the number of received rx_watermark interrupt
if (check_rx_watermark) begin
- // unset delay_read_rx_until_full to quickly fill up rx_fifo so
+ // first en_rx_watermark is unset to quickly fill up rx_fifo and
// watermark interrupt is assuredly triggered
- delay_read_rx_until_full = 1'b0;
- host_send_trans(num_trans, "ReadOnly");
+ // until rx_fifo becomes full, en_rx_watermark is set to start reading rx_fifo
+ host_send_trans(.num_trans(1), .trans_type(ReadOnly));
csr_spinwait(.ptr(ral.status.rxempty), .exp_data(1'b1));
- check_rx_watermark = 1'b0;
+ check_rx_watermark = 1'b0; // gracefully stop check_rx_watermark_intr
// for fmtilvl > 4, rx_watermark is disable (num_rx_watermark = 0)
// otherwise, num_rx_watermark must be 1
if ( rxilvl <= 4) begin
@@ -78,13 +81,12 @@
end else begin
`DV_CHECK_EQ(num_rx_watermark, 0)
end
- // during a read transaction, fmt_watermark could be triggered (e.g. since
- // read address and control byte are programmed to fmt_fifo which might cross fmtilvl)
- // if fmt_watermark is triggered, then clear it to not interfere counting the
- // fmt_watermark interrupt of next transaction (no need to verify fmt_watermark again)
+ // during a read transaction, fmt_watermark could be triggered since read address
+ // and control byte are programmed to fmt_fifo and possibly cross fmtilvl
+ // if fmt_watermark is triggered, then it should be cleared to not interfere
+ // counting fmt_watermark in the next transaction (no need to verify
+ // fmt_watermark again during read)
clear_interrupt(FmtWatermark);
- `uvm_info(`gfn, $sformatf("\nRun %0d, num_fmt_watermark %0d",
- i, num_fmt_watermark), UVM_LOW)
end
end
begin
@@ -98,11 +100,9 @@
endtask : body
task check_fmt_watermark_intr();
- bit [TL_DW-1:0] intr_state;
bit fmt_watermark;
- csr_rd(.ptr(ral.intr_state), .value(intr_state));
- fmt_watermark = bit'(get_field_val(ral.intr_state.fmt_watermark, intr_state));
+ csr_rd(.ptr(ral.intr_state.fmt_watermark), .value(fmt_watermark));
if (fmt_watermark) begin
clear_interrupt(FmtWatermark);
num_fmt_watermark++;
@@ -110,11 +110,9 @@
endtask : check_fmt_watermark_intr
task check_rx_watermark_intr();
- bit [TL_DW-1:0] intr_state;
bit rx_watermark;
- csr_rd(.ptr(ral.intr_state), .value(intr_state));
- rx_watermark = bit'(get_field_val(ral.intr_state.rx_watermark, intr_state));
+ csr_rd(.ptr(ral.intr_state.rx_watermark), .value(rx_watermark));
if (rx_watermark) begin
clear_interrupt(RxWatermark);
num_rx_watermark++;
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv
index d637964..593a1f7 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv
@@ -7,22 +7,23 @@
`uvm_object_new
- uint total_rd_bytes = 0;
+ uint total_rd_bytes;
- virtual task host_send_trans(int num_trans, string trans_type = "");
+ virtual task host_send_trans(int num_trans, tran_type_e trans_type = ReadWrite);
bit last_tran;
fmt_item = new("fmt_item");
+ total_rd_bytes = 0;
for (uint cur_tran = 1; cur_tran <= num_trans; cur_tran++) begin
// re-programming timing registers for the first transaction
// or when the previous transaction is completed
if (fmt_item.stop || cur_tran == 1) begin
under_program_regs = 1'b1;
`DV_CHECK_RANDOMIZE_FATAL(this)
- // if trans_type is not empty (default), then rw_bit is overrided
+ // if trans_type is provided, then rw_bit is overridden
// otherwise, rw_bit is randomized
- rw_bit = (trans_type == "WriteOnly") ? 1'b0 :
- ((trans_type == "ReadOnly") ? 1'b1 : rw_bit);
+ rw_bit = (trans_type == WriteOnly) ? 1'b0 :
+ ((trans_type == ReadOnly) ? 1'b1 : rw_bit);
get_timing_values();
program_registers();
under_program_regs = 1'b0;
@@ -68,8 +69,7 @@
endtask : host_program_target_address
virtual task host_read_trans(bit last_tran);
- uint real_rd_bytes;
- bit rxfull = 1'b0;
+ bit rx_sanity, rx_full, rx_overflow, rx_watermark, start_read;
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_rd_bytes)
fork
@@ -85,8 +85,8 @@
)
`DV_CHECK_EQ(fmt_item.stop | fmt_item.rcont, 1)
- real_rd_bytes = (num_rd_bytes) ? num_rd_bytes : 256;
- total_rd_bytes += real_rd_bytes;
+ // accumulate number of read byte
+ total_rd_bytes += (num_rd_bytes) ? num_rd_bytes : 256;
if (fmt_item.rcont) begin
`uvm_info(`gfn, "\nTransaction READ is chained with next READ transaction", UVM_DEBUG)
end else begin
@@ -97,35 +97,46 @@
program_format_flag(fmt_item, "program number of bytes to read");
end
begin
- do begin
- csr_rd(.ptr(ral.status.rxfull), .value(rxfull));
- // if delay_read_rx_until_full is unset, rx_fifo is quickly filled up to full
- // so watermark/overflow interrupt could be deterministically triggered
- // if delay_read_rx_until_full is set or rx_fifo is full, start reading data
- if (delay_read_rx_until_full || rxfull) begin
- // if not a chained read, read out data sent over rx_fifo
- while (!fmt_item.rcont && total_rd_bytes > 0) begin
- csr_spinwait(.ptr(ral.status.rxempty), .exp_data(1'b0));
- `DV_CHECK_MEMBER_RANDOMIZE_FATAL(rx_fifo_access_dly)
- cfg.clk_rst_vif.wait_clks(rx_fifo_access_dly);
- csr_rd(.ptr(ral.rdata), .value(rd_data));
- total_rd_bytes--;
+ // if not a chained read, read out data sent over rx_fifo
+ rx_overflow = 1'b0;
+ rx_watermark = 1'b0;
+ // decrement total_rd_bytes since one data is must be dropped in fifo_overflow test
+ if (cfg.en_rx_overflow) total_rd_bytes--;
+ while (!fmt_item.rcont && total_rd_bytes > 0) begin
+ csr_rd(.ptr(ral.status.rxfull), .value(rx_full));
+ rx_sanity = !cfg.en_rx_watermark & !cfg.en_rx_overflow;
+ rx_watermark |= cfg.en_rx_watermark && rx_full;
+ rx_overflow |= cfg.en_rx_overflow && cfg.intr_vif.pins[RxOverflow];
+
+ start_read = rx_sanity | // sanity test: read rx_fifo asap when there are valid data
+ rx_watermark | // watermark test: read rx_fifo when rx_watermark is triggered
+ rx_overflow; // overflow test: read rx_fifo when rx_overflow is triggered
+
+ while (start_read && total_rd_bytes > 0) begin
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(rx_fifo_access_dly)
+ // constraint rx_fifo access delay to a high value that ensures rx_overflow is triggered
+ if (!rx_watermark && !rx_overflow && !rx_sanity) begin
+ rx_fifo_access_dly = (I2C_RX_FIFO_DEPTH + 5) * get_byte_latency();
end
+ csr_spinwait(.ptr(ral.status.rxempty), .exp_data(1'b0));
+ cfg.clk_rst_vif.wait_clks(rx_fifo_access_dly);
+ csr_rd(.ptr(ral.rdata), .value(rd_data));
+ total_rd_bytes--;
end
- end while (!delay_read_rx_until_full && !rxfull);
+ end
end
join
endtask : host_read_trans
virtual task host_write_trans(bit last_tran);
`DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_wr_bytes)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(wr_data)
for (int i = 1; i <= num_wr_bytes; i++) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(fmt_item,
start == 1'b0;
read == 1'b0;
)
- `DV_CHECK_MEMBER_RANDOMIZE_FATAL(wr_data)
- fmt_item.fbyte = wr_data;
+ fmt_item.fbyte = wr_data[i];
// last write byte of last tran., stop flag must be set to issue stop bit
// last write byte of other tran., stop is randomly set/unset to issue stop/rstart bit
fmt_item.stop = (i != num_wr_bytes) ? 1'b0 : ((last_tran) ? 1'b1 : fmt_item.stop);
@@ -165,13 +176,7 @@
// TODO: This task could be extended along with V2 test development
virtual task clear_interrupt(i2c_intr_e intr, bit verify_clear = 1'b1);
csr_wr(.csr(ral.intr_state), .value(1 << intr));
- if (verify_clear) begin
- case (intr)
- FmtWatermark: csr_spinwait(.ptr(ral.intr_state.fmt_watermark), .exp_data(1'b0));
- RxWatermark: csr_spinwait(.ptr(ral.intr_state.rx_watermark), .exp_data(1'b0));
- default: `uvm_fatal(`gfn, "Invalid intr input")
- endcase
- end
+ if (verify_clear) wait(!cfg.intr_vif.pins[intr]);
endtask : clear_interrupt
virtual task post_start();
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv
index 5640b0c..46869a4 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv
@@ -8,4 +8,5 @@
`include "i2c_sanity_vseq.sv"
`include "i2c_override_vseq.sv"
-`include "i2c_fifo_watermark_vseq.sv"
\ No newline at end of file
+`include "i2c_fifo_watermark_vseq.sv"
+`include "i2c_fifo_overflow_vseq.sv"
\ No newline at end of file
diff --git a/hw/ip/i2c/dv/i2c_sim_cfg.hjson b/hw/ip/i2c/dv/i2c_sim_cfg.hjson
index 9e52a69..1815d52 100644
--- a/hw/ip/i2c/dv/i2c_sim_cfg.hjson
+++ b/hw/ip/i2c/dv/i2c_sim_cfg.hjson
@@ -59,6 +59,11 @@
name: i2c_fifo_watermark
uvm_test_seq: i2c_fifo_watermark_vseq
}
+
+ {
+ name: i2c_fifo_overflow
+ uvm_test_seq: i2c_fifo_overflow_vseq
+ }
]
// List of regressions.