[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/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
+