[i2c, dv] Add i2c_dv (V0)

1. update i2c_agent
2. update i2c_csr_* test

This resolves lowRISC/opentitan/pull/1441

Signed-off-by: Tung Hoang <tung.hoang.290780@gmail.com>
diff --git a/hw/dv/sv/dv_utils/dv_utils_pkg.sv b/hw/dv/sv/dv_utils/dv_utils_pkg.sv
index 68cb775..2ba3987 100644
--- a/hw/dv/sv/dv_utils/dv_utils_pkg.sv
+++ b/hw/dv/sv/dv_utils/dv_utils_pkg.sv
@@ -62,8 +62,8 @@
 
   // Enum representing a bus operation type - read or write.
   typedef enum bit {
-    BusOpRead,
-    BusOpWrite
+    BusOpWrite = 1'b0,
+    BusOpRead  = 1'b1
   } bus_op_e;
 
   // Enum representing a type of host requests - read only, write only or random read & write
diff --git a/hw/dv/sv/i2c_agent/i2c_agent.core b/hw/dv/sv/i2c_agent/i2c_agent.core
index 48c6fa0..ffde59d 100644
--- a/hw/dv/sv/i2c_agent/i2c_agent.core
+++ b/hw/dv/sv/i2c_agent/i2c_agent.core
@@ -10,16 +10,19 @@
       - lowrisc:dv:dv_utils
       - lowrisc:dv:dv_lib
     files:
-      - i2c_if.sv
       - i2c_agent_pkg.sv
+      - i2c_if.sv
       - i2c_agent_cfg.sv: {is_include_file: true}
       - i2c_agent_cov.sv: {is_include_file: true}
       - i2c_item.sv: {is_include_file: true}
       - i2c_driver.sv: {is_include_file: true}
+      - i2c_device_driver.sv: {is_include_file: true}
       - i2c_monitor.sv: {is_include_file: true}
+      - i2c_sequencer.sv: {is_include_file: true}
       - i2c_agent.sv: {is_include_file: true}
-      - seq_lib/i2c_base_seq.sv: {is_include_file: true}
       - seq_lib/i2c_seq_list.sv: {is_include_file: true}
+      - seq_lib/i2c_base_seq.sv: {is_include_file: true}
+      - seq_lib/i2c_device_seq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
 targets:
diff --git a/hw/dv/sv/i2c_agent/i2c_agent.sv b/hw/dv/sv/i2c_agent/i2c_agent.sv
index 295c98e..d8fda1b 100644
--- a/hw/dv/sv/i2c_agent/i2c_agent.sv
+++ b/hw/dv/sv/i2c_agent/i2c_agent.sv
@@ -3,11 +3,12 @@
 // SPDX-License-Identifier: Apache-2.0
 
 class i2c_agent extends dv_base_agent #(
-      .CFG_T          (i2c_agent_cfg),
-      .DRIVER_T       (i2c_driver),
-      .SEQUENCER_T    (i2c_sequencer),
-      .MONITOR_T      (i2c_monitor),
-      .COV_T          (i2c_agent_cov)
+      .CFG_T           (i2c_agent_cfg),
+      .DRIVER_T        (i2c_driver),
+      .DEVICE_DRIVER_T (i2c_device_driver),
+      .SEQUENCER_T     (i2c_sequencer),
+      .MONITOR_T       (i2c_monitor),
+      .COV_T           (i2c_agent_cov)
   );
 
   `uvm_component_utils(i2c_agent)
@@ -16,6 +17,7 @@
 
   function void build_phase(uvm_phase phase);
     super.build_phase(phase);
+
     // get i2c_if handle
     if (!uvm_config_db#(virtual i2c_if)::get(this, "", "vif", cfg.vif)) begin
       `uvm_fatal(`gfn, "failed to get i2c_if handle from uvm_config_db")
diff --git a/hw/dv/sv/i2c_agent/i2c_agent_cfg.sv b/hw/dv/sv/i2c_agent/i2c_agent_cfg.sv
index 162bd2d..f928a5b 100644
--- a/hw/dv/sv/i2c_agent/i2c_agent_cfg.sv
+++ b/hw/dv/sv/i2c_agent/i2c_agent_cfg.sv
@@ -4,12 +4,42 @@
 
 class i2c_agent_cfg extends dv_base_agent_cfg;
 
-// interface handle used by driver, monitor & the sequencer, via cfg handle
-  virtual i2c_if vif;
+  // agent cfg knobs
+  bit en_cov            = 1'b1; // enable coverage
+  bit en_monitor        = 1'b1; // enable monitor
+  if_mode_e             mode;   // host or device mode
+
+  // random variable and their constrains
+  int max_delay_ack  = 5;
+  int max_delay_nack = 5;
+  int max_delay_stop = 5;
+  int max_delay_data = 5;
+
+  // register values
+  bit [31:0] status;           // status register
+  bit [15:0] thigh;            // high period of the SCL in clock units
+  bit [15:0] tlow;             // low period of the SCL in clock units
+  bit [15:0] t_r;              // rise time of both SDA and SCL in clock units
+  bit [15:0] t_f;              // fall time of both SDA and SCL in clock units
+  bit [15:0] thd_sta;          // hold time for (repeated) START in clock units
+  bit [15:0] tsu_sta;          // setup time for repeated START in clock units
+  bit [15:0] tsu_sto;          // setup time for STOP in clock units
+  bit [15:0] tsu_dat;          // data setup time in clock units
+  bit [15:0] thd_dat;          // data hold time in clock units
+  bit [15:0] t_buf;            // bus free time between STOP and START in clock units
+  bit [30:0] timeout_val;      // max time target may stretch the clock
+  bit        timeout_enb;      // assert if target stretches clock past max
+  bit [15:0] tvd_ack = 1;      // minimum valid time for ack/nack (same to tvd_dat)
+  
+  // interface handle used by driver, monitor & the sequencer, via cfg handle
+  virtual i2c_if  vif;
 
   `uvm_object_utils_begin(i2c_agent_cfg)
+    `uvm_field_int(en_cov,           UVM_DEFAULT)
+    `uvm_field_int(en_monitor,       UVM_DEFAULT)
+    `uvm_field_enum(if_mode_e, mode, UVM_DEFAULT)
   `uvm_object_utils_end
 
   `uvm_object_new
 
-endclass
+endclass : i2c_agent_cfg
diff --git a/hw/dv/sv/i2c_agent/i2c_agent_cov.sv b/hw/dv/sv/i2c_agent/i2c_agent_cov.sv
index 36a58f2..149f06f 100644
--- a/hw/dv/sv/i2c_agent/i2c_agent_cov.sv
+++ b/hw/dv/sv/i2c_agent/i2c_agent_cov.sv
@@ -5,14 +5,11 @@
 class i2c_agent_cov extends dv_base_agent_cov #(i2c_agent_cfg);
   `uvm_component_utils(i2c_agent_cov)
 
-  // the base class provides the following handles for use:
-  // i2c_agent_cfg: cfg
-
-  // covergroups
+  //TODO: covergroups
 
   function new(string name, uvm_component parent);
     super.new(name, parent);
-    // instantiate all covergroups here
+    //TODO: instantiate all covergroups here
   endfunction : new
 
 endclass
diff --git a/hw/dv/sv/i2c_agent/i2c_agent_pkg.sv b/hw/dv/sv/i2c_agent/i2c_agent_pkg.sv
index 52188d0..8762bb0 100644
--- a/hw/dv/sv/i2c_agent/i2c_agent_pkg.sv
+++ b/hw/dv/sv/i2c_agent/i2c_agent_pkg.sv
@@ -12,26 +12,30 @@
   `include "uvm_macros.svh"
   `include "dv_macros.svh"
 
-  // parameters
+  // local macros
+  parameter uint addr_width = 8;     // [7:1]: device address, [0]: R/W bit
+  parameter uint data_width = 8;
 
-  // local types
+  typedef enum logic [3:0] {
+    BusFree,
+    HostSendStart, HostSendRestart, HostSendStop, HostSendAck, HostSendNoAck,
+    HostSendAddr, HostSendRWBit, HostReceiveData, HostWriteData,
+    TargetSendAck
+  } bus_status_e;
+
   // forward declare classes to allow typedefs below
   typedef class i2c_item;
   typedef class i2c_agent_cfg;
 
-  // reuse dv_base_seqeuencer as is with the right parameter set
-  typedef dv_base_sequencer #(.ITEM_T     (i2c_item),
-                              .CFG_T      (i2c_agent_cfg)) i2c_sequencer;
-
-  // functions
-
   // package sources
   `include "i2c_item.sv"
   `include "i2c_agent_cfg.sv"
   `include "i2c_agent_cov.sv"
-  `include "i2c_driver.sv"
   `include "i2c_monitor.sv"
+  `include "i2c_driver.sv"
+  `include "i2c_device_driver.sv"
+  `include "i2c_sequencer.sv"
   `include "i2c_agent.sv"
   `include "i2c_seq_list.sv"
 
-  endpackage: i2c_agent_pkg
+endpackage: i2c_agent_pkg
diff --git a/hw/dv/sv/i2c_agent/i2c_device_driver.sv b/hw/dv/sv/i2c_agent/i2c_device_driver.sv
index b13d19e..ceb49b3 100644
--- a/hw/dv/sv/i2c_agent/i2c_device_driver.sv
+++ b/hw/dv/sv/i2c_agent/i2c_device_driver.sv
@@ -5,22 +5,100 @@
 class i2c_device_driver extends i2c_driver;
   `uvm_component_utils(i2c_device_driver)
 
-  // the base class provides the following handles for use:
-  // i2c_agent_cfg: cfg
+  bit [data_width-1:0] data_byte;
+  bit [addr_width-1:0] addr_byte;
+  bit rw_dir;
 
   `uvm_component_new
 
   virtual task run_phase(uvm_phase phase);
-    // base class forks off reset_signals() and get_and_drive() tasks
-    super.run_phase(phase);
-  endtask
-
-  // reset signals
-  virtual task reset_signals();
-  endtask
+    get_and_drive();
+  endtask : run_phase
 
   // drive trans received from sequencer
   virtual task get_and_drive();
-  endtask
+    forever begin
+      seq_item_port.get(req);     // get req handler with type i2c_item
+      $cast(rsp, req.clone());
+      rsp.set_id_info(req);
+      `uvm_info(`gfn, $sformatf("i2c gets next item:\n%0s", req.sprint()), UVM_DEBUG)
+      // address phase
+      process_address();
+      // data phase
+      if (rw_dir == 1'b1 && cfg.en_monitor == 1'b1)      process_read_data();
+      else if (rw_dir == 1'b0 && cfg.en_monitor == 1'b1) process_write_data();
+      // send rsp back to seq
+      `uvm_info(`gfn, "device item sent", UVM_DEBUG)
+      seq_item_port.put_response(rsp);
+    end
+  endtask : get_and_drive
 
-endclass
+  task process_address();
+    // device waits for START/RESTART on bus
+    cfg.vif.wait_for_start_repstart_from_host(cfg.thd_sta, cfg.tsu_sta);
+    `uvm_info(`gfn, $sformatf("i2c request type %d (R/W=1/0)", addr_byte[0]), UVM_DEBUG)
+    // device gets address bits from host
+    for (int i = 1; i < addr_width; i++) begin
+      wait(cfg.vif.scl_i == 1'b1);
+      addr_byte[i] = cfg.vif.sda_i;
+      cfg.vif.bus_status = HostSendAddr;
+    end
+    // device gets r/w bit from host
+    wait(cfg.vif.scl_i == 1'b1);
+    addr_byte[0] = cfg.vif.sda_i;
+    rw_dir = cfg.vif.sda_i;
+    cfg.vif.bus_status = HostSendRWBit;
+    rsp.addr = addr_byte;
+    rsp.mem_addrs.push_front(addr_byte);
+    // device sends ack to host
+    cfg.vif.wait_for_dly(req.dly_to_send_ack);
+    cfg.vif.send_ack_by_device(cfg.tvd_ack);
+  endtask : process_address
+
+  task process_read_data();
+    `uvm_info(`gfn, $sformatf("i2c processes read data"), UVM_DEBUG)
+    // if mem_datas is empty then return a random read data to response host
+    data_byte = (req.mem_datas.size()>0) ? req.mem_datas.pop_back() : gen_rand_rd_data();
+    // bit serialization
+    for (int i = 0; i < data_width; i++) begin
+      cfg.vif.scl_o <= 1'b0;
+      // send data bit
+      cfg.vif.scl_o <= 1'b0;
+      cfg.vif.wait_for_dly(req.dly_to_send_data);
+      cfg.vif.scl_o <= 1'b1;
+      cfg.vif.sda_o <= data_byte[i];
+      cfg.vif.bus_status = HostReceiveData;
+      // device must ensure hold time thd_dat when sending read data
+      repeat(cfg.thd_dat) @(posedge cfg.vif.clk_i);
+    end
+    rsp.rd_data = data_byte;
+    // wait for nack from host
+    cfg.vif.wait_for_nack_from_host(cfg.tvd_ack);
+    // wait for stop/restart from host
+    cfg.vif.wait_for_stop_or_restart_from_host(cfg.tsu_sta, cfg.tsu_sto);
+  endtask : process_read_data
+
+  task process_write_data();
+    `uvm_info(`gfn, $sformatf("i2c processes write data"), UVM_DEBUG)
+    // bit concatenation
+    for (int i = 0; i < data_width; i++) begin
+      wait(cfg.vif.scl_i == 1'b1)
+      data_byte[i] = cfg.vif.sda_i;
+      cfg.vif.bus_status = HostWriteData;
+    end
+    rsp.wr_data = data_byte;
+    rsp.mem_datas.push_front(data_byte);
+    // device sends ack to host
+    cfg.vif.wait_for_dly(req.dly_to_send_ack);
+    cfg.vif.send_ack_by_device(cfg.tvd_ack);
+    // wait for stop/restart from host
+    cfg.vif.wait_for_dly(req.dly_to_send_stop);
+    cfg.vif.wait_for_stop_or_restart_from_host(cfg.tsu_sta, cfg.tsu_sto);
+  endtask : process_write_data
+
+  function gen_rand_rd_data();
+    return $urandom_range((1 << data_width) - 1, 0);
+  endfunction
+
+endclass : i2c_device_driver
+
diff --git a/hw/dv/sv/i2c_agent/i2c_driver.sv b/hw/dv/sv/i2c_agent/i2c_driver.sv
index f3ef6b6..b4cd527 100644
--- a/hw/dv/sv/i2c_agent/i2c_driver.sv
+++ b/hw/dv/sv/i2c_agent/i2c_driver.sv
@@ -5,37 +5,26 @@
 class i2c_driver extends dv_base_driver #(i2c_item, i2c_agent_cfg);
   `uvm_component_utils(i2c_driver)
 
-  // the base class provides the following handles for use:
-  i2c_agent_cfg cfg;
-
   `uvm_component_new
 
   virtual task run_phase(uvm_phase phase);
-    // base class forks off reset_signals() and get_and_drive() tasks
-    super.run_phase(phase);
     reset_signals();
-    get_and_drive();
-  endtask
+  endtask : run_phase
 
-  // reset signals
   virtual task reset_signals();
-    cfg.vif.scl_i <= 1'b1;
-    cfg.vif.sda_i <= 1'b1;
-  endtask : reset_signals
+    `uvm_info(`gtn, "driver in reset progress", UVM_DEBUG)
+    wait(cfg.vif.rst_ni === 1'b0);
+    cfg.vif.scl_o <= 1'bx;
+    cfg.vif.sda_o <= 1'bx;
+    @(cfg.vif.drv_tx_mp.drv_tx_cb);
 
-  // drive trans received from sequencer
-  virtual task get_and_drive();
-    forever begin
-      seq_item_port.get_next_item(req);
-      $cast(rsp, req.clone());
-      rsp.set_id_info(req);
-      `uvm_info(`gfn, $sformatf("rcvd item:\n%0s", req.sprint()), UVM_HIGH)
-      // TODO: do the driving part
-      //
-      // send rsp back to seq
-      `uvm_info(`gfn, "item sent", UVM_HIGH)
-      seq_item_port.item_done(rsp);
-    end
-  endtask
+    wait(cfg.vif.rst_ni === 1'b1);
+    `uvm_info(`gtn, "driver out of reset", UVM_DEBUG)
+    cfg.vif.scl_o <= 1'b1;
+    cfg.vif.sda_o <= 1'b1;
+    cfg.vif.bus_status = BusFree;
+    @(cfg.vif.drv_tx_mp.drv_tx_cb);
+
+  endtask : reset_signals
 
 endclass : i2c_driver
diff --git a/hw/dv/sv/i2c_agent/i2c_host_driver.sv b/hw/dv/sv/i2c_agent/i2c_host_driver.sv
deleted file mode 100644
index 9b82dd3..0000000
--- a/hw/dv/sv/i2c_agent/i2c_host_driver.sv
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-
-class i2c_host_driver extends i2c_driver;
-  `uvm_component_utils(i2c_host_driver)
-
-  // the base class provides the following handles for use:
-  // i2c_agent_cfg: cfg
-
-  `uvm_component_new
-
-  virtual task run_phase(uvm_phase phase);
-    // base class forks off reset_signals() and get_and_drive() tasks
-    super.run_phase(phase);
-  endtask
-
-  // reset signals
-  virtual task reset_signals();
-  endtask
-
-  // drive trans received from sequencer
-  virtual task get_and_drive();
-    forever begin
-      seq_item_port.get_next_item(req);
-      $cast(rsp, req.clone());
-      rsp.set_id_info(req);
-      `uvm_info(`gfn, $sformatf("rcvd item:\n%0s", req.sprint()), UVM_HIGH)
-      // TODO: do the driving part
-
-      // send rsp back to seq
-      `uvm_info(`gfn, "item sent", UVM_HIGH)
-      seq_item_port.item_done(rsp);
-    end
-  endtask
-
-endclass
diff --git a/hw/dv/sv/i2c_agent/i2c_if.sv b/hw/dv/sv/i2c_agent/i2c_if.sv
index 0069d21..0118067 100644
--- a/hw/dv/sv/i2c_agent/i2c_if.sv
+++ b/hw/dv/sv/i2c_agent/i2c_if.sv
@@ -2,20 +2,153 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
-interface i2c_if ();
-  // interface pins
+import i2c_agent_pkg::*;
+
+interface i2c_if;
+  logic clk_i;
+  logic rst_ni;
+  // standard i2c interface pins
   logic scl_i;
   logic scl_o;
-  logic scl_en_o;
   logic sda_i;
   logic sda_o;
-  logic sda_en_o;
 
+  // debug signal
+  bus_status_e bus_status;
+  int num_rd_trans;
+  int num_wr_trans;
+
+  clocking drv_rx_cb @(posedge clk_i);
+    input rst_ni;
+    input sda_i;
+    input scl_i;
+  endclocking: drv_rx_cb
+  modport drv_rx_mp(clocking drv_rx_cb);
+
+  clocking drv_tx_cb @(posedge clk_i);
+    output sda_o;
+    output scl_o;
+  endclocking: drv_tx_cb
+  modport drv_tx_mp(clocking drv_tx_cb);
+
+  clocking mon_rx_cb @(negedge clk_i);
+    input rst_ni;
+    input sda_i;
+    input scl_i;
+  endclocking: mon_rx_cb
+  modport mon_rx_mp(clocking mon_rx_cb);
+
+  clocking mon_tx_cb @(negedge clk_i);
+    output sda_o;
+    output scl_o;
+  endclocking: mon_tx_cb
+  modport mon_tx_mp(clocking mon_tx_cb);
+
+  //---------------------------------
+  // common tasks
+  //---------------------------------
   task automatic wait_for_idle();
+    wait(rst_ni == 1'b1);
+    wait(scl_i == 1'b1);
+    wait(scl_i == 1'b1 && sda_i == 1'b1);
+    // much less than standard value (4.7us) defined by i2c spec and less than timeout
+    #1us;
+    wait(scl_i == 1'b1 && sda_i == 1'b1);
+    #10ps;
+  endtask : wait_for_idle
+
+  task automatic wait_for_dly(int dly);
+    repeat (dly) @(posedge clk_i);
+  endtask : wait_for_dly
+
+  task automatic wait_for_start_from_host(int thd_sta);
+    wait(scl_i == 1'b1 && sda_i == 1'b1);
+    wait(sda_i == 1'b0);
+    wait_for_dly(thd_sta);
+    wait(scl_i == 1'b0);
+    bus_status = HostSendStart;
+  endtask: wait_for_start_from_host
+
+  task automatic wait_for_repstart_from_host(int tsu_sta);
+    wait(scl_i == 1'b0 && sda_i == 1'b1);
+    wait(scl_i == 1'b1);
+    wait_for_dly(tsu_sta);
+    wait(sda_i == 1'b1);
+    bus_status = HostSendRestart;
+  endtask: wait_for_repstart_from_host
+
+  task automatic wait_for_start_repstart_from_host(int thd_sta, int tsu_sta);
     fork
-      // FIXME
-      // Wait until bus free
+      begin : isolation_fork_start_or_restart
+        fork
+          begin
+            wait_for_start_from_host(thd_sta);
+          end
+          begin
+            wait_for_repstart_from_host(tsu_sta);
+          end
+        join_any
+        disable fork;
+      end : isolation_fork_start_or_restart
     join
-  endtask
+  endtask : wait_for_start_repstart_from_host
+
+  task automatic wait_for_stop_from_host(int tsu_sto);
+    wait(scl_i == 1'b0 && scl_i == 1'b0)
+    wait(scl_i == 1'b1);
+    wait_for_dly(tsu_sto);
+    wait(scl_i == 1'b1);
+    bus_status = HostSendStop;
+  endtask: wait_for_stop_from_host
+
+  task automatic wait_for_stop_or_restart_from_host(int tsu_sta, int tsu_sto);
+    fork
+      begin : isolation_fork_stop_or_restart
+        fork
+          begin
+            wait_for_stop_from_host(tsu_sto);
+          end
+          begin
+            wait_for_repstart_from_host(tsu_sta);
+          end
+        join_any
+        disable fork;
+      end : isolation_fork_stop_or_restart
+    join
+  endtask: wait_for_stop_or_restart_from_host
+
+  task automatic wait_for_bus_free(int delay_ns);
+    wait(scl_o == 1'b1 && sda_o == 1'b1)
+    #(delay_ns * 1ns) bus_status = BusFree;
+  endtask : wait_for_bus_free
+
+  task automatic wait_for_ack_from_host(int tvd_ack);
+    wait(scl_i == 1'b0 && scl_i == 1'b1)
+    wait_for_dly(tvd_ack);
+    wait(scl_i == 1'b0);
+    bus_status = HostSendAck;
+  endtask: wait_for_ack_from_host
+
+  task automatic wait_for_nack_from_host(int tvd_ack);
+    wait(scl_i == 1'b0 && scl_i == 1'b1)
+    wait_for_dly(tvd_ack);
+    wait(scl_i == 1'b0);
+    bus_status = HostSendNoAck;
+  endtask: wait_for_nack_from_host
+
+  task automatic wait_for_ack_by_device(int tvd_ack);
+    wait(scl_o == 1'b0 && sda_o == 1'b1)
+    wait_for_dly(tvd_ack);
+    wait(sda_o == 1'b0);
+    bus_status = TargetSendAck;
+  endtask: wait_for_ack_by_device
+
+  task automatic send_ack_by_device(int tvd_ack);
+    scl_o = 1'b0;
+    sda_o = 1'b1;
+    wait_for_dly(tvd_ack);
+    sda_o = 1'b0;
+    bus_status = TargetSendAck;
+  endtask: send_ack_by_device
 
 endinterface : i2c_if
diff --git a/hw/dv/sv/i2c_agent/i2c_item.sv b/hw/dv/sv/i2c_agent/i2c_item.sv
index bca5c06..c023539 100644
--- a/hw/dv/sv/i2c_agent/i2c_item.sv
+++ b/hw/dv/sv/i2c_agent/i2c_item.sv
@@ -4,27 +4,24 @@
 
 class i2c_item extends uvm_sequence_item;
 
-  // random variables
-  //rand i2c_bit_type_t  bit_type = 1'b0;
-  rand bit             sda_bit = 1'b1;
-  rand bit             scl_bit = 1'b1;
+  // queue of data/addr
+  bit [data_width-1:0] mem_datas[$];
+  bit [addr_width-1:0] mem_addrs[$];
+  // sampled signals to sb
+  bit [data_width-1:0] rd_data;
+  bit [data_width-1:0] wr_data;
+  bit [addr_width-1:0] addr; // addr[0]=r/w bit
 
-  rand bit             stop_detected = 1'b0;
-  rand bit             start_detected = 1'b0;
-
-  // dont override start_bit unless testing an error scenario
-  constraint sda_bit_c {
-    sda_bit == 1'b1;
-  }
-
-  // dont override stop_bit unless testing an error scenario
-  constraint scl_bit_c {
-    sda_bit == 1'b1;
-  }
+  rand int dly_to_send_nack;
+  rand int dly_to_send_ack;
+  rand int dly_to_send_stop;
+  rand int dly_to_send_data;
 
   `uvm_object_utils_begin(i2c_item)
-    `uvm_field_int(sda_bit,       UVM_DEFAULT | UVM_NOCOMPARE | UVM_NOPRINT)
-    `uvm_field_int(scl_bit,       UVM_DEFAULT)
+    `uvm_field_int(dly_to_send_nack, UVM_DEFAULT)
+    `uvm_field_int(dly_to_send_ack,  UVM_DEFAULT)
+    `uvm_field_int(dly_to_send_stop, UVM_DEFAULT)
+    `uvm_field_int(dly_to_send_data, UVM_DEFAULT)
   `uvm_object_utils_end
 
   `uvm_object_new
diff --git a/hw/dv/sv/i2c_agent/i2c_monitor.sv b/hw/dv/sv/i2c_agent/i2c_monitor.sv
index b5bebea..080ef23 100644
--- a/hw/dv/sv/i2c_agent/i2c_monitor.sv
+++ b/hw/dv/sv/i2c_agent/i2c_monitor.sv
@@ -10,36 +10,76 @@
   `uvm_component_utils(i2c_monitor)
 
   // the base class provides the following handles for use:
-  i2c_agent_cfg  cfg;
-  i2c_agent_cov  cov;
-
+  i2c_agent_cfg cfg;
+  i2c_agent_cov cov;
   // analize ports
-  //uvm_analysis_port #(i2c_item) analysis_port;
+  uvm_analysis_port #(i2c_item) i2c_analysis_port;
 
   `uvm_component_new
 
+  i2c_item item;
+
   function void build_phase(uvm_phase phase);
-    super.build_phase(phase);
-  endfunction
+    i2c_analysis_port = new("i2c_analysis_port", this);
+    // reset trans counters
+    cfg.vif.num_rd_trans = 0;
+    cfg.vif.num_wr_trans = 0;
+  endfunction : build_phase
 
   task run_phase(uvm_phase phase);
-    super.run_phase(phase);
-  endtask
 
-  // collect transactions forever - already forked in dv_base_moditor::run_phase
-  virtual protected task collect_trans(uvm_phase phase);
+    `uvm_info(`gtn, "start collect trans", UVM_DEBUG)
     forever begin
-      // TODO: detect event
-
-      // TODO: sample the interface
-
-      // TODO: sample the covergroups
-
-      // TODO: write trans to analysis_port
-
-      // TODO: remove the line below: it is added to prevent zero delay loop in template code
-      #1us;
+      wait(cfg.vif.mon_rx_cb.rst_ni === 1'b1);
+      if (cfg.en_monitor == 1'b1) begin
+        phase.raise_objection(this);
+        `uvm_info(`gtn, "raise_objection, write starts", UVM_DEBUG)
+        // sample host start/restart
+        cfg.vif.wait_for_start_repstart_from_host(cfg.thd_sta, cfg.tsu_sta);
+        monitor_address(item);
+        if (item.addr[0] == 1'b1) monitor_read(item);
+        else monitor_write(item);
+        i2c_analysis_port.write(item);
+        phase.drop_objection(this);
+        `uvm_info(`gtn, "drop_objection, write stops", UVM_DEBUG)
+      end else begin
+        @(cfg.vif.clk_i);
+      end
     end
-  endtask
+  endtask: run_phase
 
+  virtual task monitor_address(i2c_item item);
+    // sample address bit
+    for (int i = 1; i < addr_width; i++) begin
+      @(cfg.vif.mon_rx_cb.scl_i);
+      item.addr[i] = cfg.vif.sda_i;
+    end
+    // sample r/w bit
+    @(cfg.vif.mon_rx_cb.scl_i);
+    item.addr[0] = cfg.vif.sda_i; // 1: READ, 0: WRITE
+  endtask : monitor_address
+
+  virtual task monitor_write(i2c_item item);
+    // sample host write data
+    for (int i = 0; i < data_width; i++) begin
+      @(cfg.vif.mon_rx_cb.scl_i);
+      item.wr_data[i] = cfg.vif.sda_i;
+    end
+    // sample device ack
+    cfg.vif.wait_for_ack_by_device(cfg.tvd_ack);
+    // sample host stop/restart
+    cfg.vif.wait_for_stop_or_restart_from_host(cfg.tsu_sta, cfg.tsu_sto);
+  endtask : monitor_write
+
+  virtual task monitor_read(i2c_item item);
+    // sample host read data
+    for (int i = 0; i < data_width; i++) begin
+      @(cfg.vif.scl_o)
+      item.rd_data[i] = cfg.vif.sda_o;
+    end
+    // sample host ack
+    cfg.vif.wait_for_ack_from_host(cfg.tvd_ack);
+    // sample stop/restart
+    cfg.vif.wait_for_stop_or_restart_from_host(cfg.tsu_sta, cfg.tsu_sto);
+  endtask : monitor_read
 endclass : i2c_monitor
diff --git a/hw/dv/sv/i2c_agent/i2c_sequencer.sv b/hw/dv/sv/i2c_agent/i2c_sequencer.sv
new file mode 100644
index 0000000..15d7ce6
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_sequencer.sv
@@ -0,0 +1,10 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_sequencer extends dv_base_sequencer#(i2c_item, i2c_agent_cfg);
+  `uvm_component_utils(i2c_sequencer)
+
+  `uvm_component_new
+
+endclass
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 458d0ee..1a8ec0f 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
@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 class i2c_base_seq extends dv_base_seq #(
+    .REQ         (i2c_item),
     .CFG_T       (i2c_agent_cfg),
     .SEQUENCER_T (i2c_sequencer)
   );
@@ -14,4 +15,4 @@
     `uvm_fatal(`gtn, "Need to override this when you extend from this class!")
   endtask
 
-endclass
+endclass : i2c_base_seq
diff --git a/hw/dv/sv/i2c_agent/seq_lib/i2c_device_seq.sv b/hw/dv/sv/i2c_agent/seq_lib/i2c_device_seq.sv
new file mode 100644
index 0000000..86d3852
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/seq_lib/i2c_device_seq.sv
@@ -0,0 +1,35 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_device_seq extends i2c_base_seq;
+  `uvm_object_utils(i2c_device_seq)
+
+  `uvm_object_new
+
+  rand int dly_to_send_nack;
+  rand int dly_to_send_ack;
+  rand int dly_to_send_stop;
+  rand int dly_to_send_data;
+
+  constraint dly_to_send_ack_c     { dly_to_send_ack   inside {[0:cfg.max_delay_ack]}; }
+  constraint dly_to_send_nack_c    { dly_to_send_nack  inside {[0:cfg.max_delay_nack]}; }
+  constraint dly_to_send_stop_c    { dly_to_send_stop  inside {[0:cfg.max_delay_stop]}; }
+  constraint dly_to_send_rd_data_c { dly_to_send_data  inside {[0:cfg.max_delay_data]}; }
+
+  task body();
+    req = i2c_item::type_id::create("req");
+    start_item(req);
+    // randomize item
+    `DV_CHECK_RANDOMIZE_WITH_FATAL(req,
+        dly_to_send_nack == local::dly_to_send_nack;
+        dly_to_send_ack  == local::dly_to_send_ack;
+        dly_to_send_stop == local::dly_to_send_stop;
+        dly_to_send_data == local::dly_to_send_data;
+        )
+    finish_item(req);
+    get_response(rsp);
+    `uvm_info(`gfn, "i2c device rsp done", UVM_HIGH)
+  endtask : body
+
+endclass : i2c_device_seq
diff --git a/hw/dv/sv/i2c_agent/seq_lib/i2c_seq_list.sv b/hw/dv/sv/i2c_agent/seq_lib/i2c_seq_list.sv
index 89f7fe7..680c5ff 100644
--- a/hw/dv/sv/i2c_agent/seq_lib/i2c_seq_list.sv
+++ b/hw/dv/sv/i2c_agent/seq_lib/i2c_seq_list.sv
@@ -3,3 +3,4 @@
 // SPDX-License-Identifier: Apache-2.0
 
 `include "i2c_base_seq.sv"
+`include "i2c_device_seq.sv"
diff --git a/hw/ip/i2c/data/i2c_testplan.hjson b/hw/ip/i2c/data/i2c_testplan.hjson
index 6f01f1d..46876da 100644
--- a/hw/ip/i2c/data/i2c_testplan.hjson
+++ b/hw/ip/i2c/data/i2c_testplan.hjson
@@ -20,7 +20,7 @@
               - Check the timing behavior of START, STOP, ACK, NACK, and "repeated" START.
               - Read and write transfer matching.'''
       milestone: V1
-      tests: ["i2c_sanity_ro", "i2c_sanity_wo", "i2c_sanity_rw"]
+      tests: ["i2c_sanity"]
     }
     {
       name: override
@@ -31,7 +31,7 @@
 
             Checking:
               - Check scl_o, sda_o override by programmed values in the OVRD register.'''
-      milestone: V1
+      milestone: V2
       tests: ["i2c_override"]
     }
     {
diff --git a/hw/ip/i2c/dv/Makefile b/hw/ip/i2c/dv/Makefile
index 658737f..7ff4f43 100644
--- a/hw/ip/i2c/dv/Makefile
+++ b/hw/ip/i2c/dv/Makefile
@@ -19,17 +19,19 @@
 FUSESOC_CORE    := lowrisc:dv:i2c_sim:0.1
 COMPILE_KEY     ?= default
 
-UVM_TEST        ?= i2c_base_test
-UVM_TEST_SEQ    ?= i2c_base_vseq
-
 ####################################################################################################
 ##                     A D D    I N D I V I D U A L    T E S T S    B E L O W                     ##
 ####################################################################################################
 # common tests/seqs
 include ${DV_DIR}/../../../dv/tools/common_tests.mk
 
+TEST_NAME       ?= i2c_sanity
+UVM_TEST        ?= i2c_base_test
+UVM_TEST_SEQ    ?= i2c_base_vseq
+
 ifeq (${TEST_NAME},i2c_sanity)
   UVM_TEST_SEQ   = i2c_sanity_vseq
+  RUN_OPTS      += +en_scb=0
 endif
 
 ####################################################################################################
diff --git a/hw/ip/i2c/dv/env/i2c_env.core b/hw/ip/i2c/dv/env/i2c_env.core
index ea71417..b03ce0a 100644
--- a/hw/ip/i2c/dv/env/i2c_env.core
+++ b/hw/ip/i2c/dv/env/i2c_env.core
@@ -14,13 +14,12 @@
       - i2c_env_pkg.sv
       - i2c_env_cfg.sv: {is_include_file: true}
       - i2c_env_cov.sv: {is_include_file: true}
+      - i2c_env.sv: {is_include_file: true}
       - i2c_virtual_sequencer.sv: {is_include_file: true}
       - i2c_scoreboard.sv: {is_include_file: true}
-      - i2c_env.sv: {is_include_file: true}
       - 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_sanity_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
 targets:
diff --git a/hw/ip/i2c/dv/env/i2c_env.sv b/hw/ip/i2c/dv/env/i2c_env.sv
index 9449108..97f4bd1 100644
--- a/hw/ip/i2c/dv/env/i2c_env.sv
+++ b/hw/ip/i2c/dv/env/i2c_env.sv
@@ -23,9 +23,10 @@
   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.analysis_port.connect(scoreboard.i2c_tx_fifo.analysis_export);
+      m_i2c_agent.monitor.analysis_port.connect(scoreboard.i2c_rx_fifo.analysis_export);
     end
-    if (cfg.is_active && cfg.m_i2c_agent_cfg.is_active) begin
+    if (cfg.m_i2c_agent_cfg.is_active) begin
       virtual_sequencer.i2c_sequencer_h = m_i2c_agent.sequencer;
     end
   endfunction
diff --git a/hw/ip/i2c/dv/env/i2c_env_cfg.sv b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
index f1a35ce..2ba74c9 100644
--- a/hw/ip/i2c/dv/env/i2c_env_cfg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
@@ -26,4 +26,4 @@
     num_interrupts = ral.intr_state.get_n_used_bits();
   endfunction
 
-endclass
+endclass : i2c_env_cfg
diff --git a/hw/ip/i2c/dv/env/i2c_env_cov.sv b/hw/ip/i2c/dv/env/i2c_env_cov.sv
index d080b14..fef5f70 100644
--- a/hw/ip/i2c/dv/env/i2c_env_cov.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_cov.sv
@@ -5,14 +5,11 @@
 class i2c_env_cov extends cip_base_env_cov #(.CFG_T(i2c_env_cfg));
   `uvm_component_utils(i2c_env_cov)
 
-  // the base class provides the following handles for use:
-  // i2c_env_cfg: cfg
-
-  // covergroups
+  // TODO: define covergroups
 
   function new(string name, uvm_component parent);
     super.new(name, parent);
-    // instantiate all covergroups here
+    // 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 8fc4c33..db633c9 100644
--- a/hw/ip/i2c/dv/env/i2c_env_pkg.sv
+++ b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
@@ -12,10 +12,10 @@
   import i2c_agent_pkg::*;
   import dv_lib_pkg::*;
   import cip_base_pkg::*;
+  import i2c_reg_pkg::*;
   import i2c_ral_pkg::*;
 
   // macro includes
-  `include "uvm_macros.svh"
   `include "dv_macros.svh"
 
   // parameters
@@ -33,10 +33,19 @@
   } i2c_intr_e;
 
   // csr and mem total size for IP, TODO confirm below value with spec
-  parameter uint I2C_ADDR_MAP_SIZE  = 128;
-  // local types
-  parameter uint I2C_FMT_FIFO_DEPTH = 32;
-  parameter uint I2C_RX_FIFO_DEPTH  = 32;
+  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;
 
   // functions
   // get the number of bytes that triggers watermark interrupt
@@ -68,18 +77,6 @@
     endcase
   endfunction : get_break_bytes_by_level
 
-  // get timing values from speed mode
-  function automatic int get_timing_values_by_speed_mode(int speed_mode);
-    // TODO
-  endfunction : get_timing_values_by_speed_mode
-
-  // get speed mode from timing values
-  function automatic int get_speed_mode_by_timing_values(int timing0, int timing1,
-                                                         int timing2, int timing3,
-                                                         int timing4, int timing5);
-    // TODO
-  endfunction : get_speed_mode_by_timing_values
-
   // 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 560cc20..3fdb05a 100644
--- a/hw/ip/i2c/dv/env/i2c_scoreboard.sv
+++ b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
@@ -9,14 +9,15 @@
   );
   `uvm_component_utils(i2c_scoreboard)
 
-  // local variables
+  //****************************************************************
+  // TODO: this class will be completed later with i2c_sanity test
+  // TODO: no need for review
+  //****************************************************************
+  virtual i2c_if i2c_vif;
 
   // TLM agent fifos
   uvm_tlm_analysis_fifo #(i2c_item) i2c_fifo;
 
-  // local queues to hold incoming packets pending comparison
-  i2c_item i2c_q[$];
-
   `uvm_component_new
 
   function void build_phase(uvm_phase phase);
@@ -36,12 +37,8 @@
   endtask
 
   virtual task process_i2c_fifo();
-    i2c_item item;
-    forever begin
-      i2c_fifo.get(item);
-      `uvm_info(`gfn, $sformatf("received i2c item:\n%0s", item.sprint()), UVM_HIGH)
-    end
-  endtask
+    // TODO
+  endtask : process_i2c_fifo
 
   virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
     uvm_reg csr;
@@ -51,10 +48,10 @@
 
     // 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(item.a_addr);
+      csr = ral.default_map.get_reg_by_offset(csr_addr);
       `DV_CHECK_NE_FATAL(csr, null)
     end else begin
-      `uvm_fatal(`gfn, $sformatf("Access unexpected addr 0x%0h", csr_addr))
+      `uvm_fatal(`gfn, $sformatf("access unexpected addr 0x%0h", csr_addr))
     end
 
     if (channel == AddrChannel) begin
@@ -84,14 +81,25 @@
     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()))
+    end
+    else begin
+      `uvm_info(`gfn, $sformatf("%s item match!\nexp:\n%0s\nobs:\n%0s",
+                                 dir, exp.sprint(), act.sprint()), UVM_HIGH)
+    end
+  endfunction : compare
+
   virtual function void reset(string kind = "HARD");
-    super.reset(kind);
     // reset local fifos queues and variables
-  endfunction
+    super.reset(kind);
+  endfunction : reset
 
   function void check_phase(uvm_phase phase);
     super.check_phase(phase);
     // post test checks - ensure that all local fifos and queues are empty
   endfunction
 
-endclass
+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 12702d3..e5a1a3c 100644
--- a/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
+++ b/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
@@ -4,7 +4,6 @@
 
 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;
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 afbd110..9212343 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
@@ -17,30 +17,125 @@
   rand uint dly_to_access_fifo;
 
   // various knobs to enable certain routines
-  bit do_interrupt      = 1'b1;
+  bit do_interrupt  = 1'b1;
 
   constraint dly_to_access_fifo_c {
     dly_to_access_fifo inside {[1:100]};
   }
 
-  `uvm_object_new
+  constraint en_intr_c {
+    en_intr inside {[0: ((1 << NumI2cIntr) - 1)]};
+  }
 
-  virtual task body();
-    // override cip_base_vseq.body()
-  endtask : body
+  `uvm_object_new
 
   virtual task dut_shutdown();
     // check for pending i2c operations and wait for them to complete
     super.dut_shutdown();
-    // wait for tx and rx operations to complete
-    `uvm_info(`gfn, "waiting for idle", UVM_HIGH)
-    cfg.m_i2c_agent_cfg.vif.wait_for_idle();
-    `uvm_info(`gfn, "done waiting for idle", UVM_HIGH)
+    // wait for fmt and rx operations to complete
+    `uvm_info(`gfn, "waiting for idle", UVM_DEBUG)
+    //cfg.m_i2c_agent_cfg.vif.wait_for_idle();
+    `uvm_info(`gfn, "simulation done", UVM_DEBUG)
   endtask
 
   // setup basic i2c features
-  virtual task i2c_init();
-    //`uvm_info(`gfn, "Initialize I2C registers")
-  endtask
+  virtual task initialize_host();
+    `uvm_info(`gfn, "initialize i2c host registers", UVM_DEBUG)
+
+    // configure i2c_agent_cfg
+    cfg.m_i2c_agent_cfg.max_delay_ack  = 5;
+    cfg.m_i2c_agent_cfg.max_delay_nack = 5;
+    cfg.m_i2c_agent_cfg.max_delay_stop = 5;
+    cfg.m_i2c_agent_cfg.max_delay_data = 5;
+    // program ctrl reg
+    ral.ctrl.enablehost.set(ON);
+    csr_update(ral.ctrl);
+    // disable override the logic level of output pins
+    ral.ovrd.txovrden.set(OFF);
+    csr_update(ral.ovrd);
+    // reset fmt_fifo and rx_fifo
+    ral.fifo_ctrl.rxrst.set(ON);
+    ral.fifo_ctrl.fmtrst.set(ON);
+    csr_update(ral.fifo_ctrl);
+
+    if (do_interrupt) begin
+      // program intr_enable reg
+      ral.intr_enable.set(en_intr);
+      csr_update(ral.intr_enable);
+
+      // program timeout_ctrl reg
+      `DV_CHECK_RANDOMIZE_WITH_FATAL(ral.timeout_ctrl.val, value inside {[10 : 100]};)
+      `DV_CHECK_RANDOMIZE_FATAL(ral.timeout_ctrl.en)
+      csr_update(ral.timeout_ctrl);
+
+      // program fifo_ctrl reg
+      `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 : initialize_host
+
+  // program timing registers
+  virtual task program_timing_regs(bit [TL_DW-1:0] val[]);
+    // program timing register
+    csr_wr(.csr(ral.timing0), .value(val[0]));
+    csr_wr(.csr(ral.timing1), .value(val[1]));
+    csr_wr(.csr(ral.timing2), .value(val[2]));
+    csr_wr(.csr(ral.timing3), .value(val[3]));
+    csr_wr(.csr(ral.timing4), .value(val[4]));
+  endtask : program_timing_regs
+
+  // customized version for i2c host by overriding one defined in cip_base_vseq class
+  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
+    `DV_CHECK_EQ(cfg.intr_vif.sample(), {NUM_MAX_INTERRUPTS{1'b0}})
+  endtask : clear_all_interrupts
+
+  // task to wait for fmt fifo not full
+  virtual task wait_for_fmt_fifo_not_full();
+    bit [TL_DW-1:0] status;
+
+    if (ral.ctrl.enablehost.get_mirrored_value()) begin
+      `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_access_fifo)
+      csr_spinwait(.ptr(ral.status.fmtfull), .exp_data(1'b0),
+                   .spinwait_delay_ns(dly_to_access_fifo));
+    end
+    `uvm_info(`gfn, "wait_for_tx_fifo_not_full is done", UVM_HIGH)
+  endtask : wait_for_fmt_fifo_not_full
+
+  // task to wait for rx fifo not full, will be overriden in overflow test
+  virtual task wait_for_rx_fifo_not_full();
+    if (ral.ctrl.enablehost.get_mirrored_value()) begin
+      `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_access_fifo)
+      csr_spinwait(.ptr(ral.status.rxfull), .exp_data(1'b0),
+                   .spinwait_delay_ns(dly_to_access_fifo),
+                   .timeout_ns(50_000_000)); // use longer timeout as i2c freq is low
+    end
+    `uvm_info(`gfn, "wait_for_rx_fifo_not_full is done", UVM_HIGH)
+  endtask : wait_for_rx_fifo_not_full
+
+  // function to directly pass timing_* to i2c_agent_cfg.*
+  virtual function update_agent_cfg(bit [TL_DW-1:0] val[]);
+    {cfg.m_i2c_agent_cfg.tlow,    cfg.m_i2c_agent_cfg.thigh}   = val[0];
+    {cfg.m_i2c_agent_cfg.t_f,     cfg.m_i2c_agent_cfg.t_r}     = val[1];
+    {cfg.m_i2c_agent_cfg.thd_sta, cfg.m_i2c_agent_cfg.tsu_sta} = val[2];
+    {cfg.m_i2c_agent_cfg.tsu_dat, cfg.m_i2c_agent_cfg.thd_dat} = val[3];
+    {cfg.m_i2c_agent_cfg.t_buf,   cfg.m_i2c_agent_cfg.tsu_sto} = val[4];
+  endfunction : update_agent_cfg
+
+  // function to control i2c_agent monitor
+  virtual function config_agent_monitor(bit enb_mon);
+    cfg.m_i2c_agent_cfg.en_monitor = enb_mon;
+  endfunction : config_agent_monitor
 
 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 7fe342e..722acd3 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
@@ -6,12 +6,19 @@
   `uvm_object_utils(i2c_common_vseq)
 
   constraint num_trans_c {
-    num_trans inside {[1:2]};
+    num_trans inside {[10:25]};
   }
 
   `uvm_object_new
 
+  task pre_start();
+    super.pre_start();
+  endtask : pre_start
+
   virtual task body();
+    // disable i2c_monitor since it can not handle this test
+    `uvm_info(`gtn, $sformatf("disable i2c_monitor"), 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
 
@@ -21,77 +28,40 @@
                                            string           scope = "ral");
 
     // by default, apply all init, write, and write-read check (CsrNoExcl) for all registers
-    // write exclusions - these should not apply to hw_reset test
-    if (csr_test_type != "hw_reset") begin
-
-      // I2C oversampled values are updated by design according to setting and pin RX
-      csr_excl.add_excl({scope, ".", "val"}, CsrExclCheck);
-
+    if (csr_test_type != "hw_reset") begin   // csr_rw, csr_bit_bash, or csr_aliasing
+      // RO registers - not able to write
+      csr_excl.add_excl({scope, ".", "val"}, CsrExclWriteCheck);
       // intr_test csr is WO which - it reads back 0s, plus it affects the i2c_state csr
-      csr_excl.add_excl({scope, ".", "intr_test"}, CsrExclWrite);
-
+      csr_excl.add_excl({scope, ".", "intr_test"}, CsrExclWriteCheck);
       // fdata csr is WO which - it reads back 0s
-      csr_excl.add_excl({scope, ".", "fdata"}, CsrExclWrite);
-
+      csr_excl.add_excl({scope, ".", "fdata"}, CsrExclWriteCheck);
       // intr_state csr is affected by writes to other csrs
       csr_excl.add_excl({scope, ".", "intr_state"}, CsrExclWriteCheck);
     end
 
-    // hw_reset: resets the DUT and the block abstraction class associated with this sequence.
-    // reads all of the registers in the block, via all of the available address maps,
-    // comparing the value read with the expected reset value
-    if (csr_test_type == "hw_reset") begin
-      // specify at least one register with reset capability (resval) for this test
-      csr_excl.add_excl({scope, ".", "ctrl.enablehost"}, CsrNoExcl);
-
-      // exclude registers which are not able to verify value after reset
-      csr_excl.add_excl({scope, ".", "status"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "rdata"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "fdata"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "fifo_ctrl"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "fifo_status"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "ovrd"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "val"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "timing0"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "timing1"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "timing2"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "timing3"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "timing4"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "timeout_ctrl"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "intr_state"}, CsrExclInitCheck);
-      csr_excl.add_excl({scope, ".", "intr_test"}, CsrExclInitCheck);
-    end
-
-    // bit_bash: write 1's and 0's to every bit of non-RO registers (CsrExclWrite/WriteCheck)
-    // making sure that the resulting value matches the mirrored value
-    if (csr_test_type == "bit_bash") begin
-      // specify at least one RW register for this test
-      csr_excl.add_excl({scope, ".", "timing0"}, CsrNoExcl);
-    end
-
     // aliasing: write to specifc non-RO/non-INIT register (CsrExclWrite) and
     // read all registers (CsrExclInit/WriteCheck) then check the specifc register
     // content is updated (matching the mirrored value)
     if (csr_test_type == "aliasing") begin
-      // specify at least one RW register for this test
-      csr_excl.add_excl({scope, ".", "timing0"}, CsrNoExcl);
-
-      // exclude RO register - not able to write
-      csr_excl.add_excl({scope, ".", "status"}, CsrExclAll);
-      csr_excl.add_excl({scope, ".", "rdata"}, CsrExclAll);
-      csr_excl.add_excl({scope, ".", "fifo_status"}, CsrExclAll);
-      csr_excl.add_excl({scope, ".", "val"}, CsrExclAll);
+      // RO registers - not able to write
+      csr_excl.add_excl({scope, ".", "status"}, CsrExclWrite);
+      csr_excl.add_excl({scope, ".", "rdata"}, CsrExclWrite);
+      csr_excl.add_excl({scope, ".", "fifo_status"}, CsrExclWrite);
+      csr_excl.add_excl({scope, ".", "val"}, CsrExclWrite);
     end
 
+    // for csr_rw test
     // fmtrst and rxrst fields in fifo_ctrl are WO - it read back 0s
-    csr_excl.add_excl({scope, ".", "fifo_ctrl.*rst"}, CsrExclWrite);
-
+    csr_excl.add_excl({scope, ".", "fifo_ctrl.*rst"}, CsrExclWriteCheck);
     // read rdata when fifo is empty, dut may return unknown data
-    csr_excl.add_excl({scope, ".", "rdata"}, CsrExclCheck);
-
+    csr_excl.add_excl({scope, ".", "rdata"}, CsrExclWriteCheck);
     // status csr is RO - writing is not permitted
     csr_excl.add_excl({scope, ".", "status"}, CsrExclWrite);
 
   endfunction : add_csr_exclusions
 
+  task post_start();
+    `uvm_info(`gfn, "stop simulation", UVM_DEBUG)
+  endtask : post_start
+
 endclass : i2c_common_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
deleted file mode 100644
index 35c42ad..0000000
--- a/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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_base_vseq;
-  `uvm_object_utils(i2c_sanity_vseq)
-
-  `uvm_object_new
-
-  task body();
-    `uvm_error(`gfn, "FIXME")
-  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 7218275..cb6b288 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,4 +4,3 @@
 
 `include "i2c_base_vseq.sv"
 `include "i2c_common_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 fc12e36..801733e 100644
--- a/hw/ip/i2c/dv/i2c_sim_cfg.hjson
+++ b/hw/ip/i2c/dv/i2c_sim_cfg.hjson
@@ -26,8 +26,9 @@
                 // Common CIP test lists
                 "{proj_root}/hw/dv/data/tests/csr_tests.hjson",
                 "{proj_root}/hw/dv/data/tests/intr_test.hjson",
-                "{proj_root}/hw/dv/data/tests/tl_access_tests.hjson",
-                "{proj_root}/hw/dv/data/tests/stress_tests.hjson"]
+                "{proj_root}/hw/dv/data/tests/tl_access_tests.hjson"]
+                // TODO: enable stress tests later.
+                //"{proj_root}/hw/dv/data/tests/stress_tests.hjson"]
 
   // Add additional tops for simulation.
   sim_tops: ["-top i2c_bind"]
diff --git a/hw/ip/i2c/dv/tb/tb.sv b/hw/ip/i2c/dv/tb/tb.sv
index 393a399..a1f37c3 100644
--- a/hw/ip/i2c/dv/tb/tb.sv
+++ b/hw/ip/i2c/dv/tb/tb.sv
@@ -13,7 +13,7 @@
   `include "uvm_macros.svh"
   `include "dv_macros.svh"
 
-  wire clk, rst_n;
+  wire clk_i, rst_ni;
   wire devmode;
   wire intr_fmt_watermark;
   wire intr_rx_watermark;
@@ -26,27 +26,35 @@
   wire intr_sda_unstable;
   wire [NUM_MAX_INTERRUPTS-1:0] interrupts;
 
+  wire cio_scl_i;
+  wire cio_sda_i;
+  wire cio_scl_o;
+  wire cio_sda_o;
+  wire cio_scl_en_o;
+  wire cio_sda_en_o;
+
   // interfaces
-  clk_rst_if clk_rst_if(.clk(clk), .rst_n(rst_n));
+  clk_rst_if clk_rst_if(.clk(clk_i), .rst_n(rst_ni));
   pins_if #(NUM_MAX_INTERRUPTS) intr_if(interrupts);
   pins_if #(1) devmode_if(devmode);
-  tl_if tl_if(.clk(clk), .rst_n(rst_n));
+
+  tl_if tl_if(.clk(clk_i), .rst_n(rst_ni));
   i2c_if i2c_if();
 
   // dut
   i2c dut (
-    .clk_i                   (clk        ),
-    .rst_ni                  (rst_n      ),
+    .clk_i                   (clk_i      ),
+    .rst_ni                  (rst_ni     ),
 
     .tl_i                    (tl_if.h2d  ),
     .tl_o                    (tl_if.d2h  ),
 
-    .cio_scl_i               (i2c_if.scl_i          ),
-    .cio_scl_o               (i2c_if.scl_o          ),
-    .cio_scl_en_o            (i2c_if.scl_en_o       ),
-    .cio_sda_i               (i2c_if.sda_i          ),
-    .cio_sda_o               (i2c_if.sda_o          ),
-    .cio_sda_en_o            (i2c_if.sda_en_o       ),
+    .cio_scl_i               (cio_scl_i             ),
+    .cio_scl_o               (cio_scl_o             ),
+    .cio_scl_en_o            (cio_scl_en_o          ),
+    .cio_sda_i               (cio_sda_i             ),
+    .cio_sda_o               (cio_sda_o             ),
+    .cio_sda_en_o            (cio_sda_en_o          ),
 
     .intr_fmt_watermark_o    (intr_fmt_watermark    ),
     .intr_rx_watermark_o     (intr_rx_watermark     ),
@@ -59,7 +67,17 @@
     .intr_sda_unstable_o     (intr_sda_unstable     )
   );
 
+  // virtual open drain
+  assign i2c_if.scl_i = (cio_scl_en_o) ? cio_scl_o : ~cio_scl_o;
+  assign i2c_if.sda_i = (cio_sda_en_o) ? cio_sda_o : ~cio_sda_o;
+  assign cio_scl_i = i2c_if.scl_o;
+  assign cio_sda_i = i2c_if.sda_o;
 
+  // host -> device if
+  assign i2c_if.clk_i  = clk_i;
+  assign i2c_if.rst_ni = rst_ni;
+
+  // interrupt
   assign interrupts[FmtWatermark]   = intr_fmt_watermark;
   assign interrupts[RxWatermark]    = intr_rx_watermark;
   assign interrupts[FmtOverflow]    = intr_fmt_overflow;
@@ -84,4 +102,4 @@
     run_test();
   end
 
-endmodule
+endmodule : tb
diff --git a/hw/ip/i2c/dv/tests/i2c_base_test.sv b/hw/ip/i2c/dv/tests/i2c_base_test.sv
index d84b730..34f0de4 100644
--- a/hw/ip/i2c/dv/tests/i2c_base_test.sv
+++ b/hw/ip/i2c/dv/tests/i2c_base_test.sv
@@ -2,20 +2,19 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
-class i2c_base_test extends cip_base_test #(.ENV_T(i2c_env), .CFG_T(i2c_env_cfg));
+class i2c_base_test extends cip_base_test #(.ENV_T(i2c_env),
+                                            .CFG_T(i2c_env_cfg));
   `uvm_component_utils(i2c_base_test)
   `uvm_component_new
 
-  // the base class dv_base_test creates the following instances:
-  // i2c_env_cfg: cfg
-  // i2c_env:     env
-
   // the base class also looks up UVM_TEST_SEQ plusarg to create and run that seq in
   // the run_phase; as such, nothing more needs to be done
   virtual function void build_phase(uvm_phase phase);
     max_quit_count  = 50;
     test_timeout_ns = 600_000_000; // 600ms
     super.build_phase(phase);
+    // configure the i2c agent to be in Device mode
+    cfg.m_i2c_agent_cfg.mode = Device;
   endfunction : build_phase
 
 endclass : i2c_base_test
diff --git a/hw/ip/i2c/dv/tests/i2c_test_pkg.sv b/hw/ip/i2c/dv/tests/i2c_test_pkg.sv
index 98fce6c..c8eb557 100644
--- a/hw/ip/i2c/dv/tests/i2c_test_pkg.sv
+++ b/hw/ip/i2c/dv/tests/i2c_test_pkg.sv
@@ -5,6 +5,7 @@
 package i2c_test_pkg;
   // dep packages
   import uvm_pkg::*;
+  import dv_utils_pkg::*;
   import cip_base_pkg::*;
   import i2c_env_pkg::*;