[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.