[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::*;