Add baseline DV for I2C
diff --git a/hw/dv/sv/i2c_agent/README.md b/hw/dv/sv/i2c_agent/README.md
new file mode 100644
index 0000000..ca68b60
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/README.md
@@ -0,0 +1,3 @@
+{{% lowrisc-doc-hdr I2C DV UVM Agent }}
+
+I2C DV UVM Agent is extended from DV library agent classes.
diff --git a/hw/dv/sv/i2c_agent/i2c_agent.core b/hw/dv/sv/i2c_agent/i2c_agent.core
new file mode 100644
index 0000000..48c6fa0
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_agent.core
@@ -0,0 +1,28 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:i2c_agent:0.1"
+description: "I2C DV UVM agent"
+filesets:
+ files_dv:
+ depend:
+ - lowrisc:dv:dv_utils
+ - lowrisc:dv:dv_lib
+ files:
+ - i2c_if.sv
+ - i2c_agent_pkg.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_monitor.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}
+ file_type: systemVerilogSource
+
+targets:
+ default:
+ filesets:
+ - files_dv
diff --git a/hw/dv/sv/i2c_agent/i2c_agent.sv b/hw/dv/sv/i2c_agent/i2c_agent.sv
new file mode 100644
index 0000000..6148e9d
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_agent.sv
@@ -0,0 +1,24 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// 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)
+ );
+
+ `uvm_component_utils(i2c_agent)
+
+ `uvm_component_new
+
+ 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))
+ `uvm_fatal(`gfn, "failed to get i2c_if handle from uvm_config_db")
+ endfunction
+
+endclass
diff --git a/hw/dv/sv/i2c_agent/i2c_agent_cfg.sv b/hw/dv/sv/i2c_agent/i2c_agent_cfg.sv
new file mode 100644
index 0000000..162bd2d
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_agent_cfg.sv
@@ -0,0 +1,15 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_agent_cfg extends dv_base_agent_cfg;
+
+// interface handle used by driver, monitor & the sequencer, via cfg handle
+ virtual i2c_if vif;
+
+ `uvm_object_utils_begin(i2c_agent_cfg)
+ `uvm_object_utils_end
+
+ `uvm_object_new
+
+endclass
diff --git a/hw/dv/sv/i2c_agent/i2c_agent_cov.sv b/hw/dv/sv/i2c_agent/i2c_agent_cov.sv
new file mode 100644
index 0000000..36a58f2
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_agent_cov.sv
@@ -0,0 +1,18 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+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
+
+ function new(string name, uvm_component parent);
+ super.new(name, parent);
+ // 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
new file mode 100644
index 0000000..52188d0
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_agent_pkg.sv
@@ -0,0 +1,37 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+package i2c_agent_pkg;
+ // dep packages
+ import uvm_pkg::*;
+ import dv_utils_pkg::*;
+ import dv_lib_pkg::*;
+
+ // macro includes
+ `include "uvm_macros.svh"
+ `include "dv_macros.svh"
+
+ // parameters
+
+ // local types
+ // 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_agent.sv"
+ `include "i2c_seq_list.sv"
+
+ 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
new file mode 100644
index 0000000..b13d19e
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_device_driver.sv
@@ -0,0 +1,26 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+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
+
+ `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();
+ endtask
+
+endclass
diff --git a/hw/dv/sv/i2c_agent/i2c_driver.sv b/hw/dv/sv/i2c_agent/i2c_driver.sv
new file mode 100644
index 0000000..a85b4d2
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_driver.sv
@@ -0,0 +1,37 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+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);
+ 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_host_driver.sv b/hw/dv/sv/i2c_agent/i2c_host_driver.sv
new file mode 100644
index 0000000..9b82dd3
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_host_driver.sv
@@ -0,0 +1,37 @@
+// 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
new file mode 100644
index 0000000..60c846a
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_if.sv
@@ -0,0 +1,87 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+interface i2c_if ();
+ // interface pins
+ logic scl_i;
+ logic scl_o;
+ logic scl_en_o;
+ logic sda_i;
+ logic sda_o;
+ logic sda_en_o;
+
+ bit BusIsFree=1;
+
+/*
+ // debug signals (TBD)
+ int SclFrequency;
+ int SclClockPeriod;
+ int SclMinFreq;
+ int SclMaxFreq;
+ bus_speed_e BusSpeed;
+
+ //Timing0
+ int SclHighMinHoldTime;
+ int SclLowMinHoldTime;
+ //Timing1
+ int NormRiseTime;
+ int NormFallTime;
+ //Timing2
+ int StartMinSetupTime;
+ int StartMinHoldTime;
+ //Timing3
+ int DataMinSetupTime;
+ int DataMinHoldTime;
+ //Timing4
+ int StopMinSetupTime;
+ int StartStopMinTime;
+ //Timing5
+ int ClockStretchTimeout;
+ int ClockStretchEnable;
+
+ function void set_set_bus_frequency(int SclFrequency_i);
+ SclFrequency = SclFrequency_i; //in kHz
+ if ( SclFrequency>0 && SclFrequency<=100 ) begin
+ bus_speed_e = I2cStdSpeed;
+ set_std_speed_timing();
+ end else if (m_sclFrequency>100 && m_sclFrequency<=400) begin
+ bus_speed_e = I2cFastSpeed;
+ set_fast_speed_timing();
+ end else if (m_sclFrequency>400 && m_sclFrequency<=3400) begin
+ bus_speed_e = I2cFastSpeedPlus;
+ set_fast_speed_plus_timing();
+ end else
+ `uvm_fatal("I2C:set_set_bus_frequency",
+ $psprintf("SCL frquency setting illegal : %d kHz",SclFrequency))
+
+ //Create a SCL with 1:1 duty cycle
+ m_sclLowTime = ( 10 ** 6/(2 * SclFrequency) ) ; // ns
+ m_sclHighTime = m_sclLowTime; // ns
+ //Default bit set-up time is m_sclLowTime/2
+ m_sdaChangePoint = m_sclLowTime/2; // ns
+
+ m_sclClockPeriod = ( (10**9)/m_sclFrequency) / 1000; // ns
+
+ endfunction
+
+ always @(negedge sda_in ) begin
+ //START condition
+ if (rst==0)
+ if (scl_in==1) begin
+ #m_fHdStaMin;
+ busIsFree<=0;
+ end
+ end
+ always @(posedge sda_in) begin
+ //STOP condition
+ if (rst==0) begin
+ if (scl_in==1) begin
+ #m_tBufMin;
+ busIsFree<=1;
+ end
+ end
+ end
+*/
+
+endinterface : i2c_if
diff --git a/hw/dv/sv/i2c_agent/i2c_item.sv b/hw/dv/sv/i2c_agent/i2c_item.sv
new file mode 100644
index 0000000..97153b4
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_item.sv
@@ -0,0 +1,14 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_item extends uvm_sequence_item;
+
+ // random variables
+
+ `uvm_object_utils_begin(i2c_item)
+ `uvm_object_utils_end
+
+ `uvm_object_new
+
+endclass
diff --git a/hw/dv/sv/i2c_agent/i2c_monitor.sv b/hw/dv/sv/i2c_agent/i2c_monitor.sv
new file mode 100644
index 0000000..8c6d30d
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/i2c_monitor.sv
@@ -0,0 +1,43 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_monitor extends dv_base_monitor #(
+ .ITEM_T (i2c_item),
+ .CFG_T (i2c_agent_cfg),
+ .COV_T (i2c_agent_cov)
+ );
+ `uvm_component_utils(i2c_monitor)
+
+ // the base class provides the following handles for use:
+ // i2c_agent_cfg: cfg
+ // i2c_agent_cov: cov
+ // uvm_analysis_port #(i2c_item): analysis_port
+
+ `uvm_component_new
+
+ function void build_phase(uvm_phase phase);
+ super.build_phase(phase);
+ endfunction
+
+ 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);
+ 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;
+ end
+ endtask
+
+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
new file mode 100644
index 0000000..458d0ee
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/seq_lib/i2c_base_seq.sv
@@ -0,0 +1,17 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_base_seq extends dv_base_seq #(
+ .CFG_T (i2c_agent_cfg),
+ .SEQUENCER_T (i2c_sequencer)
+ );
+ `uvm_object_utils(i2c_base_seq)
+
+ `uvm_object_new
+
+ virtual task body();
+ `uvm_fatal(`gtn, "Need to override this when you extend from this class!")
+ endtask
+
+endclass
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
new file mode 100644
index 0000000..89f7fe7
--- /dev/null
+++ b/hw/dv/sv/i2c_agent/seq_lib/i2c_seq_list.sv
@@ -0,0 +1,5 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+`include "i2c_base_seq.sv"
diff --git a/hw/ip/i2c/dv/Makefile b/hw/ip/i2c/dv/Makefile
new file mode 100644
index 0000000..f12564f
--- /dev/null
+++ b/hw/ip/i2c/dv/Makefile
@@ -0,0 +1,80 @@
+####################################################################################################
+## Copyright lowRISC contributors. ##
+## Licensed under the Apache License, Version 2.0, see LICENSE for details. ##
+## SPDX-License-Identifier: Apache-2.0 ##
+####################################################################################################
+## Entry point test Makefile forr building and running tests. ##
+## These are generic set of option groups that apply to all testbenches. ##
+## This flow requires the following options to be set: ##
+## DV_DIR - current dv directory that contains the test Makefile ##
+## DUT_TOP - top level dut module name ##
+## TB_TOP - top level tb module name ##
+## DOTF - .f file used for compilation ##
+## COMPILE_KEY - compile option set ##
+## TEST_NAME - name of the test to run - this is supplied on the command line ##
+####################################################################################################
+DV_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+export DUT_TOP := i2c
+export TB_TOP := tb
+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 ##
+####################################################################################################
+TEST_NAME ?= i2c_wrap
+UVM_TEST ?= i2c_base_test
+UVM_TEST_SEQ ?= i2c_base_vseq
+
+ifeq (${TEST_NAME},i2c_wrap)
+ UVM_TEST_SEQ = i2c_wrap_vseq
+endif
+
+ifeq (${TEST_NAME},i2c_sanity)
+ UVM_TEST_SEQ = i2c_sanity_vseq
+endif
+
+ifeq (${TEST_NAME},i2c_intr_test)
+ UVM_TEST_SEQ = i2c_common_vseq
+ RUN_OPTS += +run_intr_test
+endif
+
+ifeq (${TEST_NAME},i2c_csr_hw_reset)
+ UVM_TEST_SEQ = i2c_common_vseq
+ RUN_OPTS += +csr_hw_reset
+ RUN_OPTS += +en_scb=0
+endif
+
+ifeq (${TEST_NAME},i2c_csr_rw)
+ UVM_TEST_SEQ = i2c_common_vseq
+ RUN_OPTS += +csr_rw
+ RUN_OPTS += +en_scb=0
+endif
+
+ifeq (${TEST_NAME},i2c_csr_bit_bash)
+ UVM_TEST_SEQ = i2c_common_vseq
+ RUN_OPTS += +csr_bit_bash
+ RUN_OPTS += +en_scb=0
+endif
+
+ifeq (${TEST_NAME},i2c_csr_aliasing)
+ UVM_TEST_SEQ = i2c_common_vseq
+ RUN_OPTS += +csr_aliasing
+ RUN_OPTS += +en_scb=0
+endif
+
+# TODO: remove this test if there are no memories in the DUT
+ ifeq (${TEST_NAME},i2c_mem_walk)
+ UVM_TEST_SEQ = i2c_common_vseq
+ RUN_OPTS += +csr_mem_walk
+ RUN_OPTS += +en_scb=0
+endif
+
+####################################################################################################
+## Include the tool Makefile below ##
+## Dont add anything else below it! ##
+####################################################################################################
+include ${DV_DIR}/../../../dv/tools/Makefile
diff --git a/hw/ip/i2c/dv/README b/hw/ip/i2c/dv/README
new file mode 100755
index 0000000..4151195
--- /dev/null
+++ b/hw/ip/i2c/dv/README
@@ -0,0 +1,21 @@
+# UART IP Verification Environment
+UART IP verification environment is extended from the [Comportable IP
+verification environment](/hw/dv/sv/cip_lib/README.md) to leverage a lot of common
+code shared across all IPs that adhere to the
+[Comportable IP](/doc/rm/ComportabilitySpecification.md) specification.
+
+## Environment
+
+### Block Diagram
+
+## Key fetures verified
+
+## Testplan
+
+## How to run simulation
+Please run the following command to build and run tests:
+`make TEST_NAME=<test-name>`
+
+Please see adjoining Makefile file for list of available tests to run. Please
+see `hw/dv/tools/README.md` for additional details on options that can be passed
+(such as enabling waves, running with specific seed etc.).
diff --git a/hw/ip/i2c/dv/env/i2c_env.core b/hw/ip/i2c/dv/env/i2c_env.core
new file mode 100644
index 0000000..97357ef
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_env.core
@@ -0,0 +1,26 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:i2c_env:0.1"
+description: "I2C DV UVM environment"
+filesets:
+ files_dv:
+ depend:
+ - lowrisc:dv:cip_lib
+ - lowrisc:dv:i2c_agent
+ files:
+ - 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_reg_block.sv: {is_include_file: true}
+ - i2c_virtual_sequencer.sv: {is_include_file: true}
+ - i2c_scoreboard.sv: {is_include_file: true}
+ - seq_lib/i2c_vseq_list.sv: {is_include_file: true}
+ file_type: systemVerilogSource
+
+targets:
+ default:
+ filesets:
+ - files_dv
diff --git a/hw/ip/i2c/dv/env/i2c_env.sv b/hw/ip/i2c/dv/env/i2c_env.sv
new file mode 100644
index 0000000..84c8048
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_env.sv
@@ -0,0 +1,33 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_env extends cip_base_env #(
+ .CFG_T (i2c_env_cfg),
+ .COV_T (i2c_env_cov),
+ .VIRTUAL_SEQUENCER_T(i2c_virtual_sequencer),
+ .SCOREBOARD_T (i2c_scoreboard)
+ );
+ `uvm_component_utils(i2c_env)
+
+ i2c_agent m_i2c_agent;
+
+ `uvm_component_new
+
+ function void build_phase(uvm_phase phase);
+ super.build_phase(phase);
+ m_i2c_agent = i2c_agent::type_id::create("m_i2c_agent", this);
+ uvm_config_db#(i2c_agent_cfg)::set(this, "m_i2c_agent*", "cfg", cfg.m_i2c_agent_cfg);
+ endfunction
+
+ 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);
+ end
+ if (cfg.is_active && cfg.m_i2c_agent_cfg.is_active) begin
+ virtual_sequencer.i2c_sequencer_h = m_i2c_agent.sequencer;
+ end
+ endfunction
+
+endclass
diff --git a/hw/ip/i2c/dv/env/i2c_env_cfg.sv b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
new file mode 100644
index 0000000..499c34b
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_env_cfg.sv
@@ -0,0 +1,27 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_env_cfg extends cip_base_env_cfg #(.RAL_T(i2c_reg_block));
+
+ // ext component cfgs
+ rand i2c_agent_cfg m_i2c_agent_cfg;
+
+ `uvm_object_utils_begin(i2c_env_cfg)
+ `uvm_field_object(m_i2c_agent_cfg, UVM_DEFAULT)
+ `uvm_object_utils_end
+
+ `uvm_object_new
+
+ virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1,
+ bit [TL_AW-1:0] csr_addr_map_size = 2048);
+ super.initialize();
+ // create i2c agent config obj
+ m_i2c_agent_cfg = i2c_agent_cfg::type_id::create("m_i2c_agent_cfg");
+
+ // set num_interrupts & num_alerts which will be used to create coverage and more
+ num_interrupts = ral.intr_state.get_n_used_bits();
+ num_alerts = 0;
+ endfunction
+
+endclass
diff --git a/hw/ip/i2c/dv/env/i2c_env_cov.sv b/hw/ip/i2c/dv/env/i2c_env_cov.sv
new file mode 100644
index 0000000..d080b14
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_env_cov.sv
@@ -0,0 +1,18 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+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
+
+ function new(string name, uvm_component parent);
+ super.new(name, parent);
+ // 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
new file mode 100644
index 0000000..a82ce53
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_env_pkg.sv
@@ -0,0 +1,49 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+package i2c_env_pkg;
+ // dep packages
+ import uvm_pkg::*;
+ import top_pkg::*;
+ import dv_utils_pkg::*;
+ import csr_utils_pkg::*;
+ import tl_agent_pkg::*;
+ import i2c_agent_pkg::*;
+ import dv_lib_pkg::*;
+ import cip_base_pkg::*;
+
+ // macro includes
+ `include "uvm_macros.svh"
+ `include "dv_macros.svh"
+
+ // parameters
+ typedef enum int {
+ FmtWatermark = 0,
+ RxWatermark = 1,
+ FmtOverflow = 2,
+ RxOverflow = 3,
+ Nak = 4,
+ SclInference = 5,
+ SdaInference = 6,
+ StretchTimeout = 7,
+ SdaUnstable = 8,
+ NumI2cIntr = 9
+ } i2c_intr_e;
+
+ // local types
+ parameter uint I2C_FMT_FIFO_DEPTH = 32;
+ parameter uint I2C_RX_FIFO_DEPTH = 32;
+
+ // functions
+
+ // package sources
+ `include "i2c_reg_block.sv"
+ `include "i2c_env_cfg.sv"
+ `include "i2c_env_cov.sv"
+ `include "i2c_virtual_sequencer.sv"
+ `include "i2c_scoreboard.sv"
+ `include "i2c_env.sv"
+ `include "i2c_vseq_list.sv"
+
+endpackage
diff --git a/hw/ip/i2c/dv/env/i2c_reg_block.sv b/hw/ip/i2c/dv/env/i2c_reg_block.sv
new file mode 100644
index 0000000..dc895fe
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_reg_block.sv
@@ -0,0 +1,957 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// UVM Registers auto-generated by `reggen` containing data structure
+// Do Not Edit directly
+
+// Forward declare all register/memory/block classes
+typedef class i2c_reg_intr_state;
+typedef class i2c_reg_intr_enable;
+typedef class i2c_reg_intr_test;
+typedef class i2c_reg_ctrl;
+typedef class i2c_reg_rdata;
+typedef class i2c_reg_fdata;
+typedef class i2c_reg_timing0;
+typedef class i2c_reg_timing1;
+typedef class i2c_reg_timing2;
+typedef class i2c_reg_timing3;
+typedef class i2c_reg_timing4;
+typedef class i2c_reg_timeout_ctrl;
+typedef class i2c_reg_block;
+
+// Block: i2c
+// Class: i2c_reg_intr_state
+class i2c_reg_intr_state extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field intr_fmt_watermark_o;
+ rand dv_base_reg_field intr_rx_watermark_o;
+ rand dv_base_reg_field intr_fmt_overflow_o;
+ rand dv_base_reg_field intr_rx_overflow_o;
+ rand dv_base_reg_field intr_nak_o;
+ rand dv_base_reg_field intr_scl_interference_o;
+ rand dv_base_reg_field intr_sda_interference_o;
+ rand dv_base_reg_field intr_stretch_timeout_o;
+ rand dv_base_reg_field intr_sda_unstable_o;
+
+ `uvm_object_utils(i2c_reg_intr_state)
+
+ function new(string name = "i2c_reg_intr_state",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ intr_fmt_watermark_o = dv_base_reg_field::type_id::create("intr_fmt_watermark_o");
+ intr_fmt_watermark_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(0),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_rx_watermark_o = dv_base_reg_field::type_id::create("intr_rx_watermark_o");
+ intr_rx_watermark_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(1),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_fmt_overflow_o = dv_base_reg_field::type_id::create("intr_fmt_overflow_o");
+ intr_fmt_overflow_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(2),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_rx_overflow_o = dv_base_reg_field::type_id::create("intr_rx_overflow_o");
+ intr_rx_overflow_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(3),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_nak_o = dv_base_reg_field::type_id::create("intr_nak_o");
+ intr_nak_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(4),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_scl_interference_o = dv_base_reg_field::type_id::create("intr_scl_interference_o");
+ intr_scl_interference_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(5),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_sda_interference_o = dv_base_reg_field::type_id::create("intr_sda_interference_o");
+ intr_sda_interference_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(6),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_stretch_timeout_o = dv_base_reg_field::type_id::create("intr_stretch_timeout_o");
+ intr_stretch_timeout_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(7),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_sda_unstable_o = dv_base_reg_field::type_id::create("intr_sda_unstable_o");
+ intr_sda_unstable_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(8),
+ .access("W1C"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_intr_state
+
+// Class: i2c_reg_intr_enable
+class i2c_reg_intr_enable extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field intr_fmt_watermark_o;
+ rand dv_base_reg_field intr_rx_watermark_o;
+ rand dv_base_reg_field intr_fmt_overflow_o;
+ rand dv_base_reg_field intr_rx_overflow_o;
+ rand dv_base_reg_field intr_nak_o;
+ rand dv_base_reg_field intr_scl_interference_o;
+ rand dv_base_reg_field intr_sda_interference_o;
+ rand dv_base_reg_field intr_stretch_timeout_o;
+ rand dv_base_reg_field intr_sda_unstable_o;
+
+ `uvm_object_utils(i2c_reg_intr_enable)
+
+ function new(string name = "i2c_reg_intr_enable",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ intr_fmt_watermark_o = dv_base_reg_field::type_id::create("intr_fmt_watermark_o");
+ intr_fmt_watermark_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(0),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_rx_watermark_o = dv_base_reg_field::type_id::create("intr_rx_watermark_o");
+ intr_rx_watermark_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(1),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_fmt_overflow_o = dv_base_reg_field::type_id::create("intr_fmt_overflow_o");
+ intr_fmt_overflow_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(2),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_rx_overflow_o = dv_base_reg_field::type_id::create("intr_rx_overflow_o");
+ intr_rx_overflow_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(3),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_nak_o = dv_base_reg_field::type_id::create("intr_nak_o");
+ intr_nak_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(4),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_scl_interference_o = dv_base_reg_field::type_id::create("intr_scl_interference_o");
+ intr_scl_interference_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(5),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_sda_interference_o = dv_base_reg_field::type_id::create("intr_sda_interference_o");
+ intr_sda_interference_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(6),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_stretch_timeout_o = dv_base_reg_field::type_id::create("intr_stretch_timeout_o");
+ intr_stretch_timeout_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(7),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_sda_unstable_o = dv_base_reg_field::type_id::create("intr_sda_unstable_o");
+ intr_sda_unstable_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(8),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_intr_enable
+
+// Class: i2c_reg_intr_test
+class i2c_reg_intr_test extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field intr_fmt_watermark_o;
+ rand dv_base_reg_field intr_rx_watermark_o;
+ rand dv_base_reg_field intr_fmt_overflow_o;
+ rand dv_base_reg_field intr_rx_overflow_o;
+ rand dv_base_reg_field intr_nak_o;
+ rand dv_base_reg_field intr_scl_interference_o;
+ rand dv_base_reg_field intr_sda_interference_o;
+ rand dv_base_reg_field intr_stretch_timeout_o;
+ rand dv_base_reg_field intr_sda_unstable_o;
+
+ `uvm_object_utils(i2c_reg_intr_test)
+
+ function new(string name = "i2c_reg_intr_test",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ intr_fmt_watermark_o = dv_base_reg_field::type_id::create("intr_fmt_watermark_o");
+ intr_fmt_watermark_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(0),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_rx_watermark_o = dv_base_reg_field::type_id::create("intr_rx_watermark_o");
+ intr_rx_watermark_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(1),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_fmt_overflow_o = dv_base_reg_field::type_id::create("intr_fmt_overflow_o");
+ intr_fmt_overflow_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(2),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_rx_overflow_o = dv_base_reg_field::type_id::create("intr_rx_overflow_o");
+ intr_rx_overflow_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(3),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_nak_o = dv_base_reg_field::type_id::create("intr_nak_o");
+ intr_nak_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(4),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_scl_interference_o = dv_base_reg_field::type_id::create("intr_scl_interference_o");
+ intr_scl_interference_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(5),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_sda_interference_o = dv_base_reg_field::type_id::create("intr_sda_interference_o");
+ intr_sda_interference_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(6),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_stretch_timeout_o = dv_base_reg_field::type_id::create("intr_stretch_timeout_o");
+ intr_stretch_timeout_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(7),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ intr_sda_unstable_o = dv_base_reg_field::type_id::create("intr_sda_unstable_o");
+ intr_sda_unstable_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(8),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_intr_test
+
+// Class: i2c_reg_ctrl
+class i2c_reg_ctrl extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field override;
+ rand dv_base_reg_field scl_o;
+ rand dv_base_reg_field sda_o;
+ rand dv_base_reg_field scl_i;
+ rand dv_base_reg_field sda_i;
+
+ `uvm_object_utils(i2c_reg_ctrl)
+
+ function new(string name = "i2c_reg_ctrl",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ override = dv_base_reg_field::type_id::create("override");
+ override.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(0),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ scl_o = dv_base_reg_field::type_id::create("scl_o");
+ scl_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(1),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ sda_o = dv_base_reg_field::type_id::create("sda_o");
+ sda_o.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(2),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ scl_i = dv_base_reg_field::type_id::create("scl_i");
+ scl_i.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(3),
+ .access("RO"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ sda_i = dv_base_reg_field::type_id::create("sda_i");
+ sda_i.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(4),
+ .access("RO"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_ctrl
+
+// Class: i2c_reg_rdata
+class i2c_reg_rdata extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field data;
+
+ `uvm_object_utils(i2c_reg_rdata)
+
+ function new(string name = "i2c_reg_rdata",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ data = dv_base_reg_field::type_id::create("data");
+ data.configure(
+ .parent(this),
+ .size(8),
+ .lsb_pos(0),
+ .access("RO"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_rdata
+
+// Class: i2c_reg_fdata
+class i2c_reg_fdata extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field byte_fmt;
+ rand dv_base_reg_field start;
+ rand dv_base_reg_field stop;
+ rand dv_base_reg_field read;
+ rand dv_base_reg_field rcont;
+ rand dv_base_reg_field nakok;
+
+ `uvm_object_utils(i2c_reg_fdata)
+
+ function new(string name = "i2c_reg_fdata",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ byte_fmt = dv_base_reg_field::type_id::create("byte_fmt");
+ byte_fmt.configure(
+ .parent(this),
+ .size(8),
+ .lsb_pos(0),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ start = dv_base_reg_field::type_id::create("start");
+ start.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(8),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ stop = dv_base_reg_field::type_id::create("stop");
+ stop.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(9),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ read = dv_base_reg_field::type_id::create("read");
+ read.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(10),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ rcont = dv_base_reg_field::type_id::create("rcont");
+ rcont.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(11),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ nakok = dv_base_reg_field::type_id::create("nakok");
+ nakok.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(12),
+ .access("WO"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_fdata
+
+// Class: i2c_reg_timing0
+class i2c_reg_timing0 extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field thigh;
+ rand dv_base_reg_field tlow;
+
+ `uvm_object_utils(i2c_reg_timing0)
+
+ function new(string name = "i2c_reg_timing0",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ thigh = dv_base_reg_field::type_id::create("thigh");
+ thigh.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(0),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ tlow = dv_base_reg_field::type_id::create("tlow");
+ tlow.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(16),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_timing0
+
+// Class: i2c_reg_timing1
+class i2c_reg_timing1 extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field t_r;
+ rand dv_base_reg_field t_f;
+
+ `uvm_object_utils(i2c_reg_timing1)
+
+ function new(string name = "i2c_reg_timing1",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ t_r = dv_base_reg_field::type_id::create("t_r");
+ t_r.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(0),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ t_f = dv_base_reg_field::type_id::create("t_f");
+ t_f.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(16),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_timing1
+
+// Class: i2c_reg_timing2
+class i2c_reg_timing2 extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field tsu_sta;
+ rand dv_base_reg_field thd_st;
+
+ `uvm_object_utils(i2c_reg_timing2)
+
+ function new(string name = "i2c_reg_timing2",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ tsu_sta = dv_base_reg_field::type_id::create("tsu_sta");
+ tsu_sta.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(0),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ thd_st = dv_base_reg_field::type_id::create("thd_st");
+ thd_st.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(16),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_timing2
+
+// Class: i2c_reg_timing3
+class i2c_reg_timing3 extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field tsu_dat;
+ rand dv_base_reg_field thd_dat;
+
+ `uvm_object_utils(i2c_reg_timing3)
+
+ function new(string name = "i2c_reg_timing3",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ tsu_dat = dv_base_reg_field::type_id::create("tsu_dat");
+ tsu_dat.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(0),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ thd_dat = dv_base_reg_field::type_id::create("thd_dat");
+ thd_dat.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(16),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_timing3
+
+// Class: i2c_reg_timing4
+class i2c_reg_timing4 extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field tsu_sto;
+ rand dv_base_reg_field t_buf;
+
+ `uvm_object_utils(i2c_reg_timing4)
+
+ function new(string name = "i2c_reg_timing4",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ tsu_sto = dv_base_reg_field::type_id::create("tsu_sto");
+ tsu_sto.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(0),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ t_buf = dv_base_reg_field::type_id::create("t_buf");
+ t_buf.configure(
+ .parent(this),
+ .size(16),
+ .lsb_pos(16),
+ .access("RW"),
+ .volatile(1),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_timing4
+
+// Class: i2c_reg_timeout_ctrl
+class i2c_reg_timeout_ctrl extends dv_base_reg;
+ // fields
+ rand dv_base_reg_field val;
+ rand dv_base_reg_field en;
+
+ `uvm_object_utils(i2c_reg_timeout_ctrl)
+
+ function new(string name = "i2c_reg_timeout_ctrl",
+ int unsigned n_bits = 32,
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, n_bits, has_coverage);
+ endfunction : new
+
+ virtual function void build();
+ // create fields
+ val = dv_base_reg_field::type_id::create("val");
+ val.configure(
+ .parent(this),
+ .size(31),
+ .lsb_pos(0),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ en = dv_base_reg_field::type_id::create("en");
+ en.configure(
+ .parent(this),
+ .size(1),
+ .lsb_pos(31),
+ .access("RW"),
+ .volatile(0),
+ .reset(0),
+ .has_reset(1),
+ .is_rand(1),
+ .individually_accessible(1));
+ endfunction : build
+
+endclass : i2c_reg_timeout_ctrl
+
+// Class: i2c_reg_block
+class i2c_reg_block extends dv_base_reg_block;
+ // registers
+ rand i2c_reg_intr_state intr_state;
+ rand i2c_reg_intr_enable intr_enable;
+ rand i2c_reg_intr_test intr_test;
+ rand i2c_reg_ctrl ctrl;
+ rand i2c_reg_rdata rdata;
+ rand i2c_reg_fdata fdata;
+ rand i2c_reg_timing0 timing0;
+ rand i2c_reg_timing1 timing1;
+ rand i2c_reg_timing2 timing2;
+ rand i2c_reg_timing3 timing3;
+ rand i2c_reg_timing4 timing4;
+ rand i2c_reg_timeout_ctrl timeout_ctrl;
+
+ `uvm_object_utils(i2c_reg_block)
+
+ function new(string name = "i2c_reg_block",
+ int has_coverage = UVM_NO_COVERAGE);
+ super.new(name, has_coverage);
+ endfunction : new
+
+ virtual function void build(uvm_reg_addr_t base_addr);
+ // create default map
+ this.default_map = create_map(.name("default_map"),
+ .base_addr(base_addr),
+ .n_bytes(4),
+ .endian(UVM_LITTLE_ENDIAN));
+
+ // create registers
+ intr_state = i2c_reg_intr_state::type_id::create("intr_state");
+ intr_state.configure(.blk_parent(this));
+ intr_state.build();
+ default_map.add_reg(.rg(intr_state),
+ .offset(32'h0),
+ .rights("RW"));
+ intr_enable = i2c_reg_intr_enable::type_id::create("intr_enable");
+ intr_enable.configure(.blk_parent(this));
+ intr_enable.build();
+ default_map.add_reg(.rg(intr_enable),
+ .offset(32'h4),
+ .rights("RW"));
+ intr_test = i2c_reg_intr_test::type_id::create("intr_test");
+ intr_test.configure(.blk_parent(this));
+ intr_test.build();
+ default_map.add_reg(.rg(intr_test),
+ .offset(32'h8),
+ .rights("WO"));
+ ctrl = i2c_reg_ctrl::type_id::create("ctrl");
+ ctrl.configure(.blk_parent(this));
+ ctrl.build();
+ default_map.add_reg(.rg(ctrl),
+ .offset(32'hc),
+ .rights("WO"));
+ rdata = i2c_reg_rdata::type_id::create("rdata");
+ rdata.configure(.blk_parent(this));
+ rdata.build();
+ default_map.add_reg(.rg(rdata),
+ .offset(32'h10),
+ .rights("RO"));
+ fdata = i2c_reg_fdata::type_id::create("fdata");
+ fdata.configure(.blk_parent(this));
+ fdata.build();
+ default_map.add_reg(.rg(fdata),
+ .offset(32'h14),
+ .rights("WO"));
+ timing0 = i2c_reg_timing0::type_id::create("timing0");
+ timing0.configure(.blk_parent(this));
+ timing0.build();
+ default_map.add_reg(.rg(timing0),
+ .offset(32'h18),
+ .rights("RW"));
+ timing1 = i2c_reg_timing1::type_id::create("timing1");
+ timing1.configure(.blk_parent(this));
+ timing1.build();
+ default_map.add_reg(.rg(timing1),
+ .offset(32'h1c),
+ .rights("RW"));
+ timing2 = i2c_reg_timing2::type_id::create("timing2");
+ timing2.configure(.blk_parent(this));
+ timing2.build();
+ default_map.add_reg(.rg(timing2),
+ .offset(32'h20),
+ .rights("RW"));
+ timing3 = i2c_reg_timing3::type_id::create("timing3");
+ timing3.configure(.blk_parent(this));
+ timing3.build();
+ default_map.add_reg(.rg(timing3),
+ .offset(32'h24),
+ .rights("RW"));
+ timing4 = i2c_reg_timing4::type_id::create("timing4");
+ timing4.configure(.blk_parent(this));
+ timing4.build();
+ default_map.add_reg(.rg(timing4),
+ .offset(32'h28),
+ .rights("RW"));
+ timeout_ctrl = i2c_reg_timeout_ctrl::type_id::create("timeout_ctrl");
+ timeout_ctrl.configure(.blk_parent(this));
+ timeout_ctrl.build();
+ default_map.add_reg(.rg(timeout_ctrl),
+ .offset(32'h2c),
+ .rights("RW"));
+ endfunction : build
+
+endclass : i2c_reg_block
diff --git a/hw/ip/i2c/dv/env/i2c_scoreboard.sv b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
new file mode 100644
index 0000000..c66253c
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_scoreboard.sv
@@ -0,0 +1,97 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_scoreboard extends cip_base_scoreboard #(
+ .CFG_T(i2c_env_cfg),
+ .RAL_T(i2c_reg_block),
+ .COV_T(i2c_env_cov)
+ );
+ `uvm_component_utils(i2c_scoreboard)
+
+ // local variables
+
+ // 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);
+ super.build_phase(phase);
+ i2c_fifo = new("i2c_fifo", this);
+ endfunction
+
+ function void connect_phase(uvm_phase phase);
+ super.connect_phase(phase);
+ endfunction
+
+ task run_phase(uvm_phase phase);
+ super.run_phase(phase);
+ fork
+ process_i2c_fifo();
+ join_none
+ 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
+
+ virtual task process_tl_access(tl_seq_item item, tl_channels_e channel = DataChannel);
+ uvm_reg csr;
+ bit do_read_check = 1'b1;
+ bit write = item.is_write();
+
+ // if access was to a valid csr, get the csr handle
+ if (item.a_addr inside {cfg.csr_addrs}) begin
+ csr = ral.default_map.get_reg_by_offset(item.a_addr);
+ `DV_CHECK_NE_FATAL(csr, null)
+ end
+ if (csr == null) begin
+ // we hit an oob addr - expect error response and return
+ `DV_CHECK_EQ(item.d_error, 1'b1)
+ return;
+ end
+
+ if (channel == AddrChannel) begin
+ // if incoming access is a write to a valid csr, then make updates right away
+ if (write) csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask));
+ end
+
+ // process the csr req
+ // for write, update local variable and fifo at address phase
+ // for read, update predication at address phase and compare at data phase
+ case (csr.get_name())
+ // add individual case item for each csr
+ default: begin
+ //`uvm_fatal(`gfn, $sformatf("invalid csr: %0s", csr.get_full_name()))
+ end
+ endcase
+
+ // On reads, if do_read_check, is set, then check mirrored_value against item.d_data
+ if (!write && channel == DataChannel) begin
+ if (do_read_check) begin
+ `DV_CHECK_EQ(csr.get_mirrored_value(), item.d_data,
+ $sformatf("reg name: %0s", csr.get_full_name()))
+ end
+ csr.predict(.value(item.d_data), .kind(UVM_PREDICT_READ));
+ end
+ endtask
+
+ virtual function void reset(string kind = "HARD");
+ super.reset(kind);
+ // reset local fifos queues and variables
+ endfunction
+
+ 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
diff --git a/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv b/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
new file mode 100644
index 0000000..16853f1
--- /dev/null
+++ b/hw/ip/i2c/dv/env/i2c_virtual_sequencer.sv
@@ -0,0 +1,15 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_virtual_sequencer extends cip_base_virtual_sequencer #(
+ .CFG_T(i2c_env_cfg),
+ .COV_T(i2c_env_cov)
+ );
+ `uvm_component_utils(i2c_virtual_sequencer)
+
+ i2c_sequencer i2c_sequencer_h;
+
+ `uvm_component_new
+
+endclass
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
new file mode 100644
index 0000000..c4cfa59
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_base_vseq.sv
@@ -0,0 +1,33 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_base_vseq extends cip_base_vseq #(
+ .CFG_T (i2c_env_cfg),
+ .RAL_T (i2c_reg_block),
+ .COV_T (i2c_env_cov),
+ .VIRTUAL_SEQUENCER_T (i2c_virtual_sequencer)
+ );
+ `uvm_object_utils(i2c_base_vseq)
+
+ // various knobs to enable certain routines
+ bit do_i2c_init = 1'b1;
+
+ `uvm_object_new
+
+ virtual task dut_init(string reset_kind = "HARD");
+ super.dut_init();
+ if (do_i2c_init) i2c_init();
+ endtask
+
+ virtual task dut_shutdown();
+ // check for pending i2c operations and wait for them to complete
+ // TODO
+ endtask
+
+ // setup basic i2c features
+ virtual task i2c_init();
+ //`uvm_info(`gfn, "Initialize I2C registers")
+ endtask
+
+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
new file mode 100644
index 0000000..ab5dc6a
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_common_vseq.sv
@@ -0,0 +1,30 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_common_vseq extends i2c_base_vseq;
+ `uvm_object_utils(i2c_common_vseq)
+
+ constraint num_trans_c {
+ num_trans inside {[1:2]};
+ }
+ `uvm_object_new
+
+ virtual task body();
+ run_common_vseq_wrapper(num_trans);
+ endtask : body
+
+ // function to add csr exclusions of the given type using the csr_excl_item item
+ virtual function void add_csr_exclusions(string csr_test_type,
+ csr_excl_item csr_excl,
+ string scope = "ral");
+
+ // write exclusions - these should not apply to hw_reset test
+ if (csr_test_type != "hw_reset") begin
+ // TODO: below is a sample
+ // status reads back unexpected values due to writes to other csrs
+ // csr_excl.add_excl({scope, ".", "status"}, CsrExclWriteCheck);
+ end
+ endfunction
+
+endclass
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv
new file mode 100644
index 0000000..35c42ad
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_sanity_vseq.sv
@@ -0,0 +1,15 @@
+// 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
new file mode 100644
index 0000000..06afade
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_vseq_list.sv
@@ -0,0 +1,8 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+`include "i2c_base_vseq.sv"
+`include "i2c_wrap_vseq.sv"
+`include "i2c_sanity_vseq.sv"
+`include "i2c_common_vseq.sv"
diff --git a/hw/ip/i2c/dv/env/seq_lib/i2c_wrap_vseq.sv b/hw/ip/i2c/dv/env/seq_lib/i2c_wrap_vseq.sv
new file mode 100644
index 0000000..fbcc4b6
--- /dev/null
+++ b/hw/ip/i2c/dv/env/seq_lib/i2c_wrap_vseq.sv
@@ -0,0 +1,219 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class i2c_wrap_vseq extends i2c_base_vseq;
+ `uvm_object_utils(i2c_wrap_vseq)
+
+ rand uint num_tx_bytes;
+ rand uint num_rx_bytes;
+ rand uint dly_to_next_trans;
+ rand uint dly_to_access_intr;
+ rand bit wait_for_idle;
+ rand uint weight_to_skip_rx_read;
+
+
+ constraint num_trans_c {
+ num_trans inside {[1:20]};
+ }
+
+ constraint num_tx_bytes_c {
+ num_tx_bytes dist {
+ 1 :/ 2,
+ [2:10] :/ 5,
+ [11:15] :/ 5,
+ [16:20] :/ 2
+ };
+ }
+
+ constraint num_rx_bytes_c {
+ num_rx_bytes dist {
+ 1 :/ 2,
+ [2:10] :/ 5,
+ [11:15] :/ 5,
+ [16:20] :/ 2
+ };
+ }
+
+ constraint dly_to_next_trans_c {
+ dly_to_next_trans dist {
+ 0 :/ 5, // more back2back transaction
+ [1:100] :/ 5,
+ [100:10000] :/ 2
+ };
+ }
+
+ constraint dly_to_access_intr_c {
+ dly_to_access_intr dist {
+ 0 :/ 1,
+ [1 :100] :/ 5,
+ [101 :10_000] :/ 3,
+ [10_001 :1_000_000] :/ 1
+ };
+ }
+
+ constraint wait_for_idle_c {
+ wait_for_idle dist {
+ 1 :/ 1,
+ 0 :/ 10
+ };
+ }
+
+ constraint weight_to_skip_rx_read_c {
+ // 3: read, 7: skip
+ weight_to_skip_rx_read == 7;
+ }
+
+ `uvm_object_new
+
+ task pre_start();
+ super.pre_start();
+ num_trans.rand_mode(0);
+ endtask
+
+ task body();
+
+ endtask: body
+
+/*
+ task body();
+ fork
+ begin
+ while (do_interrupt) process_interrupts();
+ end
+ begin
+ // repeat test sequencing upto 50 times
+ for (int i = 1; i <= num_trans; i++) begin
+ // start each new run by randomizing dut parameters
+ `DV_CHECK_RANDOMIZE_FATAL(this)
+
+ i2c_init();
+
+ `uvm_info(`gfn, $sformatf("starting run %0d/%0d", i, num_trans), UVM_MEDIUM)
+ fork
+ begin
+ `uvm_info(`gfn, $sformatf("begin sending %0d tx bytes", num_tx_bytes), UVM_MEDIUM)
+ process_tx();
+ `uvm_info(`gfn, $sformatf("done sending %0d tx bytes", num_tx_bytes), UVM_HIGH)
+ end
+ begin
+ `uvm_info(`gfn, $sformatf("begin sending %0d rx bytes", num_rx_bytes), UVM_MEDIUM)
+ process_rx();
+ `uvm_info(`gfn, $sformatf("done sending %0d rx bytes", num_rx_bytes), UVM_HIGH)
+ end
+ join
+
+ process_remaining_data();
+ `uvm_info(`gfn, $sformatf("finished run %0d/%0d", i, num_trans), UVM_LOW)
+ end
+ do_interrupt = 0; // to end thread process_interrupts gracefully
+ end
+ join
+ endtask : body
+
+ task post_start();
+ bit [TL_DW-1:0] intr_status;
+ // dut_shutdown is must for each iteration, it's called in body
+ do_dut_shutdown = 0;
+ super.post_start();
+ // need to clear fifo when tx is disabled as data isn't sent out
+ if (ral.ctrl.tx.get_mirrored_value() == 0) begin
+ clear_fifos(.clear_tx_fifo(1), .clear_rx_fifo(0));
+ end
+ // read & check, then clear up all interrupts
+ csr_rd(.ptr(ral.intr_state), .value(intr_status));
+ csr_wr(.csr(ral.intr_state), .value(intr_status));
+ endtask
+
+ // read interrupts and randomly clear interrupts if set
+ task process_interrupts();
+ bit [TL_DW-1:0] intr_status, clear_intr;
+ bit clear_rx_intr, clear_tx_intr;
+ // read interrupt
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_access_intr)
+ cfg.clk_rst_vif.wait_clks(dly_to_access_intr);
+ csr_rd(.ptr(ral.intr_state), .value(intr_status));
+
+ // clear interrupt, randomly clear the interrupt that is set, and
+ // don't clear the interrupt which isn't set
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(clear_intr,
+ foreach (clear_intr[i]) {
+ clear_intr[i] -> intr_status[i] == 1;
+ })
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_access_intr)
+ cfg.clk_rst_vif.wait_clks(dly_to_access_intr);
+
+ // for fifo interrupt, parity/frame error, don't clear it at ignored period
+ // as it hasn't been checked
+ clear_tx_intr = clear_intr[TxWatermark] | clear_intr[TxWatermark];
+ clear_rx_intr = clear_intr[RxWatermark] | clear_intr[RxOverflow] | clear_intr[RxFrameErr] |
+ clear_intr[RxParityErr];
+ wait_when_in_ignored_period(clear_tx_intr, clear_rx_intr);
+ csr_wr(.csr(ral.intr_state), .value(clear_intr));
+ endtask
+
+ virtual task process_tx();
+ for (int j = 1; j <= num_tx_bytes; j++) begin
+ byte tx_byte;
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_next_trans)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(wait_for_idle)
+
+ cfg.clk_rst_vif.wait_clks(dly_to_next_trans);
+ wait_for_tx_fifo_not_full();
+ wait_when_in_ignored_period(.tx(1));
+ `DV_CHECK_STD_RANDOMIZE_FATAL(tx_byte)
+ send_tx_byte(tx_byte);
+ if (wait_for_idle) spinwait_txidle();
+ end
+ endtask : process_tx
+
+ // control RX data from both sides independently
+ // 1. i2c device
+ // 2. register
+ virtual task process_rx();
+ bit send_rx_done = 0;
+ fork
+ begin // drive from i2c RX interface
+ for (int j = 1; j <= num_rx_bytes; j++) begin
+ byte rx_byte;
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_next_trans)
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(wait_for_idle)
+ `DV_CHECK_STD_RANDOMIZE_FATAL(rx_byte)
+
+ cfg.clk_rst_vif.wait_clks(dly_to_next_trans);
+ wait_for_rx_fifo_not_full();
+ send_rx_byte(rx_byte);
+ if (wait_for_idle) spinwait_rxidle();
+ end
+ send_rx_done = 1; // to end reading RX thread
+ end
+ begin // read RX data through register
+ while (!send_rx_done) begin
+ `DV_CHECK_MEMBER_RANDOMIZE_FATAL(dly_to_next_trans)
+ cfg.clk_rst_vif.wait_clks(dly_to_next_trans);
+ wait_when_in_ignored_period(.rx(1));
+ rand_read_rx_byte(weight_to_skip_rx_read);
+ end
+ end
+ join
+ endtask : process_rx
+
+ virtual task process_remaining_data();
+
+ fork
+ begin // TX
+ wait_for_all_tx_bytes();
+ // tx fifo is empty but still need to wait for last tx item to be flushed out
+ cfg.m_i2c_agent_cfg.vif.wait_for_tx_idle();
+ end
+ begin // RX
+ // wait for last rx item to be completed before read all of them
+ cfg.m_i2c_agent_cfg.vif.wait_for_rx_idle();
+ read_all_rx_bytes();
+ end
+ join
+
+ endtask : process_remaining_data
+*/
+
+endclass : i2c_wrap_vseq
diff --git a/hw/ip/i2c/dv/i2c_sim.core b/hw/ip/i2c/dv/i2c_sim.core
new file mode 100644
index 0000000..d790828
--- /dev/null
+++ b/hw/ip/i2c/dv/i2c_sim.core
@@ -0,0 +1,28 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:i2c_sim:0.1"
+description: "I2C DV sim target"
+filesets:
+ files_rtl:
+ depend:
+ - lowrisc:ip:i2c:0.1
+ files:
+ - tb/i2c_bind.sv
+ file_type: systemVerilogSource
+
+ files_dv:
+ depend:
+ - lowrisc:dv:i2c_test
+ files:
+ - tb/tb.sv
+ file_type: systemVerilogSource
+
+targets:
+ sim:
+ toplevel: tb
+ filesets:
+ - files_rtl
+ - files_dv
+ default_tool: vcs
diff --git a/hw/ip/i2c/dv/tb/i2c_bind.sv b/hw/ip/i2c/dv/tb/i2c_bind.sv
new file mode 100644
index 0000000..3f0606d
--- /dev/null
+++ b/hw/ip/i2c/dv/tb/i2c_bind.sv
@@ -0,0 +1,14 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+module i2c_bind;
+
+ bind i2c tlul_assert tlul_assert (
+ .clk_i,
+ .rst_ni,
+ .h2d (tl_i),
+ .d2h (tl_o)
+ );
+
+endmodule
diff --git a/hw/ip/i2c/dv/tb/tb.sv b/hw/ip/i2c/dv/tb/tb.sv
new file mode 100644
index 0000000..8f80b91
--- /dev/null
+++ b/hw/ip/i2c/dv/tb/tb.sv
@@ -0,0 +1,86 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+module tb;
+ // dep packages
+ import uvm_pkg::*;
+ import dv_utils_pkg::*;
+ import i2c_env_pkg::*;
+ import i2c_test_pkg::*;
+
+ // macro includes
+ `include "uvm_macros.svh"
+ `include "dv_macros.svh"
+
+ wire clk, rst_n;
+ wire intr_fmt_watermark;
+ wire intr_rx_watermark;
+ wire intr_fmt_overflow;
+ wire intr_rx_overflow;
+ wire intr_nak;
+ wire intr_scl_interference;
+ wire intr_sda_interference;
+ wire intr_stretch_timeout;
+ wire intr_sda_unstable;
+ wire [NUM_MAX_INTERRUPTS-1:0] interrupts;
+ wire [NUM_MAX_ALERTS-1:0] alerts;
+
+ // interfaces
+ clk_rst_if clk_rst_if(.clk(clk), .rst_n(rst_n));
+ pins_if #(NUM_MAX_INTERRUPTS) intr_if(interrupts);
+ pins_if #(NUM_MAX_ALERTS) alerts_if(alerts);
+ tl_if tl_if(.clk(clk), .rst_n(rst_n));
+ i2c_if i2c_if();
+
+ // dut
+ i2c dut (
+ .clk_i (clk ),
+ .rst_ni (rst_n ),
+
+ .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 ),
+
+ .intr_fmt_watermark_o (intr_fmt_watermark ),
+ .intr_rx_watermark_o (intr_rx_watermark ),
+ .intr_fmt_overflow_o (intr_fmt_overflow ),
+ .intr_rx_overflow_o (intr_rx_overflow ),
+ .intr_nak_o (intr_nak ),
+ .intr_scl_interference_o (intr_scl_interference ),
+ .intr_sda_interference_o (intr_sda_interference ),
+ .intr_stretch_timeout_o (intr_stretch_timeout ),
+ .intr_sda_unstable_o (intr_sda_unstable )
+ );
+
+ /*
+ assign interrupts[FmtWatermark] = intr_fmt_watermark;
+ assign interrupts[RxWatermark] = intr_rx_watermark;
+ assign interrupts[FmtOverflow] = intr_fmt_overflow;
+ assign interrupts[RxOverflow] = intr_rx_overflow;
+ assign interrupts[Nak] = intr_nak;
+ assign interrupts[SclInference] = intr_scl_interference;
+ assign interrupts[SdaInference] = intr_sda_interference;
+ assign interrupts[StretchTimeout] = intr_stretch_timeout;
+ assign interrupts[SdaUnstable] = intr_sda_unstable;
+ */
+
+ initial begin
+ // drive clk and rst_n from clk_if
+ clk_rst_if.set_active();
+ uvm_config_db#(virtual clk_rst_if)::set(null, "*.env", "clk_rst_vif", clk_rst_if);
+ uvm_config_db#(intr_vif)::set(null, "*.env", "intr_vif", intr_if);
+ uvm_config_db#(alerts_vif)::set(null, "*.env", "alerts_vif", alerts_if);
+ uvm_config_db#(virtual tl_if)::set(null, "*.env.m_tl_agent*", "vif", tl_if);
+ uvm_config_db#(virtual i2c_if)::set(null, "*.env.m_i2c_agent*", "vif", i2c_if);
+ $timeformat(-12, 0, " ps", 12);
+ run_test();
+ end
+
+endmodule
diff --git a/hw/ip/i2c/dv/tests/i2c_base_test.sv b/hw/ip/i2c/dv/tests/i2c_base_test.sv
new file mode 100644
index 0000000..d84b730
--- /dev/null
+++ b/hw/ip/i2c/dv/tests/i2c_base_test.sv
@@ -0,0 +1,22 @@
+// Copyright lowRISC contributors.
+// 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));
+ `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);
+ endfunction : build_phase
+
+endclass : i2c_base_test
+
diff --git a/hw/ip/i2c/dv/tests/i2c_test.core b/hw/ip/i2c/dv/tests/i2c_test.core
new file mode 100644
index 0000000..c2fa5ca
--- /dev/null
+++ b/hw/ip/i2c/dv/tests/i2c_test.core
@@ -0,0 +1,19 @@
+CAPI=2:
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+name: "lowrisc:dv:i2c_test:0.1"
+description: "I2C DV UVM test"
+filesets:
+ files_dv:
+ depend:
+ - lowrisc:dv:i2c_env
+ files:
+ - i2c_test_pkg.sv
+ - i2c_base_test.sv: {is_include_file: true}
+ file_type: systemVerilogSource
+
+targets:
+ default:
+ filesets:
+ - files_dv
diff --git a/hw/ip/i2c/dv/tests/i2c_test_pkg.sv b/hw/ip/i2c/dv/tests/i2c_test_pkg.sv
new file mode 100644
index 0000000..98fce6c
--- /dev/null
+++ b/hw/ip/i2c/dv/tests/i2c_test_pkg.sv
@@ -0,0 +1,22 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+package i2c_test_pkg;
+ // dep packages
+ import uvm_pkg::*;
+ import cip_base_pkg::*;
+ import i2c_env_pkg::*;
+
+ // macro includes
+ `include "uvm_macros.svh"
+ `include "dv_macros.svh"
+
+ // local types
+
+ // functions
+
+ // package sources
+ `include "i2c_base_test.sv"
+
+endpackage