Start of public OpenTitan development history

Code contributors:
Alex Bradbury <asb@lowrisc.org>
Cindy Chen <chencindy@google.com>
Eunchan Kim <eunchan@google.com>
Gaurang Chitroda <gaurangg@google.com>
Mark Hayter <mark.hayter@gmail.com>
Michael Schaffner <msf@google.com>
Miguel Osorio <miguelosorio@google.com>
Nils Graf <nilsg@google.com>
Philipp Wagner <phw@lowrisc.org>
Pirmin Vogel <vogelpi@lowrisc.org>
Ram Babu Penugonda <rampenugonda@google.com>
Scott Johnson <scottdj@google.com>
Shail Kushwah <kushwahs@google.com>
Srikrishna Iyer <sriyer@google.com>
Steve Nelson <Steve.Nelson@wdc.com>
Tao Liu <taliu@google.com>
Timothy Chen <timothytim@google.com>
Tobias Wölfel <tobias.woelfel@mailbox.org>
Weicai Yang <weicai@google.com>
diff --git a/util/uvmdvgen/Makefile.tpl b/util/uvmdvgen/Makefile.tpl
new file mode 100644
index 0000000..cd358c8
--- /dev/null
+++ b/util/uvmdvgen/Makefile.tpl
@@ -0,0 +1,71 @@
+${'####################################################################################################'}
+${'## 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  := ${name}
+export TB_TOP   := tb
+FUSESOC_CORE    := lowrisc:dv:${name}_sim:0.1
+COMPILE_KEY     ?= default
+
+UVM_TEST        ?= ${name}_base_test
+UVM_TEST_SEQ    ?= ${name}_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       ?= ${name}_sanity
+UVM_TEST        ?= ${name}_base_test
+UVM_TEST_SEQ    ?= ${name}_base_vseq
+
+ifeq (${'$'}{TEST_NAME},${name}_sanity)
+  UVM_TEST_SEQ   = ${name}_sanity_vseq
+endif
+
+ifeq (${'$'}{TEST_NAME},${name}_csr_hw_reset)
+  UVM_TEST_SEQ   = ${name}_csr_vseq
+  RUN_OPTS      += +csr_hw_reset
+  RUN_OPTS      += +en_scb=0
+endif
+
+ifeq (${'$'}{TEST_NAME},${name}_csr_rw)
+  UVM_TEST_SEQ   = ${name}_csr_vseq
+  RUN_OPTS      += +csr_rw
+  RUN_OPTS      += +en_scb=0
+endif
+
+ifeq (${'$'}{TEST_NAME},${name}_csr_bit_bash)
+  UVM_TEST_SEQ   = ${name}_csr_vseq
+  RUN_OPTS      += +csr_bit_bash
+  RUN_OPTS      += +en_scb=0
+endif
+
+ifeq (${'$'}{TEST_NAME},${name}_csr_aliasing)
+  UVM_TEST_SEQ   = ${name}_csr_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},${name}_mem_walk)
+  UVM_TEST_SEQ   = ${name}_csr_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/util/uvmdvgen/README.md b/util/uvmdvgen/README.md
new file mode 100644
index 0000000..9bb701b
--- /dev/null
+++ b/util/uvmdvgen/README.md
@@ -0,0 +1,317 @@
+# uvmdvgen: UVM agent & complete testbench boilerplate code auto-generation tool
+
+uvmdvgen is a python3 based tool to generate the boilerplate code for a UVM agent
+as well as the complete UVM testbench for a given DUT. The tool generates all
+the relevant UVM-based classes including the package and the fusesoc core file
+to make it quickly plug-and-playable. The packages import the standard
+utility and library packages wherever applicable, to conform to our existing
+methodology and style.
+
+When starting with a new DV effort, user goes through a copy-paste exercise to
+replicate an existing UVM testbench code to the current one and has to go though
+several debug cycles to get it working. This tool aims to eliminate that. Also,
+as a part of OpenTitan DV methodology, we have several utilities and base class
+structures (such as DV lib and CIP lib) that share all of the common code. By
+extending a new DV enviroment from the common code, the effort is drastically
+reducecd.
+
+### Setup
+The tool uses the mako based templates, so the following tool is required as
+dependency:
+```
+$ pip3 install --user mako
+```
+
+### Help switch (-h)
+Running the tool with `-h` switch provides a brief description of all available
+switches.
+```
+$ util/uvmdvgen.py -h
+usage: uvmdvgen.py [-h] [-a] [-s] [-e] [-c] [-ea [name] [[name] ...]]
+                   [-ao [hw/dv/sv]] [-eo [hw/ip/<ip>/dv]]
+                   [ip/block name]
+
+Command-line tool to autogenerate boilerplate DV testbench code extended from dv_lib / cip_lib
+
+positional arguments:
+  [ip/block name]       Name of the ip/block for which the UVM TB is being
+                        auto-generated
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -a, --gen_agent       Generate UVM agent code extended from DV library
+  -s, --has_separate_host_device_driver
+                        IP / block agent creates a separate driver for host
+                        and device modes. (ignored if -a switch is not passed)
+  -e, --gen_env         Generate testbench UVM environment code
+  -c, --is_cip          Is comportable IP - this will result in code being
+                        extended from CIP library. If switch is not passed,
+                        then the code will be extended from DV library
+                        instead. (ignored if -e switch is not passed)
+  -ea agt1 agt2 [agt1 agt2 ...], --env_agents agt1 agt2 [agt1 agt2 ...]
+                        Env creates an interface agent specified here. They are
+                        assumed to already exist. Note that the list is space-
+                        separated, and not comma-separated. (ignored if -e
+                        switch is not passed)
+  -ao [hw/dv/sv], --agent_outdir [hw/dv/sv]
+                        Path to place the agent code. A directory called
+                        <name>_agent is created at this location. (default set
+                        to './<name>')
+  -eo [hw/ip/<ip>/dv], --env_outdir [hw/ip/<ip>/dv]
+                        Path to place the env code. 3 directories are created
+                        - env, tb and tests. (default set to './<name>')
+```
+
+### Generating UVM agent
+The boilerplate code for a UVM agent for an interface can be generated using the
+`-a` switch. This results in the generation of complete agent with classes that
+extend from the [dv library](../../hw/dv/sv/dv_lib/README.md). Please see
+description for more details.
+
+The tool generates an interface, item, cfg, cov, monitor, driver and sequence
+library classes. Let's take `jtag` as the argument passed for the name of the IP.
+The following describes their contents in each source generated:
+
+* **jtag_if**
+
+  This is an empty shell of an interface. User is required to add content.
+
+* **jtag_item**
+
+  This is an empty transaction packet extended from `uvm_sequence_item`.
+
+* **jtag_agent_cfg**
+
+  This is the agent configuration object, it contains the virtual interface
+  handle for `jtag_if` and is called `vif`.
+
+* **jtag_agent_cov**
+
+  This is a coverage component extended from `dv_base_agent_cov`.
+
+* **jtag_monitor**
+
+  This is the monitor component extended from `dv_base_monitor`. It provides
+  the following items:
+  * **virtual protected task collect_trans(uvm_phase phase)**
+
+    This is a shell task within which user is required to add logic to detect
+    an event, sample the interface and create a transaction object and write
+    to the analysis port. This task is called in `dv_base_monitor::run_phase`.
+
+* **jtag_driver**
+
+  This is the monitor component extended from `jtag_driver` which is typedef'ed
+  in the pkg to `dv_base_driver` with the right parameter set. It provides the
+  following items:
+  * **virtual task reset_signals()**
+
+    This task is for resetting the initial value of the `vif` signals.
+
+  * **virtual task get_and_drive()**
+
+    This task is used to get the next item from the sequencer, apply it to the
+    interface and return the response back. This is again, an empty task at the
+    moment.
+
+  If the `-s` switch is passed, the tool creates `jtag_host_driver` and
+  `jtag_device_driver` instead, and their contents are exactly the same.
+
+* **seq_lib/jtag_base_seq**
+
+  This is extended from `dv_base_seq`.
+
+* **seq_lib/jtag_seq_list**
+
+  This is a list of sequences included in one place.
+
+* **jtag_agent_pkg**
+
+  This is the package file that includes all of the above sources and the imports
+  the dependent packages.
+
+* **jtag_agent.core**
+
+  This is the fusesoc core file that is used to generate the filelist for
+  the build.
+
+The tool does not create `jtag_sequencer` or `jtag_agent` classes separetely.
+Instead, it typedef's the `dv_base_sequencer` and `dv_base_agent` respectively
+with the right type-parameters in the pkg. The reason for this is having a
+dedicated sequencer and agent is not required since the `dv_base_agent` already
+has all the sub-component instantiations and connections; and `dv_base_sequencer`
+already has a handle to the agent cfg object and nothing more is typically needed.
+
+### Generating UVM environment & testbench
+The boilerplate code for a UVM environment and the testbench for a DUT can be
+generated using the `-e` switch. This results in the generation of classes
+that extend from [dv library](../../hw/dv/sv/dv_lib/README.md). If the `-c`
+switch is passed, it extends from [cip library](../../hw/dv/sv/cip_lib/README.md).
+With `-ea` switch, user can provide a list of downstream agents to create within
+the environment.
+Please see description for more details.
+
+The tool generates not only the UVM environment, but also the base test,
+testbench, top level fusesoc core file with sim target, Makefile that already
+includes the sanity and CSR test suite and more. With just a few tweaks, this
+enables the user to reach the V1 milestone much quicker.  Let's take `i2c_host`
+as the argument passed for the name of the IP. The following is the list of
+files generated with a brief description of their contents:
+
+* **env/i2c_host_env_cfg**
+
+  This is the env cfg object. It creates the downstream agent cfg objects that
+  were passed using the `-ea` switch in the ``initialize()` function which is
+  called in the `dv_base_test::build_phase()`. Since the cfg handle is passed to
+  all env components, those downstream agent cfg objects can be hierarchically
+  referenced.
+
+* **env/i2c_host_env_cov**
+
+  This is the coverage component class. A handle of this class is passed to the
+  scoreboard and the virtual sequencer so that covergroups can be sampled in the
+  scoreboard as well as sequences.
+
+* **env/i2c_host_reg_block**
+
+  This is the UVM reg based RAL model. This is created for completeness. The
+  actual RAL model needs to be generated prior to running simulations using the
+  [regtool](../reggen/README.md).
+
+* **env/i2c_host_scoreboard**
+
+  This is the scoreboard component that already creates the analysis fifos and
+  queues for the agents passed via `-ea` switch. It adds starter tasks for
+  processing each fifo in a forever loop and invokes them in the `run_phase`
+  using `fork-join` statement. If the `-c` switch is passed, it also adds a
+  `process_tl_access` task that is extended from `cip_base_scoreboard`. This
+  task provides a tilelink access packet for further processing.
+
+* **env/i2c_host_virtual_sequencer**
+
+  This is the virtual sequencer used by all test sequences to run the traffic.
+  It adds handles to downstream agent sequencers passed via `-ea` switch.
+  Sub-sequences can be started on them via the `p_sequencer` handle.
+
+* **env/seq_lib/i2c_host_base_vseq**
+
+  This is the base virtual sequence that user can use to add common tasks,
+  functions and variables that other extended test sequences can reuse. For
+  starters, it provides the `i2s_host_init()` task and `do_i2c_host_init` knob
+  for controllability.
+
+* **env/seq_lib/i2c_host_sanity_vseq**
+
+  This is the basic sanity test sequence that user needs to develop as the first
+  test sequence. It extends from `i2s_host_base_vseq`.
+
+* **env/seq_lib/i2c_host_csr_vseq**
+
+  This is the test sequence for the entire CSR suite of tests. It calls
+  `dv_base_vseq::run_csr_vseq_wrapper()` task which is a complete test sequence.
+  All the user needs to do is run the CSR tests and add exclusions if needed
+  using the `add_csr_exclusions()` function provided.
+
+* **env/seq_lib/i2c_host_vseq_list**
+
+  This is a list of test sequences included in one place.
+
+* **env/i2c_host_env**
+
+  This is the env class that creates the downstream agents passed via `-ea`
+  switch. It sets their correspodnding cfg objects (which are members of env cfg
+  object) into the `uvm_config_db`. It also makes the analysis port connections
+  in the `connect_phase` and sets the sequencer handles in the virtual
+  sequencer.
+
+* **env/i2c_host_env_pkg**
+
+  This is the env pkg file which includes all env classes and imports the
+  dependent packages.
+
+* **env/i2c_host_env.core**
+
+  This is the fusesoc core file for the env pkg compile unit.
+
+* **tests/i2c_host_base_test**
+
+  This is the base test class. The base test class it extends from already
+  creates the `env` and `cfg` objects, which are available for manipulation in
+  UVM phases. This class's name would be supplied to UVM_TESTNAME plusarg to run
+  tests using the UVM methodology.
+
+* **tests/i2c_host_test_pkg**
+
+  This is the test pkg file which includes all test classes and imports the
+  dependent packages.
+
+* **tests/i2c_host_test.core**
+
+  This is the fusesoc core file for the test pkg compile unit.
+
+* **tb/i2c_host_bind**
+
+  This is the assertion bind file that is compiled along with the testbench in a
+  multi-top architecture. If the `-c` switch is passed, it adds the `tlul_assert`
+  module bind to the `i2c_host` DUT.
+
+* **tb/tb**
+
+  This is the top level testbench module that instantiates the DUT along with
+  some of the interfaces that are required to be instantiated and connected and
+  passed on the the `uvm_config_db` since the base DV/CIP library classes
+  retrieve them. The user needs to look through the RTL and make additional
+  connections as needed.
+
+* **i2c_host_sim.core**
+
+  This is the top level fusesoc core file with the sim target. It adds the rtl
+  and dv dependencies to construct the complete filelist to pass to simulator's
+  build step.
+
+* **Makefile**
+
+  This is the simulation Makefile that is used as the starting point for
+  building and running tests using the [make flow](../../hw/dv/tools/README.md).
+  It already includes the sanity and CSR suite of tests to allow users to start
+  running tests right away.
+
+* **plan.md**
+
+  This is the empty DV plan document that will describe the entire testbench. A
+  template for this is available [here](../../hw/dv/doc/plan.tpl.md).
+
+#### Examples
+```
+util/uvmdvgen.py i2c -a
+```
+This will create `./i2c/i2c_agent` and place all sources there.
+
+```
+util/uvmdvgen.py jtag -a -ao hw/dv/sv
+```
+This will create `hw/dv/sv/jtag_agent` directory and place all the sources
+there.
+
+```
+util/uvmdvgen.py i2c -a -s -ao hw/dv/sv
+```
+This will create the I2C agent with separate 'host' mode and 'device' mode drivers.
+
+```
+util/uvmdvgen.py i2c_host -e -c -ea i2c -eo hw/ip/i2c_host/dv
+```
+This will create the complete i2c_host dv testbench extended from CIP lib and will
+instantiate `i2c_agent`.
+
+```
+util/uvmdvgen.py dma -e -eo hw/ip/dma/dv
+```
+This will create the complete dma dv testbench extended from DV lib. It does not
+instantiate any downstream agents due to absence of `-ea` switch.
+
+```
+util/uvmdvgen.py chip -e -ea uart i2c jtag -eo hw/top_earlgrey/dv
+```
+This will create the complete chip testbench DV lib and will instantiate `uart_agent`,
+`i2c_agent` and `jtag_agent` in the env.
diff --git a/util/uvmdvgen/README.md.tpl b/util/uvmdvgen/README.md.tpl
new file mode 100644
index 0000000..4164db7
--- /dev/null
+++ b/util/uvmdvgen/README.md.tpl
@@ -0,0 +1,3 @@
+{{% lowrisc-doc-hdr ${name.upper()} DV UVM Agent }}
+
+${name.upper()} DV UVM Agent is extended from DV library agent classes.
diff --git a/util/uvmdvgen/__init__.py b/util/uvmdvgen/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/util/uvmdvgen/__init__.py
diff --git a/util/uvmdvgen/agent.core.tpl b/util/uvmdvgen/agent.core.tpl
new file mode 100644
index 0000000..0acc9c8
--- /dev/null
+++ b/util/uvmdvgen/agent.core.tpl
@@ -0,0 +1,33 @@
+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:${name}_agent:0.1"
+description: "${name.upper()} DV UVM agent"
+filesets:
+  files_dv:
+    depend:
+      - lowrisc:dv:dv_utils
+      - lowrisc:dv:dv_lib
+    files:
+      - ${name}_if.sv
+      - ${name}_agent_pkg.sv
+      - ${name}_agent_cfg.sv: {is_include_file: true}
+      - ${name}_agent_cov.sv: {is_include_file: true}
+      - ${name}_item.sv: {is_include_file: true}
+% if has_separate_host_device_driver:
+      - ${name}_host_driver.sv: {is_include_file: true}
+      - ${name}_device_driver.sv: {is_include_file: true}
+% else:
+      - ${name}_driver.sv: {is_include_file: true}
+% endif
+      - ${name}_monitor.sv: {is_include_file: true}
+      - ${name}_agent.sv: {is_include_file: true}
+      - seq_lib/${name}_base_seq.sv: {is_include_file: true}
+      - seq_lib/${name}_seq_list.sv: {is_include_file: true}
+    file_type: systemVerilogSource
+
+targets:
+  default:
+    filesets:
+      - files_dv
diff --git a/util/uvmdvgen/agent.sv.tpl b/util/uvmdvgen/agent.sv.tpl
new file mode 100644
index 0000000..e6ac624
--- /dev/null
+++ b/util/uvmdvgen/agent.sv.tpl
@@ -0,0 +1,28 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class ${name}_agent extends dv_base_agent #(
+      .CFG_T          (${name}_agent_cfg),
+      .DRIVER_T       (${name}_driver),
+% if has_separate_host_device_driver:
+      .HOST_DRIVER_T  (${name}_host_driver),
+      .DEVICE_DRIVER_T(${name}_device_driver),
+% endif
+      .SEQUENCER_T    (${name}_sequencer),
+      .MONITOR_T      (${name}_monitor),
+      .COV_T          (${name}_agent_cov)
+  );
+
+  `uvm_component_utils(${name}_agent)
+
+  `uvm_component_new
+
+  function void build_phase(uvm_phase phase);
+    super.build_phase(phase);
+    // get ${name}_if handle
+    if (!uvm_config_db#(virtual ${name}_if)::get(this, "", "vif", cfg.vif))
+      `uvm_fatal(`gfn, "failed to get ${name}_if handle from uvm_config_db")
+  endfunction
+
+endclass
diff --git a/util/uvmdvgen/agent_cfg.sv.tpl b/util/uvmdvgen/agent_cfg.sv.tpl
new file mode 100644
index 0000000..f052017
--- /dev/null
+++ b/util/uvmdvgen/agent_cfg.sv.tpl
@@ -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 ${name}_agent_cfg extends dv_base_agent_cfg;
+
+// interface handle used by driver, monitor & the sequencer, via cfg handle
+  virtual ${name}_if vif;
+
+  `uvm_object_utils_begin(${name}_agent_cfg)
+  `uvm_object_utils_end
+
+  `uvm_object_new
+
+endclass
diff --git a/util/uvmdvgen/agent_cov.sv.tpl b/util/uvmdvgen/agent_cov.sv.tpl
new file mode 100644
index 0000000..91e48d4
--- /dev/null
+++ b/util/uvmdvgen/agent_cov.sv.tpl
@@ -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 ${name}_agent_cov extends dv_base_agent_cov #(${name}_agent_cfg);
+  `uvm_component_utils(${name}_agent_cov)
+
+  // the base class provides the following handles for use:
+  // ${name}_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/util/uvmdvgen/agent_pkg.sv.tpl b/util/uvmdvgen/agent_pkg.sv.tpl
new file mode 100644
index 0000000..e6630e2
--- /dev/null
+++ b/util/uvmdvgen/agent_pkg.sv.tpl
@@ -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 ${name}_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 ${name}_item;
+  typedef class ${name}_agent_cfg;
+
+% if has_separate_host_device_driver:
+  // add typedef for ${name}_driver which is dv_base_driver with the right parameter set
+  // ${name}_host_driver and ${name}_device_driver will extend from this
+  typedef dv_base_driver #(.ITEM_T        (${name}_item),
+                           .CFG_T         (${name}_agent_cfg)) ${name}_driver;
+
+% endif
+  // reuse dv_base_seqeuencer as is with the right parameter set
+  typedef dv_base_sequencer #(.ITEM_T     (${name}_item),
+                              .CFG_T      (${name}_agent_cfg)) ${name}_sequencer;
+
+  // functions
+
+  // package sources
+  `include "${name}_item.sv"
+  `include "${name}_agent_cfg.sv"
+  `include "${name}_agent_cov.sv"
+% if has_separate_host_device_driver:
+  `include "${name}_host_driver.sv"
+  `include "${name}_device_driver.sv"
+% else:
+  `include "${name}_driver.sv"
+% endif
+  `include "${name}_monitor.sv"
+  `include "${name}_agent.sv"
+  `include "${name}_seq_list.sv"
+
+  endpackage: ${name}_agent_pkg
diff --git a/util/uvmdvgen/base_seq.sv.tpl b/util/uvmdvgen/base_seq.sv.tpl
new file mode 100644
index 0000000..e3f523d
--- /dev/null
+++ b/util/uvmdvgen/base_seq.sv.tpl
@@ -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 ${name}_base_seq extends dv_base_seq #(
+    .CFG_T       (${name}_agent_cfg),
+    .SEQUENCER_T (${name}_sequencer)
+  );
+  `uvm_object_utils(${name}_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/util/uvmdvgen/base_test.sv.tpl b/util/uvmdvgen/base_test.sv.tpl
new file mode 100644
index 0000000..d713bdf
--- /dev/null
+++ b/util/uvmdvgen/base_test.sv.tpl
@@ -0,0 +1,20 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class ${name}_base_test extends dv_base_test #(
+    .ENV_T(${name}_env),
+    .CFG_T(${name}_env_cfg)
+  );
+  `uvm_component_utils(${name}_base_test)
+  `uvm_component_new
+
+  // the base class dv_base_test creates the following instances:
+  // ${name}_env_cfg: cfg
+  // ${name}_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
+
+endclass : ${name}_base_test
+
diff --git a/util/uvmdvgen/base_vseq.sv.tpl b/util/uvmdvgen/base_vseq.sv.tpl
new file mode 100644
index 0000000..4278bfc
--- /dev/null
+++ b/util/uvmdvgen/base_vseq.sv.tpl
@@ -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
+
+% if is_cip:
+class ${name}_base_vseq extends cip_base_vseq #(
+% else:
+class ${name}_base_vseq extends dv_base_vseq #(
+% endif
+    .CFG_T               (${name}_env_cfg),
+    .RAL_T               (${name}_reg_block),
+    .COV_T               (${name}_env_cov),
+    .VIRTUAL_SEQUENCER_T (${name}_virtual_sequencer)
+  );
+  `uvm_object_utils(${name}_base_vseq)
+
+  // various knobs to enable certain routines
+  bit do_${name}_init = 1'b1;
+
+  `uvm_object_new
+
+  virtual task dut_init(string reset_kind = "HARD");
+    super.dut_init();
+    if (do_${name}_init) ${name}_init();
+  endtask
+
+  virtual task dut_shutdown();
+    // check for pending ${name} operations and wait for them to complete
+    // TODO
+  endtask
+
+  // setup basic ${name} features
+  virtual task ${name}_init();
+    `uvm_error(`gfn, "FIXME")
+  endtask
+
+endclass : ${name}_base_vseq
diff --git a/util/uvmdvgen/bind.sv.tpl b/util/uvmdvgen/bind.sv.tpl
new file mode 100644
index 0000000..9570e68
--- /dev/null
+++ b/util/uvmdvgen/bind.sv.tpl
@@ -0,0 +1,16 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+module ${name}_bind;
+% if is_cip:
+
+  bind ${name} tlul_assert tlul_assert (
+    .clk_i,
+    .rst_ni,
+    .h2d  (tl_i),
+    .d2h  (tl_o)
+  );
+% endif
+
+endmodule
diff --git a/util/uvmdvgen/csr_vseq.sv.tpl b/util/uvmdvgen/csr_vseq.sv.tpl
new file mode 100644
index 0000000..bbdce09
--- /dev/null
+++ b/util/uvmdvgen/csr_vseq.sv.tpl
@@ -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 ${name}_csr_vseq extends ${name}_base_vseq;
+  `uvm_object_utils(${name}_csr_vseq)
+
+  constraint num_trans_c {
+    num_trans inside {[1:2]};
+  }
+  `uvm_object_new
+
+  virtual task body();
+    run_csr_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/util/uvmdvgen/device_driver.sv.tpl b/util/uvmdvgen/device_driver.sv.tpl
new file mode 100644
index 0000000..ae46511
--- /dev/null
+++ b/util/uvmdvgen/device_driver.sv.tpl
@@ -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 ${name}_device_driver extends ${name}_driver;
+  `uvm_component_utils(${name}_device_driver)
+
+  // the base class provides the following handles for use:
+  // ${name}_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/util/uvmdvgen/driver.sv.tpl b/util/uvmdvgen/driver.sv.tpl
new file mode 100644
index 0000000..074280c
--- /dev/null
+++ b/util/uvmdvgen/driver.sv.tpl
@@ -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 ${name}_driver extends dv_base_driver #(${name}_item, ${name}_agent_cfg);
+  `uvm_component_utils(${name}_driver)
+
+  // the base class provides the following handles for use:
+  // ${name}_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/util/uvmdvgen/env.core.tpl b/util/uvmdvgen/env.core.tpl
new file mode 100644
index 0000000..ecc6081
--- /dev/null
+++ b/util/uvmdvgen/env.core.tpl
@@ -0,0 +1,34 @@
+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:${name}_env:0.1"
+description: "${name.upper()} DV UVM environment"
+filesets:
+  files_dv:
+    depend:
+% if is_cip:
+      - lowrisc:dv:cip_lib
+% else:
+      - lowrisc:dv:dv_lib
+% endif
+% for agent in env_agents:
+      - lowrisc:dv:${agent}_agent
+% endfor
+    files:
+      - ${name}_env_pkg.sv
+      - ${name}_env_cfg.sv: {is_include_file: true}
+      - ${name}_env_cov.sv: {is_include_file: true}
+      - ${name}_env.sv: {is_include_file: true}
+      - ${name}_reg_block.sv: {is_include_file: true}
+% if env_agents != []:
+      - ${name}_virtual_sequencer.sv: {is_include_file: true}
+% endif
+      - ${name}_scoreboard.sv: {is_include_file: true}
+      - seq_lib/${name}_vseq_list.sv: {is_include_file: true}
+    file_type: systemVerilogSource
+
+targets:
+  default:
+    filesets:
+      - files_dv
diff --git a/util/uvmdvgen/env.sv.tpl b/util/uvmdvgen/env.sv.tpl
new file mode 100644
index 0000000..58773e9
--- /dev/null
+++ b/util/uvmdvgen/env.sv.tpl
@@ -0,0 +1,51 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+% if is_cip:
+class ${name}_env extends cip_base_env #(
+% else:
+class ${name}_env extends dv_base_env #(
+% endif
+    .CFG_T              (${name}_env_cfg),
+    .COV_T              (${name}_env_cov),
+    .VIRTUAL_SEQUENCER_T(${name}_virtual_sequencer),
+    .SCOREBOARD_T       (${name}_scoreboard)
+  );
+  `uvm_component_utils(${name}_env)
+% if env_agents != []:
+
+% for agent in env_agents:
+  ${agent}_agent m_${agent}_agent;
+% endfor
+% endif
+
+  `uvm_component_new
+
+  function void build_phase(uvm_phase phase);
+    super.build_phase(phase);
+% for agent in env_agents:
+    m_${agent}_agent = ${agent}_agent::type_id::create("m_${agent}_agent", this);
+    uvm_config_db#(${agent}_agent_cfg)::set(this, "m_${agent}_agent*", "cfg", cfg.m_${agent}_agent_cfg);
+% endfor
+  endfunction
+
+  function void connect_phase(uvm_phase phase);
+    super.connect_phase(phase);
+% if env_agents != []:
+    if (cfg.en_scb) begin
+% endif
+% for agent in env_agents:
+      m_${agent}_agent.monitor.analysis_port.connect(scoreboard.${agent}_fifo.analysis_export);
+% endfor
+% if env_agents != []:
+    end
+% endif
+% for agent in env_agents:
+    if (cfg.is_active && cfg.m_${agent}_agent_cfg.is_active) begin
+      virtual_sequencer.${agent}_sequencer_h = m_${agent}_agent.sequencer;
+    end
+% endfor
+  endfunction
+
+endclass
diff --git a/util/uvmdvgen/env_cfg.sv.tpl b/util/uvmdvgen/env_cfg.sv.tpl
new file mode 100644
index 0000000..fb13359
--- /dev/null
+++ b/util/uvmdvgen/env_cfg.sv.tpl
@@ -0,0 +1,39 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+% if is_cip:
+class ${name}_env_cfg extends cip_base_env_cfg #(.RAL_T(${name}_reg_block));
+% else:
+class ${name}_env_cfg extends dv_base_env_cfg #(.RAL_T(${name}_reg_block));
+% endif
+
+  // ext component cfgs
+% for agent in env_agents:
+  rand ${agent}_agent_cfg m_${agent}_agent_cfg;
+% endfor
+
+  `uvm_object_utils_begin(${name}_env_cfg)
+% for agent in env_agents:
+    `uvm_field_object(m_${agent}_agent_cfg, UVM_DEFAULT)
+% endfor
+  `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();
+% for agent in env_agents:
+    // create ${agent} agent config obj
+    m_${agent}_agent_cfg = ${agent}_agent_cfg::type_id::create("m_${agent}_agent_cfg");
+% endfor
+% if is_cip:
+
+    // 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;
+% endif
+  endfunction
+
+endclass
diff --git a/util/uvmdvgen/env_cov.sv.tpl b/util/uvmdvgen/env_cov.sv.tpl
new file mode 100644
index 0000000..c07b312
--- /dev/null
+++ b/util/uvmdvgen/env_cov.sv.tpl
@@ -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
+
+% if is_cip:
+class ${name}_env_cov extends cip_base_env_cov #(.CFG_T(${name}_env_cfg));
+% else:
+class ${name}_env_cov extends dv_base_env_cov #(.CFG_T(${name}_env_cfg));
+% endif
+  `uvm_component_utils(${name}_env_cov)
+
+  // the base class provides the following handles for use:
+  // ${name}_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/util/uvmdvgen/env_pkg.sv.tpl b/util/uvmdvgen/env_pkg.sv.tpl
new file mode 100644
index 0000000..c2eea67
--- /dev/null
+++ b/util/uvmdvgen/env_pkg.sv.tpl
@@ -0,0 +1,59 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+package ${name}_env_pkg;
+  // dep packages
+  import uvm_pkg::*;
+  import top_pkg::*;
+  import dv_utils_pkg::*;
+  import csr_utils_pkg::*;
+  import tl_agent_pkg::*;
+% for agent in env_agents:
+  import ${agent}_agent_pkg::*;
+% endfor
+  import dv_lib_pkg::*;
+% if is_cip:
+  import cip_base_pkg::*;
+% endif
+
+  // macro includes
+  `include "uvm_macros.svh"
+  `include "dv_macros.svh"
+
+  // parameters
+
+  // types
+% if env_agents == []:
+  // forward declare classes to allow typedefs below
+  typedef class ${name}_env_cfg;
+  typedef class ${name}_env_cov;
+
+% endif
+% if env_agents == [] and is_cip:
+  // reuse cip_base_virtual_seqeuencer as is with the right parameter set
+  typedef class cip_base_virtual_sequencer #(
+% elif env_agents == [] and not is_cip:
+  // reuse dv_base_virtual_seqeuencer as is with the right parameter set
+  typedef class dv_base_virtual_sequencer #(
+% endif
+% if env_agents == []:
+      .CFG_T(${name}_env_cfg),
+      .COV_T(${name}_env_cov)
+  ) ${name}_virtual_sequencer;
+% endif
+
+  // functions
+
+  // package sources
+  `include "${name}_reg_block.sv"
+  `include "${name}_env_cfg.sv"
+  `include "${name}_env_cov.sv"
+% if env_agents != []:
+  `include "${name}_virtual_sequencer.sv"
+% endif
+  `include "${name}_scoreboard.sv"
+  `include "${name}_env.sv"
+  `include "${name}_vseq_list.sv"
+
+endpackage
diff --git a/util/uvmdvgen/gen_agent.py b/util/uvmdvgen/gen_agent.py
new file mode 100644
index 0000000..afb848a
--- /dev/null
+++ b/util/uvmdvgen/gen_agent.py
@@ -0,0 +1,59 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""Generate SystemVerilog UVM agent extended freom our DV lib
+"""
+
+import os
+
+from mako.template import Template
+from pkg_resources import resource_filename
+
+
+def gen_agent(name, has_separate_host_device_driver, root_dir):
+    # set sub name
+    agent_dir = root_dir + "/" + name + "_agent"
+
+    # yapf: disable
+    # 4-tuple - path, ip name, class name, file ext
+    agent_srcs = [(agent_dir,               name + '_', 'if',            '.sv'),
+                  (agent_dir,               name + '_', 'item',          '.sv'),
+                  (agent_dir,               name + '_', 'agent_cfg',     '.sv'),
+                  (agent_dir,               name + '_', 'agent_cov',     '.sv'),
+                  (agent_dir,               name + '_', 'monitor',       '.sv'),
+                  (agent_dir,               name + '_', 'driver',        '.sv'),
+                  (agent_dir,               name + '_', 'host_driver',   '.sv'),
+                  (agent_dir,               name + '_', 'device_driver', '.sv'),
+                  (agent_dir,               name + '_', 'agent_pkg',     '.sv'),
+                  (agent_dir,               name + '_', 'agent',         '.sv'),
+                  (agent_dir,               name + '_', 'agent',         '.core'),
+                  (agent_dir,               "",         'README',        '.md'),
+                  (agent_dir + "/seq_lib",  name + '_', 'seq_list',      '.sv'),
+                  (agent_dir + "/seq_lib",  name + '_', 'base_seq',      '.sv')]
+    # yapf: enable
+
+    for tup in agent_srcs:
+        path_dir = tup[0]
+        src_prefix = tup[1]
+        src = tup[2]
+        src_suffix = tup[3]
+
+        if has_separate_host_device_driver:
+            if src == "driver": continue
+        else:
+            if src == "host_driver": continue
+            if src == "device_driver": continue
+
+        ftpl = src + src_suffix + '.tpl'
+        fname = src_prefix + src + src_suffix
+
+        # read template
+        tpl = Template(filename=resource_filename('uvmdvgen', ftpl))
+
+        if not os.path.exists(path_dir): os.system("mkdir -p " + path_dir)
+        with open(path_dir + "/" + fname, 'w') as fout:
+            fout.write(
+                tpl.render(
+                    name=name,
+                    has_separate_host_device_driver=
+                    has_separate_host_device_driver))
diff --git a/util/uvmdvgen/gen_env.py b/util/uvmdvgen/gen_env.py
new file mode 100644
index 0000000..a7b2437
--- /dev/null
+++ b/util/uvmdvgen/gen_env.py
@@ -0,0 +1,55 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""Generate SystemVerilog UVM agent extended freom our DV lib
+"""
+
+import os
+
+from mako.template import Template
+from pkg_resources import resource_filename
+
+
+def gen_env(name, is_cip, env_agents, root_dir):
+    # yapf: disable
+    # 4-tuple - sub-path, ip name, class name, file ext
+    env_srcs = [('env',         name + '_', 'env_cfg',            '.sv'),
+                ('env',         name + '_', 'env_cov',            '.sv'),
+                ('env',         name + '_', 'env_pkg',            '.sv'),
+                ('env',         name + '_', 'reg_block',          '.sv'),
+                ('env',         name + '_', 'scoreboard',         '.sv'),
+                ('env',         name + '_', 'virtual_sequencer',  '.sv'),
+                ('env',         name + '_', 'env',                '.sv'),
+                ('env',         name + '_', 'env',                '.core'),
+                ('env/seq_lib', name + '_', 'base_vseq',          '.sv'),
+                ('env/seq_lib', name + '_', 'sanity_vseq',        '.sv'),
+                ('env/seq_lib', name + '_', 'csr_vseq',           '.sv'),
+                ('env/seq_lib', name + '_', 'vseq_list',          '.sv'),
+                ('tb',          '',         'tb',                 '.sv'),
+                ('tb',          name + '_', 'bind',               '.sv'),
+                ('tests',       name + '_', 'base_test',          '.sv'),
+                ('tests',       name + '_', 'test_pkg',           '.sv'),
+                ('tests',       name + '_', 'test',               '.core'),
+                ('.',           '',         'Makefile',           ''),
+                ('.',           '',         'plan',               '.md'),
+                ('.',           name + '_', 'sim',                '.core')]
+    # yapf: enable
+
+    for tup in env_srcs:
+        path_dir = root_dir + '/' + tup[0]
+        src_prefix = tup[1]
+        src = tup[2]
+        src_suffix = tup[3]
+
+        if env_agents == [] and src == "virtual_sequencer": continue
+
+        ftpl = src + src_suffix + '.tpl'
+        fname = src_prefix + src + src_suffix
+
+        # read template
+        tpl = Template(filename=resource_filename('uvmdvgen', ftpl))
+
+        if not os.path.exists(path_dir): os.system("mkdir -p " + path_dir)
+        with open(path_dir + "/" + fname, 'w') as fout:
+            fout.write(
+                tpl.render(name=name, is_cip=is_cip, env_agents=env_agents))
diff --git a/util/uvmdvgen/host_driver.sv.tpl b/util/uvmdvgen/host_driver.sv.tpl
new file mode 100644
index 0000000..08103e0
--- /dev/null
+++ b/util/uvmdvgen/host_driver.sv.tpl
@@ -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 ${name}_host_driver extends ${name}_driver;
+  `uvm_component_utils(${name}_host_driver)
+
+  // the base class provides the following handles for use:
+  // ${name}_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/util/uvmdvgen/if.sv.tpl b/util/uvmdvgen/if.sv.tpl
new file mode 100644
index 0000000..5f15a32
--- /dev/null
+++ b/util/uvmdvgen/if.sv.tpl
@@ -0,0 +1,11 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+interface ${name}_if ();
+
+  // interface pins
+
+  // debug signals
+
+endinterface
diff --git a/util/uvmdvgen/item.sv.tpl b/util/uvmdvgen/item.sv.tpl
new file mode 100644
index 0000000..715c6d1
--- /dev/null
+++ b/util/uvmdvgen/item.sv.tpl
@@ -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 ${name}_item extends uvm_sequence_item;
+
+  // random variables
+
+  `uvm_object_utils_begin(${name}_item)
+  `uvm_object_utils_end
+
+  `uvm_object_new
+
+endclass
diff --git a/util/uvmdvgen/monitor.sv.tpl b/util/uvmdvgen/monitor.sv.tpl
new file mode 100644
index 0000000..7366fc1
--- /dev/null
+++ b/util/uvmdvgen/monitor.sv.tpl
@@ -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 ${name}_monitor extends dv_base_monitor #(
+    .ITEM_T (${name}_item),
+    .CFG_T  (${name}_agent_cfg),
+    .COV_T  (${name}_agent_cov)
+  );
+  `uvm_component_utils(${name}_monitor)
+
+  // the base class provides the following handles for use:
+  // ${name}_agent_cfg: cfg
+  // ${name}_agent_cov: cov
+  // uvm_analysis_port #(${name}_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/util/uvmdvgen/plan.md.tpl b/util/uvmdvgen/plan.md.tpl
new file mode 100644
index 0000000..f7beb69
--- /dev/null
+++ b/util/uvmdvgen/plan.md.tpl
@@ -0,0 +1,116 @@
+<!-- Copy this file to hw/ip/${name}/dv/plan.md and make changes as needed. For
+convenience '${name}' in the document can be searched and replaced easily with the
+desired IP (with case sensitivity!). Also, use the testbench block diagram here:
+https://drive.google.com/open?id=1LfnTSutIW5E6zSCOCf4-scS8MQ8lXhPAPgSfFx2Aqh0
+as a starting point and modify it to reflect your ${name} testbench and save it
+to hw/ip/${name}/dv/tb.svg. It should get linked and rendered under the block
+diagram section below. Once done, remove this comment before making a PR. -->
+
+{{% lowrisc-doc-hdr Foo DV Plan }}
+
+{{% toc 3 }}
+
+## Current status
+* [${name.upper()} regression dashboard](../../../dv/regressions/weekly/${name}/dashboard.html)
+* Design milestone: D#
+* Verification milestone: [V#](v#_cl.md)
+
+## Design features
+For detailed information on ${name.upper()} design features, please see the
+[${name.upper()} design specification](../doc/${name}.md).
+
+## Testplan
+<!-- TODO add automation to get the testplan hjson to expand here -->
+{{% path to testplan hjson }}
+
+## Testbench architecture
+${name.upper()} testbench has been constructed based on the
+[CIP testbench architecture](../../../dv/sv/cip_lib/README.md).
+<!-- TODO if ${name.upper()} is not a CIP, then indicated that it is extended from DV
+library instead, if applicable. -->
+
+### Block diagram
+![Block diagram](tb.svg)
+
+### Testbench
+Top level testbench is located at `hw/ip/${name}/dv/tb/tb.sv`. It instantiates the
+${name.upper()} DUT module `hw/ip/${name}/rtl/${name}.sv`. In addition, it instantiates several
+interfaces for driving/sampling clock and reset, devmode, interrupts, alerts and
+tilelink host.
+
+### Common DV utility components
+* [common_ifs](../../../dv/sv/common_ifs/README.md)
+* [dv_utils_pkg](../../../dv/sv/dv_utils/README.md)
+* [csr_utils_pkg](../../../dv/sv/csr_utils/README.md)
+
+### Compile-time configurations
+[list compile time configurations, if any]
+
+### Local types & methods
+The following local types and methods defined in `foo_env_pkg` are in use:
+
+[list parameters, types & methods]
+
+### UVC/agent 1
+[Describe here or add link to its README]
+
+### UVC/agent 2
+[Describe here or add link to its README]
+
+### RAL
+The ${name.upper()} RAL model is constructed using the
+[regtool.py script](../../../../util/doc/rm/RegisterTool.md)
+and is placed at `env/foo_reg_block.sv`.
+
+### Reference models
+[Describe reference models in use if applicable, example: SHA256/HMAC]
+
+### Stimulus strategy
+#### Test sequences
+All test sequences reside in `hw/ip/${name}/dv/env/seq_lib`. The `foo_base_vseq`
+virtual sequence is extended from `cip_base_vseq` and serves as a starting point.
+All test sequences are extended from it. It provides commonly used handles,
+variables, functions and tasks that the test sequences can simple use / call.
+Some of the most commonly used tasks / functions are as
+follows:
+* task 1:
+* task 2:
+
+#### Functional coverage
+To ensure high quality constrained random stimulus, it is necessary to develop
+functional coverage model. The following covergroups have been developed to prove
+that the test intent has been adequately met:
+* cg1:
+* cg2:
+
+### Self-checking strategy
+#### Scoreboard
+The `foo_scoreboard` is primarily used for end to end checking. It creates the
+following analysis ports to retrieve the data monitored by corresponding
+interface agents:
+* analysis port1:
+* analysis port2:
+<!-- explain inputs monitored, flow of data and outputs checked -->
+
+#### Assertions
+* TLUL assertions: The `tb/foo_bind.sv` binds the `tlul_assert` assertions
+  to ${name} to ensure TileLink interface protocol compliance.
+* assert prop 1:
+* assert prop 2:
+
+## Building and running tests
+We are using our in-house developed
+[regression tool](../../../dv/tools/README.md)
+for building and running our tests and regressions. Please take a look at the link
+for detailed information on the usage, capabilities, features and known
+issues. Here's how to run a basic sanity test:
+```
+  $ cd hw/ip/${name}/dv
+  $ make TEST_NAME=foo_sanity
+```
+
+### Test list
+Tests developed towards executing the testplan are specifed in `hw/ip/${name}/dv/sim/tests`.
+
+### Regression list
+Regressions are specified in `hw/ip/${name}/dv/sim/regressions`.
diff --git a/util/uvmdvgen/reg_block.sv.tpl b/util/uvmdvgen/reg_block.sv.tpl
new file mode 100644
index 0000000..f06d9b3
--- /dev/null
+++ b/util/uvmdvgen/reg_block.sv.tpl
@@ -0,0 +1,16 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+class ${name}_reg_block extends dv_base_reg_block;
+  `uvm_object_utils(${name}_reg_block)
+
+  function new(string name = "${name}_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);
+    `uvm_fatal(`gfn, "this file does not seem to be auto-generated!")
+  endfunction : build
+
+endclass : ${name}_reg_block
diff --git a/util/uvmdvgen/sanity_vseq.sv.tpl b/util/uvmdvgen/sanity_vseq.sv.tpl
new file mode 100644
index 0000000..7fbc61b
--- /dev/null
+++ b/util/uvmdvgen/sanity_vseq.sv.tpl
@@ -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 ${name}_sanity_vseq extends ${name}_base_vseq;
+  `uvm_object_utils(${name}_sanity_vseq)
+
+  `uvm_object_new
+
+  task body();
+    `uvm_error(`gfn, "FIXME")
+  endtask : body
+
+endclass : ${name}_sanity_vseq
diff --git a/util/uvmdvgen/scoreboard.sv.tpl b/util/uvmdvgen/scoreboard.sv.tpl
new file mode 100644
index 0000000..3a2f9ab
--- /dev/null
+++ b/util/uvmdvgen/scoreboard.sv.tpl
@@ -0,0 +1,113 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+% if is_cip:
+class ${name}_scoreboard extends cip_base_scoreboard #(
+% else:
+class ${name}_scoreboard extends dv_base_scoreboard #(
+% endif
+    .CFG_T(${name}_env_cfg),
+    .RAL_T(${name}_reg_block),
+    .COV_T(${name}_env_cov)
+  );
+  `uvm_component_utils(${name}_scoreboard)
+
+  // local variables
+
+  // TLM agent fifos
+% for agent in env_agents:
+  uvm_tlm_analysis_fifo #(${agent}_item) ${agent}_fifo;
+% endfor
+
+  // local queues to hold incoming packets pending comparison
+% for agent in env_agents:
+  ${agent}_item ${agent}_q[$];
+% endfor
+
+  `uvm_component_new
+
+  function void build_phase(uvm_phase phase);
+    super.build_phase(phase);
+% for agent in env_agents:
+    ${agent}_fifo = new("${agent}_fifo", this);
+% endfor
+  endfunction
+
+  function void connect_phase(uvm_phase phase);
+    super.connect_phase(phase);
+  endfunction
+
+  task run_phase(uvm_phase phase);
+    super.run_phase(phase);
+    fork
+% for agent in env_agents:
+      process_${agent}_fifo();
+% endfor
+    join_none
+  endtask
+% for agent in env_agents:
+
+  virtual task process_${agent}_fifo();
+    ${agent}_item item;
+    forever begin
+      ${agent}_fifo.get(item);
+      `uvm_info(`gfn, $sformatf("received ${agent} item:\n%0s", item.sprint()), UVM_HIGH)
+    end
+  endtask
+% endfor
+% if is_cip:
+
+  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
+% endif
+
+  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/util/uvmdvgen/seq_list.sv.tpl b/util/uvmdvgen/seq_list.sv.tpl
new file mode 100644
index 0000000..9b51507
--- /dev/null
+++ b/util/uvmdvgen/seq_list.sv.tpl
@@ -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 "${name}_base_seq.sv"
diff --git a/util/uvmdvgen/sim.core.tpl b/util/uvmdvgen/sim.core.tpl
new file mode 100644
index 0000000..740bf6a
--- /dev/null
+++ b/util/uvmdvgen/sim.core.tpl
@@ -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:${name}_sim:0.1"
+description: "${name.upper()} DV sim target"
+filesets:
+  files_rtl:
+    depend:
+      - lowrisc:ip:${name}:0.1
+    files:
+      - tb/${name}_bind.sv
+    file_type: systemVerilogSource
+
+  files_dv:
+    depend:
+      - lowrisc:dv:${name}_test
+    files:
+      - tb/tb.sv
+    file_type: systemVerilogSource
+
+targets:
+  sim:
+    toplevel: tb
+    filesets:
+      - files_rtl
+      - files_dv
+    default_tool: vcs
diff --git a/util/uvmdvgen/tb.sv.tpl b/util/uvmdvgen/tb.sv.tpl
new file mode 100644
index 0000000..6a44017
--- /dev/null
+++ b/util/uvmdvgen/tb.sv.tpl
@@ -0,0 +1,67 @@
+// 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 ${name}_env_pkg::*;
+  import ${name}_test_pkg::*;
+
+  // macro includes
+  `include "uvm_macros.svh"
+  `include "dv_macros.svh"
+
+  wire clk, rst_n;
+% if is_cip:
+  wire [NUM_MAX_INTERRUPTS-1:0] interrupts;
+  wire [NUM_MAX_ALERTS-1:0] alerts;
+% endif
+
+  // interfaces
+  clk_rst_if clk_rst_if(.clk(clk), .rst_n(rst_n));
+% if is_cip:
+  pins_if #(NUM_MAX_INTERRUPTS) intr_if(interrupts);
+  pins_if #(NUM_MAX_ALERTS) alerts_if(alerts);
+  pins_if #(1) devmode_if();
+  tl_if tl_if(.clk(clk), .rst_n(rst_n));
+% endif
+% for agent in env_agents:
+  ${agent}_if ${agent}_if();
+% endfor
+
+  // dut
+  ${name} dut (
+    .clk_i                (clk        ),
+% if is_cip:
+    .rst_ni               (rst_n      ),
+
+    .tl_i                 (tl_if.h2d  ),
+    .tl_o                 (tl_if.d2h  )
+
+% else:
+    .rst_ni               (rst_n      )
+
+% endif
+    // TODO: add remaining IOs and hook them
+  );
+
+  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);
+% if is_cip:
+    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#(devmode_vif)::set(null, "*.env", "devmode_vif", devmode_if);
+    uvm_config_db#(virtual tl_if)::set(null, "*.env.m_tl_agent*", "vif", tl_if);
+% endif
+% for agent in env_agents:
+    uvm_config_db#(virtual ${agent}_if)::set(null, "*.env.m_${agent}_agent*", "vif", ${agent}_if);
+% endfor
+    $timeformat(-12, 0, " ps", 12);
+    run_test();
+  end
+
+endmodule
diff --git a/util/uvmdvgen/test.core.tpl b/util/uvmdvgen/test.core.tpl
new file mode 100644
index 0000000..905850b
--- /dev/null
+++ b/util/uvmdvgen/test.core.tpl
@@ -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:${name}_test:0.1"
+description: "${name.upper()} DV UVM test"
+filesets:
+  files_dv:
+    depend:
+      - lowrisc:dv:${name}_env
+    files:
+      - ${name}_test_pkg.sv
+      - ${name}_base_test.sv: {is_include_file: true}
+    file_type: systemVerilogSource
+
+targets:
+  default:
+    filesets:
+      - files_dv
diff --git a/util/uvmdvgen/test_pkg.sv.tpl b/util/uvmdvgen/test_pkg.sv.tpl
new file mode 100644
index 0000000..3bd6ea5
--- /dev/null
+++ b/util/uvmdvgen/test_pkg.sv.tpl
@@ -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
+
+package ${name}_test_pkg;
+  // dep packages
+  import uvm_pkg::*;
+% if is_cip:
+  import cip_base_pkg::*;
+% else:
+  import dv_lib_pkg::*;
+% endif
+  import ${name}_env_pkg::*;
+
+  // macro includes
+  `include "uvm_macros.svh"
+  `include "dv_macros.svh"
+
+  // local types
+
+  // functions
+
+  // package sources
+  `include "${name}_base_test.sv"
+
+endpackage
diff --git a/util/uvmdvgen/virtual_sequencer.sv.tpl b/util/uvmdvgen/virtual_sequencer.sv.tpl
new file mode 100644
index 0000000..e4cd3c4
--- /dev/null
+++ b/util/uvmdvgen/virtual_sequencer.sv.tpl
@@ -0,0 +1,21 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+% if is_cip:
+class ${name}_virtual_sequencer extends cip_base_virtual_sequencer #(
+% else:
+class ${name}_virtual_sequencer extends dv_base_virtual_sequencer #(
+% endif
+    .CFG_T(${name}_env_cfg),
+    .COV_T(${name}_env_cov)
+  );
+  `uvm_component_utils(${name}_virtual_sequencer)
+
+% for agent in env_agents:
+  ${agent}_sequencer ${agent}_sequencer_h;
+% endfor
+
+  `uvm_component_new
+
+endclass
diff --git a/util/uvmdvgen/vseq_list.sv.tpl b/util/uvmdvgen/vseq_list.sv.tpl
new file mode 100644
index 0000000..18a18f9
--- /dev/null
+++ b/util/uvmdvgen/vseq_list.sv.tpl
@@ -0,0 +1,7 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+`include "${name}_base_vseq.sv"
+`include "${name}_sanity_vseq.sv"
+`include "${name}_csr_vseq.sv"