[i2c, dv] Add i2c_sanity_vseq for V1

1) i2c_sanity_vseq generates following transaction types
  - b2b-read  trans without STOP bit at the end (issue repSTART after)
  - b2b-read  trans with    STOP bit at the end (issue    START after)
  - b2b-write trans without STOP bit at the end (issue repSTART after)
  - b2b-write trans with    STOP bit at the end (issue    START after)
  - interleave read and write transactions (randomly change direction)
  - support clock streching by device
2) i2c_scoreboard
      - verify transaction types (generated by i2c_sanity_vseq)
3) fix monitor_ready_to_end task in i2c_monitor.sv to avoid timeout

TODO: b2b-read chained trans

Signed-off-by: Tung Hoang <tung.hoang.290780@gmail.com>
diff --git a/hw/dv/sv/i2c_agent/i2c_driver.sv b/hw/dv/sv/i2c_agent/i2c_driver.sv
index 03fad7e..5c05325 100644
--- a/hw/dv/sv/i2c_agent/i2c_driver.sv
+++ b/hw/dv/sv/i2c_agent/i2c_driver.sv
@@ -8,14 +8,14 @@
   `uvm_component_new
 
   virtual task reset_signals();
-    `uvm_info(`gtn, "driver in reset progress", UVM_HIGH)
-    @(posedge !cfg.vif.rst_ni);
+    `uvm_info(`gfn, "driver in reset progress", UVM_HIGH)
+    @(negedge cfg.vif.rst_ni);
     cfg.vif.scl_o <= 1'b0;
     cfg.vif.sda_o <= 1'b0;
     @(posedge cfg.vif.rst_ni);
     cfg.vif.scl_o <= 1'b1;
     cfg.vif.sda_o <= 1'b1;
-    `uvm_info(`gtn, "driver out of reset", UVM_HIGH)
+    `uvm_info(`gfn, "driver out of reset", UVM_HIGH)
   endtask : reset_signals
 
 endclass : i2c_driver
diff --git a/hw/dv/sv/i2c_agent/i2c_monitor.sv b/hw/dv/sv/i2c_agent/i2c_monitor.sv
index 8edf08e..6903306 100644
--- a/hw/dv/sv/i2c_agent/i2c_monitor.sv
+++ b/hw/dv/sv/i2c_agent/i2c_monitor.sv
@@ -174,8 +174,7 @@
   virtual task monitor_ready_to_end();
     forever begin
       @(cfg.vif.scl_i or cfg.vif.sda_i or cfg.vif.scl_o or cfg.vif.sda_o);
-      ok_to_end = (cfg.vif.scl_i == 1'b1) && (cfg.vif.sda_i == 1'b1) &&
-                  (cfg.vif.scl_o == 1'b1) && (cfg.vif.sda_o == 1'b1);
+      ok_to_end = (cfg.vif.scl_i == 1'b1) && (cfg.vif.sda_i == 1'b1);
     end
   endtask : monitor_ready_to_end
 
diff --git a/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv b/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv
index 1a8ec0f..073e42b 100644
--- a/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv
+++ b/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv
@@ -12,7 +12,7 @@
   `uvm_object_new
 
   virtual task body();
-    `uvm_fatal(`gtn, "Need to override this when you extend from this class!")
+    `uvm_fatal(`gfn, "Need to override this when you extend from this class!")
   endtask
 
 endclass : i2c_base_seq
diff --git a/hw/ip/i2c/data/i2c.hjson b/hw/ip/i2c/data/i2c.hjson
index b4fec12..b81e4d0 100644
--- a/hw/ip/i2c/data/i2c.hjson
+++ b/hw/ip/i2c/data/i2c.hjson
@@ -14,10 +14,10 @@
   // INTERRUPT pins
   interrupt_list: [
     { name: "fmt_watermark"
-      desc: "raised when the FMT FIFO depth falls below the lowwater mark."
+      desc: "raised when the FMT FIFO depth falls below the low watermark."
     }
     { name: "rx_watermark"
-      desc: "raised if the RX FIFO is past the highwater mark."
+      desc: "raised if the RX FIFO is past the high watermark."
     }
     { name: "fmt_overflow"
       desc: "raised if the FMT FIFO has overflowed."
diff --git a/hw/ip/i2c/data/i2c_testplan.hjson b/hw/ip/i2c/data/i2c_testplan.hjson
index 2b4de9e..a6d8d65 100644
--- a/hw/ip/i2c/data/i2c_testplan.hjson
+++ b/hw/ip/i2c/data/i2c_testplan.hjson
@@ -19,7 +19,7 @@
             Checking:
               - Check the timing behavior of START, STOP, ACK, NACK, and "repeated" START.
               - Read and write transfer matching.'''
-      milestone: V2
+      milestone: V1
       tests: ["i2c_sanity"]
     }
     {
diff --git a/hw/ip/i2c/dv/env/i2c_env.core b/hw/ip/i2c/dv/env/i2c_env.core
index 8af27c3..e1a3ba9 100644
--- a/hw/ip/i2c/dv/env/i2c_env.core
+++ b/hw/ip/i2c/dv/env/i2c_env.core
@@ -21,6 +21,8 @@
       - seq_lib/i2c_vseq_list.sv: {is_include_file: true}
       - seq_lib/i2c_base_vseq.sv: {is_include_file: true}
       - seq_lib/i2c_common_vseq.sv: {is_include_file: true}
+      - seq_lib/i2c_rx_tx_vseq.sv: {is_include_file: true}
+      - seq_lib/i2c_sanity_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
 generate:
diff --git a/hw/ip/i2c/dv/env/i2c_env.sv b/hw/ip/i2c/dv/env/i2c_env.sv
index 16e65a3..5d91633 100644
--- a/hw/ip/i2c/dv/env/i2c_env.sv
+++ b/hw/ip/i2c/dv/env/i2c_env.sv
@@ -18,16 +18,16 @@
     super.build_phase(phase);
     m_i2c_agent = i2c_agent::type_id::create("m_i2c_agent", this);
     uvm_config_db#(i2c_agent_cfg)::set(this, "m_i2c_agent*", "cfg", cfg.m_i2c_agent_cfg);
-  endfunction
+  endfunction : build_phase
 
   function void connect_phase(uvm_phase phase);
     super.connect_phase(phase);
     if (cfg.en_scb) begin
-      m_i2c_agent.monitor.analysis_port.connect(scoreboard.i2c_fifo.analysis_export);
+      m_i2c_agent.monitor.rd_item_port.connect(scoreboard.rd_item_fifo.analysis_export);
+      m_i2c_agent.monitor.wr_item_port.connect(scoreboard.wr_item_fifo.analysis_export);
     end
-    if (cfg.m_i2c_agent_cfg.is_active) begin
-      virtual_sequencer.i2c_sequencer_h = m_i2c_agent.sequencer;
-    end
+
+    virtual_sequencer.i2c_sequencer_h = m_i2c_agent.sequencer;
   endfunction
 
 endclass : i2c_env
diff --git a/hw/ip/i2c/dv/env/i2c_env_cfg.sv b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
index 2ba74c9..e7a0dd8 100644
--- a/hw/ip/i2c/dv/env/i2c_env_cfg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
@@ -4,7 +4,10 @@
 
 class i2c_env_cfg extends cip_base_env_cfg #(.RAL_T(i2c_reg_block));
 
-  // ext component cfgs
+  // TODO: various knobs to enable certain routines
+  bit do_rd_overflow  = 1'b0;
+  bit do_wr_overflow  = 1'b0;
+
   rand i2c_agent_cfg m_i2c_agent_cfg;
 
   `uvm_object_utils_begin(i2c_env_cfg)
@@ -19,10 +22,7 @@
 
   virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1);
     super.initialize(csr_base_addr);
-    // create i2c agent config obj
     m_i2c_agent_cfg = i2c_agent_cfg::type_id::create("m_i2c_agent_cfg");
-
-    // set num_interrupts & num_alerts which will be used to create coverage and more
     num_interrupts = ral.intr_state.get_n_used_bits();
   endfunction
 
diff --git a/hw/ip/i2c/dv/env/i2c_env_cov.sv b/hw/ip/i2c/dv/env/i2c_env_cov.sv
index fef5f70..8dcb285 100644
--- a/hw/ip/i2c/dv/env/i2c_env_cov.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_cov.sv
@@ -5,11 +5,9 @@
 class i2c_env_cov extends cip_base_env_cov #(.CFG_T(i2c_env_cfg));
   `uvm_component_utils(i2c_env_cov)
 
-  // TODO: define covergroups
-
+  // TODO: instantiate all covergroups here
   function new(string name, uvm_component parent);
     super.new(name, parent);
-    // TODO: instantiate all covergroups here
   endfunction : new
 
 endclass
diff --git a/hw/ip/i2c/dv/env/i2c_env_pkg.sv b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
index db633c9..b97bbc4 100644
--- a/hw/ip/i2c/dv/env/i2c_env_pkg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
@@ -36,16 +36,24 @@
   parameter uint I2C_ADDR_MAP_SIZE = 128;
 
   // for constrains
-  parameter uint I2C_MIN_ADDR   = 0;
-  parameter uint I2C_MAX_ADDR   = 127;
-  parameter uint I2C_MIN_DATA   = 0;
-  parameter uint I2C_MAX_DATA   = 255;
-  parameter uint I2C_MIN_DLY    = 1;
-  parameter uint I2C_MAX_DLY    = 2;
-  parameter uint I2C_MIN_TIMING = 2;
-  parameter uint I2C_MAX_TIMING = 2;
-  parameter bit  I2C_FLAG_ON    = 1'b1;
-  parameter bit  I2C_FLAG_OFF   = 1'b0;
+  parameter uint I2C_MIN_TRAN    = 10;
+  parameter uint I2C_MAX_TRAN    = 200;
+  parameter uint I2C_MIN_ADDR    = 0;
+  parameter uint I2C_MAX_ADDR    = 127;
+  parameter uint I2C_MIN_DLY     = 0;
+  parameter uint I2C_MAX_DLY     = 2;
+  parameter uint I2C_MIN_DATA    = 0;
+  parameter uint I2C_MAX_DATA    = 255;
+  parameter uint I2C_MIN_TIMING  = 1;     // at least 1
+  parameter uint I2C_MAX_TIMING  = 5;
+  parameter uint I2C_TIME_RANGE  = I2C_MAX_TIMING - I2C_MIN_TIMING;
+  parameter uint I2C_TIMEOUT_ENB = 1;
+  parameter uint I2C_MIN_TIMEOUT = 1;
+  parameter uint I2C_MAX_TIMEOUT = 2;
+  parameter uint I2C_IDLE_TIME   = 1200;
+
+  // ok_to_end_delay_ns for EoT
+  parameter uint DELAY_FOR_EOT_NS = 5000;
 
   // functions
   // get the number of bytes that triggers watermark interrupt
@@ -86,3 +94,4 @@
   `include "i2c_vseq_list.sv"
 
 endpackage : i2c_env_pkg
+
diff --git a/hw/ip/i2c/dv/env/i2c_scoreboard.sv b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
index 3fdb05a..12f5ef0 100644
--- a/hw/ip/i2c/dv/env/i2c_scoreboard.sv
+++ b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
@@ -9,43 +9,52 @@
   );
   `uvm_component_utils(i2c_scoreboard)
 
-  //****************************************************************
-  // TODO: this class will be completed later with i2c_sanity test
-  // TODO: no need for review
-  //****************************************************************
-  virtual i2c_if i2c_vif;
+  virtual i2c_if  i2c_vif;
 
-  // TLM agent fifos
-  uvm_tlm_analysis_fifo #(i2c_item) i2c_fifo;
+  local i2c_item  exp_rd_item;
+  local i2c_item  exp_wr_item;
+  local i2c_item  rd_pending_item;
+  local i2c_item  rd_pending_q[$];
+  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;
+  local uint      num_exp_tran = 0;
+
+  // use seperate fifos for dut_rd and dut_wr item
+  uvm_tlm_analysis_fifo #(i2c_item) rd_item_fifo;
+  uvm_tlm_analysis_fifo #(i2c_item) wr_item_fifo;
 
   `uvm_component_new
 
   function void build_phase(uvm_phase phase);
     super.build_phase(phase);
-    i2c_fifo = new("i2c_fifo", this);
-  endfunction
-
-  function void connect_phase(uvm_phase phase);
-    super.connect_phase(phase);
-  endfunction
+    rd_item_fifo = new("rd_item_fifo", this);
+    wr_item_fifo = new("wr_item_fifo", this);
+    rd_pending_item = new("rd_pending_item" );
+    exp_rd_item = new("exp_rd_item");
+    exp_wr_item = new("exp_wr_item");
+  endfunction : build_phase
 
   task run_phase(uvm_phase phase);
     super.run_phase(phase);
     fork
-      process_i2c_fifo();
-    join_none
-  endtask
-
-  virtual task process_i2c_fifo();
-    // TODO
-  endtask : process_i2c_fifo
+      compare_trans(BusOpWrite);
+      compare_trans(BusOpRead);
+    join
+  endtask : run_phase
 
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
-    uvm_reg csr;
+    uvm_reg   csr;
+    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);
 
+    uvm_reg_addr_t csr_addr = get_normalized_addr(item.a_addr);
     // if access was to a valid csr, get the csr handle
     if (csr_addr inside {cfg.csr_addrs}) begin
       csr = ral.default_map.get_reg_by_offset(csr_addr);
@@ -54,52 +63,204 @@
       `uvm_fatal(`gfn, $sformatf("access unexpected addr 0x%0h", csr_addr))
     end
 
-    if (channel == AddrChannel) begin
-      // if incoming access is a write to a valid csr, then make updates right away
-      if (write) begin
-        void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
+    sb_exp_wr_item = new();
+    sb_exp_rd_item = new();
+    if (write && channel == AddrChannel) begin
+      // incoming access is a write to a valid csr, then make updates right away
+      void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask)));
+
+      // process the csr req
+      // for write, update local variable and fifo at address phase
+      // for read, update predication at address phase and compare at data phase
+      case (csr.get_name())
+        // add individual case item for each csr
+        "ctrl": begin
+          host_init = ral.ctrl.enablehost.get_mirrored_value();
+        end
+
+        "fdata": begin
+          bit [7:0] fbyte;
+          bit start, stop, read, rcont, nakok;
+
+          if (host_init) begin
+            fbyte = get_field_val(ral.fdata.fbyte, item.a_data);
+            start = bit'(get_field_val(ral.fdata.start, item.a_data));
+            stop  = bit'(get_field_val(ral.fdata.stop, item.a_data));
+            read  = bit'(get_field_val(ral.fdata.read, item.a_data));
+            rcont = bit'(get_field_val(ral.fdata.rcont, item.a_data));
+            nakok = bit'(get_field_val(ral.fdata.nakok, item.a_data));
+
+            // transaction is being started/rstarted, target address is programmed
+            if (start) begin
+              tran_id++;
+              if (exp_wr_item.start && !sb_exp_wr_item.stop &&
+                  exp_wr_item.bus_op == BusOpWrite) begin
+                // this is a write end with rstart, write to sb_wr_fifo
+                exp_wr_item.rstart = 1'b1;
+                `downcast(sb_exp_wr_item, exp_wr_item.clone());
+                sb_exp_wr_item.stop = 1'b1;
+                exp_wr_item.clear_all();
+              end
+              `DV_CHECK_EQ(read, 1'b0)
+              `DV_CHECK_EQ(stop, 1'b0)
+              if (fbyte[0]) begin
+                // read transaction
+                exp_rd_item.tran_id = tran_id;
+                exp_rd_item.bus_op  = BusOpRead;
+                exp_rd_item.addr    = fbyte[7:1];
+                exp_rd_item.start   = start;
+                exp_rd_item.stop    = stop;
+              end else begin
+                // write transaction
+                exp_wr_item.tran_id = tran_id;
+                exp_wr_item.bus_op  = BusOpWrite;
+                exp_wr_item.addr    = fbyte[7:1];
+                exp_wr_item.start   = start;
+                exp_wr_item.stop    = stop;
+              end
+            end else begin // transaction has been 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
+                  // this is a write end with stop, write to the sb_wr_fifo
+                  `downcast(sb_exp_wr_item, exp_wr_item.clone());
+                  exp_wr_item.clear_all();
+                end
+              end
+              // read transaction
+              if (exp_rd_item.start && exp_rd_item.bus_op == BusOpRead) begin
+                if (read) begin  // read flag
+                  i2c_item tmp_rd_item;
+
+                  // get the number of byte to read, fmt_read flag is set
+                  exp_rd_item.num_data = (fbyte == 8'd0) ? 256 : fbyte;
+                  exp_rd_item.stop  = stop;
+                  `DV_CHECK_EQ(read, 1'b1)
+                  exp_rd_item.read   = read;
+                  exp_rd_item.rcont  = rcont;
+                  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;
+                  //push into temporal rd_pending_q (data still empty)
+                  `downcast(tmp_rd_item, exp_rd_item.clone());
+                  rd_pending_q.push_back(tmp_rd_item);
+                  exp_rd_item.start = 0;
+                  exp_rd_item.clear_data();
+                end
+              end
+            end
+          end
+        end
+      endcase
+
+      // push sb_exp_wr_item into exp_fifo
+      if (host_init && sb_exp_wr_item.start && sb_exp_wr_item.stop) begin
+        //exp_wr_port.write(sb_exp_wr_item);
+        exp_wr_q.push_back(sb_exp_wr_item);
+        num_exp_tran++;
+        `uvm_info(`gfn, $sformatf("\n\nscoreboard, exp_wr_item\n\%s",
+            sb_exp_wr_item.sprint()), UVM_DEBUG)
       end
     end
 
-    // process the csr req
-    // for write, update local variable and fifo at address phase
-    // for read, update predication at address phase and compare at data phase
-    case (csr.get_name())
-      // add individual case item for each csr
-      default: begin
-        //`uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
-      end
-    endcase
-
     // On reads, if do_read_check, is set, then check mirrored_value against item.d_data
     if (!write && channel == DataChannel) begin
       if (do_read_check) begin
         `DV_CHECK_EQ(csr.get_mirrored_value(), item.d_data,
-                     $sformatf("reg name: %0s", csr.get_full_name()))
+            $sformatf("reg name: %0s", csr.get_full_name()))
       end
       void'(csr.predict(.value(item.d_data), .kind(UVM_PREDICT_READ)));
-    end
-  endtask
 
-  function void compare(i2c_item act, i2c_item exp, string dir = "TX");
-    if (!act.compare(exp)) begin
-      `uvm_error(`gfn, $sformatf("%s item mismatch!\nexp:\n%0s\nobs:\n%0s",
-                                  dir, exp.sprint(), act.sprint()))
+      case (csr.get_name())
+        "rdata": begin
+          if (host_init) begin
+            if (rdata_cnt == 0) begin
+              `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++;
+            // get all read data bytes
+            if (rdata_cnt == rd_pending_item.num_data) begin
+              rd_pending_item.stop = 1'b1;
+              `downcast(sb_exp_rd_item, rd_pending_item.clone());
+              rdata_cnt = 0;
+            end
+          end
+        end
+      endcase
+
+      // push sb_exp_rd_item into exp_fifo
+      if (host_init && sb_exp_rd_item.start && sb_exp_rd_item.stop) begin
+        //exp_rd_port.write(sb_exp_rd_item);
+        exp_rd_q.push_back(sb_exp_rd_item);
+        num_exp_tran++;
+        `uvm_info(`gfn, $sformatf("\n\nscoreboard exp_rd_item\n\%s",
+            sb_exp_rd_item.sprint()), UVM_DEBUG)
+      end
     end
-    else begin
-      `uvm_info(`gfn, $sformatf("%s item match!\nexp:\n%0s\nobs:\n%0s",
-                                 dir, exp.sprint(), act.sprint()), UVM_HIGH)
+  endtask : process_tl_access
+
+  task compare_trans(bus_op_e dir = BusOpWrite);
+    i2c_item   exp_item;
+    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();
+      end else begin  // BusOpRead
+        rd_item_fifo.get(dut_trn);
+        wait(exp_rd_q.size() > 0);
+        exp_item = 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()))
+      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)
+      end
     end
-  endfunction : compare
+  endtask : compare_trans
 
   virtual function void reset(string kind = "HARD");
-    // reset local fifos queues and variables
     super.reset(kind);
+    // reset local fifos queues and variables
+    rd_item_fifo.flush();
+    wr_item_fifo.flush();
+    exp_rd_q.delete();
+    exp_wr_q.delete();
+    rd_pending_q.delete();
+    host_init  = 1'b0;
+    tran_id    = 0;
+    rdata_cnt  = 0;
+    num_exp_tran = 0;
   endfunction : reset
 
+  function void report_phase(uvm_phase phase);
+    string str;
+
+    super.report_phase(phase);
+    `uvm_info(`gfn, $sformatf("%s", cfg.convert2string()), UVM_DEBUG)
+    if (cfg.en_scb) begin
+      str = {$sformatf("\n\n*** SCOREBOARD CHECK\n")};
+      str = {str, $sformatf("    - Total checked trans   %0d\n", num_exp_tran)};
+      `uvm_info(`gfn, $sformatf("%s", str), UVM_LOW)
+    end
+  endfunction : report_phase
+
   function void check_phase(uvm_phase phase);
     super.check_phase(phase);
-    // post test checks - ensure that all local fifos and queues are empty
+    `DV_EOT_PRINT_Q_CONTENTS(i2c_item, exp_wr_q)
+    `DV_EOT_PRINT_Q_CONTENTS(i2c_item, exp_rd_q)
+    `DV_EOT_PRINT_Q_CONTENTS(i2c_item, rd_pending_q)
+    `DV_EOT_PRINT_TLM_FIFO_CONTENTS(i2c_item, rd_item_fifo)
+    `DV_EOT_PRINT_TLM_FIFO_CONTENTS(i2c_item, wr_item_fifo)
   endfunction
 
 endclass : i2c_scoreboard
diff --git a/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv b/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
index e5a1a3c..3e45d99 100644
--- a/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
+++ b/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
@@ -4,10 +4,9 @@
 
 class i2c_virtual_sequencer extends cip_base_virtual_sequencer #(.CFG_T(i2c_env_cfg),
                                                                  .COV_T(i2c_env_cov));
-  `uvm_component_utils(i2c_virtual_sequencer)
-
   i2c_sequencer    i2c_sequencer_h;
 
+  `uvm_component_utils(i2c_virtual_sequencer)
   `uvm_component_new
 
 endclass : i2c_virtual_sequencer
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 a5e0a8b..e8dfef8 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
@@ -10,8 +10,207 @@
   );
   `uvm_object_utils(i2c_base_vseq)
 
+  // class property
+  local timing_cfg_t          timing_cfg;
+  bit [7:0]                   rd_data;
+  bit                         do_interrupt = 1'b1;
+  bit                         force_use_incorrect_config = 1'b0;
+  // random property
+  rand uint                   fmt_fifo_access_dly;
+  rand uint                   rx_fifo_access_dly;
+
+  rand bit   [NumI2cIntr-1:0] en_intr;
+  rand uint                   num_trans;
+  rand uint                   num_wr_bytes;
+  rand uint                   num_rd_bytes;
+  rand bit   [7:0]            wr_data;
+  rand bit   [6:0]            addr;
+  rand bit                    rw_bit;
+  i2c_item                    fmt_item;
+
+  // timing property
+  rand bit [15:0]             thigh;      // high period of the SCL in clock units
+  rand bit [15:0]             tlow;       // low period of the SCL in clock units
+  rand bit [15:0]             t_r;        // rise time of both SDA and SCL in clock units
+  rand bit [15:0]             t_f;        // fall time of both SDA and SCL in clock units
+  rand bit [15:0]             thd_sta;    // hold time for (repeated) START in clock units
+  rand bit [15:0]             tsu_sta;    // setup time for repeated START in clock units
+  rand bit [15:0]             tsu_sto;    // setup time for STOP in clock units
+  rand bit [15:0]             tsu_dat;    // data setup time in clock units
+  rand bit [15:0]             thd_dat;    // data hold time in clock units
+  rand bit [15:0]             t_buf;      // bus free time between STOP and START in clock units
+  rand bit [30:0]             t_timeout;  // max time target may stretch the clock
+  rand bit                    e_timeout;  // max time target may stretch the clock
+
+  // constraints
+  constraint rw_bit_c    { rw_bit     inside {0, 1}; }
+  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 num_trans_c { num_trans  inside {[I2C_MIN_TRAN : I2C_MAX_TRAN]}; }
+
+  constraint timing_val_c {
+    thigh     inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    t_r       inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    t_f       inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    thd_sta   inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    tsu_sto   inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    tsu_dat   inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    thd_dat   inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    t_timeout inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+    e_timeout inside { [0 : I2C_TIMEOUT_ENB] };
+
+    solve t_r, tsu_dat, thd_dat before tlow;
+    solve t_r                   before t_buf;
+    if (force_use_incorrect_config) {
+      // force derived timing parameters to be negative (incorrect DUT config)
+      tsu_sta == t_r + t_buf + 1;  // negative tHoldStop
+      tlow    == 2;                // negative tClockLow
+      t_buf   == 2;
+    } else {
+      tsu_sta   inside { [I2C_MIN_TIMING : I2C_MAX_TIMING] };
+      // force derived timing parameters to be positive (correct DUT config)
+      tlow     inside { [(t_r + tsu_dat + thd_dat + 1) :
+                         (t_r + tsu_dat + thd_dat + 1) + I2C_TIME_RANGE] };
+      t_buf    inside { [(tsu_sta - t_r + 1) :
+                         (tsu_sta - t_r + 1) + I2C_TIME_RANGE] };
+    }
+  }
+
+  constraint num_wr_bytes_c {
+    num_wr_bytes dist {
+      1      :/ 2,
+      [2:5]  :/ 5,
+      [6:9]  :/ 5,
+      [9:12] :/ 2
+    };
+  }
+  constraint num_rd_bytes_c {
+    num_rd_bytes dist {
+      0      :/ 1,
+      1      :/ 2,
+      [2:5]  :/ 5,
+      [6:9]  :/ 5,
+      [9:12] :/ 2
+    };
+  }
+  constraint en_intr_c {
+    en_intr inside {[0: ((1 << NumI2cIntr) - 1)]};
+  }
+  constraint fmt_fifo_access_dly_c {
+    fmt_fifo_access_dly inside {[1:5]};
+  }
+  constraint rx_fifo_access_dly_c {
+    rx_fifo_access_dly inside {[1:5]};
+  }
+
   `uvm_object_new
 
-  // TODO: this vseq will be updated later (with i2c_sanity)
+  virtual task device_init();
+    i2c_device_seq m_dev_seq;
 
+    m_dev_seq = i2c_device_seq::type_id::create("m_dev_seq");
+    `uvm_info(`gfn, "start i2c_device sequence", UVM_DEBUG)
+    fork
+      m_dev_seq.start(p_sequencer.i2c_sequencer_h);
+    join_none
+  endtask : device_init
+
+  virtual task host_init();
+    `uvm_info(`gfn, "initialize i2c host registers", UVM_DEBUG)
+    ral.ctrl.enablehost.set(1'b1);
+    csr_update(ral.ctrl);
+    if (do_interrupt) begin
+      ral.intr_enable.set(en_intr);
+      csr_update(ral.intr_enable);
+      `DV_CHECK_RANDOMIZE_WITH_FATAL(ral.fifo_ctrl.rxilvl, value <= 4;)
+      `DV_CHECK_RANDOMIZE_FATAL(ral.fifo_ctrl.fmtilvl)
+      csr_update(ral.fifo_ctrl);
+    end
+  endtask : host_init
+
+  function automatic void get_timing_values();
+    // derived timing parameters
+    timing_cfg.enbTimeOut  = e_timeout;
+    timing_cfg.tTimeOut    = t_timeout;
+    timing_cfg.tSetupStart = t_r + tsu_sta;
+    timing_cfg.tHoldStart  = t_f + thd_sta;
+    timing_cfg.tClockStart = thd_dat;
+    timing_cfg.tClockLow   = tlow - t_r - tsu_dat - thd_dat;
+    timing_cfg.tSetupBit   = t_r + tsu_dat;
+    timing_cfg.tClockPulse = t_r + thigh + t_f;
+    timing_cfg.tHoldBit    = t_f + thd_dat;
+    timing_cfg.tClockStop  = t_f + tlow - thd_dat;
+    timing_cfg.tSetupStop  = t_r + tsu_sto;
+    timing_cfg.tHoldStop   = t_r + t_buf - tsu_sta;
+    // ensure these parameter must be greater than zeros
+    if (!force_use_incorrect_config) begin
+      `DV_CHECK_GT_FATAL(timing_cfg.tClockLow, 0)
+      `DV_CHECK_GT_FATAL(timing_cfg.tClockStop, 0)
+      `DV_CHECK_GT_FATAL(timing_cfg.tHoldStop, 0)
+    end
+  endfunction : get_timing_values
+
+  virtual task program_timing_regs();
+    csr_wr(.csr(ral.timing0), .value({tlow, thigh}));
+    csr_wr(.csr(ral.timing1), .value({t_f, t_r}));
+    csr_wr(.csr(ral.timing2), .value({thd_sta, tsu_sta}));
+    csr_wr(.csr(ral.timing3), .value({thd_dat, tsu_dat}));
+    csr_wr(.csr(ral.timing4), .value({t_buf,   tsu_sto}));
+    csr_wr(.csr(ral.timeout_ctrl), .value({e_timeout, t_timeout}));
+    // configure i2c_agent_cfg
+    cfg.m_i2c_agent_cfg.timing_cfg = timing_cfg;
+    // set time to stop test
+    cfg.m_i2c_agent_cfg.ok_to_end_delay_ns = DELAY_FOR_EOT_NS;
+  endtask : program_timing_regs
+
+  virtual task program_format_flag(i2c_item item, string msg="");
+    bit [TL_DW-1:0] reg_val;
+
+    csr_spinwait(.ptr(ral.status.fmtfull), .exp_data(1'b0));
+    reg_val = {19'd0, item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte};
+    csr_wr(.csr(ral.fdata), .value(reg_val));
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_fifo_access_dly)
+    cfg.m_i2c_agent_cfg.vif.wait_for_dly(fmt_fifo_access_dly);
+    print_format_flag(item, msg);
+  endtask : program_format_flag
+
+  task print_format_flag(i2c_item item, string msg="");
+    string str;
+
+    str = {str, $sformatf("\n%s, format flags 0x%h \n", msg,
+                {item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte})};
+    if (item.start) begin
+      str = {str, $sformatf("  | %5s | %5s | %5s | %5s | %5s | %8s | %3s |\n",
+          "nakok", "rcont", "read", "stop", "start", "addr", "r/w")};
+      str = {str, $sformatf("  | %5d | %5d | %5d | %5d | %5d | %8x | %3s |",
+          item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte[7:1],
+          (item.fbyte[0]) ? "R" : "W")};
+    end else begin
+      str = {str, $sformatf("  | %5s | %5s | %5s | %5s | %5s | %8s |\n",
+          "nakok", "rcont", "read", "stop", "start", "fbyte")};
+      str = {str, $sformatf("  | %5d | %5d | %5d | %5d | %5d | %8x |",
+          item.nakok, item.rcont, item.read, item.stop, item.start, item.fbyte)};
+    end
+    `uvm_info(`gfn, $sformatf("%s", str), UVM_DEBUG)
+  endtask : print_format_flag
+
+  //*******************************************
+  // TODO: below functions/tasks
+  //*******************************************
+  virtual task clear_all_interrupts();
+    bit [TL_DW-1:0] data;
+
+    foreach (intr_state_csrs[i]) begin
+      csr_rd(.ptr(intr_state_csrs[i]), .value(data));
+      `uvm_info(`gfn, $sformatf("%s 0x%08h", intr_state_csrs[i].get_name(), data), UVM_DEBUG)
+      if (data != 0) begin
+        csr_wr(.csr(intr_state_csrs[i]), .value(data));
+        csr_rd(.ptr(intr_state_csrs[i]), .value(data));
+        // TODO: check the initial value fmt_watermark interrupt in/out of reset
+        //`DV_CHECK_EQ(data, 0)
+      end
+    end
+    // TODO:
+    //`DV_CHECK_EQ(cfg.intr_vif.sample(), {NUM_MAX_INTERRUPTS{1'b0}})
+  endtask : clear_all_interrupts
 endclass : i2c_base_vseq
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv
index 15b2ba5..f964d3c 100644
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv
@@ -17,8 +17,8 @@
 
   virtual task body();
     // disable i2c_monitor since it can not handle this test
-    `uvm_info(`gfn, $sformatf("disable i2c_monitor"), UVM_DEBUG)
-    cfg.m_i2c_agent_cfg.en_monitor <= 1'b0;
+    `uvm_info(`gfn, $sformatf("disable i2c_monitor and i2c_scoreboard"), UVM_DEBUG)
+    cfg.m_i2c_agent_cfg.en_monitor = 1'b0;
     run_common_vseq_wrapper(num_trans); // inherit from cip_base_vseq.sv
   endtask : body
 
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
new file mode 100644
index 0000000..283aa3d
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_rx_tx_vseq.sv
@@ -0,0 +1,128 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_rx_tx_vseq extends i2c_base_vseq;
+  `uvm_object_utils(i2c_rx_tx_vseq)
+
+  `uvm_object_new
+
+  virtual task host_init();
+    bit             fmtrst,  rxrst;
+    bit [1:0]       fmtilvl, rxilvl;
+    bit [TL_DW-1:0] reg_val;
+
+    super.host_init();
+    // diable override
+    ral.ovrd.txovrden.set(1'b0);
+    csr_update(ral.ovrd);
+    // reset and set level of rx and fmt fifos
+    rxrst   = 1'b1;
+    fmtrst  = 1'b1;
+    rxilvl  = 2'd3;
+    fmtilvl = 2'd3;
+    reg_val = {24'd0, fmtilvl, rxilvl, fmtrst, rxrst};
+    csr_wr(.csr(ral.fifo_ctrl), .value(reg_val));
+  endtask : host_init
+
+  virtual task host_send_trans(int num_trans);
+    bit   last_tran;
+
+    // setup config
+    force_use_incorrect_config = 1'b0;
+    fmt_item = new("fmt_item");
+    `DV_CHECK_RANDOMIZE_FATAL(this)
+    get_timing_values();
+    program_timing_regs();
+
+    for (uint cur_tran = 1; cur_tran <= num_trans; cur_tran++) begin
+      last_tran = (cur_tran == num_trans) ? 1'b1 : 1'b0;
+      // not program address for chained reads
+      host_program_target_address();
+      `uvm_info(`gfn, $sformatf("start sending %s transaction %0d/%0d",
+          (rw_bit) ? "READ" : "WRITE", cur_tran, num_trans), UVM_DEBUG)
+      if (rw_bit) host_read_trans(last_tran);
+      else        host_write_trans(last_tran);
+      `uvm_info(`gfn, $sformatf("finish sending %s transaction, %0s at the end,  %0d/%0d, ",
+          (rw_bit) ? "read" : "write",
+          (fmt_item.stop) ? "stop" : "rstart", cur_tran, num_trans), UVM_DEBUG)
+    end
+  endtask : host_send_trans
+
+  virtual task host_program_target_address();
+    // programm fmt flags for address
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_item)
+    fmt_item.start = 1'b1;
+    fmt_item.stop  = 1'b0;
+    fmt_item.read  = 1'b0;
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(addr)
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(rw_bit)
+    fmt_item.fbyte = (rw_bit) ? {addr, BusOpRead} : {addr, BusOpWrite};
+    program_format_flag(fmt_item, "host_program_target_address");
+  endtask : host_program_target_address
+
+  virtual task host_read_trans(bit last_tran);
+    uint real_rd_bytes;
+    uint total_rd_bytes = 0;
+
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_rd_bytes)
+    fork
+      begin
+        `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_item)
+        fmt_item.fbyte = num_rd_bytes;
+        fmt_item.start = 1'b0;
+        fmt_item.read  = 1'b1;
+        fmt_item.rcont = 1'b0; // TODO: no chained read support
+        // for the last write byte of last tran., stop flag must be set to issue stop bit (stimulus end)
+        // otherwise, stop can be randomly set/unset to issue stop/rstart bit respectively
+        fmt_item.stop  = (last_tran) ? 1'b1 : $urandom_range(0, 1);
+        real_rd_bytes = (num_rd_bytes) ? num_rd_bytes : 256;
+        total_rd_bytes += real_rd_bytes;
+        program_format_flag(fmt_item, "program number of bytes to read");
+      end
+      begin
+        if (!cfg.do_rd_overflow) begin
+          while (total_rd_bytes > 0) begin
+            csr_spinwait(.ptr(ral.status.rxempty), .exp_data(1'b0));
+            csr_rd(.ptr(ral.rdata), .value(rd_data));
+            total_rd_bytes--;
+            `DV_CHECK_MEMBER_RANDOMIZE_FATAL(rx_fifo_access_dly)
+            cfg.m_i2c_agent_cfg.vif.wait_for_dly(rx_fifo_access_dly);
+          end
+        end
+      end
+    join
+  endtask : host_read_trans
+
+  virtual task host_write_trans(bit last_tran);
+    `DV_CHECK_MEMBER_RANDOMIZE_FATAL(num_wr_bytes)
+    for (int i = 1; i <= num_wr_bytes; i++) begin
+      `DV_CHECK_MEMBER_RANDOMIZE_FATAL(fmt_item)
+      fmt_item.start = 1'b0;
+      fmt_item.read  = 1'b0;
+      `DV_CHECK_MEMBER_RANDOMIZE_FATAL(wr_data)
+      fmt_item.fbyte = wr_data;
+      // 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);
+      program_format_flag(fmt_item, "host_write_trans");
+    end
+  endtask : host_write_trans
+
+  virtual task process_interrupts();
+    bit [TL_DW-1:0] intr_status, clear_intr;
+    bit             clear_rx_intr, clear_tx_intr;
+
+    csr_rd(.ptr(ral.intr_state), .value(intr_status));
+    `uvm_info(`gfn, $sformatf("intr_state 0x%08h", intr_status), UVM_HIGH)
+    // TODO: interrupts must be properly handled before EoT
+  endtask : process_interrupts
+
+  virtual task post_start();
+    bit [TL_DW-1:0] intr_status;
+
+    do_dut_shutdown = 0;
+    super.post_start();
+  endtask : post_start
+
+endclass : i2c_rx_tx_vseq
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv
new file mode 100644
index 0000000..e0fa0b5
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv
@@ -0,0 +1,29 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// basic sanity test vseq
+class i2c_sanity_vseq extends i2c_rx_tx_vseq;
+  `uvm_object_utils(i2c_sanity_vseq)
+  `uvm_object_new
+
+  constraint num_wr_bytes_c { num_wr_bytes  inside {[1 : 5]}; }
+  constraint num_rd_bytes_c { num_rd_bytes  inside {[1 : 5]}; }
+  constraint num_trans_c    { num_trans     inside {100}; }
+
+  task pre_start();
+    super.pre_start();
+    do_interrupt = 1'b0;
+  endtask : pre_start
+
+  task body();
+    device_init();
+    host_init();
+
+    fork
+      while (do_interrupt) process_interrupts();
+      host_send_trans(num_trans);
+    join
+  endtask : body
+
+endclass : i2c_sanity_vseq
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 cb6b288..478df59 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
@@ -4,3 +4,5 @@
 
 `include "i2c_base_vseq.sv"
 `include "i2c_common_vseq.sv"
+`include "i2c_rx_tx_vseq.sv"
+`include "i2c_sanity_vseq.sv"
diff --git a/hw/ip/i2c/dv/i2c_sim_cfg.hjson b/hw/ip/i2c/dv/i2c_sim_cfg.hjson
index 66c829c..34ed3d4 100644
--- a/hw/ip/i2c/dv/i2c_sim_cfg.hjson
+++ b/hw/ip/i2c/dv/i2c_sim_cfg.hjson
@@ -29,6 +29,7 @@
                 // Common CIP test lists
                 "{proj_root}/hw/dv/data/tests/csr_tests.hjson",
                 "{proj_root}/hw/dv/data/tests/intr_test.hjson",
+                //TODO: enable later in V2
                 //"{proj_root}/hw/dv/data/tests/stress_tests.hjson",
                 "{proj_root}/hw/dv/data/tests/tl_access_tests.hjson"]
 
@@ -51,10 +52,10 @@
   ]
 
   // List of regressions.
-  //regressions: [
-  //  {
-  //     name: sanity
-  //     tests: ["i2c_sanity"]
-  //  }
-  //]
+  regressions: [
+    {
+       name: sanity
+       tests: ["i2c_sanity"]
+    }
+  ]
 }