mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-23 21:47:20 -04:00
[dv] initial icache testbench (#711)
* [dv] add vendor .hjson files for dv tools Signed-off-by: Udi Jonnalagadda <udij@google.com> * Update common_ifs to lowRISC/opentitan@0d7f7ac7 Update code from subdir hw/dv/sv/common_ifs in upstream repository https://github.com/lowRISC/opentitan to revision 0d7f7ac755d4e00811257027dd814edb2afca050 Signed-off-by: Udi Jonnalagadda <udij@google.com> * Update csr_utils to lowRISC/opentitan@0d7f7ac7 Update code from subdir hw/dv/sv/csr_utils in upstream repository https://github.com/lowRISC/opentitan to revision 0d7f7ac755d4e00811257027dd814edb2afca050 Signed-off-by: Udi Jonnalagadda <udij@google.com> * Update dv_lib to lowRISC/opentitan@0d7f7ac7 Update code from subdir hw/dv/sv/dv_lib in upstream repository https://github.com/lowRISC/opentitan to revision 0d7f7ac755d4e00811257027dd814edb2afca050 Signed-off-by: Udi Jonnalagadda <udij@google.com> * Update dvsim to lowRISC/opentitan@0d7f7ac7 Update code from subdir util/dvsim in upstream repository https://github.com/lowRISC/opentitan to revision 0d7f7ac755d4e00811257027dd814edb2afca050 Signed-off-by: Udi Jonnalagadda <udij@google.com> * Update uvmdvgen to lowRISC/opentitan@0d7f7ac7 Update code from subdir util/uvmdvgen in upstream repository https://github.com/lowRISC/opentitan to revision 0d7f7ac755d4e00811257027dd814edb2afca050 Signed-off-by: Udi Jonnalagadda <udij@google.com> * Update dv_utils to lowRISC/opentitan@0d7f7ac7 Update code from subdir hw/dv/sv/dv_utils in upstream repository https://github.com/lowRISC/opentitan to revision 0d7f7ac755d4e00811257027dd814edb2afca050 Signed-off-by: Udi Jonnalagadda <udij@google.com> * [dv] initial icache testbench Signed-off-by: Udi Jonnalagadda <udij@google.com> * [dv] add top_pkg and its core file to icache/dv Signed-off-by: Udi Jonnalagadda <udij@google.com> * [dv] update ibex_core and ibex_icache corefile dependencies Signed-off-by: Udi Jonnalagadda <udij@google.com> * [dv] add .vpd support for wave-dumping Signed-off-by: Udi Jonnalagadda <udij@google.com>
This commit is contained in:
parent
08f2271c75
commit
f69c6fbabd
161 changed files with 12145 additions and 28 deletions
|
@ -1,17 +1,20 @@
|
|||
Verification
|
||||
============
|
||||
|
||||
Overview
|
||||
--------
|
||||
Ibex Core
|
||||
---------
|
||||
|
||||
This is a SV/UVM testbench for verification of the Ibex core.
|
||||
Overview
|
||||
^^^^^^^^
|
||||
|
||||
This is a SV/UVM testbench for verification of the Ibex core, located in `dv/uvm/core_ibex`.
|
||||
At a high level, this testbench uses the open source `RISCV-DV random instruction generator
|
||||
<https://github.com/google/riscv-dv>`_ to generate compiled instruction binaries, loads them into a
|
||||
simple memory model, stimulates the Ibex core to run this program in memory, and then compares the
|
||||
core trace log against a golden model ISS trace log to check for correctness of execution.
|
||||
|
||||
Testbench Architecture
|
||||
----------------------
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As previously mentioned, this testbench has been constructed based on its usage of the RISCV-DV
|
||||
random instruction generator developed by Google.
|
||||
|
@ -23,28 +26,28 @@ A block diagram of the testbench is below.
|
|||
Architecture of the UVM testbench for Ibex core
|
||||
|
||||
Memory Interface Agents
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
"""""""""""""""""""""""
|
||||
|
||||
The code can be found in the `dv/uvm/common/ibex_mem_intf_agent
|
||||
<https://github.com/lowRISC/ibex/tree/master/dv/uvm/common/ibex_mem_intf_agent>`_ directory.
|
||||
The code can be found in the `dv/uvm/core_ibex/common/ibex_mem_intf_agent
|
||||
<https://github.com/lowRISC/ibex/tree/master/dv/uvm/core_ibex/common/ibex_mem_intf_agent>`_ directory.
|
||||
Two of these agents are instantiated within the testbench, one for the instruction fetch interface,
|
||||
and the second for the LSU interface.
|
||||
These agents run slave sequences that wait for memory requests from the core, and then grant the
|
||||
requests for instructions or for data.
|
||||
|
||||
Interrupt Interface Agent
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
"""""""""""""""""""""""""
|
||||
|
||||
The code can be found in the
|
||||
`dv/uvm/common/irq_agent <https://github.com/lowRISC/ibex/tree/master/dv/uvm/common/irq_agent>`_ directory.
|
||||
`dv/uvm/core_ibex/common/irq_agent <https://github.com/lowRISC/ibex/tree/master/dv/uvm/core_ibex/common/irq_agent>`_ directory.
|
||||
This agent is used to drive stimulus onto the Ibex core's interrupt pins randomly during test
|
||||
execution.
|
||||
|
||||
Memory Model
|
||||
~~~~~~~~~~~~
|
||||
""""""""""""
|
||||
|
||||
The code can be found in the
|
||||
`dv/uvm/common/mem_model <https://github.com/lowRISC/ibex/tree/master/dv/uvm/common/mem_model>`_
|
||||
`dv/uvm/core_ibex/common/mem_model <https://github.com/lowRISC/ibex/tree/master/dv/uvm/core_ibex/common/mem_model>`_
|
||||
directory.
|
||||
The testbench instantiates a single instance of this memory model that it loads the compiled
|
||||
assembly test program into at the beginning of each test.
|
||||
|
@ -52,10 +55,10 @@ This acts as a unified instruction/data memory that serves all requests from bot
|
|||
memory interface agents.
|
||||
|
||||
Test and Sequence Library
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
"""""""""""""""""""""""""
|
||||
|
||||
The code can be found in the
|
||||
`dv/uvm/tests <https://github.com/lowRISC/ibex/tree/master/dv/uvm/tests>`_ directory.
|
||||
`dv/uvm/core_ibex/tests <https://github.com/lowRISC/ibex/tree/master/dv/uvm/core_ibex/tests>`_ directory.
|
||||
The tests here are the main sources of external stimulus generation and checking for this testbench,
|
||||
as the memory interface slave sequences simply serve the core's memory requests.
|
||||
The tests here are all extended from ``core_ibex_base_test``, and coordinate the entire flow for a
|
||||
|
@ -64,21 +67,21 @@ checking the Ibex core status during the test and dealing with test timeouts.
|
|||
The sequences here are used to drive interrupt and debug stimulus into the core.
|
||||
|
||||
Testplan
|
||||
~~~~~~~~
|
||||
""""""""
|
||||
|
||||
The goal of this bench is to fully verify the Ibex core with 100%
|
||||
coverage. This includes testing all RV32IMC instructions, privileged
|
||||
spec compliance, exception and interrupt testing, Debug Mode operation etc.
|
||||
The complete test list can be found in the file `dv/uvm/riscv_dv_extension/testlist.yaml
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/riscv_dv_extension/testlist.yaml>`_.
|
||||
The complete test list can be found in the file `dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/riscv_dv_extension/testlist.yaml>`_.
|
||||
|
||||
Please note that verification is still a work in progress.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Prerequisites & Environment Setup
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
"""""""""""""""""""""""""""""""""
|
||||
|
||||
In order to run the co-simulation flow, you'll need:
|
||||
|
||||
|
@ -119,7 +122,7 @@ you have installed the corresponding instruction set simulator)
|
|||
.. _riscv-toolchain-releases: https://github.com/lowRISC/lowrisc-toolchains/releases
|
||||
|
||||
End-to-end RTL/ISS co-simulation flow
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
.. figure:: images/dv-flow.png
|
||||
:alt: RTL/ISS co-simulation flow chart
|
||||
|
@ -143,12 +146,12 @@ any analysis that is required to increase verification effectiveness.
|
|||
This mechanism is explained in detail at https://github.com/google/riscv-dv/blob/master/HANDSHAKE.md.
|
||||
As a sidenote, the signature address that this testbench uses for the handshaking is ``0x8ffffffc``.
|
||||
Additionally, as is mentioned in the RISCV-DV documentation of this handshake, a small set of API
|
||||
tasks are provided in `dv/uvm/tests/core_ibex_base_test.sv
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/tests/core_ibex_base_tests.sv>`_ to enable easy
|
||||
tasks are provided in `dv/uvm/core_ibex/tests/core_ibex_base_test.sv
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_base_tests.sv>`_ to enable easy
|
||||
and efficient integration and usage of this mechanism in this test environment.
|
||||
To see how this handshake is used during real simulations, look in
|
||||
`dv/uvm/tests/core_ibex_test_lib.sv
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/tests/core_ibex_test_lib.sv>`_.
|
||||
`dv/uvm/core_ibex/tests/core_ibex_test_lib.sv
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/tests/core_ibex_test_lib.sv>`_.
|
||||
As can be seen, this mechanism is extensively used to provide runtime verification for situations involving external debug
|
||||
requests, interrupt assertions, and memory faults.
|
||||
To add another layer of correctness checking to the checking already provided by the handshake
|
||||
|
@ -203,12 +206,55 @@ The entirety of this flow is controlled by the Makefile found at
|
|||
make COV=1
|
||||
|
||||
Run with a different RTL simulator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
You can add any compile/runtime options in `dv/uvm/yaml/simulator.yaml
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/yaml/rtl_simulation.yaml>`_.
|
||||
You can add any compile/runtime options in `dv/uvm/core_ibex/yaml/simulator.yaml
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/core_ibex/yaml/rtl_simulation.yaml>`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Use the new RTL simulator to run
|
||||
make ... SIMULATOR=xxx
|
||||
|
||||
|
||||
Instruction Cache
|
||||
-----------------
|
||||
|
||||
Overview
|
||||
^^^^^^^^
|
||||
|
||||
NOTE: Icache verification, as well as documentation, is still in very early stages.
|
||||
|
||||
Due to the complexity of the instruction cache, a separate testbench is used to
|
||||
ensure that full verification and coverage closure is performed on this module.
|
||||
This testbench is located at `dv/uvm/icache/dv
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/icache/dv>`_.
|
||||
|
||||
As Icache verification is being carried out as part of the OpenTitan open-source
|
||||
project, the testbench derives from the `dv_lib UVM class library
|
||||
<https://github.com/lowRISC/opentitan/tree/master/hw/dv/sv/dv_lib>`_, which is a set of extended UVM
|
||||
classes that provides basic UVM testbench functionality and components.
|
||||
|
||||
This DV environment will be compiled and simulated using the `dvsim simulation tool
|
||||
<https://github.com/lowRISC/opentitan/tree/master/util/dvsim>`_.
|
||||
The master ``.hjson`` file that controls simulation with ``dvsim`` can be found
|
||||
at `dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson>`_.
|
||||
The associated testplan ``.hjson`` file is located at `dv/uvm/icache/data/ibex_icache_testplan.hjson
|
||||
<https://github.com/lowRISC/ibex/blob/master/dv/uvm/icache/data/ibex_icache_testplan.hjson>`_.
|
||||
As this testbench is still in its infancy, it is currently only able to be compiled, as no tests or
|
||||
sequences are implemented, nor are there any entries in the testplan file.
|
||||
To build the testbench locally using the VCS simulator, run the following command from the root of
|
||||
the Ibex repository:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./vendor/lowrisc_ip/dvsim/dvsim.py dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson --build-only
|
||||
--skip-ral --purge --sr sim_out
|
||||
|
||||
Specify the intended output directory using either the ``--sr`` or ``-scratch-root`` option.
|
||||
The ``--skip-ral`` option is mandatory for building/simulating the Icache testbench, as it does not
|
||||
have any CSRs, excluding this option will lead to build errors.
|
||||
``--purge`` directs the tool to ``rm -rf`` the output directory before running the tool, this can be
|
||||
removed if not desired.
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
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:ibex_mem_intf_agent:0.1"
|
||||
description: "IBEX DV UVM environment"
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:mem_model
|
||||
files:
|
||||
- ibex_mem_intf.sv
|
||||
- ibex_mem_intf_agent_pkg.sv
|
||||
- ibex_mem_intf_master_agent.sv: {is_include_file: true}
|
||||
- ibex_mem_intf_master_driver.sv: {is_include_file: true}
|
||||
- ibex_mem_intf_monitor.sv: {is_include_file: true}
|
||||
- ibex_mem_intf_seq_item.sv: {is_include_file: true}
|
||||
- ibex_mem_intf_slave_agent.sv: {is_include_file: true}
|
||||
- ibex_mem_intf_slave_driver.sv: {is_include_file: true}
|
||||
- ibex_mem_intf_slave_seq_lib.sv: {is_include_file: true}
|
||||
- ibex_mem_intf_slave_sequencer.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`include "ibex_mem_intf.sv"
|
||||
|
||||
package ibex_mem_intf_agent_pkg;
|
||||
|
||||
|
|
20
dv/uvm/core_ibex/common/mem_model/mem_model.core
Normal file
20
dv/uvm/core_ibex/common/mem_model/mem_model.core
Normal file
|
@ -0,0 +1,20 @@
|
|||
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:mem_model"
|
||||
description: "DV Memory Model"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_utils
|
||||
files:
|
||||
- mem_model_pkg.sv
|
||||
- mem_model.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
38
dv/uvm/data/common_modes.hjson
Normal file
38
dv/uvm/data/common_modes.hjson
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
// Sim modes are collection of build_opts and run_opts
|
||||
// These are only set on the command line
|
||||
// These are different from the build modes in the sense that these collection of
|
||||
// options are appended to actual build_modes
|
||||
build_modes: [
|
||||
{
|
||||
name: waves
|
||||
is_sim_mode: 1
|
||||
en_build_modes: ["{tool}_waves"]
|
||||
}
|
||||
{
|
||||
name: cov
|
||||
is_sim_mode: 1
|
||||
en_build_modes: ["{tool}_cov"]
|
||||
}
|
||||
{
|
||||
name: profile
|
||||
is_sim_mode: 1
|
||||
en_build_modes: ["{tool}_profile"]
|
||||
}
|
||||
{
|
||||
name: xprop
|
||||
is_sim_mode: 1
|
||||
en_build_modes: ["{tool}_xprop"]
|
||||
}
|
||||
]
|
||||
|
||||
run_modes: [
|
||||
{
|
||||
name: uvm_trace
|
||||
run_opts: ["+UVM_PHASE_TRACE", "+UVM_CONFIG_DB_TRACE", "+UVM_OBJECTION_TRACE"]
|
||||
}
|
||||
]
|
||||
}
|
29
dv/uvm/data/common_project_cfg.hjson
Normal file
29
dv/uvm/data/common_project_cfg.hjson
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
project: opentitan
|
||||
doc_server: docs.opentitan.org
|
||||
results_server: reports.opentitan.org
|
||||
|
||||
// Default directory structure for the output
|
||||
scratch_base_path: "{scratch_root}/{dut}.{flow}.{tool}"
|
||||
scratch_path: "{scratch_base_path}/{branch}"
|
||||
tool_srcs_dir: "{scratch_path}/{tool}"
|
||||
|
||||
// Results server stuff - indicate what command to use to copy over the results.
|
||||
// Workaround for gsutil to fall back to using python2.7.
|
||||
results_server_prefix: "gs://"
|
||||
results_server_url_prefix: "https://"
|
||||
results_server_cmd: "CLOUDSDK_PYTHON=/usr/bin/python2.7 /usr/bin/gsutil"
|
||||
results_server_css_path: "{results_server_url_prefix}{results_server}/css/style.css"
|
||||
|
||||
results_server_path: "{results_server_prefix}{results_server}/{rel_path}"
|
||||
results_server_dir: "{results_server_path}/latest"
|
||||
|
||||
results_server_html: "results.html"
|
||||
results_server_page: "{results_server_dir}/{results_server_html}"
|
||||
|
||||
results_summary_server_html: "summary.html"
|
||||
results_summary_server_page: "{results_server_path}/{results_summary_server_html}"
|
||||
}
|
108
dv/uvm/data/common_sim_cfg.hjson
Normal file
108
dv/uvm/data/common_sim_cfg.hjson
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
project: opentitan
|
||||
doc_server: docs.opentitan.org
|
||||
results_server: reports.opentitan.org
|
||||
|
||||
flow: sim
|
||||
flow_makefile: "{proj_root}/dv/uvm/data/sim.mk"
|
||||
|
||||
import_cfgs: ["{proj_root}/dv/uvm/data/common_project_cfg.hjson",
|
||||
"{proj_root}/dv/uvm/data/common_modes.hjson",
|
||||
"{proj_root}/dv/uvm/data/fusesoc.hjson",
|
||||
"{proj_root}/dv/uvm/data/{tool}/{tool}.hjson"]
|
||||
|
||||
// Default directory structure for the output
|
||||
build_dir: "{scratch_path}/{build_mode}"
|
||||
run_dir_name: "{index}.{test}"
|
||||
run_dir: "{scratch_path}/{run_dir_name}/out"
|
||||
sw_build_dir: ""
|
||||
sw_root_dir: ""
|
||||
|
||||
// pass and fail patterns
|
||||
build_pass_patterns: []
|
||||
build_fail_patterns: []
|
||||
run_pass_patterns: ["^TEST PASSED (UVM_)?CHECKS$"]
|
||||
run_fail_patterns: ["^UVM_ERROR\\s[^:].*$",
|
||||
"^UVM_FATAL\\s[^:].*$",
|
||||
"^Assert failed: ",
|
||||
"^\\s*Offending '.*'",
|
||||
"^TEST FAILED (UVM_)?CHECKS$"]
|
||||
|
||||
// Default TileLink widths
|
||||
tl_aw: 32
|
||||
tl_dw: 32
|
||||
tl_dbw: 4
|
||||
|
||||
// Default UVM verbosity settings
|
||||
n: UVM_NONE
|
||||
l: UVM_LOW
|
||||
m: UVM_MEDIUM
|
||||
h: UVM_HIGH
|
||||
d: UVM_DEBUG
|
||||
|
||||
// Default waves dump settings
|
||||
dump_file: waves.{dump}
|
||||
|
||||
// Top level simulation entities.
|
||||
sim_tops: ["-top {tb}"]
|
||||
|
||||
// Default build and run opts
|
||||
build_opts: [// List multiple tops for the simulation
|
||||
"{sim_tops}",
|
||||
// Standard UVM defines
|
||||
"+define+UVM",
|
||||
"+define+UVM_NO_DEPRECATED",
|
||||
"+define+UVM_REGEX_NO_DPI",
|
||||
"+define+UVM_REG_ADDR_WIDTH={tl_aw}",
|
||||
"+define+UVM_REG_DATA_WIDTH={tl_dw}",
|
||||
"+define+UVM_REG_BYTENABLE_WIDTH={tl_dbw}"]
|
||||
|
||||
run_opts: ["+UVM_NO_RELNOTES",
|
||||
"+UVM_VERBOSITY={verbosity}"]
|
||||
|
||||
// Default list of things to export to shell
|
||||
exports: [
|
||||
DUMP_FILE: {dump_file}
|
||||
WAVES: {waves}
|
||||
DUT_TOP: {dut}
|
||||
TB_TOP: {tb}
|
||||
]
|
||||
|
||||
// Regressions are tests that can be grouped together and run in one shot
|
||||
// By default, two regressions are made available - "all" and "nightly". Both
|
||||
// run all available tests for the DUT. "nightly" enables coverage as well.
|
||||
// The 'tests' key is set to an empty list, which indicates "run everything".
|
||||
// Test sets can enable sim modes, which are a set of build_opts and run_opts
|
||||
// that are grouped together. These are appended to the build modes used by the
|
||||
// tests.
|
||||
regressions: [
|
||||
{
|
||||
name: sanity
|
||||
reseed: 1
|
||||
}
|
||||
|
||||
{
|
||||
name: all
|
||||
tests: []
|
||||
}
|
||||
|
||||
{
|
||||
name: all_once
|
||||
reseed: 1
|
||||
tests: []
|
||||
}
|
||||
|
||||
{
|
||||
name: nightly
|
||||
tests: []
|
||||
en_sim_modes: ["cov"]
|
||||
}
|
||||
]
|
||||
|
||||
// Project defaults for VCS
|
||||
vcs_cov_hier: "-cm_hier {tool_srcs_dir}/cover.cfg"
|
||||
vcs_cov_excl_files: []
|
||||
}
|
12
dv/uvm/data/fusesoc.hjson
Normal file
12
dv/uvm/data/fusesoc.hjson
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
sv_flist_gen_cmd: fusesoc
|
||||
fusesoc_core_: "{eval_cmd} echo \"{fusesoc_core}\" | tr ':' '_'"
|
||||
sv_flist_gen_opts: ["--cores-root {proj_root}",
|
||||
"run --target=sim --build-root={build_dir}",
|
||||
"--setup {fusesoc_core}"]
|
||||
sv_flist_gen_dir: "{build_dir}/sim-vcs"
|
||||
sv_flist: "{sv_flist_gen_dir}/{fusesoc_core_}.scr"
|
||||
}
|
95
dv/uvm/data/sim.mk
Normal file
95
dv/uvm/data/sim.mk
Normal file
|
@ -0,0 +1,95 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
all: build run
|
||||
|
||||
########################
|
||||
## RAL target ##
|
||||
########################
|
||||
ral:
|
||||
ifneq (${skip_ral},1)
|
||||
mkdir -p ${gen_ral_pkg_dir} && \
|
||||
${gen_ral_pkg_cmd} ${gen_ral_pkg_opts}
|
||||
endif
|
||||
|
||||
|
||||
###############################
|
||||
## sim build and run targets ##
|
||||
###############################
|
||||
build: compile_result
|
||||
|
||||
pre_compile:
|
||||
@echo "[make]: pre_compile"
|
||||
mkdir -p ${build_dir} && env | sort > ${build_dir}/env_vars
|
||||
mkdir -p ${tool_srcs_dir}
|
||||
cp -Ru ${tool_srcs} ${tool_srcs_dir}/.
|
||||
|
||||
gen_sv_flist: pre_compile ral
|
||||
@echo "[make]: gen_sv_flist"
|
||||
cd ${build_dir} && ${sv_flist_gen_cmd} ${sv_flist_gen_opts}
|
||||
|
||||
compile: gen_sv_flist
|
||||
@echo "[make]: compile"
|
||||
cd ${sv_flist_gen_dir} && ${build_cmd} ${build_opts}
|
||||
|
||||
post_compile: compile
|
||||
@echo "[make]: post_compile"
|
||||
|
||||
compile_result: post_compile
|
||||
@echo "[make]: compile_result"
|
||||
|
||||
run: run_result
|
||||
|
||||
pre_run:
|
||||
@echo "[make]: pre_run"
|
||||
mkdir -p ${run_dir} && env | sort > ${run_dir}/env_vars
|
||||
|
||||
simulate:
|
||||
@echo "[make]: simulate"
|
||||
cd ${run_dir} && ${run_cmd} ${run_opts}
|
||||
|
||||
post_run: simulate
|
||||
@echo "[make]: post_run"
|
||||
|
||||
run_result: post_run
|
||||
@echo "[make]: run_result"
|
||||
|
||||
#######################
|
||||
## Load waves target ##
|
||||
#######################
|
||||
debug_waves:
|
||||
${debug_waves_cmd} ${debug_waves_opts}
|
||||
|
||||
############################
|
||||
## coverage rated targets ##
|
||||
############################
|
||||
# Merge coverage if there are multiple builds.
|
||||
cov_merge:
|
||||
${cov_merge_cmd} ${cov_merge_opts}
|
||||
|
||||
# Open coverage tool to review and create report or exclusion file.
|
||||
cov_analyze:
|
||||
${cov_analyze_cmd} ${cov_analyze_opts}
|
||||
|
||||
# Generate coverage reports.
|
||||
cov_report:
|
||||
${cov_report_cmd} ${cov_report_opts}
|
||||
|
||||
clean:
|
||||
echo "[make]: clean"
|
||||
rm -rf ${scratch_root}/${dut}/*
|
||||
|
||||
.PHONY: build \
|
||||
run \
|
||||
reg \
|
||||
pre_compile \
|
||||
compile \
|
||||
post_compile \
|
||||
compile_result \
|
||||
pre_run \
|
||||
simulate \
|
||||
post_run \
|
||||
run_result
|
5
dv/uvm/data/vcs/cover.cfg
Normal file
5
dv/uvm/data/vcs/cover.cfg
Normal file
|
@ -0,0 +1,5 @@
|
|||
+tree tb.dut
|
||||
begin tgl(portsonly)
|
||||
-tree tb
|
||||
+tree tb.dut 1
|
||||
end
|
156
dv/uvm/data/vcs/vcs.hjson
Normal file
156
dv/uvm/data/vcs/vcs.hjson
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
build_cmd: "{job_prefix} vcs"
|
||||
build_ex: "{build_dir}/simv"
|
||||
run_cmd: "{job_prefix} {build_ex}"
|
||||
|
||||
// Indicate the tool specific helper sources - these are copied over to the
|
||||
// {tool_srcs_dir} before running the simulation.
|
||||
tool_srcs: ["{proj_root}/dv/uvm/data/vcs/*"]
|
||||
|
||||
build_opts: ["-sverilog -full64 -licqueue -kdb -ntb_opts uvm-1.2",
|
||||
"-timescale=1ns/1ps",
|
||||
"-Mdir={build_ex}.csrc",
|
||||
"-o {build_ex}",
|
||||
"-f {sv_flist}",
|
||||
"+incdir+{build_dir}",
|
||||
// Turn on warnings for non-void functions called with return values ignored
|
||||
"+warn=SV-NFIVC",
|
||||
"+warn=noUII-L",
|
||||
// Below option required for $error/$fatal system calls
|
||||
"-assert svaext",
|
||||
// Force DPI-C compilation in C99 mode
|
||||
"-CFLAGS \"--std=c99\"",
|
||||
// Without this magic LDFLAGS argument below, we get compile time errors with
|
||||
// VCS on Google Linux machines that look like this:
|
||||
// .../libvcsnew.so: undefined reference to `snpsReallocFunc'
|
||||
// .../libvcsnew.so: undefined reference to `snpsCheckStrdupFunc'
|
||||
// .../libvcsnew.so: undefined reference to `snpsGetMemBytes'
|
||||
"-LDFLAGS \"-Wl,--no-as-needed\""]
|
||||
|
||||
run_opts: ["-licqueue",
|
||||
"-ucli -do {tool_srcs_dir}/vcs_fsdb.tcl",
|
||||
"+ntb_random_seed={seed}",
|
||||
"+UVM_TESTNAME={uvm_test}",
|
||||
"+UVM_TEST_SEQ={uvm_test_seq}"]
|
||||
|
||||
// Coverage related.
|
||||
cov_db_dir: "{build_dir}/cov.vdb"
|
||||
|
||||
// Individual test specific coverage data - this will be deleted if the test fails
|
||||
// so that coverage from failiing tests is not included in the final report.
|
||||
cov_db_test_dir_name: "{run_dir_name}.{seed}"
|
||||
cov_db_test_dir: "{cov_db_dir}/snps/coverage/db/testdata/{cov_db_test_dir_name}"
|
||||
|
||||
// Merging coverage.
|
||||
// "cov_db_dirs" is a special variable that appends all build directories in use.
|
||||
// It is constructed by the tool itself.
|
||||
cov_merge_dir: "{scratch_base_path}/cov_merge"
|
||||
cov_merge_db_dir: "{cov_merge_dir}/merged.vdb"
|
||||
cov_merge_cmd: "{job_prefix} urg"
|
||||
cov_merge_opts: ["-full64",
|
||||
"+urg+lic+wait",
|
||||
"-nocheck",
|
||||
"-noreport",
|
||||
"-flex_merge drop",
|
||||
"-group merge_across_scopes",
|
||||
"-parallel",
|
||||
"-parallel_split 20",
|
||||
// Use cov_db_dirs var for dir args; append -dir in front of each
|
||||
'''{eval_cmd} dirs=`echo {cov_db_dirs}`; dir_args=; \
|
||||
for d in $dirs; do dir_args="$dir_args -dir $d"; done; \
|
||||
echo $dir_args
|
||||
''',
|
||||
"-dbname {cov_merge_db_dir}"]
|
||||
|
||||
// Generate coverage reports in text as well as html.
|
||||
cov_report_dir: "{scratch_base_path}/cov_report"
|
||||
cov_report_cmd: "{job_prefix} urg"
|
||||
cov_report_opts: ["-full64",
|
||||
"+urg+lic+wait",
|
||||
"-dir {cov_merge_db_dir}",
|
||||
"-group instcov_for_score",
|
||||
"-line nocasedef",
|
||||
"-format both",
|
||||
"-report {cov_report_dir}"]
|
||||
cov_report_dashboard: "{cov_report_dir}/dashboard.txt"
|
||||
|
||||
// Analyzing coverage - this is done by invoking --cov-analyze switch. It opens up the
|
||||
// GUI for visual analysis.
|
||||
cov_analyze_dir: "{scratch_base_path}/cov_analyze"
|
||||
cov_analyze_cmd: "{job_prefix} verdi"
|
||||
cov_analyze_opts: ["-cov",
|
||||
"-covdir {cov_merge_db_dir}",
|
||||
"-line nocasedef"
|
||||
"-elfile {vcs_cov_excl_files}"]
|
||||
|
||||
// Vars that need to exported to the env.
|
||||
exports: [
|
||||
VCS_ARCH_OVERRIDE: linux
|
||||
VCS_LIC_EXPIRE_WARNING: 1
|
||||
]
|
||||
|
||||
// Defaults for VCS
|
||||
cov_metrics: "line+cond+fsm+tgl+branch+assert"
|
||||
vcs_cov_hier: ""
|
||||
vcs_cov_assert_hier: ""
|
||||
vcs_cov_excl_files: []
|
||||
|
||||
// pass and fail patterns
|
||||
build_fail_patterns: ["^Error-.*$"]
|
||||
run_fail_patterns: ["^Error-.*$"] // Null pointer error
|
||||
|
||||
build_modes: [
|
||||
{
|
||||
name: vcs_waves
|
||||
is_sim_mode: 1
|
||||
build_opts: ["-debug_access+all"]
|
||||
}
|
||||
{
|
||||
name: vcs_cov
|
||||
is_sim_mode: 1
|
||||
build_opts: [// Enable the required cov metrics
|
||||
"-cm {cov_metrics}",
|
||||
// Set the coverage hierarchy
|
||||
"{vcs_cov_hier}",
|
||||
// Cover all continuous assignments
|
||||
"-cm_line contassign",
|
||||
// Dump toggle coverage on mdas, array of structs and on ports only
|
||||
"-cm_tgl mda+structarr+portsonly",
|
||||
// Ignore initial blocks for coverage
|
||||
"-cm_report noinitial",
|
||||
// Filter unreachable/statically constant blocks
|
||||
"-cm_noconst",
|
||||
// Don't count coverage that's coming from zero-time glitches
|
||||
"-cm_glitch 0",
|
||||
// Ignore warnings about not applying cm_glitch to path and FSM
|
||||
"+warn=noVCM-OPTIGN",
|
||||
// Coverage database output location
|
||||
"-cm_dir {cov_db_dir}"]
|
||||
|
||||
run_opts: [// Enable the required cov metrics
|
||||
"-cm {cov_metrics}",
|
||||
// Same directory as build
|
||||
"-cm_dir {build_dir}/cov.vdb",
|
||||
// Don't output cm.log which can be quite large
|
||||
"-cm_log /dev/null",
|
||||
// Provide a name to the coverage collected for this test
|
||||
"-cm_name {cov_db_test_dir_name}",
|
||||
// Don't dump all the coverage assertion attempts at the end of simulation
|
||||
"-assert nopostproc"]
|
||||
}
|
||||
{
|
||||
name: vcs_xprop
|
||||
is_sim_mode: 1
|
||||
build_opts: ["-xprop={tool_srcs_dir}/xprop.cfg"]
|
||||
}
|
||||
{
|
||||
name: vcs_profile
|
||||
is_sim_mode: 1
|
||||
build_opts: ["-simprofile"]
|
||||
run_opts: ["-simprofile {profile}"]
|
||||
}
|
||||
]
|
||||
}
|
44
dv/uvm/data/vcs/vcs_fsdb.tcl
Normal file
44
dv/uvm/data/vcs/vcs_fsdb.tcl
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# TCL file invoked from VCS's simv at run-time using this: -ucli -do <this file>
|
||||
|
||||
if {[info exists ::env(WAVES)]} {
|
||||
# Use FSDB for dumping only if Verdi is set up
|
||||
|
||||
# Syntax: fsdbDumpvars [depth] [instance] [option]*
|
||||
##############################################################################
|
||||
# Option Description
|
||||
##############################################################################
|
||||
# +mda Dumps memory and MDA signals in all scopes.
|
||||
# +packedmda Dumps packed signals
|
||||
# +struct Dumps structs
|
||||
# +skip_cell_instance=mode Enables or disables cell dumping
|
||||
# +strength Enables strength dumping
|
||||
# +parameter Dumps parameters
|
||||
# +power Dumps power-related signals
|
||||
# +trace_process Dumps VHDL processes
|
||||
# +no_functions Disables dumping of functions
|
||||
# +sva Dumps assertions
|
||||
# +Reg_Only Dumps only reg type signals
|
||||
# +IO_Only Dumps only IO port signals
|
||||
# +by_file=<filename> File to specify objects to add
|
||||
# +all Dumps memories, MDA signals, structs, unions,power, and packed structs
|
||||
|
||||
if {$::env(WAVES) == 1} {
|
||||
if { [info exists ::env(VERDI_HOME)] } {
|
||||
fsdbDumpfile $::env(DUMP_FILE)
|
||||
fsdbDumpvars 0 $::env(TB_TOP) +all
|
||||
fsdbDumpSVA 0 $::env(TB_TOP)
|
||||
} else {
|
||||
# Verdi is not set up, so use standard dumping format
|
||||
set dump_file $::env(DUMP_FILE)
|
||||
dump -file "${dump_file}"
|
||||
dump -add { tb } -depth 0 -aggregates -scope "."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run
|
||||
quit
|
4
dv/uvm/data/vcs/xprop.cfg
Normal file
4
dv/uvm/data/vcs/xprop.cfg
Normal file
|
@ -0,0 +1,4 @@
|
|||
merge = xmerge;
|
||||
|
||||
// Turn on xprop for dut only
|
||||
instance { tb.dut } { xpropOn };
|
84
dv/uvm/data/xcelium/xcelium.hjson
Normal file
84
dv/uvm/data/xcelium/xcelium.hjson
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
build_cmd: "{job_prefix} xrun"
|
||||
run_cmd: "{job_prefix} xrun"
|
||||
|
||||
// Indicate the tool specific helper sources - these are copied over to the
|
||||
// {tool_srcs_dir} before running the simulation.
|
||||
tool_srcs: ["{proj_root}/dv/uvm/data/xcelium/*"]
|
||||
|
||||
build_opts: [" -elaborate -64bit -access +r -sv",
|
||||
"-messages -errormax 50",
|
||||
"-timescale 1ns/1ps",
|
||||
"-f {sv_flist}",
|
||||
"-uvmhome {UVM_HOME}",
|
||||
"-xmlibdirname {build_dir}/xcelium.d"]
|
||||
|
||||
run_opts: ["-input {tool_srcs_dir}/xcelium_{dump}.tcl",
|
||||
"-64bit -xmlibdirname {build_dir}/xcelium.d -R",
|
||||
"+SVSEED={seed}",
|
||||
"+UVM_TESTNAME={uvm_test}",
|
||||
"+UVM_TEST_SEQ={uvm_test_seq}"]
|
||||
|
||||
// Coverage related.
|
||||
// TODO: These options have to be filled in.
|
||||
cov_db_dir: ""
|
||||
|
||||
// Individual test specific coverage data - this will be deleted if the test fails
|
||||
// so that coverage from failiing tests is not included in the final report.
|
||||
cov_db_test_dir_name: "{run_dir_name}.{seed}"
|
||||
cov_db_test_dir: ""
|
||||
|
||||
// Merging coverage.
|
||||
// "cov_db_dirs" is a special variable that appends all build directories in use.
|
||||
// It is constructed by the tool itself.
|
||||
cov_merge_dir: "{scratch_base_path}/cov_merge"
|
||||
cov_merge_db_dir: ""
|
||||
cov_merge_cmd: ""
|
||||
cov_merge_opts: []
|
||||
|
||||
// Generate covreage reports in text as well as html.
|
||||
cov_report_dir: "{scratch_base_path}/cov_report"
|
||||
cov_report_cmd: ""
|
||||
cov_report_opts: []
|
||||
cov_report_dashboard: ""
|
||||
|
||||
// Analyzing coverage - this is done by invoking --cov-analyze switch. It opens up the
|
||||
// GUI for visual analysis.
|
||||
cov_analyze_dir: "{scratch_base_path}/cov_analyze"
|
||||
cov_analyze_cmd: ""
|
||||
cov_analyze_opts: []
|
||||
|
||||
// pass and fail patterns
|
||||
build_fail_patterns: ["\\*E.*$"]
|
||||
run_fail_patterns: ["\\*E.*$"] // Null pointer error
|
||||
|
||||
build_modes: [
|
||||
{
|
||||
name: xcelium_waves
|
||||
is_sim_mode: 1
|
||||
}
|
||||
// TODO support coverage for xcelium
|
||||
{
|
||||
name: xcelium_cov
|
||||
is_sim_mode: 1
|
||||
build_opts: []
|
||||
run_opts: []
|
||||
}
|
||||
// TODO support profile for xcelium
|
||||
{
|
||||
name: xcelium_profile
|
||||
is_sim_mode: 1
|
||||
build_opts: []
|
||||
run_opts: []
|
||||
}
|
||||
{
|
||||
name: xcelium_xprop
|
||||
is_sim_mode: 1
|
||||
# -xverbose << add to see which modules does not have xprop enabled
|
||||
build_opts: ["-xprop F"]
|
||||
}
|
||||
]
|
||||
}
|
30
dv/uvm/icache/data/ibex_icache_testplan.hjson
Normal file
30
dv/uvm/icache/data/ibex_icache_testplan.hjson
Normal file
|
@ -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
|
||||
{
|
||||
name: "ibex_icache"
|
||||
|
||||
entries: [
|
||||
{
|
||||
name: sanity
|
||||
desc: '''**Goal**: Basic sanity test acessing a major datapath in IBEX_ICACHE.
|
||||
|
||||
**Stimulus**: Describe the stimulus procedure.
|
||||
|
||||
**Checks**": Describe the self-check procedure.
|
||||
- add bullets as needed
|
||||
- second bullet<br>
|
||||
describe second bullet
|
||||
|
||||
Start a new paragraph.'''
|
||||
milestone: V1
|
||||
tests: ["ibex_icache_sanity"]
|
||||
}
|
||||
{
|
||||
name: feature1
|
||||
desc: '''Add more test entries here like above.'''
|
||||
milestone: V1
|
||||
tests: []
|
||||
}
|
||||
]
|
||||
}
|
99
dv/uvm/icache/doc/ibex_icache_dv_plan.md
Normal file
99
dv/uvm/icache/doc/ibex_icache_dv_plan.md
Normal file
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
title: "IBEX_ICACHE DV Plan"
|
||||
---
|
||||
|
||||
## Goals
|
||||
* **DV**
|
||||
* Verify all IBEX_ICACHE IP features by running dynamic simulations with a SV/UVM based testbench
|
||||
* Develop and run all tests based on the [testplan](#testplan) below towards closing code and functional coverage on the IP and all of its sub-modules
|
||||
* **FPV**
|
||||
|
||||
## Current status
|
||||
<!-- TODO for now -->
|
||||
* [Design & verification stage]({{< relref "doc/project/hw_dashboard" >}})
|
||||
* [HW development stages]({{< relref "doc/project/hw_stages" >}})
|
||||
* [Simulation results](https://reports.opentitan.org/hw/ip/ibex_icache/dv/latest/results.html)
|
||||
|
||||
## Design features
|
||||
For detailed information on IBEX_ICACHE design features, please see the [IBEX_ICACHE technical specification]({{< relref "doc/icache.rst" >}}).
|
||||
|
||||
## Testbench architecture
|
||||
IBEX_ICACHE testbench has been constructed based on the [DV_LIB testbench architecture]({{< relref "vendor/lowrisc_ip/dv_lib/" >}}).
|
||||
|
||||
### Block diagram
|
||||

|
||||
|
||||
### Top level testbench
|
||||
Top level testbench is located at `dv/uvm/icache/dv/tb/tb.sv`. It instantiates the IBEX_ICACHE DUT module `rtl/ibex_icache.sv`.
|
||||
In addition, it instantiates the following interfaces, connects them to the DUT and sets their handle into `uvm_config_db`:
|
||||
* [Clock and reset interface]({{< relref "vendor/lowrisc_ip/common_ifs" >}})
|
||||
<!-- TODO -->
|
||||
* IBEX_ICACHE IOs
|
||||
|
||||
### Common DV utility components
|
||||
The following utilities provide generic helper tasks and functions to perform activities that are common across the project:
|
||||
* [dv_utils_pkg]({{< relref "vendor/lowrisc_ip/dv_utils/README.md" >}})
|
||||
|
||||
### Compile-time configurations
|
||||
[list compile time configurations, if any and what are they used for]
|
||||
|
||||
### Global types & methods
|
||||
All common types and methods defined at the package level can be found in
|
||||
`ibex_icache_env_pkg`. Some of them in use are:
|
||||
```systemverilog
|
||||
[list a few parameters, types & methods; no need to mention all]
|
||||
```
|
||||
|
||||
### IBEX_ICACHE Agent
|
||||
[Describe here or add link to its README]
|
||||
### IBEX_MEM_INTF_SLAVE Agent
|
||||
[Describe here or add link to its README]
|
||||
|
||||
### UVC/agent 1
|
||||
[Describe here or add link to its README]
|
||||
|
||||
### UVC/agent 2
|
||||
[Describe here or add link to its README]
|
||||
|
||||
|
||||
### Reference models
|
||||
[Describe reference models in use if applicable, example: SHA256/HMAC]
|
||||
|
||||
### Stimulus strategy
|
||||
#### Test sequences
|
||||
All test sequences reside in `dv/uvm/icache/dv/env/seq_lib`.
|
||||
The `ibex_icache_base_vseq` virtual sequence is extended from `cip_base_vseq` and serves as a starting point.
|
||||
All test sequences are extended from `ibex_icache_base_vseq`.
|
||||
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 a 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 `ibex_icache_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
|
||||
<!-- only keep this section if any FPV is done on icache -->
|
||||
|
||||
## Building and running tests
|
||||
We are using our in-house developed [regression tool]({{< relref "vendor/lowrisc_ip/dvsim" >}}) 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:
|
||||
```console
|
||||
$ cd hw/ip/ibex_icache/dv
|
||||
$ make TEST_NAME=ibex_icache_sanity
|
||||
```
|
||||
|
||||
## Testplan
|
||||
{{</* testplan "dv/uvm/icache/data/ibex_icache_testplan.hjson" */>}}
|
1
dv/uvm/icache/doc/tb.svg
Normal file
1
dv/uvm/icache/doc/tb.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 674 KiB |
29
dv/uvm/icache/dv/env/ibex_icache_env.core
vendored
Normal file
29
dv/uvm/icache/dv/env/ibex_icache_env.core
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
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:ibex_icache_env:0.1"
|
||||
description: "IBEX_ICACHE DV UVM environment"
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_lib
|
||||
- lowrisc:dv:ibex_icache_agent
|
||||
- lowrisc:dv:ibex_mem_intf_agent
|
||||
files:
|
||||
- ibex_icache_env_pkg.sv
|
||||
- ibex_icache_env_cfg.sv: {is_include_file: true}
|
||||
- ibex_icache_env_cov.sv: {is_include_file: true}
|
||||
- ibex_icache_virtual_sequencer.sv: {is_include_file: true}
|
||||
- ibex_icache_scoreboard.sv: {is_include_file: true}
|
||||
- ibex_icache_env.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_vseq_list.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_base_vseq.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_common_vseq.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_sanity_vseq.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
41
dv/uvm/icache/dv/env/ibex_icache_env.sv
vendored
Normal file
41
dv/uvm/icache/dv/env/ibex_icache_env.sv
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_icache_env extends dv_base_env #(
|
||||
.CFG_T (ibex_icache_env_cfg),
|
||||
.COV_T (ibex_icache_env_cov),
|
||||
.VIRTUAL_SEQUENCER_T(ibex_icache_virtual_sequencer),
|
||||
.SCOREBOARD_T (ibex_icache_scoreboard)
|
||||
);
|
||||
`uvm_component_utils(ibex_icache_env)
|
||||
|
||||
ibex_icache_agent m_ibex_icache_agent;
|
||||
ibex_mem_intf_slave_agent m_ibex_mem_intf_slave_agent;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
// create components
|
||||
m_ibex_icache_agent = ibex_icache_agent::type_id::create("m_ibex_icache_agent", this);
|
||||
uvm_config_db#(ibex_icache_agent_cfg)::set(this, "m_ibex_icache_agent*", "cfg", cfg.m_ibex_icache_agent_cfg);
|
||||
// create components
|
||||
m_ibex_mem_intf_slave_agent = ibex_mem_intf_slave_agent::type_id::create("m_ibex_mem_intf_slave_agent", this);
|
||||
endfunction
|
||||
|
||||
function void connect_phase(uvm_phase phase);
|
||||
super.connect_phase(phase);
|
||||
if (cfg.en_scb) begin
|
||||
m_ibex_icache_agent.monitor.analysis_port.connect(scoreboard.ibex_icache_fifo.analysis_export);
|
||||
m_ibex_mem_intf_slave_agent.monitor.addr_ph_port.connect(scoreboard.ibex_mem_intf_slave_fifo.analysis_export);
|
||||
end
|
||||
if (cfg.is_active && cfg.m_ibex_icache_agent_cfg.is_active) begin
|
||||
virtual_sequencer.ibex_icache_sequencer_h = m_ibex_icache_agent.sequencer;
|
||||
end
|
||||
if (cfg.is_active && m_ibex_mem_intf_slave_agent.get_is_active()) begin
|
||||
virtual_sequencer.ibex_mem_intf_slave_sequencer_h = m_ibex_mem_intf_slave_agent.sequencer;
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
23
dv/uvm/icache/dv/env/ibex_icache_env_cfg.sv
vendored
Normal file
23
dv/uvm/icache/dv/env/ibex_icache_env_cfg.sv
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_icache_env_cfg extends dv_base_env_cfg;
|
||||
|
||||
// ext component cfgs
|
||||
rand ibex_icache_agent_cfg m_ibex_icache_agent_cfg;
|
||||
|
||||
`uvm_object_utils_begin(ibex_icache_env_cfg)
|
||||
`uvm_field_object(m_ibex_icache_agent_cfg, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
|
||||
virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1);
|
||||
// create ibex_icache agent config obj
|
||||
m_ibex_icache_agent_cfg = ibex_icache_agent_cfg::type_id::create("m_ibex_icache_agent_cfg");
|
||||
// create ibex_mem_intf_slave agent config obj
|
||||
endfunction
|
||||
|
||||
endclass
|
30
dv/uvm/icache/dv/env/ibex_icache_env_cov.sv
vendored
Normal file
30
dv/uvm/icache/dv/env/ibex_icache_env_cov.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
/**
|
||||
* Covergoups that are dependent on run-time parameters that may be available
|
||||
* only in build_phase can be defined here
|
||||
* Covergroups may also be wrapped inside helper classes if needed.
|
||||
*/
|
||||
|
||||
class ibex_icache_env_cov extends dv_base_env_cov #(.CFG_T(ibex_icache_env_cfg));
|
||||
`uvm_component_utils(ibex_icache_env_cov)
|
||||
|
||||
// the base class provides the following handles for use:
|
||||
// ibex_icache_env_cfg: cfg
|
||||
|
||||
// covergroups
|
||||
// [add covergroups here]
|
||||
|
||||
function new(string name, uvm_component parent);
|
||||
super.new(name, parent);
|
||||
// [instantiate covergroups here]
|
||||
endfunction : new
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
// [or instantiate covergroups here]
|
||||
endfunction
|
||||
|
||||
endclass
|
33
dv/uvm/icache/dv/env/ibex_icache_env_pkg.sv
vendored
Normal file
33
dv/uvm/icache/dv/env/ibex_icache_env_pkg.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
package ibex_icache_env_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import top_pkg::*;
|
||||
import dv_utils_pkg::*;
|
||||
import ibex_icache_agent_pkg::*;
|
||||
import ibex_mem_intf_agent_pkg::*;
|
||||
import dv_lib_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
||||
// parameters
|
||||
|
||||
// types
|
||||
typedef dv_base_reg_block ibex_icache_reg_block;
|
||||
|
||||
// functions
|
||||
|
||||
// package sources
|
||||
`include "ibex_icache_env_cfg.sv"
|
||||
`include "ibex_icache_env_cov.sv"
|
||||
`include "ibex_icache_virtual_sequencer.sv"
|
||||
`include "ibex_icache_scoreboard.sv"
|
||||
`include "ibex_icache_env.sv"
|
||||
`include "ibex_icache_vseq_list.sv"
|
||||
|
||||
endpackage
|
67
dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv
vendored
Normal file
67
dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
class ibex_icache_scoreboard extends dv_base_scoreboard #(
|
||||
.CFG_T(ibex_icache_env_cfg),
|
||||
.COV_T(ibex_icache_env_cov)
|
||||
);
|
||||
`uvm_component_utils(ibex_icache_scoreboard)
|
||||
|
||||
// local variables
|
||||
|
||||
// TLM agent fifos
|
||||
uvm_tlm_analysis_fifo #(ibex_icache_item) ibex_icache_fifo;
|
||||
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) ibex_mem_intf_slave_fifo;
|
||||
|
||||
// local queues to hold incoming packets pending comparison
|
||||
ibex_icache_item ibex_icache_q[$];
|
||||
ibex_mem_intf_seq_item ibex_mem_intf_slave_q[$];
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
ibex_icache_fifo = new("ibex_icache_fifo", this);
|
||||
ibex_mem_intf_slave_fifo = new("ibex_mem_intf_slave_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_ibex_icache_fifo();
|
||||
process_ibex_mem_intf_slave_fifo();
|
||||
join_none
|
||||
endtask
|
||||
|
||||
virtual task process_ibex_icache_fifo();
|
||||
ibex_icache_item item;
|
||||
forever begin
|
||||
ibex_icache_fifo.get(item);
|
||||
`uvm_info(`gfn, $sformatf("received ibex_icache item:\n%0s", item.sprint()), UVM_HIGH)
|
||||
end
|
||||
endtask
|
||||
|
||||
virtual task process_ibex_mem_intf_slave_fifo();
|
||||
ibex_mem_intf_seq_item item;
|
||||
forever begin
|
||||
ibex_mem_intf_slave_fifo.get(item);
|
||||
`uvm_info(`gfn, $sformatf("received ibex_mem_intf_seq item:\n%0s", item.sprint()), UVM_HIGH)
|
||||
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
|
16
dv/uvm/icache/dv/env/ibex_icache_virtual_sequencer.sv
vendored
Normal file
16
dv/uvm/icache/dv/env/ibex_icache_virtual_sequencer.sv
vendored
Normal file
|
@ -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 ibex_icache_virtual_sequencer extends dv_base_virtual_sequencer #(
|
||||
.CFG_T(ibex_icache_env_cfg),
|
||||
.COV_T(ibex_icache_env_cov)
|
||||
);
|
||||
`uvm_component_utils(ibex_icache_virtual_sequencer)
|
||||
|
||||
ibex_icache_sequencer ibex_icache_sequencer_h;
|
||||
ibex_mem_intf_slave_sequencer ibex_mem_intf_slave_sequencer_h;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
32
dv/uvm/icache/dv/env/seq_lib/ibex_icache_base_vseq.sv
vendored
Normal file
32
dv/uvm/icache/dv/env/seq_lib/ibex_icache_base_vseq.sv
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_icache_base_vseq extends dv_base_vseq #(
|
||||
.CFG_T (ibex_icache_env_cfg),
|
||||
.COV_T (ibex_icache_env_cov),
|
||||
.VIRTUAL_SEQUENCER_T (ibex_icache_virtual_sequencer)
|
||||
);
|
||||
`uvm_object_utils(ibex_icache_base_vseq)
|
||||
|
||||
// various knobs to enable certain routines
|
||||
bit do_ibex_icache_init = 1'b1;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task dut_init(string reset_kind = "HARD");
|
||||
super.dut_init();
|
||||
if (do_ibex_icache_init) ibex_icache_init();
|
||||
endtask
|
||||
|
||||
virtual task dut_shutdown();
|
||||
// check for pending ibex_icache operations and wait for them to complete
|
||||
// TODO
|
||||
endtask
|
||||
|
||||
// setup basic ibex_icache features
|
||||
virtual task ibex_icache_init();
|
||||
`uvm_error(`gfn, "FIXME")
|
||||
endtask
|
||||
|
||||
endclass : ibex_icache_base_vseq
|
18
dv/uvm/icache/dv/env/seq_lib/ibex_icache_common_vseq.sv
vendored
Normal file
18
dv/uvm/icache/dv/env/seq_lib/ibex_icache_common_vseq.sv
vendored
Normal file
|
@ -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 ibex_icache_common_vseq extends ibex_icache_base_vseq;
|
||||
`uvm_object_utils(ibex_icache_common_vseq)
|
||||
|
||||
constraint num_trans_c {
|
||||
num_trans inside {[1:2]};
|
||||
}
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
// TODO: implement the body of the common virtual sequence
|
||||
endtask : body
|
||||
|
||||
|
||||
endclass
|
15
dv/uvm/icache/dv/env/seq_lib/ibex_icache_sanity_vseq.sv
vendored
Normal file
15
dv/uvm/icache/dv/env/seq_lib/ibex_icache_sanity_vseq.sv
vendored
Normal file
|
@ -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 ibex_icache_sanity_vseq extends ibex_icache_base_vseq;
|
||||
`uvm_object_utils(ibex_icache_sanity_vseq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
task body();
|
||||
`uvm_error(`gfn, "FIXME")
|
||||
endtask : body
|
||||
|
||||
endclass : ibex_icache_sanity_vseq
|
7
dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv
vendored
Normal file
7
dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv
vendored
Normal file
|
@ -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 "ibex_icache_base_vseq.sv"
|
||||
`include "ibex_icache_sanity_vseq.sv"
|
||||
`include "ibex_icache_common_vseq.sv"
|
3
dv/uvm/icache/dv/ibex_icache_agent/README.md
Normal file
3
dv/uvm/icache/dv/ibex_icache_agent/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# IBEX_ICACHE UVM Agent
|
||||
|
||||
IBEX_ICACHE UVM Agent is extended from DV library agent classes.
|
28
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent.core
Normal file
28
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent.core
Normal file
|
@ -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:ibex_icache_agent:0.1"
|
||||
description: "IBEX_ICACHE DV UVM agent"
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_utils
|
||||
- lowrisc:dv:dv_lib
|
||||
files:
|
||||
- ibex_icache_if.sv
|
||||
- ibex_icache_agent_pkg.sv
|
||||
- ibex_icache_item.sv: {is_include_file: true}
|
||||
- ibex_icache_agent_cfg.sv: {is_include_file: true}
|
||||
- ibex_icache_agent_cov.sv: {is_include_file: true}
|
||||
- ibex_icache_driver.sv: {is_include_file: true}
|
||||
- ibex_icache_monitor.sv: {is_include_file: true}
|
||||
- ibex_icache_agent.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_base_seq.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_seq_list.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
25
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent.sv
Normal file
25
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent.sv
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_icache_agent extends dv_base_agent #(
|
||||
.CFG_T (ibex_icache_agent_cfg),
|
||||
.DRIVER_T (ibex_icache_driver),
|
||||
.SEQUENCER_T (ibex_icache_sequencer),
|
||||
.MONITOR_T (ibex_icache_monitor),
|
||||
.COV_T (ibex_icache_agent_cov)
|
||||
);
|
||||
|
||||
`uvm_component_utils(ibex_icache_agent)
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
// get ibex_icache_if handle
|
||||
if (!uvm_config_db#(virtual ibex_icache_if)::get(this, "", "vif", cfg.vif)) begin
|
||||
`uvm_fatal(`gfn, "failed to get ibex_icache_if handle from uvm_config_db")
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
15
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent_cfg.sv
Normal file
15
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent_cfg.sv
Normal file
|
@ -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 ibex_icache_agent_cfg extends dv_base_agent_cfg;
|
||||
|
||||
// interface handle used by driver, monitor & the sequencer, via cfg handle
|
||||
virtual ibex_icache_if vif;
|
||||
|
||||
`uvm_object_utils_begin(ibex_icache_agent_cfg)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
18
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent_cov.sv
Normal file
18
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent_cov.sv
Normal file
|
@ -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 ibex_icache_agent_cov extends dv_base_agent_cov #(ibex_icache_agent_cfg);
|
||||
`uvm_component_utils(ibex_icache_agent_cov)
|
||||
|
||||
// the base class provides the following handles for use:
|
||||
// ibex_icache_agent_cfg: cfg
|
||||
|
||||
// covergroups
|
||||
|
||||
function new(string name, uvm_component parent);
|
||||
super.new(name, parent);
|
||||
// instantiate all covergroups here
|
||||
endfunction : new
|
||||
|
||||
endclass
|
37
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent_pkg.sv
Normal file
37
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_agent_pkg.sv
Normal file
|
@ -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 ibex_icache_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 ibex_icache_item;
|
||||
typedef class ibex_icache_agent_cfg;
|
||||
|
||||
// reuse dv_base_seqeuencer as is with the right parameter set
|
||||
typedef dv_base_sequencer #(.ITEM_T(ibex_icache_item),
|
||||
.CFG_T (ibex_icache_agent_cfg)) ibex_icache_sequencer;
|
||||
|
||||
// functions
|
||||
|
||||
// package sources
|
||||
`include "ibex_icache_item.sv"
|
||||
`include "ibex_icache_agent_cfg.sv"
|
||||
`include "ibex_icache_agent_cov.sv"
|
||||
`include "ibex_icache_driver.sv"
|
||||
`include "ibex_icache_monitor.sv"
|
||||
`include "ibex_icache_agent.sv"
|
||||
`include "ibex_icache_seq_list.sv"
|
||||
|
||||
endpackage: ibex_icache_agent_pkg
|
37
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_driver.sv
Normal file
37
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_driver.sv
Normal file
|
@ -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 ibex_icache_driver extends dv_base_driver #(ibex_icache_item, ibex_icache_agent_cfg);
|
||||
`uvm_component_utils(ibex_icache_driver)
|
||||
|
||||
// the base class provides the following handles for use:
|
||||
// ibex_icache_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
|
11
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_if.sv
Normal file
11
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_if.sv
Normal file
|
@ -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 ibex_icache_if ();
|
||||
|
||||
// interface pins
|
||||
|
||||
// debug signals
|
||||
|
||||
endinterface
|
14
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_item.sv
Normal file
14
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_item.sv
Normal file
|
@ -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 ibex_icache_item extends uvm_sequence_item;
|
||||
|
||||
// random variables
|
||||
|
||||
`uvm_object_utils_begin(ibex_icache_item)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
43
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_monitor.sv
Normal file
43
dv/uvm/icache/dv/ibex_icache_agent/ibex_icache_monitor.sv
Normal file
|
@ -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 ibex_icache_monitor extends dv_base_monitor #(
|
||||
.ITEM_T (ibex_icache_item),
|
||||
.CFG_T (ibex_icache_agent_cfg),
|
||||
.COV_T (ibex_icache_agent_cov)
|
||||
);
|
||||
`uvm_component_utils(ibex_icache_monitor)
|
||||
|
||||
// the base class provides the following handles for use:
|
||||
// ibex_icache_agent_cfg: cfg
|
||||
// ibex_icache_agent_cov: cov
|
||||
// uvm_analysis_port #(ibex_icache_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
|
|
@ -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 ibex_icache_base_seq extends dv_base_seq #(
|
||||
.REQ (ibex_icache_item),
|
||||
.CFG_T (ibex_icache_agent_cfg),
|
||||
.SEQUENCER_T (ibex_icache_sequencer)
|
||||
);
|
||||
`uvm_object_utils(ibex_icache_base_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
`uvm_fatal(`gtn, "Need to override this when you extend from this class!")
|
||||
endtask
|
||||
|
||||
endclass
|
|
@ -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 "ibex_icache_base_seq.sv"
|
25
dv/uvm/icache/dv/ibex_icache_sim.core
Normal file
25
dv/uvm/icache/dv/ibex_icache_sim.core
Normal file
|
@ -0,0 +1,25 @@
|
|||
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:ibex_icache_sim:0.1"
|
||||
description: "IBEX_ICACHE DV sim target"
|
||||
filesets:
|
||||
files_rtl:
|
||||
depend:
|
||||
- lowrisc:ibex:ibex_icache:0.1
|
||||
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:ibex_icache_test
|
||||
files:
|
||||
- tb/tb.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
sim:
|
||||
filesets:
|
||||
- files_rtl
|
||||
- files_dv
|
||||
toplevel: tb
|
||||
default_tool: vcs
|
58
dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson
Normal file
58
dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
// Name of the sim cfg - typically same as the name of the DUT.
|
||||
name: ibex_icache
|
||||
|
||||
// Top level dut name (sv module).
|
||||
dut: ibex_icache
|
||||
|
||||
// Top level testbench name (sv module).
|
||||
tb: tb
|
||||
|
||||
// Simulator used to sign off this block
|
||||
tool: vcs
|
||||
|
||||
// Fusesoc core file used for building the file list.
|
||||
fusesoc_core: lowrisc:dv:ibex_icache_sim:0.1
|
||||
|
||||
// Testplan hjson file.
|
||||
testplan: "{proj_root}/dv/uvm/icache/data/ibex_icache_testplan.hjson"
|
||||
|
||||
|
||||
// Import additional common sim cfg files.
|
||||
// TODO: remove imported cfgs that do not apply.
|
||||
import_cfgs: [// Project wide common sim cfg file
|
||||
"{proj_root}/dv/uvm/data/common_sim_cfg.hjson"]
|
||||
|
||||
// Default iterations for all tests - each test entry can override this.
|
||||
reseed: 50
|
||||
|
||||
gen_ral_pkg_cmd: ""
|
||||
gen_ral_pkg_dir: ""
|
||||
gen_ral_pkg_opts: []
|
||||
|
||||
// Default UVM test and seq class name.
|
||||
uvm_test: ibex_icache_base_test
|
||||
uvm_test_seq: ibex_icache_base_vseq
|
||||
|
||||
// List of test specifications.
|
||||
tests: [
|
||||
{
|
||||
name: ibex_icache_sanity
|
||||
uvm_test_seq: ibex_icache_sanity_vseq
|
||||
}
|
||||
|
||||
// TODO: add more tests here
|
||||
]
|
||||
|
||||
// List of regressions.
|
||||
regressions: [
|
||||
{
|
||||
name: sanity
|
||||
tests: ["ibex_icache_sanity"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
41
dv/uvm/icache/dv/tb/tb.sv
Normal file
41
dv/uvm/icache/dv/tb/tb.sv
Normal file
|
@ -0,0 +1,41 @@
|
|||
// 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 ibex_icache_env_pkg::*;
|
||||
import ibex_icache_test_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
||||
wire clk, rst_n;
|
||||
|
||||
// interfaces
|
||||
clk_rst_if clk_rst_if(.clk(clk), .rst_n(rst_n));
|
||||
ibex_icache_if ibex_icache_if();
|
||||
ibex_mem_intf ibex_mem_intf();
|
||||
|
||||
// dut
|
||||
ibex_icache dut (
|
||||
.clk_i (clk ),
|
||||
.rst_ni (rst_n )
|
||||
|
||||
// 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);
|
||||
uvm_config_db#(virtual ibex_icache_if)::set(null, "*.env.m_ibex_icache_agent*", "vif", ibex_icache_if);
|
||||
uvm_config_db#(virtual ibex_mem_intf)::set(null, "*.env.m_ibex_mem_intf_slave_agent*", "vif", ibex_mem_intf);
|
||||
$timeformat(-12, 0, " ps", 12);
|
||||
run_test();
|
||||
end
|
||||
|
||||
endmodule
|
26
dv/uvm/icache/dv/tests/ibex_icache_base_test.sv
Normal file
26
dv/uvm/icache/dv/tests/ibex_icache_base_test.sv
Normal file
|
@ -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 ibex_icache_base_test extends dv_base_test #(
|
||||
.CFG_T(ibex_icache_env_cfg),
|
||||
.ENV_T(ibex_icache_env)
|
||||
);
|
||||
|
||||
`uvm_component_utils(ibex_icache_base_test)
|
||||
`uvm_component_new
|
||||
|
||||
// the base class dv_base_test creates the following instances:
|
||||
// ibex_icache_env_cfg: cfg
|
||||
// ibex_icache_env: env
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
cfg.has_ral = 1'b0;
|
||||
endfunction
|
||||
|
||||
// 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 : ibex_icache_base_test
|
||||
|
19
dv/uvm/icache/dv/tests/ibex_icache_test.core
Normal file
19
dv/uvm/icache/dv/tests/ibex_icache_test.core
Normal file
|
@ -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:ibex_icache_test:0.1"
|
||||
description: "IBEX_ICACHE DV UVM test"
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:ibex_icache_env
|
||||
files:
|
||||
- ibex_icache_test_pkg.sv
|
||||
- ibex_icache_base_test.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
22
dv/uvm/icache/dv/tests/ibex_icache_test_pkg.sv
Normal file
22
dv/uvm/icache/dv/tests/ibex_icache_test_pkg.sv
Normal file
|
@ -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 ibex_icache_test_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import dv_lib_pkg::*;
|
||||
import ibex_icache_env_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
||||
// local types
|
||||
|
||||
// functions
|
||||
|
||||
// package sources
|
||||
`include "ibex_icache_base_test.sv"
|
||||
|
||||
endpackage
|
20
dv/uvm/icache/dv/top_pkg.core
Normal file
20
dv/uvm/icache/dv/top_pkg.core
Normal file
|
@ -0,0 +1,20 @@
|
|||
CAPI=2:
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# XXX: This name is currently required as global identifier until we have
|
||||
# support for "interfaces" or a similar concept.
|
||||
# Tracked in https://github.com/olofk/fusesoc/issues/235
|
||||
name: "lowrisc:constants:top_pkg"
|
||||
description: "Toplevel-wide constants needed for dv_utils"
|
||||
filesets:
|
||||
files_rtl:
|
||||
files:
|
||||
- top_pkg.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_rtl
|
25
dv/uvm/icache/dv/top_pkg.sv
Normal file
25
dv/uvm/icache/dv/top_pkg.sv
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package top_pkg;
|
||||
|
||||
localparam TL_AW=32;
|
||||
localparam TL_DW=32; // = TL_DBW * 8; TL_DBW must be a power-of-two
|
||||
localparam TL_AIW=8; // a_source, d_source
|
||||
localparam TL_DIW=1; // d_sink
|
||||
localparam TL_DUW=16; // d_user
|
||||
localparam TL_DBW=(TL_DW>>3);
|
||||
localparam TL_SZW=$clog2($clog2(TL_DBW)+1);
|
||||
localparam FLASH_BANKS=2;
|
||||
localparam FLASH_PAGES_PER_BANK=256;
|
||||
localparam FLASH_WORDS_PER_PAGE=256;
|
||||
localparam FLASH_BYTES_PER_WORD=4;
|
||||
localparam FLASH_BKW = $clog2(FLASH_BANKS);
|
||||
localparam FLASH_PGW = $clog2(FLASH_PAGES_PER_BANK);
|
||||
localparam FLASH_WDW = $clog2(FLASH_WORDS_PER_PAGE);
|
||||
localparam FLASH_AW = FLASH_BKW + FLASH_PGW + FLASH_WDW;
|
||||
localparam FLASH_DW = FLASH_BYTES_PER_WORD * 8;
|
||||
|
||||
endpackage
|
|
@ -10,6 +10,7 @@ filesets:
|
|||
depend:
|
||||
- lowrisc:prim:assert
|
||||
- lowrisc:ibex:ibex_pkg
|
||||
- lowrisc:ibex:ibex_icache
|
||||
files:
|
||||
- rtl/ibex_alu.sv
|
||||
- rtl/ibex_compressed_decoder.sv
|
||||
|
@ -19,7 +20,6 @@ filesets:
|
|||
- rtl/ibex_decoder.sv
|
||||
- rtl/ibex_ex_block.sv
|
||||
- rtl/ibex_fetch_fifo.sv
|
||||
- rtl/ibex_icache.sv
|
||||
- rtl/ibex_id_stage.sv
|
||||
- rtl/ibex_if_stage.sv
|
||||
- rtl/ibex_load_store_unit.sv
|
||||
|
|
20
ibex_icache.core
Normal file
20
ibex_icache.core
Normal file
|
@ -0,0 +1,20 @@
|
|||
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:ibex:ibex_icache:0.1"
|
||||
description: "IBEX_ICACHE DV sim target"
|
||||
filesets:
|
||||
files_rtl:
|
||||
depend:
|
||||
- lowrisc:ibex:sim_shared
|
||||
files:
|
||||
- rtl/ibex_icache.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default: &default_target
|
||||
filesets:
|
||||
- files_rtl
|
||||
toplevel: ibex_icache
|
||||
default_tool: vcs
|
15
vendor/lowrisc_common_ifs.lock.hjson
vendored
Normal file
15
vendor/lowrisc_common_ifs.lock.hjson
vendored
Normal file
|
@ -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
|
||||
|
||||
// This file is generated by the util/vendor script. Please do not modify it
|
||||
// manually.
|
||||
|
||||
{
|
||||
upstream:
|
||||
{
|
||||
url: https://github.com/lowRISC/opentitan
|
||||
rev: 0d7f7ac755d4e00811257027dd814edb2afca050
|
||||
only_subdir: hw/dv/sv/common_ifs
|
||||
}
|
||||
}
|
13
vendor/lowrisc_common_ifs.vendor.hjson
vendored
Normal file
13
vendor/lowrisc_common_ifs.vendor.hjson
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
name: "common_ifs",
|
||||
target_dir: "lowrisc_ip/common_ifs",
|
||||
|
||||
upstream: {
|
||||
url: "https://github.com/lowRISC/opentitan"
|
||||
rev: "master"
|
||||
only_subdir: "hw/dv/sv/common_ifs"
|
||||
}
|
||||
}
|
15
vendor/lowrisc_csr_utils.lock.hjson
vendored
Normal file
15
vendor/lowrisc_csr_utils.lock.hjson
vendored
Normal file
|
@ -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
|
||||
|
||||
// This file is generated by the util/vendor script. Please do not modify it
|
||||
// manually.
|
||||
|
||||
{
|
||||
upstream:
|
||||
{
|
||||
url: https://github.com/lowRISC/opentitan
|
||||
rev: 0d7f7ac755d4e00811257027dd814edb2afca050
|
||||
only_subdir: hw/dv/sv/csr_utils
|
||||
}
|
||||
}
|
13
vendor/lowrisc_csr_utils.vendor.hjson
vendored
Normal file
13
vendor/lowrisc_csr_utils.vendor.hjson
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
name: "csr_utils",
|
||||
target_dir: "lowrisc_ip/csr_utils",
|
||||
|
||||
upstream: {
|
||||
url: "https://github.com/lowRISC/opentitan"
|
||||
rev: "master"
|
||||
only_subdir: "hw/dv/sv/csr_utils"
|
||||
}
|
||||
}
|
15
vendor/lowrisc_dv_lib.lock.hjson
vendored
Normal file
15
vendor/lowrisc_dv_lib.lock.hjson
vendored
Normal file
|
@ -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
|
||||
|
||||
// This file is generated by the util/vendor script. Please do not modify it
|
||||
// manually.
|
||||
|
||||
{
|
||||
upstream:
|
||||
{
|
||||
url: https://github.com/lowRISC/opentitan
|
||||
rev: 0d7f7ac755d4e00811257027dd814edb2afca050
|
||||
only_subdir: hw/dv/sv/dv_lib
|
||||
}
|
||||
}
|
13
vendor/lowrisc_dv_lib.vendor.hjson
vendored
Normal file
13
vendor/lowrisc_dv_lib.vendor.hjson
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
name: "dv_lib",
|
||||
target_dir: "lowrisc_ip/dv_lib",
|
||||
|
||||
upstream: {
|
||||
url: "https://github.com/lowRISC/opentitan"
|
||||
rev: "master"
|
||||
only_subdir: "hw/dv/sv/dv_lib"
|
||||
}
|
||||
}
|
15
vendor/lowrisc_dv_utils.lock.hjson
vendored
Normal file
15
vendor/lowrisc_dv_utils.lock.hjson
vendored
Normal file
|
@ -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
|
||||
|
||||
// This file is generated by the util/vendor script. Please do not modify it
|
||||
// manually.
|
||||
|
||||
{
|
||||
upstream:
|
||||
{
|
||||
url: https://github.com/lowRISC/opentitan
|
||||
rev: 0d7f7ac755d4e00811257027dd814edb2afca050
|
||||
only_subdir: hw/dv/sv/dv_utils
|
||||
}
|
||||
}
|
14
vendor/lowrisc_dv_utils.vendor.hjson
vendored
Normal file
14
vendor/lowrisc_dv_utils.vendor.hjson
vendored
Normal file
|
@ -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
|
||||
{
|
||||
name: "dv_utils",
|
||||
target_dir: "lowrisc_ip/dv_utils",
|
||||
patch_dir: "patches/lowrisc_dv_utils",
|
||||
|
||||
upstream: {
|
||||
url: "https://github.com/lowRISC/opentitan"
|
||||
rev: "master"
|
||||
only_subdir: "hw/dv/sv/dv_utils"
|
||||
}
|
||||
}
|
15
vendor/lowrisc_dvsim.lock.hjson
vendored
Normal file
15
vendor/lowrisc_dvsim.lock.hjson
vendored
Normal file
|
@ -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
|
||||
|
||||
// This file is generated by the util/vendor script. Please do not modify it
|
||||
// manually.
|
||||
|
||||
{
|
||||
upstream:
|
||||
{
|
||||
url: https://github.com/lowRISC/opentitan
|
||||
rev: 0d7f7ac755d4e00811257027dd814edb2afca050
|
||||
only_subdir: util/dvsim
|
||||
}
|
||||
}
|
13
vendor/lowrisc_dvsim.vendor.hjson
vendored
Normal file
13
vendor/lowrisc_dvsim.vendor.hjson
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
{
|
||||
name: "dvsim",
|
||||
target_dir: "lowrisc_ip/dvsim",
|
||||
|
||||
upstream: {
|
||||
url: "https://github.com/lowRISC/opentitan"
|
||||
rev: "master"
|
||||
only_subdir: "util/dvsim"
|
||||
}
|
||||
}
|
29
vendor/lowrisc_ip/common_ifs/clk_if.sv
vendored
Normal file
29
vendor/lowrisc_ip/common_ifs/clk_if.sv
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//----------------------------- DESCRIPTION ------------------------------------
|
||||
//
|
||||
// Generic clock interface for clock events in various utilities
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
interface clk_if(input logic clk);
|
||||
|
||||
clocking cb @(posedge clk);
|
||||
endclocking
|
||||
|
||||
clocking cbn @(negedge clk);
|
||||
endclocking
|
||||
|
||||
// Wait for 'n' clocks based of postive clock edge
|
||||
task automatic wait_clks(int num_clks);
|
||||
repeat (num_clks) @cb;
|
||||
endtask
|
||||
|
||||
// Wait for 'n' clocks based of negative clock edge
|
||||
task automatic wait_n_clks(int num_clks);
|
||||
repeat (num_clks) @cbn;
|
||||
endtask
|
||||
|
||||
endinterface
|
224
vendor/lowrisc_ip/common_ifs/clk_rst_if.sv
vendored
Normal file
224
vendor/lowrisc_ip/common_ifs/clk_rst_if.sv
vendored
Normal file
|
@ -0,0 +1,224 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//
|
||||
// Interface: clk_rst_if
|
||||
// Generic clock and reset interface for clock events in various utilities
|
||||
// It also generates o_clk and o_rst_n signals for driving clk and rst_n in the tb. The advantage is
|
||||
// clk and rst_n can be completely controlled in course of the simulation.
|
||||
// This interface provides methods to set freq/period, wait for clk/rst_n, apply rst_n among other
|
||||
// things. See individual method descriptions below.
|
||||
// inout clk
|
||||
// inout rst_n
|
||||
|
||||
interface clk_rst_if #(
|
||||
parameter string IfName = "main"
|
||||
) (
|
||||
inout clk,
|
||||
inout rst_n
|
||||
);
|
||||
|
||||
`ifndef VERILATOR
|
||||
// include macros and import pkgs
|
||||
`include "dv_macros.svh"
|
||||
`include "uvm_macros.svh"
|
||||
import uvm_pkg::*;
|
||||
`endif
|
||||
|
||||
bit drive_clk; // enable clk generation
|
||||
logic o_clk; // output clk
|
||||
|
||||
bit drive_rst_n; // enable rst_n generation
|
||||
logic o_rst_n; // output rst_n
|
||||
|
||||
// clk params
|
||||
bit clk_gate = 1'b0; // clk gate signal
|
||||
int clk_period_ps = 20_000; // 50MHz default
|
||||
int clk_freq_mhz = 50; // 50MHz default
|
||||
int duty_cycle = 50; // 50% default
|
||||
int max_jitter_ps = 1000; // 1ns default
|
||||
bit recompute = 1'b1; // compute half periods when period/freq/duty are changed
|
||||
int clk_hi_ps; // half period hi in ps
|
||||
int clk_lo_ps; // half period lo in ps
|
||||
int jitter_chance_pc = 0; // jitter chance in percentage on clock edge - disabled by default
|
||||
|
||||
// use IfName as a part of msgs to indicate which clk_rst_vif instance
|
||||
string msg_id = {"clk_rst_if::", IfName};
|
||||
|
||||
clocking cb @(posedge clk);
|
||||
endclocking
|
||||
|
||||
clocking cbn @(negedge clk);
|
||||
endclocking
|
||||
|
||||
// Wait for 'n' clocks based of postive clock edge
|
||||
task automatic wait_clks(int num_clks);
|
||||
repeat (num_clks) @cb;
|
||||
endtask
|
||||
|
||||
// Wait for 'n' clocks based of negative clock edge
|
||||
task automatic wait_n_clks(int num_clks);
|
||||
repeat (num_clks) @cbn;
|
||||
endtask
|
||||
|
||||
// wait for rst_n to assert and then deassert
|
||||
task automatic wait_for_reset(bit wait_negedge = 1'b1, bit wait_posedge = 1'b1);
|
||||
if (wait_negedge && ($isunknown(rst_n) || rst_n === 1'b1)) @(negedge rst_n);
|
||||
if (wait_posedge && (rst_n === 1'b0)) @(posedge rst_n);
|
||||
endtask
|
||||
|
||||
// set the clk frequency in mhz
|
||||
function automatic void set_freq_mhz(int freq_mhz);
|
||||
clk_freq_mhz = freq_mhz;
|
||||
clk_period_ps = 1000_000 / clk_freq_mhz;
|
||||
recompute = 1'b1;
|
||||
endfunction
|
||||
|
||||
// call this function at t=0 (from tb top) to enable clk and rst_n to be driven
|
||||
function automatic void set_active(bit drive_clk_val = 1'b1, bit drive_rst_n_val = 1'b1);
|
||||
time t = $time;
|
||||
if (t == 0) begin
|
||||
drive_clk = drive_clk_val;
|
||||
drive_rst_n = drive_rst_n_val;
|
||||
end
|
||||
else begin
|
||||
`ifdef VERILATOR
|
||||
$error({msg_id, "this function can only be called at t=0"});
|
||||
`else
|
||||
`uvm_fatal(msg_id, "this function can only be called at t=0")
|
||||
`endif
|
||||
end
|
||||
endfunction
|
||||
|
||||
// set the clk period in ns
|
||||
function automatic void set_period_ns(int period_ps);
|
||||
clk_period_ps = period_ps;
|
||||
clk_freq_mhz = 1000_000 / clk_period_ps;
|
||||
recompute = 1'b1;
|
||||
endfunction
|
||||
|
||||
// set the duty cycle (1-99)
|
||||
function automatic void set_duty_cycle(int duty);
|
||||
if (!(duty inside {[1:99]})) begin
|
||||
`ifdef VERILATOR
|
||||
$error({msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty)});
|
||||
`else
|
||||
`uvm_fatal(msg_id, $sformatf("duty cycle %0d is not inside [1:99]", duty))
|
||||
`endif
|
||||
end
|
||||
duty_cycle = duty;
|
||||
recompute = 1'b1;
|
||||
endfunction
|
||||
|
||||
// set maximum jitter in ps
|
||||
function automatic void set_max_jitter_ps(int jitter_ps);
|
||||
max_jitter_ps = jitter_ps;
|
||||
endfunction
|
||||
|
||||
// set jitter chance in percentage (0 - 100)
|
||||
// 0 - dont add any jitter; 100 - add jitter on every clock edge
|
||||
function automatic void set_jitter_chance_pc(int jitter_chance);
|
||||
if (!(jitter_chance inside {[0:100]})) begin
|
||||
`ifdef VERILATOR
|
||||
$error({msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance)});
|
||||
`else
|
||||
`uvm_fatal(msg_id, $sformatf("jitter_chance %0d is not inside [0:100]", jitter_chance))
|
||||
`endif
|
||||
end
|
||||
jitter_chance_pc = jitter_chance;
|
||||
endfunction
|
||||
|
||||
// start / ungate the clk
|
||||
task automatic start_clk(bit wait_for_posedge = 1'b0);
|
||||
clk_gate = 1'b0;
|
||||
if (wait_for_posedge) wait_clks(1);
|
||||
endtask
|
||||
|
||||
// stop / gate the clk
|
||||
function automatic void stop_clk();
|
||||
clk_gate = 1'b1;
|
||||
endfunction
|
||||
|
||||
// add jitter to clk_hi and clk_lo half periods based on jitter_chance_pc
|
||||
function automatic void add_jitter();
|
||||
int jitter_ps;
|
||||
if ($urandom_range(1, 100) <= jitter_chance_pc) begin
|
||||
`ifndef VERILATOR
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
|
||||
jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
|
||||
`endif
|
||||
clk_hi_ps += jitter_ps;
|
||||
end
|
||||
if ($urandom_range(1, 100) <= jitter_chance_pc) begin
|
||||
`ifndef VERILATOR
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(jitter_ps,
|
||||
jitter_ps inside {[-1*max_jitter_ps:max_jitter_ps]};, "", msg_id)
|
||||
`endif
|
||||
clk_lo_ps += jitter_ps;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// apply reset with specified scheme
|
||||
// TODO make this enum?
|
||||
// rst_n_scheme
|
||||
// 0 - fullly synchronous reset - it is asserted and deasserted on clock edges
|
||||
// 1 - async assert, sync dessert (default)
|
||||
// 2 - async assert, async dessert
|
||||
// 3 - clk gated when reset asserted
|
||||
// Note: for power on reset, please ensure pre_reset_dly_clks is set to 0
|
||||
task automatic apply_reset(int pre_reset_dly_clks = 0,
|
||||
int reset_width_clks = $urandom_range(4, 20),
|
||||
int post_reset_dly_clks = 0,
|
||||
int rst_n_scheme = 1);
|
||||
int dly_ps;
|
||||
dly_ps = $urandom_range(0, clk_period_ps);
|
||||
wait_clks(pre_reset_dly_clks);
|
||||
case (rst_n_scheme)
|
||||
0: begin : sync_assert_deassert
|
||||
o_rst_n <= 1'b0;
|
||||
wait_clks(reset_width_clks);
|
||||
o_rst_n <= 1'b1;
|
||||
end
|
||||
1: begin : async_assert_sync_deassert
|
||||
#(dly_ps * 1ps);
|
||||
o_rst_n <= 1'b0;
|
||||
wait_clks(reset_width_clks);
|
||||
o_rst_n <= 1'b1;
|
||||
end
|
||||
2: begin : async_assert_async_deassert
|
||||
#(dly_ps * 1ps);
|
||||
o_rst_n <= 1'b0;
|
||||
wait_clks(reset_width_clks);
|
||||
dly_ps = $urandom_range(0, clk_period_ps);
|
||||
#(dly_ps * 1ps);
|
||||
o_rst_n <= 1'b1;
|
||||
end
|
||||
endcase
|
||||
wait_clks(post_reset_dly_clks);
|
||||
endtask
|
||||
|
||||
// clk gen
|
||||
initial begin
|
||||
// start driving clk only after the first por reset assertion
|
||||
wait_for_reset(.wait_posedge(1'b0));
|
||||
#1ps o_clk = 1'b0;
|
||||
forever begin
|
||||
if (recompute) begin
|
||||
clk_hi_ps = clk_period_ps * duty_cycle / 100;
|
||||
clk_lo_ps = clk_period_ps - clk_hi_ps;
|
||||
recompute = 1'b0;
|
||||
end
|
||||
if (jitter_chance_pc != 0) add_jitter();
|
||||
#(clk_lo_ps * 1ps);
|
||||
// wiggle output clk if not gated
|
||||
if (!clk_gate) o_clk = 1'b1;
|
||||
#(clk_hi_ps * 1ps);
|
||||
o_clk = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
assign clk = drive_clk ? o_clk : 1'bz;
|
||||
assign rst_n = drive_rst_n ? o_rst_n : 1'bz;
|
||||
|
||||
endinterface
|
20
vendor/lowrisc_ip/common_ifs/common_ifs.core
vendored
Normal file
20
vendor/lowrisc_ip/common_ifs/common_ifs.core
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
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:common_ifs"
|
||||
description: "Common interfaces used in DV"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:pins_if
|
||||
files:
|
||||
- clk_if.sv
|
||||
- clk_rst_if.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
81
vendor/lowrisc_ip/common_ifs/index.md
vendored
Normal file
81
vendor/lowrisc_ip/common_ifs/index.md
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
# Common interfaces
|
||||
|
||||
|
||||
## Overview
|
||||
In this directory, we provide commonly used interfaces used to construct
|
||||
testbenches for DV. These interfaces are instantiated inside `tb` module for
|
||||
connecting dut signals. They are described in detail below.
|
||||
|
||||
### `clk_if`
|
||||
This is a passive clock interface that is used to wait for clock events in
|
||||
testbenches.
|
||||
This interface has two clocking blocks: `cb` and `cbn` for synchronizing to
|
||||
positive and negative clock edges. This interface consists of following tasks:
|
||||
* `wait_clks`: waits for specified number of positive clock edges
|
||||
* `wait_n_clks`: waits for specified number of negative clock edges
|
||||
|
||||
### `clk_rst_if`
|
||||
This interface provides the ability to drive / sample clock and reset signal.
|
||||
It provides various methods related to clock and reset generation. These
|
||||
methods can be categorized into `setup methods` and `drive / sample` methods.
|
||||
Following are `setup methods` of `pins_if`:
|
||||
* `set_freq_mhz`: set the clk frequency in mhz and calclate period in ns
|
||||
* `set_duty_cycle`: set the duty cycle (1-99)
|
||||
* `set_active`: enables `clk` and `rst_n` generation
|
||||
typically, called at t=0 (from tb top)
|
||||
* `set_period_ns`: set the clk period in ns and calculate frequency in mhz
|
||||
* `set_jitter_chance_pc`: set jitter chance in percentage (0 - 100)
|
||||
* 0: do not add any jitter
|
||||
* 100: add jitter on every clock edge
|
||||
* `set_max_jitter_ps`: set maximum jitter in ps
|
||||
Following are `drive / sample` methods of `pins_if`:
|
||||
* `wait_for_reset`: wait for rst_n to assert and then deassert
|
||||
* `apply_reset`: apply reset with specified scheme out of following:
|
||||
* fullly synchronous reset
|
||||
* async assert, sync dessert
|
||||
* async assert, async dessert
|
||||
* clk gated when reset asserted
|
||||
* `add_jitter`: add jitter to `clk_hi` and `clk_lo` half periods based on
|
||||
`jitter_chance_pc`
|
||||
* `start_clk`: start / ungate clock
|
||||
* `stop_clk`: stop / gate the clk
|
||||
* `wait_clks`: waits for specified number of positive clock edges
|
||||
* `wait_n_clks`: waits for specified number of negative clock edges
|
||||
|
||||
### `pins_if`
|
||||
This paramterized interface provides the ability to drive / sample any signal
|
||||
in the DUT.
|
||||
```systemverilog
|
||||
interface pins_if #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
inout [Width-1:0] pins
|
||||
);
|
||||
```
|
||||
The member `pins` is inout type and it can be connected to any of input or
|
||||
output port within of dut to drive or sample them. `pins` can be driven either
|
||||
internally using `pins_o` and `pins_oe` signals, that constitute a tri-state
|
||||
buffer implementation. This provide an ability to disconnects `pins` by driving
|
||||
them to high impedance state. `pins` may also be driven through an external
|
||||
driver that it gets connected to. This interface also provides capability
|
||||
to drive weak pull-up or pull-down on `pins` in case of no internal or external
|
||||
drivers. The members `pins_pu` and `pins_pd` control weak pull-up or pull-down
|
||||
functionality. Following diagram explains working of `pins_if`:
|
||||
|
||||
## `pins_if` block diagram
|
||||

|
||||
|
||||
Some of the commonly used methods of `pins_if` are as follows:
|
||||
* `drive_en_pin`: Drive specified value `val` on specified index `idx` of
|
||||
`pins_oe` signal
|
||||
* `drive_en`: Drive `pins_oe` signal to specified value `val`
|
||||
* `drive_pin`: Drive specified index `idx` of pins_oe signal to 1, and the same
|
||||
index of `pins_o` to specified value `val`
|
||||
value
|
||||
* `drive`: Drive `pins_oe` to all 1's and specified value `val` on `pins_o`
|
||||
* `sample_pin`: Sample and return value of `pins[idx]` for specified index `idx`
|
||||
* `sample`: Sample and return value of `pins`
|
||||
* `set_pullup_en`: Implement pull-up on specific bits of `pins` based on
|
||||
specified value `val`
|
||||
* `set_pulldown_en`: Implement pull-down on specifc bits of `pins` based on
|
||||
specified value `val`
|
96
vendor/lowrisc_ip/common_ifs/pins_if.sv
vendored
Normal file
96
vendor/lowrisc_ip/common_ifs/pins_if.sv
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Interface: pins_if
|
||||
// Description: Pin interface for driving and sampling individual pins such as interrupts, alerts
|
||||
// and gpios.
|
||||
`ifndef SYNTHESIS
|
||||
|
||||
interface pins_if #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
inout [Width-1:0] pins
|
||||
);
|
||||
|
||||
|
||||
logic [Width-1:0] pins_o; // value to be driven out
|
||||
wire [Width-1:0] pins_int; // value of pin using internal pull-up / pull-down
|
||||
bit [Width-1:0] pins_oe = '0; // output enable
|
||||
bit [Width-1:0] pins_pd = '0; // pull down enable
|
||||
bit [Width-1:0] pins_pu = '0; // pull up enable
|
||||
|
||||
// function to set pin output enable for specific pin (useful for single pin interface)
|
||||
function automatic void drive_en_pin(int idx = 0, bit val);
|
||||
pins_oe[idx] = val;
|
||||
endfunction
|
||||
|
||||
// function to set pin output enable for all pins
|
||||
function automatic void drive_en(bit [Width-1:0] val);
|
||||
pins_oe = val;
|
||||
endfunction
|
||||
|
||||
// function to drive a specific pin with a value (useful for single pin interface)
|
||||
function automatic void drive_pin(int idx = 0, logic val);
|
||||
pins_oe[idx] = 1'b1;
|
||||
pins_o[idx] = val;
|
||||
endfunction // drive_pin
|
||||
|
||||
// function to drive all pins
|
||||
function automatic void drive(logic [Width-1:0] val);
|
||||
pins_oe = {Width{1'b1}};
|
||||
pins_o = val;
|
||||
endfunction // drive
|
||||
|
||||
// function to drive all pull down values
|
||||
function automatic void set_pulldown_en(bit [Width-1:0] val);
|
||||
pins_pd = val;
|
||||
endfunction // set_pulldown_en
|
||||
|
||||
// function to drive all pull up values
|
||||
function automatic void set_pullup_en(bit [Width-1:0] val);
|
||||
pins_pu = val;
|
||||
endfunction // set_pullup_en
|
||||
|
||||
// function to drive the pull down value on a specific pin
|
||||
function automatic void set_pulldown_en_pin(int idx = 0, bit val);
|
||||
pins_pd[idx] = val;
|
||||
endfunction // set_pulldown_en_pin
|
||||
|
||||
// function to drive the pull up value on a specific pin
|
||||
function automatic void set_pullup_en_pin(int idx = 0, bit val);
|
||||
pins_pu[idx] = val;
|
||||
endfunction // set_pullup_en_pin
|
||||
|
||||
// function to sample a specific pin (useful for single pin interface)
|
||||
function automatic logic sample_pin(int idx = 0);
|
||||
return pins[idx];
|
||||
endfunction
|
||||
|
||||
// function to sample all pins
|
||||
function automatic logic [Width-1:0] sample();
|
||||
return pins;
|
||||
endfunction
|
||||
|
||||
// make connections
|
||||
generate
|
||||
for (genvar i = 0; i < Width; i++) begin : each_pin
|
||||
assign pins_int[i] = pins_pd[i] ? 1'b0 :
|
||||
pins_pu[i] ? 1'b1 : 1'bz;
|
||||
// If output enable is 1, strong driver assigns pin to 'value to be driven out';
|
||||
// the external strong driver can still affect pin, if exists.
|
||||
// Else if output enable is 0, weak pullup or pulldown is applied to pin.
|
||||
// By doing this, we make sure that weak pullup or pulldown does not override
|
||||
// any 'x' value on pin, that may result due to conflicting values
|
||||
// between 'value to be driven out' and the external driver's value.
|
||||
assign pins[i] = pins_oe[i] ? pins_o[i] : 1'bz;
|
||||
`ifdef VERILATOR
|
||||
assign pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
|
||||
`else
|
||||
assign (pull0, pull1) pins[i] = ~pins_oe[i] ? pins_int[i] : 1'bz;
|
||||
`endif
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endinterface
|
||||
`endif
|
1
vendor/lowrisc_ip/common_ifs/pins_if.svg
vendored
Normal file
1
vendor/lowrisc_ip/common_ifs/pins_if.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 56 KiB |
17
vendor/lowrisc_ip/common_ifs/pins_ifs.core
vendored
Normal file
17
vendor/lowrisc_ip/common_ifs/pins_ifs.core
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
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:pins_if"
|
||||
description: "Common interfaces used in DV"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
files:
|
||||
- pins_if.sv
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
159
vendor/lowrisc_ip/csr_utils/README.md
vendored
Normal file
159
vendor/lowrisc_ip/csr_utils/README.md
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
# CSR utilities
|
||||
|
||||
|
||||
This csr_utils folder intends to implement CSR related methods and test sequences for DV
|
||||
to share across all testbenches.
|
||||
|
||||
### CSR utility package
|
||||
`csr_utils_pkg` provides common methods and properties to support and manage CSR accesses
|
||||
and CSR related test sequences.
|
||||
|
||||
#### Global types and variables
|
||||
All common types and variables are defined at this package level. Examples are:
|
||||
```systemverilog
|
||||
uint outstanding_accesses = 0;
|
||||
uint default_timeout_ns = 1_000_000;
|
||||
```
|
||||
|
||||
##### Outstanding_accesses
|
||||
`csr_utils_pkg` used an internal variable to store the number of accesses
|
||||
(read or write) that have not yet completed. This variable is shared among all methods of
|
||||
register reading and writing. Directly accessing this variable is discouraged. Instead,
|
||||
the following methods are used to control this variable to keep track of non-blocking
|
||||
accesses made in the testbench:
|
||||
```systemverilog
|
||||
function automatic void increment_outstanding_access();
|
||||
outstanding_accesses++;
|
||||
endfunction
|
||||
|
||||
function automatic void decrement_outstanding_access();
|
||||
outstanding_accesses--;
|
||||
endfunction
|
||||
|
||||
task automatic wait_no_outstanding_access();
|
||||
wait(outstanding_accesses == 0);
|
||||
endtask
|
||||
|
||||
function automatic void clear_outstanding_access();
|
||||
outstanding_accesses = 0;
|
||||
endfunction
|
||||
```
|
||||
|
||||
##### CSR spinwait
|
||||
One of the commonly used tasks in `csr_utils_pkg` is `csr_spinwait`. This task
|
||||
can poll a CSR or CSR field continuously or periodically until it reads out the
|
||||
expected value. This task also has a timeout check in case due to DUT or testbench
|
||||
issue, the CSR or CSR field never returns the expected value.
|
||||
Example below uses the `csr_spinwait` to wait until the CSR `fifo_status` field
|
||||
`fifo_full` reaches value bit 1:
|
||||
```systemverilog
|
||||
csr_spinwait(.ptr(ral.status.fifo_full), .exp_data(1'b0));
|
||||
```
|
||||
|
||||
##### Read and check all CSRs
|
||||
The purpose of the `read_and_check_all_csrs` task is to read all valid CSRs from
|
||||
the given `uvm_reg_block` and check against their expected values from RAL. This
|
||||
task is primarily implemented to use after reset, to make sure all the CSRs are
|
||||
being reset to the default value.
|
||||
|
||||
##### Under_reset
|
||||
Due to `csr_utils_pkg` is not connected to any interface, methods inside
|
||||
this package are not able to get reset information. Current the `under_reset`
|
||||
bit is declared with two functions:
|
||||
```systemverilog
|
||||
function automatic void reset_asserted();
|
||||
under_reset = 1;
|
||||
endfunction
|
||||
|
||||
function automatic void reset_deasserted();
|
||||
under_reset = 0;
|
||||
endfunction
|
||||
```
|
||||
This reset information is updated in `dv_lib/dv_base_vseq.sv`. When the
|
||||
`apply_reset` task is triggered, it will set and reset the `under_reset` bit
|
||||
via the functions above.
|
||||
|
||||
#### Global CSR util methods
|
||||
##### Global methods for CSR and MEM attributes
|
||||
This package provides methods to access CSR or Memory attributes, such as address,
|
||||
value, etc. Examples are:
|
||||
* `get_csr_addrs`
|
||||
* `get_mem_addr_ranges`
|
||||
* `decode_csr_or_field`
|
||||
|
||||
##### Global methods for CSR access
|
||||
The CSR access methods are based on `uvm_reg` methods, such as `uvm_reg::read()`,
|
||||
`uvm_reg::write()`, `uvm_reg::update()`. For all CSR methods, user can
|
||||
pass either a register or a field handle. Examples are:
|
||||
* `csr_rd_check`: Given the uvm_reg or uvm_reg_field object, this method will
|
||||
compare the CSR value with the expected value (given as an input) or with
|
||||
the RAL mirrored value
|
||||
* `csr_update`: Given the uvm_reg object, this method will update the value of the
|
||||
register in DUT to match the desired value
|
||||
|
||||
To enhance the usability, these methods support CSR blocking, non-blocking
|
||||
read/write, and a timeout checking.
|
||||
* A blocking thread will not execute the next sequence until the current CSR
|
||||
access is finished
|
||||
* A non-blocking thread allows multiple CSR accesses to be issued back-to-back
|
||||
without waiting for the response
|
||||
* A timeout check will discard the ongoing CSR access by disabling the forked
|
||||
thread and will throw a UVM_ERROR once the process exceeds the max timeout setting
|
||||
|
||||
### CSR sequence library
|
||||
`csr_seq_lib.sv` provides common CSR related test sequences to share across all testbenches.
|
||||
These test sequences are based off the standard sequences provided in UVM1.2 RAL.
|
||||
The parent class (DUT-specific test or sequence class) that creates them needs to provide them
|
||||
with the DUT RAL model. The list of CSRs are then extracted from the RAL model to performs the checks.
|
||||
In addition, the test sequences provide an ability to exclude a CSR from writes or reads (or both)
|
||||
depending on the behavior of the CSR in the design. This is explained more in the
|
||||
[CSR exclusion methodology](#csr-exclusion-methodology) section below.
|
||||
All CSR accesses in these sequences are made non-blocking to ensure back-to-back scenarios
|
||||
are exercised.
|
||||
Supported CSR test sequences are:
|
||||
* `csr_hw_reset`: Write all CSRs with random values and then reset the DUT.
|
||||
After reset, read all CSRs and compare with expected values
|
||||
* `csr_rw`: Write a randomly selected CSRs, then read out the updated
|
||||
CSR or CSR field and compare with expected value
|
||||
* `csr_bit_bash`: Randomly select a CSR and write 1's and 0's to
|
||||
every bit, then read the CSR to compare with expected value
|
||||
* `csr_aliasing`: Randomly write a CSR, then read all CSRs to
|
||||
verify that only the CSR that was written was updated
|
||||
* `mem_walk`: Write and read all valid addresses in the memory. Compare
|
||||
the read results with the expected values
|
||||
|
||||
### CSR exclusion methodology
|
||||
The CSR test sequences listed above intend to perform a sanity check to CSR
|
||||
read/write accesses, but do not intend to check specific DUT functionalities. Thus the
|
||||
sequences might need to exclude reading or writing certain CSRs depending on the
|
||||
specific testbench.
|
||||
`csr_excl_item` is a class that supports adding exclusions to CSR test sequences.
|
||||
Examples of useful functions in this class are:
|
||||
* `add_excl`: Add exclusions to the CSR test sequences. This function has two inputs:
|
||||
- Exclusion scope: A hierarchical path name at all levels including block,
|
||||
CSR, and field. This input supports * and ? wildcards for glob style matching
|
||||
- CSR_exclude type: An enumeration defined as below:
|
||||
```systemverilog
|
||||
typedef enum bit[2:0] {
|
||||
CsrNoExcl = 3'b000, // no exclusions
|
||||
CsrExclInitCheck = 3'b001, // exclude csr from init val check
|
||||
CsrExclWriteCheck = 3'b010, // exclude csr from write-read check
|
||||
CsrExclCheck = 3'b011, // exclude csr from init or write-read check
|
||||
CsrExclWrite = 3'b100, // exclude csr from write
|
||||
CsrExclAll = 3'b111 // exclude csr from init or write or writ-read check
|
||||
} csr_excl_type_e;
|
||||
```
|
||||
|
||||
One example to use this function in HMAC to exclude all CSRs or fields with
|
||||
names starting with "key":
|
||||
```systemverilog
|
||||
csr_excl.add_excl({scope, ".", "key?"}, CsrExclWrite);
|
||||
```
|
||||
|
||||
* `has_excl`: Check if the CSR has a match in the existing exclusions loopup,
|
||||
and is not intended to use externally
|
||||
|
||||
### CSR sequence framework
|
||||
The [cip_lib]({{< relref "hw/dv/sv/cip_lib/doc" >}}) includes a virtual sequence named `cip_base_vseq`,
|
||||
that provides a common framework for all testbenchs to run these CSR test sequences and
|
||||
add exclusions.
|
111
vendor/lowrisc_ip/csr_utils/csr_excl_item.sv
vendored
Normal file
111
vendor/lowrisc_ip/csr_utils/csr_excl_item.sv
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Class: csr_excl_item
|
||||
// Description: CSR exclusion item that holds exclusions applied for a given set of blocks /
|
||||
// registers / fields provided and maintained as strings.
|
||||
class csr_excl_item extends uvm_object;
|
||||
`uvm_object_utils(csr_excl_item)
|
||||
|
||||
typedef struct {
|
||||
int csr_test_type;
|
||||
csr_excl_type_e csr_excl_type;
|
||||
} csr_excl_s;
|
||||
local csr_excl_s exclusions[string];
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
// add exclusion for an individual block, csr or field
|
||||
// arg obj: this is the hierarchical path name to the block, csr or field - passing * and ?
|
||||
// wildcards for glob style matching is allowed. User needs to take care that wildcards does not
|
||||
// end up inadvertently matching more that what was desired. Examples:
|
||||
// To exclude ral.ctrl.tx field from writes, obj can be "ral.ctrl.tx" or "*.ctrl.tx"; passing
|
||||
// "*.tx" might be too generic
|
||||
virtual function void add_excl(string obj,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type = CsrAllTests);
|
||||
bit [2:0] val = CsrNoExcl;
|
||||
bit [NUM_CSR_TESTS-1:0] test = CsrInvalidTest;
|
||||
csr_excl_s csr_excl_item;
|
||||
if (csr_test_type == CsrInvalidTest) begin
|
||||
`uvm_fatal(`gfn, $sformatf("add %s exclusion without a test", obj))
|
||||
end
|
||||
val = csr_excl_type | exclusions[obj].csr_excl_type;
|
||||
test = csr_test_type | exclusions[obj].csr_test_type;
|
||||
exclusions[obj].csr_excl_type = csr_excl_type_e'(val);
|
||||
exclusions[obj].csr_test_type = test;
|
||||
endfunction
|
||||
|
||||
// function to check if given blk / csr or field AND its parent has been excluded with the
|
||||
// supplied exclusion type
|
||||
// arg uvm_object obj: given blk, csr or field
|
||||
// arg csr_excl_type_e csr_excl_type: exclusion type
|
||||
function bit is_excl(uvm_object obj,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type);
|
||||
uvm_reg_block blk;
|
||||
uvm_reg csr;
|
||||
|
||||
// if supplied obj is a uvm_reg_block or uvm_reg, then its parent is a uvm_reg_block
|
||||
// check if obj's parent is excluded
|
||||
if ($cast(blk, obj)) begin
|
||||
if (blk.get_parent() != null) begin
|
||||
blk = blk.get_parent();
|
||||
if (has_excl(blk.`gfn, csr_excl_type, csr_test_type)) return 1'b1;
|
||||
end
|
||||
end
|
||||
if ($cast(csr, obj)) begin
|
||||
blk = csr.get_parent();
|
||||
if (has_excl(blk.`gfn, csr_excl_type, csr_test_type)) return 1'b1;
|
||||
end
|
||||
// TODO: check if any parent in the hierarchy above is excluded
|
||||
// check if obj is excluded
|
||||
return (has_excl(obj.`gfn, csr_excl_type, csr_test_type));
|
||||
endfunction
|
||||
|
||||
// check if applied string obj has a match in existing exclusions lookup in defined csr_test_type
|
||||
// function is to not be called externally
|
||||
local function bit has_excl(string obj,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type);
|
||||
// check if obj exists verbatim
|
||||
if (exclusions.exists(obj)) begin
|
||||
`uvm_info(`gfn, $sformatf("has_excl: found exact excl match for %0s: %0s",
|
||||
obj, exclusions[obj].csr_excl_type.name()), UVM_DEBUG)
|
||||
// check if bit(s) corresponding to csr_excl_type are set in defined csr_test_type
|
||||
if ((exclusions[obj].csr_test_type & csr_test_type) != CsrInvalidTest) begin
|
||||
if ((exclusions[obj].csr_excl_type & csr_excl_type) != CsrNoExcl) return 1'b1;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// attempt glob style matching
|
||||
foreach (exclusions[str]) begin
|
||||
if (!uvm_re_match(str, obj)) begin
|
||||
`uvm_info(`gfn, $sformatf("has_excl: found glob excl match for %0s(%0s): %0s",
|
||||
obj, str, exclusions[str].csr_excl_type.name()), UVM_DEBUG)
|
||||
// check if bit(s) corresponding to csr_excl_type are set in defined csr_test_type
|
||||
if ((exclusions[str].csr_test_type & csr_test_type) != CsrInvalidTest) begin
|
||||
if ((exclusions[str].csr_excl_type & csr_excl_type) != CsrNoExcl) return 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return 1'b0;
|
||||
endfunction
|
||||
|
||||
// print all exclusions for ease of debug (call this ideally after adding all exclusions)
|
||||
virtual function void print_exclusions(uvm_verbosity verbosity = UVM_HIGH);
|
||||
string test_names;
|
||||
for (int i = 0; i < NUM_CSR_TESTS; i++) begin
|
||||
csr_test_type_e csr_test = csr_test_type_e'(1 << i);
|
||||
test_names = {test_names, csr_test.name(), " "};
|
||||
end
|
||||
foreach (exclusions[item]) begin
|
||||
`uvm_info(`gfn, $sformatf("CSR/field [%0s] excluded with %0s in csr_tests: {%s}={%0b}",
|
||||
item, exclusions[item].csr_excl_type.name(), test_names,
|
||||
exclusions[item].csr_test_type), verbosity)
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
477
vendor/lowrisc_ip/csr_utils/csr_seq_lib.sv
vendored
Normal file
477
vendor/lowrisc_ip/csr_utils/csr_seq_lib.sv
vendored
Normal file
|
@ -0,0 +1,477 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// CSR suite of sequences that do writes and reads to csrs
|
||||
// includes hw_reset, rw, bit_bash and aliasing tests for csrs, and mem_walk for uvm_mems
|
||||
// TODO: when mem backdoor is implemented, add uvm_mem_access_seq for backdoor rd
|
||||
// The sequences perform csr writes and reads and follow the standard csr test suite. If external
|
||||
// checker is enabled, then the external entity is required to update the mirrored value on
|
||||
// writes. If not enabled, the sequences themselves call predict function to update the mirrored
|
||||
// value. Consequently, the read values are checked against the mirrored value and not the
|
||||
// previously written value. This approach is better since it takes care of special
|
||||
// register and field access policies. Also, we use csr_rd_check task instead of csr_mirror to take
|
||||
// field exclusions into account.
|
||||
//
|
||||
// Csrs to be tested is accumulated and shuffled from the supplied reg models.
|
||||
// What / how many csrs to test can be further controlled in 3 ways -
|
||||
// 1. Externally add specific csrs to test_csrs queue (highest prio)
|
||||
// 2. Set num_test_csrs test a randomly picked set of csrs from the supplied models
|
||||
// 3. Set / pass via plusarg, num_csr_chunks / test_csr_chunk
|
||||
//
|
||||
// Exclusions are to be provided using the csr_excl_item item (see class for more details).
|
||||
class csr_base_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));
|
||||
`uvm_object_utils(csr_base_seq)
|
||||
|
||||
uvm_reg_block models[$];
|
||||
uvm_reg all_csrs[$];
|
||||
uvm_reg test_csrs[$];
|
||||
csr_excl_item m_csr_excl_item;
|
||||
|
||||
// By default, assume external checker (example, scoreboard) is turned off. If that is the case,
|
||||
// then writes are followed by call to predict function to update the mirrored value. Reads are
|
||||
// then checked against the mirrored value using csr_rd_check task. If external checker is
|
||||
// enabled, then we let the external checker do the predict and compare.
|
||||
// In either case, we should be able to do completely non-blocking writes and reads.
|
||||
bit external_checker = 1'b0;
|
||||
|
||||
// either use num_test_csrs or {test_csr_chunk, num_csr_chunks} to test slice of all csrs
|
||||
int num_test_csrs = 0;
|
||||
int test_csr_chunk = 1;
|
||||
int num_csr_chunks = 1;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
// pre_start
|
||||
virtual task pre_start();
|
||||
super.pre_start();
|
||||
|
||||
// create test_csrs list only if its empty
|
||||
if (test_csrs.size() == 0) set_csr_test_range();
|
||||
|
||||
// create dummy m_csr_excl_item if not supplied
|
||||
if (m_csr_excl_item == null) begin
|
||||
`uvm_info(`gtn, "m_csr_excl_item is null, creating a dummy one locally", UVM_LOW)
|
||||
m_csr_excl_item = csr_excl_item::type_id::create("m_csr_excl_item");
|
||||
end
|
||||
endtask
|
||||
|
||||
// post_start
|
||||
virtual task post_start();
|
||||
super.post_start();
|
||||
wait_no_outstanding_access();
|
||||
test_csrs.delete();
|
||||
endtask
|
||||
|
||||
function void set_csr_excl_item(csr_excl_item item);
|
||||
this.m_csr_excl_item = item;
|
||||
endfunction
|
||||
|
||||
// extract csrs and split and prune to a specified test_csr_chunk
|
||||
virtual function void set_csr_test_range();
|
||||
int start_idx;
|
||||
int end_idx;
|
||||
int chunk_size;
|
||||
|
||||
// extract all csrs from the model
|
||||
// TODO: add and use function here instead that allows pre filtering csrs
|
||||
all_csrs.delete();
|
||||
foreach (models[i]) begin
|
||||
models[i].get_registers(all_csrs);
|
||||
end
|
||||
|
||||
if (num_test_csrs != 0) begin
|
||||
num_csr_chunks = all_csrs.size / num_test_csrs + 1;
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(test_csr_chunk,
|
||||
test_csr_chunk inside {[1:num_csr_chunks]};)
|
||||
end
|
||||
else begin
|
||||
// extract test_csr_chunk, num_csr_chunks from plusargs
|
||||
void'($value$plusargs("test_csr_chunk=%0d", test_csr_chunk));
|
||||
void'($value$plusargs("num_csr_chunks=%0d", num_csr_chunks));
|
||||
end
|
||||
|
||||
if (!(test_csr_chunk inside {[1:num_csr_chunks]})) begin
|
||||
`uvm_fatal(`gtn, $sformatf({{"invalid opt +test_csr_chunk=%0d, +num_csr_chunks=%0d "},
|
||||
{"(1 <= test_csr_chunk <= num_csr_chunks)"}},
|
||||
test_csr_chunk, num_csr_chunks))
|
||||
end
|
||||
chunk_size = (num_test_csrs != 0) ? num_test_csrs : (all_csrs.size / num_csr_chunks + 1);
|
||||
start_idx = (test_csr_chunk - 1) * chunk_size;
|
||||
end_idx = test_csr_chunk * chunk_size;
|
||||
if (end_idx >= all_csrs.size())
|
||||
end_idx = all_csrs.size() - 1;
|
||||
|
||||
test_csrs = all_csrs[start_idx:end_idx];
|
||||
`uvm_info(`gtn, $sformatf("testing %0d csrs [%0d - %0d] in all supplied models",
|
||||
test_csrs.size(), start_idx, end_idx), UVM_MEDIUM)
|
||||
foreach (test_csrs[i]) begin
|
||||
`uvm_info(`gtn, $sformatf("test_csrs list: %0s, reset: 0x%0x", test_csrs[i].get_full_name(),
|
||||
test_csrs[i].get_mirrored_value()), UVM_HIGH)
|
||||
end
|
||||
test_csrs.shuffle();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_hw_reset_seq
|
||||
// Brief Description: This sequence reads all CSRs and checks it against the reset value provided
|
||||
// in the RAL specification. Note that this does not sufficiently qualify as the CSR HW reset test.
|
||||
// The 'full' CSR HW reset test is constructed externally by running the csr_write_seq below first,
|
||||
// issuing reset and only then running this sequence.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_hw_reset_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_hw_reset_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
foreach (test_csrs[i]) begin
|
||||
uvm_reg_data_t compare_mask;
|
||||
|
||||
// check if parent block or register is excluded from init check
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclInitCheck, CsrHwResetTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclInitCheck exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Verifying reset value of register %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclInitCheck, CsrHwResetTest,
|
||||
m_csr_excl_item);
|
||||
csr_rd_check(.ptr (test_csrs[i]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (compare_mask));
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_write_seq
|
||||
// Brief Description: This sequence writes a random value to all CSRs. It does not perform any
|
||||
// checks. It is run as the first step of the CSR HW reset test.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_write_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_write_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
uvm_reg_data_t wdata;
|
||||
|
||||
foreach (test_csrs[i]) begin
|
||||
// check if parent block or register is excluded from write
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrHwResetTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Writing random data to register %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
|
||||
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrHwResetTest, m_csr_excl_item);
|
||||
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0));
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_rw_seq
|
||||
// Brief Description: This seq writes a random value to a CSR and reads it back. The read value
|
||||
// is checked for correctness while adhering to its access policies. A random choice is made between
|
||||
// reading back the CSR as a whole or reading fields individually, so that partial accesses are made
|
||||
// into the DUT as well.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_rw_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_rw_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
rand bit do_csr_rd_check;
|
||||
rand bit do_csr_field_rd_check;
|
||||
|
||||
constraint csr_or_field_rd_check_c {
|
||||
// at least one of them should be set
|
||||
do_csr_rd_check || do_csr_field_rd_check;
|
||||
}
|
||||
|
||||
virtual task body();
|
||||
foreach (test_csrs[i]) begin
|
||||
uvm_reg_data_t wdata;
|
||||
uvm_reg_data_t compare_mask;
|
||||
uvm_reg_field test_fields[$];
|
||||
|
||||
// check if parent block or register is excluded from write
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrRwTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Verifying register read/write for %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
`DV_CHECK_FATAL(randomize(do_csr_rd_check, do_csr_field_rd_check))
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
|
||||
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrRwTest, m_csr_excl_item);
|
||||
|
||||
// if external checker is not enabled and writes are made non-blocking, then we need to
|
||||
// pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
|
||||
// might pick up stale mirrored value
|
||||
// the pre-predict also needs to happen after the register is being written, to make sure the
|
||||
// register is getting the updated access information.
|
||||
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0), .predict(!external_checker));
|
||||
|
||||
// check if parent block or register is excluded from read-check
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWriteCheck, CsrRwTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWriteCheck exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
compare_mask = get_mask_excl_fields(test_csrs[i], CsrExclWriteCheck, CsrRwTest,
|
||||
m_csr_excl_item);
|
||||
if (do_csr_rd_check) begin
|
||||
csr_rd_check(.ptr (test_csrs[i]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (compare_mask));
|
||||
end
|
||||
if (do_csr_field_rd_check) begin
|
||||
test_csrs[i].get_fields(test_fields);
|
||||
test_fields.shuffle();
|
||||
foreach (test_fields[j]) begin
|
||||
bit compare = !m_csr_excl_item.is_excl(test_fields[j], CsrExclWriteCheck, CsrRwTest);
|
||||
csr_rd_check(.ptr (test_fields[j]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker && compare),
|
||||
.compare_vs_ral(1'b1));
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_bit_bash_seq
|
||||
// Brief Description: This sequence walks a 1 through each CSR by writing one bit at a time and
|
||||
// reading the CSR back. The read value is checked for correctness while adhering to its access
|
||||
// policies. This verifies that there is no aliasing within the fields / bits of a CSR.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_bit_bash_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_bit_bash_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
foreach (test_csrs[i]) begin
|
||||
// check if parent block or register is excluded from write
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrBitBashTest) ||
|
||||
m_csr_excl_item.is_excl(test_csrs[i], CsrExclWriteCheck, CsrBitBashTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite/WriteCheck exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Verifying register bit bash for %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
begin
|
||||
uvm_reg_field fields[$];
|
||||
string mode[`UVM_REG_DATA_WIDTH];
|
||||
uvm_reg_data_t dc_mask; // dont write or read
|
||||
uvm_reg_data_t cmp_mask; // read but dont compare
|
||||
int n_bits;
|
||||
string field_access;
|
||||
int next_lsb;
|
||||
|
||||
n_bits = test_csrs[i].get_n_bytes() * 8;
|
||||
|
||||
// Let's see what kind of bits we have...
|
||||
test_csrs[i].get_fields(fields);
|
||||
|
||||
next_lsb = 0;
|
||||
dc_mask = 0;
|
||||
cmp_mask = 0;
|
||||
|
||||
foreach (fields[j]) begin
|
||||
int lsb, w, dc, cmp;
|
||||
|
||||
field_access = fields[j].get_access(test_csrs[i].get_default_map());
|
||||
cmp = (fields[j].get_compare() == UVM_NO_CHECK);
|
||||
lsb = fields[j].get_lsb_pos();
|
||||
w = fields[j].get_n_bits();
|
||||
|
||||
// Exclude write-only fields from compare because you are not supposed to read them
|
||||
case (field_access)
|
||||
"WO", "WOC", "WOS", "WO1", "NOACCESS", "": cmp = 1;
|
||||
endcase
|
||||
|
||||
// skip fields that are wr-excluded
|
||||
if (m_csr_excl_item.is_excl(fields[j], CsrExclWrite, CsrBitBashTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping field %0s due to CsrExclWrite exclusion",
|
||||
fields[j].get_full_name()), UVM_MEDIUM)
|
||||
dc = 1;
|
||||
end
|
||||
|
||||
// ignore fields that are init or rd-excluded
|
||||
cmp = m_csr_excl_item.is_excl(fields[j], CsrExclInitCheck, CsrBitBashTest) ||
|
||||
m_csr_excl_item.is_excl(fields[j], CsrExclWriteCheck, CsrBitBashTest) ;
|
||||
|
||||
// Any unused bits on the right side of the LSB?
|
||||
while (next_lsb < lsb) mode[next_lsb++] = "RO";
|
||||
|
||||
repeat (w) begin
|
||||
mode[next_lsb] = field_access;
|
||||
dc_mask[next_lsb] = dc;
|
||||
cmp_mask[next_lsb] = cmp;
|
||||
next_lsb++;
|
||||
end
|
||||
end
|
||||
|
||||
// Any unused bits on the left side of the MSB?
|
||||
while (next_lsb < `UVM_REG_DATA_WIDTH)
|
||||
mode[next_lsb++] = "RO";
|
||||
|
||||
// Bash the kth bit
|
||||
for (int k = 0; k < n_bits; k++) begin
|
||||
// Cannot test unpredictable bit behavior
|
||||
if (dc_mask[k]) continue;
|
||||
bash_kth_bit(test_csrs[i], k, mode[k], cmp_mask);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endtask
|
||||
|
||||
task bash_kth_bit(uvm_reg rg,
|
||||
int k,
|
||||
string mode,
|
||||
uvm_reg_data_t mask);
|
||||
|
||||
uvm_reg_data_t val;
|
||||
string err_msg;
|
||||
|
||||
`uvm_info(`gtn, $sformatf("bashing %0s bit #%0d", mode, k), UVM_HIGH)
|
||||
repeat (2) begin
|
||||
val = rg.get();
|
||||
val[k] = ~val[k];
|
||||
err_msg = $sformatf("Wrote %0s[%0d]: %0b", rg.get_full_name(), k, val[k]);
|
||||
csr_wr(.csr(rg), .value(val), .blocking(1));
|
||||
|
||||
// if external checker is not enabled and writes are made non-blocking, then we need to
|
||||
// pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
|
||||
// might pick up stale mirrored value
|
||||
if (!external_checker) begin
|
||||
void'(rg.predict(.value(val), .kind(UVM_PREDICT_WRITE)));
|
||||
end
|
||||
|
||||
// TODO, outstanding access to same reg isn't supported in uvm_reg. Need to add another seq
|
||||
// uvm_reg waits until transaction is completed, before start another read/write in same reg
|
||||
csr_rd_check(.ptr (rg),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (~mask),
|
||||
.err_msg (err_msg));
|
||||
end
|
||||
endtask: bash_kth_bit
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_aliasing_seq
|
||||
// Brief Description: For each CSR, this sequence writes a random value to it and reads ALL CSRs
|
||||
// back. The read value of the CSR that was written is checked for correctness while adhering to its
|
||||
// access policies. The read value of all other CSRs are compared against their previous values.
|
||||
// This verifies that there is no aliasing across the address bits within the valid CSR space.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_aliasing_seq extends csr_base_seq;
|
||||
`uvm_object_utils(csr_aliasing_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
foreach(test_csrs[i]) begin
|
||||
uvm_reg_data_t wdata;
|
||||
|
||||
// check if parent block or register is excluded
|
||||
if (m_csr_excl_item.is_excl(test_csrs[i], CsrExclWrite, CsrAliasingTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclWrite exclusion",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
`uvm_info(`gtn, $sformatf("Verifying register aliasing for %0s",
|
||||
test_csrs[i].get_full_name()), UVM_MEDIUM)
|
||||
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(wdata)
|
||||
wdata &= get_mask_excl_fields(test_csrs[i], CsrExclWrite, CsrAliasingTest, m_csr_excl_item);
|
||||
csr_wr(.csr(test_csrs[i]), .value(wdata), .blocking(0));
|
||||
|
||||
// if external checker is not enabled and writes are made non-blocking, then we need to
|
||||
// pre-predict so that the mirrored value will be updated. if we dont, then csr_rd_check task
|
||||
// might pick up stale mirrored value
|
||||
if (!external_checker) begin
|
||||
void'(test_csrs[i].predict(.value(wdata), .kind(UVM_PREDICT_WRITE)));
|
||||
end
|
||||
|
||||
all_csrs.shuffle();
|
||||
foreach (all_csrs[j]) begin
|
||||
uvm_reg_data_t compare_mask;
|
||||
|
||||
// check if parent block or register is excluded
|
||||
if (m_csr_excl_item.is_excl(all_csrs[j], CsrExclInitCheck, CsrAliasingTest) ||
|
||||
m_csr_excl_item.is_excl(all_csrs[j], CsrExclWriteCheck, CsrAliasingTest)) begin
|
||||
`uvm_info(`gtn, $sformatf("Skipping register %0s due to CsrExclInit/WriteCheck exclusion",
|
||||
all_csrs[j].get_full_name()), UVM_MEDIUM)
|
||||
continue;
|
||||
end
|
||||
|
||||
compare_mask = get_mask_excl_fields(all_csrs[j], CsrExclWriteCheck, CsrAliasingTest,
|
||||
m_csr_excl_item);
|
||||
csr_rd_check(.ptr (all_csrs[j]),
|
||||
.blocking (0),
|
||||
.compare (!external_checker),
|
||||
.compare_vs_ral(1'b1),
|
||||
.compare_mask (compare_mask));
|
||||
end
|
||||
wait_no_outstanding_access();
|
||||
end
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Class: csr_mem_walk_seq
|
||||
// Brief Description: This seq walks through each address of the memory by running the default
|
||||
// UVM mem walk sequence.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class csr_mem_walk_seq extends csr_base_seq;
|
||||
uvm_mem_walk_seq mem_walk_seq;
|
||||
|
||||
`uvm_object_utils(csr_mem_walk_seq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task body();
|
||||
mem_walk_seq = uvm_mem_walk_seq::type_id::create("mem_walk_seq");
|
||||
foreach (models[i]) begin
|
||||
mem_walk_seq.model = models[i];
|
||||
mem_walk_seq.start(null);
|
||||
end
|
||||
endtask : body
|
||||
|
||||
endclass
|
21
vendor/lowrisc_ip/csr_utils/csr_utils.core
vendored
Normal file
21
vendor/lowrisc_ip/csr_utils/csr_utils.core
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
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:csr_utils"
|
||||
description: "CSR utilities"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_utils
|
||||
files:
|
||||
- csr_utils_pkg.sv
|
||||
- csr_excl_item.sv: {is_include_file: true}
|
||||
- csr_seq_lib.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
571
vendor/lowrisc_ip/csr_utils/csr_utils_pkg.sv
vendored
Normal file
571
vendor/lowrisc_ip/csr_utils/csr_utils_pkg.sv
vendored
Normal file
|
@ -0,0 +1,571 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package csr_utils_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import dv_utils_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
||||
// local types and variables
|
||||
uint outstanding_accesses = 0;
|
||||
uint default_timeout_ns = 1_000_000; // 1ms
|
||||
uint default_spinwait_timeout_ns = 10_000_000; // 10ms
|
||||
string msg_id = "csr_utils";
|
||||
bit default_csr_blocking = 1;
|
||||
bit under_reset = 0;
|
||||
|
||||
// global paramters for number of csr tests (including memory test)
|
||||
parameter uint NUM_CSR_TESTS = 4;
|
||||
|
||||
// csr field struct - hold field specific params
|
||||
typedef struct {
|
||||
uvm_reg csr;
|
||||
uvm_reg_field field;
|
||||
uvm_reg_data_t mask;
|
||||
uint shift;
|
||||
} csr_field_s;
|
||||
|
||||
// csr test types
|
||||
typedef enum bit [NUM_CSR_TESTS-1:0] {
|
||||
CsrInvalidTest = 4'h0,
|
||||
// elementary test types
|
||||
CsrHwResetTest = 4'h1,
|
||||
CsrRwTest = 4'h2,
|
||||
CsrBitBashTest = 4'h4,
|
||||
CsrAliasingTest = 4'h8,
|
||||
// combinational test types (combinations of the above), used for exclusion tagging
|
||||
CsrNonInitTests = 4'he, // all but HwReset test
|
||||
CsrAllTests = 4'hf // all tests
|
||||
} csr_test_type_e;
|
||||
|
||||
// csr exclusion indications
|
||||
typedef enum bit [2:0] {
|
||||
CsrNoExcl = 3'b000, // no exclusions
|
||||
CsrExclInitCheck = 3'b001, // exclude csr from init val check
|
||||
CsrExclWriteCheck = 3'b010, // exclude csr from write-read check
|
||||
CsrExclCheck = 3'b011, // exclude csr from init or write-read check
|
||||
CsrExclWrite = 3'b100, // exclude csr from write
|
||||
CsrExclAll = 3'b111 // exclude csr from init or write or writ-read check
|
||||
} csr_excl_type_e;
|
||||
|
||||
function automatic void increment_outstanding_access();
|
||||
outstanding_accesses++;
|
||||
endfunction
|
||||
|
||||
function automatic void decrement_outstanding_access();
|
||||
outstanding_accesses--;
|
||||
endfunction
|
||||
|
||||
task automatic wait_no_outstanding_access();
|
||||
wait(outstanding_accesses == 0);
|
||||
endtask
|
||||
|
||||
function automatic void clear_outstanding_access();
|
||||
outstanding_accesses = 0;
|
||||
endfunction
|
||||
|
||||
function automatic void reset_asserted();
|
||||
under_reset = 1;
|
||||
endfunction
|
||||
|
||||
function automatic void reset_deasserted();
|
||||
under_reset = 0;
|
||||
endfunction
|
||||
|
||||
// Get all valid csr addrs - useful to check if incoming addr falls in the csr range.
|
||||
function automatic void get_csr_addrs(input uvm_reg_block ral, ref uvm_reg_addr_t csr_addrs[$]);
|
||||
uvm_reg csrs[$];
|
||||
ral.get_registers(csrs);
|
||||
csr_addrs.delete();
|
||||
foreach (csrs[i]) begin
|
||||
csr_addrs.push_back(csrs[i].get_address());
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Get all valid mem addr ranges - useful to check if incoming addr falls in the mem range.
|
||||
function automatic void get_mem_addr_ranges(uvm_reg_block ral, ref addr_range_t mem_ranges[$]);
|
||||
uvm_mem mems[$];
|
||||
ral.get_memories(mems);
|
||||
mems.delete();
|
||||
foreach (mems[i]) begin
|
||||
addr_range_t mem_range;
|
||||
mem_range.start_addr = mems[i].get_address();
|
||||
mem_range.end_addr = mem_range.start_addr +
|
||||
mems[i].get_size() * mems[i].get_n_bytes() - 1;
|
||||
mem_ranges.push_back(mem_range);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// This fucntion return mirrored value of reg/field of given RAL
|
||||
function automatic uvm_reg_data_t get_reg_fld_mirror_value(uvm_reg_block ral,
|
||||
string reg_name,
|
||||
string field_name = "");
|
||||
uvm_reg csr;
|
||||
uvm_reg_field fld;
|
||||
uvm_reg_data_t result;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::get_reg_fld_mirror_value"};
|
||||
csr = ral.get_reg_by_name(reg_name);
|
||||
`DV_CHECK_NE_FATAL(csr, null, "", msg_id)
|
||||
// return field mirror value if field_name is passed, else return reg mirror value
|
||||
if (field_name != "") begin
|
||||
fld = csr.get_field_by_name(field_name);
|
||||
`DV_CHECK_NE_FATAL(fld, null, "", msg_id)
|
||||
result = fld.get_mirrored_value();
|
||||
end
|
||||
else begin
|
||||
result = csr.get_mirrored_value();
|
||||
end
|
||||
return result;
|
||||
endfunction : get_reg_fld_mirror_value
|
||||
|
||||
// This function attempts to cast a given uvm_object ptr into uvm_reg or uvm_reg_field. If cast
|
||||
// is successful on either, then set the appropriate csr_field_s return values.
|
||||
function automatic csr_field_s decode_csr_or_field(input uvm_object ptr);
|
||||
uvm_reg csr;
|
||||
uvm_reg_field fld;
|
||||
csr_field_s result;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::decode_csr_or_field"};
|
||||
|
||||
if ($cast(csr, ptr)) begin
|
||||
// return csr object with null field; set the mask to all 1s and shift to 0
|
||||
result.csr = csr;
|
||||
result.mask = '1;
|
||||
result.shift = 0;
|
||||
end
|
||||
else if ($cast(fld, ptr)) begin
|
||||
// return csr field object; return the appropriate mask and shift values
|
||||
result.csr = fld.get_parent();
|
||||
result.field = fld;
|
||||
result.mask = (1 << fld.get_n_bits()) - 1;
|
||||
result.shift = fld.get_lsb_pos();
|
||||
end
|
||||
else begin
|
||||
`uvm_fatal(msg_id, $sformatf("ptr %0s is not of type uvm_reg or uvm_reg_field",
|
||||
ptr.get_full_name()))
|
||||
end
|
||||
return result;
|
||||
endfunction : decode_csr_or_field
|
||||
|
||||
// mask and shift data to extract the value specific to that supplied field
|
||||
function automatic uvm_reg_data_t get_field_val(uvm_reg_field field,
|
||||
uvm_reg_data_t value);
|
||||
uvm_reg_data_t mask = (1 << field.get_n_bits()) - 1;
|
||||
uint shift = field.get_lsb_pos();
|
||||
get_field_val = (value >> shift) & mask;
|
||||
endfunction
|
||||
|
||||
// get updated reg value by using new specific field value
|
||||
function automatic uvm_reg_data_t get_csr_val_with_updated_field(uvm_reg_field field,
|
||||
uvm_reg_data_t csr_value,
|
||||
uvm_reg_data_t field_value);
|
||||
uvm_reg_data_t mask = (1 << field.get_n_bits()) - 1;
|
||||
uint shift = field.get_lsb_pos();
|
||||
csr_value = csr_value & ~(mask << shift) | ((mask & field_value) << shift);
|
||||
return csr_value;
|
||||
endfunction
|
||||
|
||||
// wait until current csr op is complete
|
||||
task automatic csr_wait(input uvm_reg csr);
|
||||
`uvm_info(msg_id, $sformatf("%0s: wait_busy: %0b",
|
||||
csr.get_full_name(), csr.m_is_busy), UVM_HIGH)
|
||||
wait(csr.m_is_busy == 1'b0);
|
||||
`uvm_info(msg_id, $sformatf("%0s: done wait_busy: %0b",
|
||||
csr.get_full_name(), csr.m_is_busy), UVM_HIGH)
|
||||
endtask
|
||||
|
||||
task automatic csr_update(input uvm_reg csr,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
if (blocking) begin
|
||||
csr_update_sub(csr, check, path, timeout_ns, map);
|
||||
end else begin
|
||||
fork
|
||||
csr_update_sub(csr, check, path, timeout_ns, map);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// subroutine of csr_update, don't use it directly
|
||||
task automatic csr_update_sub(input uvm_reg csr,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_update"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
csr.update(.status(status), .path(path), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_update %0s (addr=0x%0h)",
|
||||
csr.get_full_name(), csr.get_address()));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
task automatic csr_wr(input uvm_reg csr,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input bit predict = 0,
|
||||
input uvm_reg_map map = null);
|
||||
if (blocking) begin
|
||||
csr_wr_sub(csr, value, check, path, timeout_ns, map);
|
||||
if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
|
||||
end else begin
|
||||
fork
|
||||
begin
|
||||
csr_wr_sub(csr, value, check, path, timeout_ns, map);
|
||||
// predict after csr_wr_sub, to ensure predict after enable register overwrite the locked
|
||||
// registers' access information
|
||||
if (predict) void'(csr.predict(.value(value), .kind(UVM_PREDICT_WRITE)));
|
||||
end
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// subroutine of csr_wr, don't use it directly
|
||||
task automatic csr_wr_sub(input uvm_reg csr,
|
||||
input uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_wr"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
csr.write(.status(status), .value(value), .path(path), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_wr %0s (addr=0x%0h)",
|
||||
csr.get_full_name(), csr.get_address()));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
task automatic csr_rd(input uvm_object ptr, // accept reg or field
|
||||
output uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
if (blocking) begin
|
||||
csr_rd_sub(ptr, value, check, path, timeout_ns, map);
|
||||
end else begin
|
||||
fork
|
||||
csr_rd_sub(ptr, value, check, path, timeout_ns, map);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask
|
||||
|
||||
// subroutine of csr_rd, don't use it directly
|
||||
task automatic csr_rd_sub(input uvm_object ptr, // accept reg or field
|
||||
output uvm_reg_data_t value,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
csr_field_s csr_or_fld;
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_rd"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
csr_or_fld = decode_csr_or_field(ptr);
|
||||
if (csr_or_fld.field != null) begin
|
||||
csr_or_fld.field.read(.status(status), .value(value), .path(path), .map(map),
|
||||
.prior(100));
|
||||
end else begin
|
||||
csr_or_fld.csr.read(.status(status), .value(value), .path(path), .map(map),
|
||||
.prior(100));
|
||||
end
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_rd %0s (addr=0x%0h)",
|
||||
ptr.get_full_name(), csr_or_fld.csr.get_address()));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
task automatic csr_rd_check(input uvm_object ptr,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input bit compare = 1'b1,
|
||||
input bit compare_vs_ral = 1'b0,
|
||||
input uvm_reg_data_t compare_mask = '1,
|
||||
input uvm_reg_data_t compare_value = 0,
|
||||
input string err_msg = "",
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
fork
|
||||
begin
|
||||
csr_field_s csr_or_fld;
|
||||
uvm_status_e status;
|
||||
uvm_reg_data_t obs;
|
||||
uvm_reg_data_t exp;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_rd_check"};
|
||||
|
||||
increment_outstanding_access();
|
||||
csr_or_fld = decode_csr_or_field(ptr);
|
||||
|
||||
csr_rd(.ptr(ptr), .value(obs), .check(check), .path(path),
|
||||
.blocking(1), .timeout_ns(timeout_ns), .map(map));
|
||||
|
||||
// get mirrored value after read to make sure the read reg access is updated
|
||||
if (csr_or_fld.field != null) begin
|
||||
exp = csr_or_fld.field.get_mirrored_value();
|
||||
end else begin
|
||||
exp = csr_or_fld.csr.get_mirrored_value();
|
||||
end
|
||||
if (compare && !under_reset) begin
|
||||
obs = obs & compare_mask;
|
||||
exp = (compare_vs_ral ? exp : compare_value) & compare_mask;
|
||||
`DV_CHECK_EQ(obs, exp, {"Regname: ", ptr.get_full_name(), " ", err_msg},
|
||||
error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
join_none
|
||||
if (blocking) wait fork;
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
else #0;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
// task to read all csrs and check against ral expected value. Mainly used after reset
|
||||
task automatic read_and_check_all_csrs(input uvm_reg_block ral);
|
||||
uvm_reg ral_csrs[$];
|
||||
ral.get_registers(ral_csrs);
|
||||
ral_csrs.shuffle();
|
||||
|
||||
foreach (ral_csrs[i]) csr_rd_check(.ptr(ral_csrs[i]), .compare_vs_ral(1));
|
||||
endtask
|
||||
|
||||
// poll a csr or csr field continuously until it reads the expected value.
|
||||
task automatic csr_spinwait(input uvm_object ptr,
|
||||
input uvm_reg_data_t exp_data,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_path_e path = UVM_DEFAULT_PATH,
|
||||
input uvm_reg_map map = null,
|
||||
input uint spinwait_delay_ns = 0,
|
||||
input uint timeout_ns = default_spinwait_timeout_ns,
|
||||
input compare_op_e compare_op = CompareOpEq,
|
||||
input uvm_verbosity verbosity = UVM_HIGH);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
csr_field_s csr_or_fld;
|
||||
uvm_reg_data_t read_data;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::csr_spinwait"};
|
||||
|
||||
csr_or_fld = decode_csr_or_field(ptr);
|
||||
fork
|
||||
while (!under_reset) begin
|
||||
if (spinwait_delay_ns) #(spinwait_delay_ns * 1ns);
|
||||
csr_rd(.ptr(ptr), .value(read_data), .check(check), .path(path),
|
||||
.blocking(1), .map(map));
|
||||
`uvm_info(msg_id, $sformatf("ptr %0s == 0x%0h",
|
||||
ptr.get_full_name(), read_data), verbosity)
|
||||
case (compare_op)
|
||||
CompareOpEq: if (read_data == exp_data) break;
|
||||
CompareOpCaseEq: if (read_data === exp_data) break;
|
||||
CompareOpNe: if (read_data != exp_data) break;
|
||||
CompareOpCaseNe: if (read_data !== exp_data) break;
|
||||
CompareOpGt: if (read_data > exp_data) break;
|
||||
CompareOpGe: if (read_data >= exp_data) break;
|
||||
CompareOpLt: if (read_data < exp_data) break;
|
||||
CompareOpLe: if (read_data <= exp_data) break;
|
||||
default: begin
|
||||
`uvm_fatal(ptr.get_full_name(), $sformatf("invalid operator:%0s", compare_op))
|
||||
end
|
||||
endcase
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id, $sformatf("timeout %0s (addr=0x%0h) == 0x%0h",
|
||||
ptr.get_full_name(), csr_or_fld.csr.get_address(), exp_data));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask
|
||||
|
||||
task automatic mem_rd(input uvm_mem ptr,
|
||||
input int offset,
|
||||
output bit[31:0] data,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
if (blocking) begin
|
||||
mem_rd_sub(ptr, offset, data, check, timeout_ns, map);
|
||||
end else begin
|
||||
fork
|
||||
mem_rd_sub(ptr, offset, data, check, timeout_ns, map);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask : mem_rd
|
||||
|
||||
task automatic mem_rd_sub(input uvm_mem ptr,
|
||||
input int offset,
|
||||
output bit[31:0] data,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolating_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::mem_rd"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
ptr.read(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin : mem_rd_timeout
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_rd %0s (addr=0x%0h)",
|
||||
ptr.get_full_name(), offset));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolating_fork
|
||||
join
|
||||
endtask : mem_rd_sub
|
||||
|
||||
task automatic mem_wr(input uvm_mem ptr,
|
||||
input int offset,
|
||||
input bit[31:0] data,
|
||||
input bit blocking = default_csr_blocking,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_reg_map map = null);
|
||||
if (blocking) begin
|
||||
mem_wr_sub(ptr, offset, data, timeout_ns, check, map);
|
||||
end else begin
|
||||
fork
|
||||
mem_wr_sub(ptr, offset, data, timeout_ns, check, map);
|
||||
join_none
|
||||
// Add #0 to ensure that this thread starts executing before any subsequent call
|
||||
#0;
|
||||
end
|
||||
endtask : mem_wr
|
||||
|
||||
task automatic mem_wr_sub(input uvm_mem ptr,
|
||||
input int offset,
|
||||
input bit[31:0] data,
|
||||
input uint timeout_ns = default_timeout_ns,
|
||||
input uvm_check_e check = UVM_CHECK,
|
||||
input uvm_reg_map map = null);
|
||||
fork
|
||||
begin : isolation_fork
|
||||
uvm_status_e status;
|
||||
string msg_id = {csr_utils_pkg::msg_id, "::mem_wr"};
|
||||
|
||||
fork
|
||||
begin
|
||||
increment_outstanding_access();
|
||||
ptr.write(.status(status), .offset(offset), .value(data), .map(map), .prior(100));
|
||||
if (check == UVM_CHECK) begin
|
||||
`DV_CHECK_EQ(status, UVM_IS_OK, "", error, msg_id)
|
||||
end
|
||||
decrement_outstanding_access();
|
||||
end
|
||||
begin
|
||||
wait_timeout(timeout_ns, msg_id,
|
||||
$sformatf("Timeout waiting to csr_wr %0s (addr=0x%0h)",
|
||||
ptr.get_full_name(), offset));
|
||||
end
|
||||
join_any
|
||||
disable fork;
|
||||
end : isolation_fork
|
||||
join
|
||||
endtask : mem_wr_sub
|
||||
|
||||
`include "csr_excl_item.sv"
|
||||
|
||||
// Fields could be excluded from writes & reads - This function zeros out the excluded fields
|
||||
function automatic uvm_reg_data_t get_mask_excl_fields(uvm_reg csr,
|
||||
csr_excl_type_e csr_excl_type,
|
||||
csr_test_type_e csr_test_type,
|
||||
csr_excl_item m_csr_excl_item);
|
||||
uvm_reg_field flds[$];
|
||||
csr.get_fields(flds);
|
||||
get_mask_excl_fields = '1;
|
||||
foreach (flds[i]) begin
|
||||
if (m_csr_excl_item.is_excl(flds[i], csr_excl_type, csr_test_type)) begin
|
||||
csr_field_s fld_params = decode_csr_or_field(flds[i]);
|
||||
`uvm_info(msg_id, $sformatf("Skipping field %0s due to %0s exclusion",
|
||||
flds[i].get_full_name(), csr_excl_type.name()), UVM_MEDIUM)
|
||||
get_mask_excl_fields &= ~(fld_params.mask << fld_params.shift);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
// sources
|
||||
`include "csr_seq_lib.sv"
|
||||
|
||||
endpackage
|
56
vendor/lowrisc_ip/dv_lib/README.md
vendored
Normal file
56
vendor/lowrisc_ip/dv_lib/README.md
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: "DV Library Classes"
|
||||
---
|
||||
|
||||
# DV library classes
|
||||
|
||||
## Overview
|
||||
The DV library classes form the base layer / framework for constructing UVM
|
||||
testbenches. These classes provide features (settings, methods, hooks and other
|
||||
constructs used in verification) that are generic enough to be reused across
|
||||
all testbenches.
|
||||
|
||||
In this doc, we will capture some of the most salient / frequently used features
|
||||
in extended classes. These classes are being updated frequently. So, for a more
|
||||
detailed understanding, please read the class definitions directly.
|
||||
|
||||
The DV library classes fall into 3 categories - UVM RAL (register abstraction
|
||||
layer), UVM agent, and UVM environment extensions.
|
||||
|
||||
### UVM RAL extensions
|
||||
The RAL model generated using the [reggen]({{< relref "util/reggen/README.md" >}}) tool
|
||||
extend from these classes. These themselves extend from the corresponding RAL
|
||||
classes provided in UVM.
|
||||
|
||||
#### `dv_base_reg_field`
|
||||
Currently, this class does not provide any additional features. One of the
|
||||
features planned for future is setting exclusion tags at the field level for the
|
||||
CSR suite of tests that will be extracted automatically from the Hjson-based
|
||||
IP CSR specification.
|
||||
|
||||
#### `dv_base_reg`
|
||||
This class provides the following functions to support verification:
|
||||
* `gen_n_used_bits()`: This function returns the actual number of bits used in
|
||||
the CSR (sum of all available field widths).
|
||||
* `get_msb_pos()`: This function returns the MSB bit position of all available
|
||||
fields. CSR either ends at this bit (bit \`TL_DW - 1) or has reserved / invalid
|
||||
bits beyond this bit.
|
||||
|
||||
#### `dv_base_reg_block`
|
||||
* ` build(uvm_reg_addr_t base_addr)`: This function is implemented as a pseudo
|
||||
pure virtual function (returns a fatal error if called directly). It is used
|
||||
for building the complete RAL model. For a polymorphic approach, the DV user
|
||||
can use this class handle to create the extended (IP specific) class instance
|
||||
and call this function to build the actual RAL model. This is exactly how it
|
||||
is done in [dv_base_env_cfg](#dv_base_env_cfg).
|
||||
|
||||
#### `dv_base_reg_map`
|
||||
Currently, this class does not provide any additional features. Having this
|
||||
extension provides an opportunity to add common features in future.
|
||||
|
||||
### UVM Agent extensions
|
||||
TODO
|
||||
|
||||
### UVM Environment extensions
|
||||
TODO
|
||||
|
60
vendor/lowrisc_ip/dv_lib/dv_base_agent.sv
vendored
Normal file
60
vendor/lowrisc_ip/dv_lib/dv_base_agent.sv
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_agent #(type CFG_T = dv_base_agent_cfg,
|
||||
type DRIVER_T = dv_base_driver,
|
||||
type HOST_DRIVER_T = DRIVER_T,
|
||||
type DEVICE_DRIVER_T = DRIVER_T,
|
||||
type SEQUENCER_T = dv_base_sequencer,
|
||||
type MONITOR_T = dv_base_monitor,
|
||||
type COV_T = dv_base_agent_cov) extends uvm_agent;
|
||||
|
||||
`uvm_component_param_utils(dv_base_agent #(CFG_T, DRIVER_T, HOST_DRIVER_T, DEVICE_DRIVER_T,
|
||||
SEQUENCER_T, MONITOR_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
COV_T cov;
|
||||
DRIVER_T driver;
|
||||
SEQUENCER_T sequencer;
|
||||
MONITOR_T monitor;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
// get CFG_T object from uvm_config_db
|
||||
if (!uvm_config_db#(CFG_T)::get(this, "", "cfg", cfg)) begin
|
||||
`uvm_fatal(`gfn, $sformatf("failed to get %s from uvm_config_db", cfg.get_type_name()))
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("\n%0s", cfg.sprint()), UVM_HIGH)
|
||||
|
||||
// create components
|
||||
if (cfg.en_cov) begin
|
||||
cov = COV_T ::type_id::create("cov", this);
|
||||
cov.cfg = cfg;
|
||||
end
|
||||
|
||||
monitor = MONITOR_T::type_id::create("monitor", this);
|
||||
monitor.cfg = cfg;
|
||||
monitor.cov = cov;
|
||||
|
||||
if (cfg.is_active) begin
|
||||
sequencer = SEQUENCER_T::type_id::create("sequencer", this);
|
||||
sequencer.cfg = cfg;
|
||||
|
||||
if (cfg.if_mode == Host) driver = HOST_DRIVER_T::type_id::create("driver", this);
|
||||
else driver = DEVICE_DRIVER_T::type_id::create("driver", this);
|
||||
driver.cfg = cfg;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void connect_phase(uvm_phase phase);
|
||||
super.connect_phase(phase);
|
||||
if (cfg.is_active) begin
|
||||
driver.seq_item_port.connect(sequencer.seq_item_export);
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
20
vendor/lowrisc_ip/dv_lib/dv_base_agent_cfg.sv
vendored
Normal file
20
vendor/lowrisc_ip/dv_lib/dv_base_agent_cfg.sv
vendored
Normal file
|
@ -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 dv_base_agent_cfg extends uvm_object;
|
||||
|
||||
// agent cfg knobs
|
||||
bit is_active = 1'b1; // active driver or passive monitor
|
||||
bit en_cov = 1'b1; // enable coverage
|
||||
if_mode_e if_mode; // interface mode - Host or Device
|
||||
|
||||
`uvm_object_utils_begin(dv_base_agent_cfg)
|
||||
`uvm_field_int (is_active, UVM_DEFAULT)
|
||||
`uvm_field_int (en_cov, UVM_DEFAULT)
|
||||
`uvm_field_enum(if_mode_e, if_mode, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
12
vendor/lowrisc_ip/dv_lib/dv_base_agent_cov.sv
vendored
Normal file
12
vendor/lowrisc_ip/dv_lib/dv_base_agent_cov.sv
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_agent_cov #(type CFG_T = dv_base_agent_cfg) extends uvm_component;
|
||||
`uvm_component_param_utils(dv_base_agent_cov #(CFG_T))
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
32
vendor/lowrisc_ip/dv_lib/dv_base_driver.sv
vendored
Normal file
32
vendor/lowrisc_ip/dv_lib/dv_base_driver.sv
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_driver #(type ITEM_T = uvm_sequence_item,
|
||||
type CFG_T = dv_base_agent_cfg) extends uvm_driver #(ITEM_T);
|
||||
`uvm_component_param_utils(dv_base_driver #())
|
||||
|
||||
bit under_reset;
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
fork
|
||||
reset_signals();
|
||||
get_and_drive();
|
||||
join
|
||||
endtask
|
||||
|
||||
// reset signals
|
||||
virtual task reset_signals();
|
||||
`uvm_fatal(`gfn, "this is implemented as pure virtual task - please extend")
|
||||
endtask
|
||||
|
||||
// drive trans received from sequencer
|
||||
virtual task get_and_drive();
|
||||
`uvm_fatal(`gfn, "this is implemented as pure virtual task - please extend")
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
60
vendor/lowrisc_ip/dv_lib/dv_base_env.sv
vendored
Normal file
60
vendor/lowrisc_ip/dv_lib/dv_base_env.sv
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_env #(type CFG_T = dv_base_env_cfg,
|
||||
type VIRTUAL_SEQUENCER_T = dv_base_virtual_sequencer,
|
||||
type SCOREBOARD_T = dv_base_scoreboard,
|
||||
type COV_T = dv_base_env_cov) extends uvm_env;
|
||||
`uvm_component_param_utils(dv_base_env #(CFG_T, VIRTUAL_SEQUENCER_T, SCOREBOARD_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
VIRTUAL_SEQUENCER_T virtual_sequencer;
|
||||
SCOREBOARD_T scoreboard;
|
||||
COV_T cov;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
// get dv_base_env_cfg object from uvm_config_db
|
||||
if (!uvm_config_db#(CFG_T)::get(this, "", "cfg", cfg)) begin
|
||||
`uvm_fatal(`gfn, $sformatf("failed to get %s from uvm_config_db", cfg.get_type_name()))
|
||||
end
|
||||
|
||||
// get vifs
|
||||
if (!uvm_config_db#(virtual clk_rst_if)::get(this, "", "clk_rst_vif", cfg.clk_rst_vif)) begin
|
||||
`uvm_fatal(get_full_name(), "failed to get clk_rst_if from uvm_config_db")
|
||||
end
|
||||
cfg.clk_rst_vif.set_freq_mhz(cfg.clk_freq_mhz);
|
||||
|
||||
// create components
|
||||
if (cfg.en_cov) begin
|
||||
cov = COV_T::type_id::create("cov", this);
|
||||
cov.cfg = cfg;
|
||||
end
|
||||
|
||||
if (cfg.is_active) begin
|
||||
virtual_sequencer = VIRTUAL_SEQUENCER_T::type_id::create("virtual_sequencer", this);
|
||||
virtual_sequencer.cfg = cfg;
|
||||
virtual_sequencer.cov = cov;
|
||||
end
|
||||
|
||||
// scb also monitors the reset and call cfg.reset_asserted/reset_deasserted for reset
|
||||
scoreboard = SCOREBOARD_T::type_id::create("scoreboard", this);
|
||||
scoreboard.cfg = cfg;
|
||||
scoreboard.cov = cov;
|
||||
endfunction
|
||||
|
||||
virtual function void end_of_elaboration_phase(uvm_phase phase);
|
||||
super.end_of_elaboration_phase(phase);
|
||||
if (cfg.has_ral) begin
|
||||
// Lock the ral model
|
||||
cfg.ral.lock_model();
|
||||
// Get list of valid csr addresses (useful in seq to randomize addr as well as in scb checks)
|
||||
get_csr_addrs(cfg.ral, cfg.csr_addrs);
|
||||
get_mem_addr_ranges(cfg.ral, cfg.mem_ranges);
|
||||
end
|
||||
endfunction : end_of_elaboration_phase
|
||||
|
||||
endclass
|
94
vendor/lowrisc_ip/dv_lib/dv_base_env_cfg.sv
vendored
Normal file
94
vendor/lowrisc_ip/dv_lib/dv_base_env_cfg.sv
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_env_cfg #(type RAL_T = dv_base_reg_block) extends uvm_object;
|
||||
|
||||
bit is_active = 1;
|
||||
bit en_scb = 1; // can be changed at run-time
|
||||
bit en_cov = 1;
|
||||
bit has_ral = 1;
|
||||
bit under_reset = 0;
|
||||
|
||||
// bit to configure all uvcs with zero delays to create high bw test
|
||||
rand bit zero_delays;
|
||||
|
||||
// reg model & q of valid csr addresses
|
||||
RAL_T ral;
|
||||
bit [TL_AW-1:0] csr_addrs[$];
|
||||
addr_range_t mem_ranges[$];
|
||||
// mem access support, if not enabled, will trigger error
|
||||
bit en_mem_byte_write = 0;
|
||||
bit en_mem_read = 1;
|
||||
|
||||
// ral base address and size
|
||||
bit [TL_AW-1:0] csr_base_addr; // base address where csr map begins
|
||||
bit [TL_AW:0] csr_addr_map_size; // csr addr region allocated to the ip, max: 1 << TL_AW
|
||||
|
||||
// clk_rst_if & freq
|
||||
virtual clk_rst_if clk_rst_vif;
|
||||
rand clk_freq_mhz_e clk_freq_mhz;
|
||||
|
||||
// set zero_delays 40% of the time
|
||||
constraint zero_delays_c {
|
||||
zero_delays dist {1'b0 := 6, 1'b1 := 4};
|
||||
}
|
||||
|
||||
`uvm_object_param_utils_begin(dv_base_env_cfg #(RAL_T))
|
||||
`uvm_field_int (is_active, UVM_DEFAULT)
|
||||
`uvm_field_int (en_scb, UVM_DEFAULT)
|
||||
`uvm_field_int (en_cov, UVM_DEFAULT)
|
||||
`uvm_field_int (zero_delays, UVM_DEFAULT)
|
||||
`uvm_field_int (csr_base_addr, UVM_DEFAULT)
|
||||
`uvm_field_int (csr_addr_map_size, UVM_DEFAULT)
|
||||
`uvm_field_enum (clk_freq_mhz_e, clk_freq_mhz, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual function void initialize(bit [TL_AW-1:0] csr_base_addr = '1);
|
||||
initialize_csr_addr_map_size();
|
||||
`DV_CHECK_NE_FATAL(csr_addr_map_size, 0, "csr_addr_map_size can't be 0")
|
||||
// use locally randomized csr base address, unless provided as arg to this function
|
||||
if (csr_base_addr != '1) begin
|
||||
bit is_aligned;
|
||||
this.csr_base_addr = csr_base_addr;
|
||||
// check alignment
|
||||
is_aligned = ~|(this.csr_base_addr & (this.csr_addr_map_size - 1));
|
||||
`DV_CHECK_EQ_FATAL(is_aligned, 1'b1)
|
||||
end else begin
|
||||
// base address needs to be aligned to csr_addr_map_size
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(csr_base_addr,
|
||||
~|(csr_base_addr & (csr_addr_map_size - 1));)
|
||||
this.csr_base_addr = csr_base_addr;
|
||||
end
|
||||
// build the ral model
|
||||
if (has_ral) begin
|
||||
ral = RAL_T::type_id::create("ral");
|
||||
ral.build(this.csr_base_addr, null);
|
||||
apply_ral_fixes();
|
||||
end
|
||||
endfunction
|
||||
|
||||
// This function must be implemented in extended class to
|
||||
// initialize value of csr_addr_map_size member
|
||||
virtual function void initialize_csr_addr_map_size();
|
||||
`uvm_fatal(`gfn, "This task must be implemented in the extended class!")
|
||||
endfunction : initialize_csr_addr_map_size
|
||||
|
||||
// ral flow is limited in terms of setting correct field access policies and reset values
|
||||
// We apply those fixes here - please note these fixes need to be reflected in the scoreboard
|
||||
protected virtual function void apply_ral_fixes();
|
||||
// fix access policies & reset values
|
||||
endfunction
|
||||
|
||||
virtual function void reset_asserted();
|
||||
this.under_reset = 1;
|
||||
csr_utils_pkg::reset_asserted();
|
||||
endfunction
|
||||
|
||||
virtual function void reset_deasserted();
|
||||
this.under_reset = 0;
|
||||
csr_utils_pkg::reset_deasserted();
|
||||
endfunction
|
||||
endclass
|
42
vendor/lowrisc_ip/dv_lib/dv_base_env_cov.sv
vendored
Normal file
42
vendor/lowrisc_ip/dv_lib/dv_base_env_cov.sv
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// TODO - We are enclosing generic covergroups inside class so that we can
|
||||
// take avoid tool limitation of not allowing arrays of covergroup
|
||||
// Refer to Issue#375 for more details
|
||||
class dv_base_generic_cov_obj;
|
||||
|
||||
// Covergroup: bit_toggle_cg
|
||||
// Generic covergroup definition
|
||||
covergroup bit_toggle_cg(string name, bit toggle_cov_en = 1) with function sample(bit value);
|
||||
option.per_instance = 1;
|
||||
option.name = name;
|
||||
cp_value: coverpoint value;
|
||||
cp_transitions: coverpoint value {
|
||||
option.weight = toggle_cov_en;
|
||||
bins rising = (0 => 1);
|
||||
bins falling = (1 => 0);
|
||||
}
|
||||
endgroup : bit_toggle_cg
|
||||
|
||||
// Function: new
|
||||
function new(string name = "dv_base_generic_cov_obj", bit toggle_cov_en = 1);
|
||||
bit_toggle_cg = new(name, toggle_cov_en);
|
||||
endfunction : new
|
||||
|
||||
// Function: sample
|
||||
function void sample(bit value);
|
||||
bit_toggle_cg.sample(value);
|
||||
endfunction : sample
|
||||
|
||||
endclass : dv_base_generic_cov_obj
|
||||
|
||||
class dv_base_env_cov #(type CFG_T = dv_base_env_cfg) extends uvm_component;
|
||||
`uvm_component_param_utils(dv_base_env_cov #(CFG_T))
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
16
vendor/lowrisc_ip/dv_lib/dv_base_mem.sv
vendored
Normal file
16
vendor/lowrisc_ip/dv_lib/dv_base_mem.sv
vendored
Normal file
|
@ -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
|
||||
//
|
||||
// base register reg class which will be used to generate the reg mem
|
||||
class dv_base_mem extends uvm_mem;
|
||||
|
||||
function new(string name,
|
||||
longint unsigned size,
|
||||
int unsigned n_bits,
|
||||
string access = "RW",
|
||||
int has_coverage = UVM_NO_COVERAGE);
|
||||
super.new(name, size, n_bits, access, has_coverage);
|
||||
endfunction : new
|
||||
|
||||
endclass
|
35
vendor/lowrisc_ip/dv_lib/dv_base_monitor.sv
vendored
Normal file
35
vendor/lowrisc_ip/dv_lib/dv_base_monitor.sv
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_monitor #(type ITEM_T = uvm_sequence_item,
|
||||
type CFG_T = dv_base_agent_cfg,
|
||||
type COV_T = dv_base_agent_cov) extends uvm_monitor;
|
||||
`uvm_component_param_utils(dv_base_monitor #(ITEM_T, CFG_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
COV_T cov;
|
||||
|
||||
// Analysis port for the collected transfer.
|
||||
uvm_analysis_port #(ITEM_T) analysis_port;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
analysis_port = new("analysis_port", this);
|
||||
endfunction
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
fork
|
||||
collect_trans(phase);
|
||||
join
|
||||
endtask
|
||||
|
||||
// collect transactions forever
|
||||
virtual protected task collect_trans(uvm_phase phase);
|
||||
`uvm_fatal(`gfn, "this method is not supposed to be called directly!")
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
69
vendor/lowrisc_ip/dv_lib/dv_base_reg.sv
vendored
Normal file
69
vendor/lowrisc_ip/dv_lib/dv_base_reg.sv
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// base register class which will be used to generate the reg
|
||||
class dv_base_reg extends uvm_reg;
|
||||
|
||||
function new(string name = "",
|
||||
int unsigned n_bits,
|
||||
int has_coverage);
|
||||
super.new(name, n_bits, has_coverage);
|
||||
endfunction : new
|
||||
|
||||
|
||||
local dv_base_reg locked_regs[$];
|
||||
|
||||
function void get_dv_base_reg_fields(ref dv_base_reg_field dv_fields[$]);
|
||||
uvm_reg_field ral_fields[$];
|
||||
get_fields(ral_fields);
|
||||
foreach (ral_fields[i]) `downcast(dv_fields[i], ral_fields[i])
|
||||
endfunction
|
||||
|
||||
// get_n_bits will return number of all the bits in the csr
|
||||
// while this function will return actual number of bits used in reg field
|
||||
function uint get_n_used_bits();
|
||||
uvm_reg_field fields[$];
|
||||
get_fields(fields);
|
||||
foreach (fields[i]) get_n_used_bits += fields[i].get_n_bits();
|
||||
endfunction
|
||||
|
||||
// loop all the fields to find the msb position of this reg
|
||||
function uint get_msb_pos();
|
||||
uvm_reg_field fields[$];
|
||||
get_fields(fields);
|
||||
foreach (fields[i]) begin
|
||||
uint field_msb_pos = fields[i].get_lsb_pos() + fields[i].get_n_bits() - 1;
|
||||
if (field_msb_pos > get_msb_pos) get_msb_pos = field_msb_pos;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// if the register is an enable reg, it will add controlled registers in the queue
|
||||
function void add_locked_reg(dv_base_reg locked_reg);
|
||||
locked_regs.push_back(locked_reg);
|
||||
endfunction
|
||||
|
||||
function bit is_enable_reg();
|
||||
return (locked_regs.size() > 0);
|
||||
endfunction
|
||||
|
||||
// if enable register is set to 1, the locked registers will be set to RO access
|
||||
// once enable register is reset to 0, the locked registers will be set back to original access
|
||||
function void set_locked_regs_access(string access = "original_access");
|
||||
foreach (locked_regs[i]) begin
|
||||
dv_base_reg_field locked_fields[$];
|
||||
locked_regs[i].get_dv_base_reg_fields(locked_fields);
|
||||
foreach (locked_fields[i]) locked_fields[i].set_locked_fields_access(access);
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void get_locked_regs(ref dv_base_reg locked_regs_q[$]);
|
||||
locked_regs_q = locked_regs;
|
||||
endfunction
|
||||
|
||||
// post_write callback to handle reg enables
|
||||
virtual task post_write(uvm_reg_item rw);
|
||||
if (is_enable_reg() && (rw.value[0] & 1)) set_locked_regs_access("RO");
|
||||
endtask
|
||||
|
||||
endclass
|
56
vendor/lowrisc_ip/dv_lib/dv_base_reg_block.sv
vendored
Normal file
56
vendor/lowrisc_ip/dv_lib/dv_base_reg_block.sv
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// base register block class which will be used to generate the reg blocks
|
||||
class dv_base_reg_block extends uvm_reg_block;
|
||||
`uvm_object_utils(dv_base_reg_block)
|
||||
|
||||
csr_excl_item csr_excl;
|
||||
|
||||
function new (string name = "", int has_coverage = UVM_NO_COVERAGE);
|
||||
super.new(name, has_coverage);
|
||||
endfunction
|
||||
|
||||
// provide build function to supply base addr
|
||||
virtual function void build(uvm_reg_addr_t base_addr, csr_utils_pkg::csr_excl_item csr_excl);
|
||||
`uvm_fatal(`gfn, "this method is not supposed to be called directly!")
|
||||
endfunction
|
||||
|
||||
function void get_dv_base_reg_blocks(ref dv_base_reg_block blks[$]);
|
||||
uvm_reg_block uvm_blks[$];
|
||||
this.get_blocks(uvm_blks);
|
||||
foreach (uvm_blks[i]) `downcast(blks[i], uvm_blks[i])
|
||||
endfunction
|
||||
|
||||
function void get_dv_base_regs(ref dv_base_reg dv_regs[$]);
|
||||
uvm_reg ral_regs[$];
|
||||
this.get_registers(ral_regs);
|
||||
foreach (ral_regs[i]) `downcast(dv_regs[i], ral_regs[i])
|
||||
endfunction
|
||||
|
||||
function void get_enable_regs(ref dv_base_reg enable_regs[$]);
|
||||
dv_base_reg_block blks[$];
|
||||
this.get_dv_base_reg_blocks(blks);
|
||||
if (blks.size() == 0) begin
|
||||
dv_base_reg all_regs[$];
|
||||
this.get_dv_base_regs(all_regs);
|
||||
foreach (all_regs[i]) begin
|
||||
if (all_regs[i].is_enable_reg()) enable_regs.push_back(all_regs[i]);
|
||||
end
|
||||
return;
|
||||
end else begin
|
||||
foreach (blks[i]) blks[i].get_enable_regs(enable_regs);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// override RAL's reset function to support enable registers
|
||||
// when reset issued - the locked registers' access will be reset to original access
|
||||
virtual function void reset(string kind = "HARD");
|
||||
dv_base_reg enable_regs[$];
|
||||
super.reset(kind);
|
||||
get_enable_regs(enable_regs);
|
||||
foreach (enable_regs[i]) enable_regs[i].set_locked_regs_access();
|
||||
endfunction
|
||||
|
||||
endclass
|
42
vendor/lowrisc_ip/dv_lib/dv_base_reg_field.sv
vendored
Normal file
42
vendor/lowrisc_ip/dv_lib/dv_base_reg_field.sv
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// base register reg class which will be used to generate the reg field
|
||||
class dv_base_reg_field extends uvm_reg_field;
|
||||
local string m_original_access;
|
||||
|
||||
`uvm_object_utils(dv_base_reg_field)
|
||||
`uvm_object_new
|
||||
|
||||
// when use UVM_PREDICT_WRITE and the CSR access is WO, this function will return the default
|
||||
// val of the register, rather than the written value
|
||||
virtual function uvm_reg_data_t XpredictX(uvm_reg_data_t cur_val,
|
||||
uvm_reg_data_t wr_val,
|
||||
uvm_reg_map map);
|
||||
|
||||
if (get_access(map) == "WO") return cur_val;
|
||||
else return super.XpredictX(cur_val, wr_val, map);
|
||||
endfunction
|
||||
|
||||
virtual function string get_original_access();
|
||||
return m_original_access;
|
||||
endfunction
|
||||
|
||||
virtual function void set_original_access(string access);
|
||||
if (m_original_access == "") begin
|
||||
m_original_access = access;
|
||||
end else begin
|
||||
`uvm_fatal(`gfn, "register original access can only be written once")
|
||||
end
|
||||
endfunction
|
||||
|
||||
virtual function void set_locked_fields_access(string access = "original_access");
|
||||
case (access)
|
||||
"RO": void'(this.set_access(access));
|
||||
"original_access": void'(this.set_access(m_original_access));
|
||||
default: `uvm_fatal(`gfn, $sformatf("attempt to set access to %s", access))
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
endclass
|
9
vendor/lowrisc_ip/dv_lib/dv_base_reg_map.sv
vendored
Normal file
9
vendor/lowrisc_ip/dv_lib/dv_base_reg_map.sv
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// base register reg class which will be used to generate the reg map
|
||||
class dv_base_reg_map extends uvm_reg_map;
|
||||
`uvm_object_utils(dv_base_reg_map)
|
||||
`uvm_object_new
|
||||
endclass
|
80
vendor/lowrisc_ip/dv_lib/dv_base_scoreboard.sv
vendored
Normal file
80
vendor/lowrisc_ip/dv_lib/dv_base_scoreboard.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
class dv_base_scoreboard #(type RAL_T = dv_base_reg_block,
|
||||
type CFG_T = dv_base_env_cfg,
|
||||
type COV_T = dv_base_env_cov) extends uvm_component;
|
||||
`uvm_component_param_utils(dv_base_scoreboard #(RAL_T, CFG_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
RAL_T ral;
|
||||
COV_T cov;
|
||||
|
||||
bit obj_raised = 1'b0;
|
||||
bit under_pre_abort = 1'b0;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
ral = cfg.ral;
|
||||
endfunction
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
super.run_phase(phase);
|
||||
fork
|
||||
monitor_reset();
|
||||
join_none
|
||||
endtask
|
||||
|
||||
virtual task monitor_reset();
|
||||
forever begin
|
||||
if (!cfg.clk_rst_vif.rst_n) begin
|
||||
`uvm_info(`gfn, "reset occurred", UVM_HIGH)
|
||||
cfg.reset_asserted();
|
||||
@(posedge cfg.clk_rst_vif.rst_n);
|
||||
reset();
|
||||
cfg.reset_deasserted();
|
||||
`uvm_info(`gfn, "out of reset", UVM_HIGH)
|
||||
end
|
||||
else begin
|
||||
// wait for a change to rst_n
|
||||
@(cfg.clk_rst_vif.rst_n);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
// raise / drop objections based on certain events
|
||||
virtual function void process_objections(bit raise);
|
||||
if (raise && !obj_raised) begin
|
||||
m_current_phase.raise_objection(this, $sformatf("%s objection raised", `gfn));
|
||||
obj_raised = 1'b1;
|
||||
end
|
||||
else if (!raise && obj_raised) begin
|
||||
m_current_phase.drop_objection(this, $sformatf("%s objection dropped", `gfn));
|
||||
obj_raised = 1'b0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
virtual function void reset(string kind = "HARD");
|
||||
// reset the ral model
|
||||
if (cfg.has_ral) ral.reset(kind);
|
||||
endfunction
|
||||
|
||||
virtual function void pre_abort();
|
||||
super.pre_abort();
|
||||
// use under_pre_abort flag to prevent deadloop described below:
|
||||
// when fatal_err occurred, it will skip check_phase. We add the additional check_phase call
|
||||
// here to help debugging. But if inside the check_phase there are UVM_ERRORs, and the err cnt
|
||||
// is larger than max_err_cnt, then check_phase will call pre_abort again. This will end up
|
||||
// creating a deadloop.
|
||||
if (has_uvm_fatal_occurred() && !under_pre_abort) begin
|
||||
under_pre_abort = 1;
|
||||
check_phase(m_current_phase);
|
||||
under_pre_abort = 0;
|
||||
end
|
||||
endfunction : pre_abort
|
||||
|
||||
endclass
|
||||
|
25
vendor/lowrisc_ip/dv_lib/dv_base_seq.sv
vendored
Normal file
25
vendor/lowrisc_ip/dv_lib/dv_base_seq.sv
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_seq #(type REQ = uvm_sequence_item,
|
||||
type RSP = REQ,
|
||||
type CFG_T = dv_base_agent_cfg,
|
||||
type SEQUENCER_T = dv_base_sequencer) extends uvm_sequence#(REQ, RSP);
|
||||
`uvm_object_param_utils(dv_base_seq #(REQ, RSP, CFG_T, SEQUENCER_T))
|
||||
`uvm_declare_p_sequencer(SEQUENCER_T)
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
task pre_start();
|
||||
super.pre_start();
|
||||
cfg = p_sequencer.cfg;
|
||||
endtask
|
||||
|
||||
task body();
|
||||
`uvm_fatal(`gtn, "Need to override this when you extend from this class!")
|
||||
endtask : body
|
||||
|
||||
endclass
|
13
vendor/lowrisc_ip/dv_lib/dv_base_sequencer.sv
vendored
Normal file
13
vendor/lowrisc_ip/dv_lib/dv_base_sequencer.sv
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_sequencer #(type ITEM_T = uvm_sequence_item,
|
||||
type CFG_T = dv_base_agent_cfg) extends uvm_sequencer #(ITEM_T);
|
||||
`uvm_component_param_utils(dv_base_sequencer #(ITEM_T, CFG_T))
|
||||
|
||||
CFG_T cfg;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
76
vendor/lowrisc_ip/dv_lib/dv_base_test.sv
vendored
Normal file
76
vendor/lowrisc_ip/dv_lib/dv_base_test.sv
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_test #(type CFG_T = dv_base_env_cfg,
|
||||
type ENV_T = dv_base_env) extends uvm_test;
|
||||
`uvm_component_param_utils(dv_base_test #(CFG_T, ENV_T))
|
||||
|
||||
ENV_T env;
|
||||
CFG_T cfg;
|
||||
bit run_test_seq = 1'b1;
|
||||
string test_seq_s;
|
||||
|
||||
uint max_quit_count = 1;
|
||||
uint64 test_timeout_ns = 200_000_000; // 200ms
|
||||
uint drain_time_ns = 2_000; // 2us
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual function void build_phase(uvm_phase phase);
|
||||
dv_report_server m_dv_report_server = new();
|
||||
uvm_report_server::set_server(m_dv_report_server);
|
||||
|
||||
super.build_phase(phase);
|
||||
|
||||
env = ENV_T::type_id::create("env", this);
|
||||
cfg = CFG_T::type_id::create("cfg", this);
|
||||
// don't add args for initialize. Use default value instead
|
||||
cfg.initialize();
|
||||
`DV_CHECK_RANDOMIZE_FATAL(cfg)
|
||||
uvm_config_db#(CFG_T)::set(this, "env", "cfg", cfg);
|
||||
|
||||
// knob to en/dis scb (enabled by default)
|
||||
void'($value$plusargs("en_scb=%0b", cfg.en_scb));
|
||||
// knob to cfg all agents with zero delays
|
||||
void'($value$plusargs("zero_delays=%0b", cfg.zero_delays));
|
||||
endfunction : build_phase
|
||||
|
||||
virtual function void end_of_elaboration_phase(uvm_phase phase);
|
||||
super.end_of_elaboration_phase(phase);
|
||||
void'($value$plusargs("max_quit_count=%0d", max_quit_count));
|
||||
set_max_quit_count(max_quit_count);
|
||||
void'($value$plusargs("test_timeout_ns=%0d", test_timeout_ns));
|
||||
uvm_top.set_timeout((test_timeout_ns * 1ns));
|
||||
endfunction : end_of_elaboration_phase
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
void'($value$plusargs("drain_time_ns=%0d", drain_time_ns));
|
||||
phase.phase_done.set_drain_time(this, (drain_time_ns * 1ns));
|
||||
void'($value$plusargs("UVM_TEST_SEQ=%0s", test_seq_s));
|
||||
if (run_test_seq) begin
|
||||
run_seq(test_seq_s, phase);
|
||||
end
|
||||
// TODO: add hook for end of test checking
|
||||
endtask : run_phase
|
||||
|
||||
virtual task run_seq(string test_seq_s, uvm_phase phase);
|
||||
uvm_sequence test_seq = create_seq_by_name(test_seq_s);
|
||||
|
||||
// provide virtual_sequencer earlier, so we may use the p_sequencer in constraint
|
||||
test_seq.set_sequencer(env.virtual_sequencer);
|
||||
`DV_CHECK_RANDOMIZE_FATAL(test_seq)
|
||||
|
||||
`uvm_info(`gfn, {"starting vseq ", test_seq_s}, UVM_MEDIUM)
|
||||
phase.raise_objection(this, $sformatf("%s objection raised", `gn));
|
||||
test_seq.start(env.virtual_sequencer);
|
||||
phase.drop_objection(this, $sformatf("%s objection dropped", `gn));
|
||||
phase.phase_done.display_objections();
|
||||
`uvm_info(`gfn, {"finished vseq ", test_seq_s}, UVM_MEDIUM)
|
||||
endtask
|
||||
|
||||
// TODO: add default report_phase implementation
|
||||
|
||||
endclass : dv_base_test
|
||||
|
||||
|
14
vendor/lowrisc_ip/dv_lib/dv_base_virtual_sequencer.sv
vendored
Normal file
14
vendor/lowrisc_ip/dv_lib/dv_base_virtual_sequencer.sv
vendored
Normal file
|
@ -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 dv_base_virtual_sequencer #(type CFG_T = dv_base_env_cfg,
|
||||
type COV_T = dv_base_env_cov) extends uvm_sequencer;
|
||||
`uvm_component_param_utils(dv_base_virtual_sequencer #(CFG_T, COV_T))
|
||||
|
||||
CFG_T cfg;
|
||||
COV_T cov;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
endclass
|
187
vendor/lowrisc_ip/dv_lib/dv_base_vseq.sv
vendored
Normal file
187
vendor/lowrisc_ip/dv_lib/dv_base_vseq.sv
vendored
Normal file
|
@ -0,0 +1,187 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class dv_base_vseq #(type RAL_T = dv_base_reg_block,
|
||||
type CFG_T = dv_base_env_cfg,
|
||||
type COV_T = dv_base_env_cov,
|
||||
type VIRTUAL_SEQUENCER_T = dv_base_virtual_sequencer) extends uvm_sequence;
|
||||
`uvm_object_param_utils(dv_base_vseq #(RAL_T, CFG_T, COV_T, VIRTUAL_SEQUENCER_T))
|
||||
`uvm_declare_p_sequencer(VIRTUAL_SEQUENCER_T)
|
||||
|
||||
// number of iterations to run the test seq - please override constraint in extended vseq
|
||||
// randomization for this is disabled in pre_start since we don't want to re-randomize it again
|
||||
rand uint num_trans;
|
||||
|
||||
constraint num_trans_c {
|
||||
num_trans inside {[1:20]};
|
||||
}
|
||||
|
||||
// handles for ease of op
|
||||
CFG_T cfg;
|
||||
RAL_T ral;
|
||||
COV_T cov;
|
||||
|
||||
// knobs to enable pre_start routines
|
||||
bit do_dut_init = 1'b1;
|
||||
bit do_apply_reset = 1'b1;
|
||||
bit do_wait_for_reset = 1'b1;
|
||||
|
||||
// knobs to enable post_start routines
|
||||
bit do_dut_shutdown = 1'b1;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
task pre_start();
|
||||
super.pre_start();
|
||||
cfg = p_sequencer.cfg;
|
||||
cov = p_sequencer.cov;
|
||||
ral = cfg.ral;
|
||||
if (do_dut_init) dut_init("HARD");
|
||||
num_trans.rand_mode(0);
|
||||
endtask
|
||||
|
||||
task body();
|
||||
`uvm_fatal(`gtn, "Need to override this when you extend from this class!")
|
||||
endtask : body
|
||||
|
||||
task post_start();
|
||||
super.post_start();
|
||||
if (do_dut_shutdown) dut_shutdown();
|
||||
endtask
|
||||
|
||||
/*
|
||||
* startup, reset and shutdown related tasks
|
||||
*/
|
||||
virtual task dut_init(string reset_kind = "HARD");
|
||||
if (do_apply_reset) apply_reset(reset_kind);
|
||||
else if (do_wait_for_reset) wait_for_reset(reset_kind);
|
||||
// delay after reset for tl agent check seq_item_port empty
|
||||
#1ps;
|
||||
endtask
|
||||
|
||||
virtual task apply_reset(string kind = "HARD");
|
||||
if (kind == "HARD") begin
|
||||
csr_utils_pkg::reset_asserted();
|
||||
cfg.clk_rst_vif.apply_reset();
|
||||
csr_utils_pkg::reset_deasserted();
|
||||
end
|
||||
if (cfg.has_ral) ral.reset(kind);
|
||||
endtask
|
||||
|
||||
virtual task wait_for_reset(string reset_kind = "HARD",
|
||||
bit wait_for_assert = 1,
|
||||
bit wait_for_deassert = 1);
|
||||
if (wait_for_assert) begin
|
||||
`uvm_info(`gfn, "waiting for rst_n assertion...", UVM_MEDIUM)
|
||||
@(negedge cfg.clk_rst_vif.rst_n);
|
||||
end
|
||||
if (wait_for_deassert) begin
|
||||
`uvm_info(`gfn, "waiting for rst_n de-assertion...", UVM_MEDIUM)
|
||||
@(posedge cfg.clk_rst_vif.rst_n);
|
||||
end
|
||||
`uvm_info(`gfn, "wait_for_reset done", UVM_HIGH)
|
||||
endtask
|
||||
|
||||
// dut shutdown - this is called in post_start if do_dut_shutdown bit is set
|
||||
virtual task dut_shutdown();
|
||||
csr_utils_pkg::wait_no_outstanding_access();
|
||||
endtask
|
||||
|
||||
// function to add csr exclusions of the given type using the csr_excl_item item
|
||||
// arg csr_test_type: this the the type of csr test run - we may want additional exclusions
|
||||
// depending on what test seq we are running
|
||||
// arg csr_excl: this is the csr exclusion object that maintains the list of exclusions
|
||||
// the same object handle is to be passed to csr sequences in csr_seq_lib so that they can query
|
||||
// those exclusions
|
||||
virtual function void add_csr_exclusions(string csr_test_type,
|
||||
csr_excl_item csr_excl,
|
||||
string scope = "ral");
|
||||
`uvm_info(`gfn, "no exclusion item added from this function", UVM_DEBUG)
|
||||
endfunction
|
||||
|
||||
// TODO: temp support, can delete this once all IPs update their exclusion in hjson
|
||||
virtual function csr_excl_item add_and_return_csr_excl(string csr_test_type);
|
||||
add_csr_exclusions(csr_test_type, ral.csr_excl);
|
||||
ral.csr_excl.print_exclusions();
|
||||
return ral.csr_excl;
|
||||
endfunction
|
||||
|
||||
// wrapper task around run_csr_vseq - the purpose is to be able to call this directly for actual
|
||||
// csr tests (as opposed to higher level stress test that could also run csr seq as a fork by
|
||||
// calling run_csr_vseq(..) task)
|
||||
virtual task run_csr_vseq_wrapper(int num_times = 1);
|
||||
string csr_test_type;
|
||||
csr_excl_item csr_excl;
|
||||
|
||||
// env needs to have a ral instance
|
||||
`DV_CHECK_EQ_FATAL(cfg.has_ral, 1'b1)
|
||||
|
||||
// get csr_test_type from plusarg
|
||||
void'($value$plusargs("csr_%0s", csr_test_type));
|
||||
|
||||
// create csr exclusions before running the csr seq
|
||||
csr_excl = add_and_return_csr_excl(csr_test_type);
|
||||
|
||||
// run the csr seq
|
||||
for (int i = 1; i <= num_times; i++) begin
|
||||
`uvm_info(`gfn, $sformatf("running csr %0s vseq iteration %0d/%0d",
|
||||
csr_test_type, i, num_times), UVM_LOW)
|
||||
run_csr_vseq(.csr_test_type(csr_test_type), .csr_excl(csr_excl));
|
||||
end
|
||||
endtask
|
||||
|
||||
// capture the entire csr seq as a task that can be overridden if desired
|
||||
// arg csr_test_type: what csr test to run {hw_reset, rw, bit_bash, aliasing}
|
||||
// arg csr_excl: csr exclusion object - needs to be created and exclusions set before call
|
||||
// arg num_test_csrs:instead of testing the entire ral model or passing test chunk info via
|
||||
// plusarg, provide ability to set a random number of csrs to test from higher level sequence
|
||||
virtual task run_csr_vseq(string csr_test_type = "",
|
||||
csr_excl_item csr_excl = null,
|
||||
int num_test_csrs = 0,
|
||||
bit do_rand_wr_and_reset = 1);
|
||||
csr_base_seq m_csr_seq;
|
||||
|
||||
// env needs to have a ral instance
|
||||
`DV_CHECK_EQ_FATAL(cfg.has_ral, 1'b1)
|
||||
|
||||
// check which csr test type
|
||||
case (csr_test_type)
|
||||
"hw_reset": csr_base_seq::type_id::set_type_override(csr_hw_reset_seq::get_type());
|
||||
"rw" : csr_base_seq::type_id::set_type_override(csr_rw_seq::get_type());
|
||||
"bit_bash": csr_base_seq::type_id::set_type_override(csr_bit_bash_seq::get_type());
|
||||
"aliasing": csr_base_seq::type_id::set_type_override(csr_aliasing_seq::get_type());
|
||||
"mem_walk": csr_base_seq::type_id::set_type_override(csr_mem_walk_seq::get_type());
|
||||
default : `uvm_fatal(`gfn, $sformatf("specified opt is invalid: +csr_%0s", csr_test_type))
|
||||
endcase
|
||||
|
||||
// if hw_reset test, then write all CSRs first and reset the whole dut
|
||||
if (csr_test_type == "hw_reset" && do_rand_wr_and_reset) begin
|
||||
string reset_type = "HARD";
|
||||
csr_write_seq m_csr_write_seq;
|
||||
|
||||
// run write-only sequence to randomize the csr values
|
||||
m_csr_write_seq = csr_write_seq::type_id::create("m_csr_write_seq");
|
||||
m_csr_write_seq.models.push_back(ral);
|
||||
m_csr_write_seq.set_csr_excl_item(csr_excl);
|
||||
m_csr_write_seq.external_checker = cfg.en_scb;
|
||||
m_csr_write_seq.start(null);
|
||||
|
||||
// run dut_shutdown before asserting reset
|
||||
dut_shutdown();
|
||||
|
||||
// issue reset
|
||||
void'($value$plusargs("do_reset=%0s", reset_type));
|
||||
dut_init(reset_type);
|
||||
end
|
||||
|
||||
// create base csr seq and pass our ral
|
||||
m_csr_seq = csr_base_seq::type_id::create("m_csr_seq");
|
||||
m_csr_seq.num_test_csrs = num_test_csrs;
|
||||
m_csr_seq.models.push_back(ral);
|
||||
m_csr_seq.set_csr_excl_item(csr_excl);
|
||||
m_csr_seq.external_checker = cfg.en_scb;
|
||||
m_csr_seq.start(null);
|
||||
endtask
|
||||
|
||||
endclass
|
42
vendor/lowrisc_ip/dv_lib/dv_lib.core
vendored
Normal file
42
vendor/lowrisc_ip/dv_lib/dv_lib.core
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
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:dv_lib"
|
||||
description: "DV base class UVM library"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:dv_utils
|
||||
- lowrisc:dv:csr_utils
|
||||
files:
|
||||
- dv_lib_pkg.sv
|
||||
|
||||
- dv_base_reg_field.sv: {is_include_file: true}
|
||||
- dv_base_reg.sv: {is_include_file: true}
|
||||
- dv_base_mem.sv: {is_include_file: true}
|
||||
- dv_base_reg_block.sv: {is_include_file: true}
|
||||
- dv_base_reg_map.sv: {is_include_file: true}
|
||||
|
||||
- dv_base_agent_cfg.sv: {is_include_file: true}
|
||||
- dv_base_agent_cov.sv: {is_include_file: true}
|
||||
- dv_base_monitor.sv: {is_include_file: true}
|
||||
- dv_base_sequencer.sv: {is_include_file: true}
|
||||
- dv_base_driver.sv: {is_include_file: true}
|
||||
- dv_base_agent.sv: {is_include_file: true}
|
||||
- dv_base_seq.sv: {is_include_file: true}
|
||||
|
||||
- dv_base_env_cfg.sv: {is_include_file: true}
|
||||
- dv_base_env_cov.sv: {is_include_file: true}
|
||||
- dv_base_virtual_sequencer.sv: {is_include_file: true}
|
||||
- dv_base_scoreboard.sv: {is_include_file: true}
|
||||
- dv_base_env.sv: {is_include_file: true}
|
||||
- dv_base_vseq.sv: {is_include_file: true}
|
||||
- dv_base_test.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
51
vendor/lowrisc_ip/dv_lib/dv_lib_pkg.sv
vendored
Normal file
51
vendor/lowrisc_ip/dv_lib/dv_lib_pkg.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
package dv_lib_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import top_pkg::*;
|
||||
import dv_utils_pkg::*;
|
||||
import csr_utils_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
||||
// package variables
|
||||
string msg_id = "dv_lib_pkg";
|
||||
|
||||
// package sources
|
||||
// base ral
|
||||
`include "dv_base_reg_field.sv"
|
||||
`include "dv_base_reg.sv"
|
||||
`include "dv_base_mem.sv"
|
||||
`include "dv_base_reg_block.sv"
|
||||
`include "dv_base_reg_map.sv"
|
||||
|
||||
// base agent
|
||||
`include "dv_base_agent_cfg.sv"
|
||||
`include "dv_base_agent_cov.sv"
|
||||
`include "dv_base_monitor.sv"
|
||||
`include "dv_base_sequencer.sv"
|
||||
`include "dv_base_driver.sv"
|
||||
`include "dv_base_agent.sv"
|
||||
|
||||
// base seq
|
||||
`include "dv_base_seq.sv"
|
||||
|
||||
// base env
|
||||
`include "dv_base_env_cfg.sv"
|
||||
`include "dv_base_env_cov.sv"
|
||||
`include "dv_base_virtual_sequencer.sv"
|
||||
`include "dv_base_scoreboard.sv"
|
||||
`include "dv_base_env.sv"
|
||||
|
||||
// base test vseq
|
||||
`include "dv_base_vseq.sv"
|
||||
|
||||
// base test
|
||||
`include "dv_base_test.sv"
|
||||
|
||||
endpackage
|
0
vendor/lowrisc_ip/dv_utils/README.md
vendored
Normal file
0
vendor/lowrisc_ip/dv_utils/README.md
vendored
Normal file
288
vendor/lowrisc_ip/dv_utils/dv_macros.svh
vendored
Normal file
288
vendor/lowrisc_ip/dv_utils/dv_macros.svh
vendored
Normal file
|
@ -0,0 +1,288 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// UVM speficic macros
|
||||
`ifndef gfn
|
||||
`define gfn get_full_name()
|
||||
`endif
|
||||
|
||||
`ifndef gtn
|
||||
`define gtn get_type_name()
|
||||
`endif
|
||||
|
||||
`ifndef gn
|
||||
`define gn get_name()
|
||||
`endif
|
||||
|
||||
`ifndef gmv
|
||||
`define gmv(csr) csr.get_mirrored_value()
|
||||
`endif
|
||||
|
||||
// cast base class obj holding extended class handle to extended class handle;
|
||||
// throw error if cast fails
|
||||
`ifndef downcast
|
||||
`define downcast(EXT_, BASE_, MSG_="", SEV_=fatal, ID_=`gfn) \
|
||||
if (!$cast(EXT_, BASE_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf({"Cast failed: base class variable %0s ", \
|
||||
"does not hold extended class %0s handle %s"}, \
|
||||
`"BASE_`", `"EXT_`", MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
// Note, UVM provides a macro `uvm_new_func -- which only applies to uvm_components
|
||||
`ifndef uvm_object_new
|
||||
`define uvm_object_new \
|
||||
function new (string name=""); \
|
||||
super.new(name); \
|
||||
endfunction : new
|
||||
`endif
|
||||
|
||||
`ifndef uvm_create_obj
|
||||
`define uvm_create_obj(_type_name_, _inst_name_) \
|
||||
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`");
|
||||
`endif
|
||||
|
||||
`ifndef uvm_component_new
|
||||
`define uvm_component_new \
|
||||
function new (string name="", uvm_component parent=null); \
|
||||
super.new(name, parent); \
|
||||
endfunction : new
|
||||
`endif
|
||||
|
||||
`ifndef uvm_create_comp
|
||||
`define uvm_create_comp(_type_name_, _inst_name_) \
|
||||
_inst_name_ = _type_name_::type_id::create(`"_inst_name_`", this);
|
||||
`endif
|
||||
|
||||
// Common check macros used by DV_CHECK error and fatal macros.
|
||||
// Note: Should not be called by user code
|
||||
`ifndef DV_CHECK
|
||||
`define DV_CHECK(T_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(T_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed (%s) %s ", `"T_`", MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_EQ
|
||||
`define DV_CHECK_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ == EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s == %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_NE
|
||||
`define DV_CHECK_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ != EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s != %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_CASE_EQ
|
||||
`define DV_CHECK_CASE_EQ(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ === EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s === %s (0x%0h [%0b] vs 0x%0h [%0b]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_CASE_NE
|
||||
`define DV_CHECK_CASE_NE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ !== EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s !== %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_LT
|
||||
`define DV_CHECK_LT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ < EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s < %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_GT
|
||||
`define DV_CHECK_GT(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ > EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s > %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_LE
|
||||
`define DV_CHECK_LE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ <= EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s <= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_GE
|
||||
`define DV_CHECK_GE(ACT_, EXP_, MSG_="", SEV_=error, ID_=`gfn) \
|
||||
if (!(ACT_ >= EXP_)) begin \
|
||||
`uvm_``SEV_(ID_, $sformatf("Check failed %s >= %s (%0d [0x%0h] vs %0d [0x%0h]) %s", \
|
||||
`"ACT_`", `"EXP_`", ACT_, ACT_, EXP_, EXP_, MSG_)) \
|
||||
end
|
||||
`endif
|
||||
|
||||
// Fatal version of the checks
|
||||
`ifndef DV_CHECK_FATAL
|
||||
`define DV_CHECK_FATAL(T_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK(T_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_EQ_FATAL
|
||||
`define DV_CHECK_EQ_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_EQ(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_NE_FATAL
|
||||
`define DV_CHECK_NE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_NE(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_LT_FATAL
|
||||
`define DV_CHECK_LT_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_LT(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_GT_FATAL
|
||||
`define DV_CHECK_GT_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_GT(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_LE_FATAL
|
||||
`define DV_CHECK_LE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_LE(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
`ifndef DV_CHECK_GE_FATAL
|
||||
`define DV_CHECK_GE_FATAL(ACT_, EXP_, MSG_="", ID_=`gfn) \
|
||||
`DV_CHECK_GE(ACT_, EXP_, MSG_, fatal, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common foo.randomize() + fatal check
|
||||
`ifndef DV_CHECK_RANDOMIZE_FATAL
|
||||
`define DV_CHECK_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(VAR_.randomize(), MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common foo.randomize() with { } + fatal check
|
||||
`ifndef DV_CHECK_RANDOMIZE_WITH_FATAL
|
||||
`define DV_CHECK_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(VAR_.randomize() with {WITH_C_}, MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common std::randomize(foo) + fatal check
|
||||
`ifndef DV_CHECK_STD_RANDOMIZE_FATAL
|
||||
`define DV_CHECK_STD_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(std::randomize(VAR_), MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common std::randomize(foo) with { } + fatal check
|
||||
`ifndef DV_CHECK_STD_RANDOMIZE_WITH_FATAL
|
||||
`define DV_CHECK_STD_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_, MSG_="Randomization failed!",ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(std::randomize(VAR_) with {WITH_C_}, MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common this.randomize(foo) + fatal check
|
||||
`ifndef DV_CHECK_MEMBER_RANDOMIZE_FATAL
|
||||
`define DV_CHECK_MEMBER_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(this.randomize(VAR_), MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// Shorthand for common this.randomize(foo) with { } + fatal check
|
||||
`ifndef DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL
|
||||
`define DV_CHECK_MEMBER_RANDOMIZE_WITH_FATAL(VAR_, C_, MSG_="Randomization failed!", ID_=`gfn) \
|
||||
`DV_CHECK_FATAL(this.randomize(VAR_) with {C_}, MSG_, ID_)
|
||||
`endif
|
||||
|
||||
// print static/dynamic 1d array or queue
|
||||
`ifndef DV_PRINT_ARR_CONTENTS
|
||||
`define DV_PRINT_ARR_CONTENTS(ARR_, V_=UVM_MEDIUM, ID_=`gfn) \
|
||||
foreach (ARR_[i]) begin \
|
||||
`uvm_info(ID_, $sformatf("%s[%0d] = 0x%0d[0x%0h]", `"ARR_`", i, ARR_[i], ARR_[i]), V_) \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_TLM_FIFO_CONTENTS
|
||||
`define DV_EOT_PRINT_TLM_FIFO_CONTENTS(TYP_, FIFO_, SEV_=error, ID_=`gfn) \
|
||||
while (!FIFO_.is_empty()) begin \
|
||||
TYP_ item; \
|
||||
void'(FIFO_.try_get(item)); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"FIFO_`", item.sprint())) \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_TLM_FIFO_ARR_CONTENTS
|
||||
`define DV_EOT_PRINT_TLM_FIFO_ARR_CONTENTS(TYP_, FIFO_, SEV_=error, ID_=`gfn) \
|
||||
foreach (FIFO_[i]) begin \
|
||||
while (!FIFO_[i].is_empty()) begin \
|
||||
TYP_ item; \
|
||||
void'(FIFO_[i].try_get(item)); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s[%0d] item uncompared:\n%s", `"FIFO_`", i, item.sprint())) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_Q_CONTENTS
|
||||
`define DV_EOT_PRINT_Q_CONTENTS(TYP_, Q_, SEV_=error, ID_=`gfn) \
|
||||
while (Q_.size() != 0) begin \
|
||||
TYP_ item = Q_.pop_front(); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"Q_`", item.sprint())) \
|
||||
end
|
||||
`endif
|
||||
|
||||
// print non-empty tlm fifos that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_Q_ARR_CONTENTS
|
||||
`define DV_EOT_PRINT_Q_ARR_CONTENTS(TYP_, Q_, SEV_=error, ID_=`gfn) \
|
||||
foreach (Q_[i]) begin \
|
||||
while (Q_[i].size() != 0) begin \
|
||||
TYP_ item = Q_[i].pop_front(); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s[%0d] item uncompared:\n%s", `"Q_`", i, item.sprint())) \
|
||||
end \
|
||||
end
|
||||
`endif
|
||||
|
||||
// check for non-empty mailbox and print items that were uncompared at end of test
|
||||
`ifndef DV_EOT_PRINT_MAILBOX_CONTENTS
|
||||
`define DV_EOT_PRINT_MAILBOX_CONTENTS(TYP_, MAILBOX_, SEV_=error, ID_=`gfn) \
|
||||
while (MAILBOX_.num() != 0) begin \
|
||||
TYP_ item; \
|
||||
void'(MAILBOX_.try_get(item)); \
|
||||
`uvm_``SEV_(ID_, $sformatf("%s item uncompared:\n%s", `"MAILBOX_`", item.sprint())) \
|
||||
end
|
||||
`endif
|
||||
|
||||
// get parity - implemented as a macro so that it can be invoked in constraints as well
|
||||
`ifndef GET_PARITY
|
||||
`define GET_PARITY(val, odd=0) (^val ^ odd)
|
||||
`endif
|
||||
|
||||
// wait a task or statement with timer watchdog
|
||||
// input WAIT_ need to be a statement. Here are some examples
|
||||
// `DV_SPINWAIT(wait(...);, "Wait for ...")
|
||||
// `DV_SPINWAIT(
|
||||
// while (1) begin
|
||||
// ...
|
||||
// end)
|
||||
`ifndef DV_SPINWAIT
|
||||
`define DV_SPINWAIT(WAIT_, MSG_ = "", TIMEOUT_NS_ = default_timeout_ns, ID_ =`gfn) \
|
||||
fork begin \
|
||||
fork \
|
||||
begin \
|
||||
WAIT_ \
|
||||
end \
|
||||
begin \
|
||||
wait_timeout(TIMEOUT_NS_, ID_, MSG_); \
|
||||
end \
|
||||
join_any \
|
||||
disable fork; \
|
||||
end join
|
||||
`endif
|
87
vendor/lowrisc_ip/dv_utils/dv_report_server.sv
vendored
Normal file
87
vendor/lowrisc_ip/dv_utils/dv_report_server.sv
vendored
Normal file
|
@ -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
|
||||
|
||||
// Standardize look & feel of report phase and uvm logging messages.
|
||||
class dv_report_server extends uvm_default_report_server;
|
||||
|
||||
bit show_file_line = 1'b1;
|
||||
bit use_default_uvm_report_message_format = 1'b0;
|
||||
|
||||
function new (string name = "");
|
||||
super.new(name);
|
||||
// provide ability to override these knobs over cli
|
||||
void'($value$plusargs("show_file_line=%0b", show_file_line));
|
||||
void'($value$plusargs("use_default_uvm_report_message_format=%0b",
|
||||
use_default_uvm_report_message_format));
|
||||
endfunction
|
||||
|
||||
function void report_summarize(UVM_FILE file = 0);
|
||||
int num_uvm_warning;
|
||||
int num_uvm_error;
|
||||
int num_uvm_fatal;
|
||||
|
||||
num_uvm_warning = get_severity_count(UVM_WARNING);
|
||||
num_uvm_error = get_severity_count(UVM_ERROR);
|
||||
num_uvm_fatal = get_severity_count(UVM_FATAL);
|
||||
|
||||
// Print default summary report
|
||||
super.report_summarize(file);
|
||||
|
||||
// Print final test pass-fail - external tool can use this signature for test status
|
||||
// Treat UVM_WARNINGs as a sign of test failure since it could silently result in false pass
|
||||
if ((num_uvm_warning + num_uvm_error + num_uvm_fatal) == 0) begin
|
||||
$display("\nTEST PASSED CHECKS");
|
||||
$display(" _____ _ _ _ ");
|
||||
$display("|_ _|__ ___| |_ _ __ __ _ ___ ___ ___ __| | |");
|
||||
$display(" | |/ _ \\/ __| __| | '_ \\ / _` / __/ __|/ _ \\/ _` | |");
|
||||
$display(" | | __/\\__ \\ |_ | |_) | (_| \\__ \\__ \\ __/ (_| |_|");
|
||||
$display(" |_|\\___||___/\\__| | .__/ \\__,_|___/___/\\___|\\__,_(_)");
|
||||
$display(" |_| \n");
|
||||
end
|
||||
else begin
|
||||
$display("\nTEST FAILED CHECKS");
|
||||
$display(" _____ _ __ _ _ _ _ ");
|
||||
$display("|_ _|__ ___| |_ / _| __ _(_) | ___ __| | |");
|
||||
$display(" | |/ _ \\/ __| __| | |_ / _` | | |/ _ \\/ _` | |");
|
||||
$display(" | | __/\\__ \\ |_ | _| (_| | | | __/ (_| |_|");
|
||||
$display(" |_|\\___||___/\\__| |_| \\__,_|_|_|\\___|\\__,_(_)\n");
|
||||
end
|
||||
|
||||
endfunction
|
||||
|
||||
// Override default messaging format to standard "pretty" format for all testbenches
|
||||
virtual function string compose_report_message(uvm_report_message report_message,
|
||||
string report_object_name = "");
|
||||
|
||||
if (use_default_uvm_report_message_format) begin
|
||||
return (super.compose_report_message(report_message, report_object_name));
|
||||
end else begin
|
||||
uvm_severity severity = report_message.get_severity();
|
||||
string filename = report_message.get_filename();
|
||||
int line = report_message.get_line();
|
||||
string obj_name = report_message.get_report_object().get_full_name();
|
||||
string id = report_message.get_id();
|
||||
string message = report_message.get_message();
|
||||
string file_line;
|
||||
|
||||
if (show_file_line && filename != "") begin
|
||||
filename = get_no_hier_filename(filename);
|
||||
file_line = $sformatf("(%0s:%0d) ", filename, line);
|
||||
end
|
||||
obj_name = {obj_name, ((obj_name != "") ? " " : "")};
|
||||
compose_report_message = $sformatf({"%0s @ %t: ", file_line, obj_name, "[%0s] %0s"},
|
||||
severity.name(), $realtime, id, message);
|
||||
return compose_report_message;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// get we don't really want the full path to the filename
|
||||
// this should be reasonably lightweight
|
||||
local function string get_no_hier_filename(string filename);
|
||||
int idx;
|
||||
for (idx = filename.len() - 1; idx >= 0; idx--) if (filename[idx] == "/") break;
|
||||
return (filename.substr(idx + 1, filename.len() - 1));
|
||||
endfunction
|
||||
|
||||
endclass
|
23
vendor/lowrisc_ip/dv_utils/dv_utils.core
vendored
Normal file
23
vendor/lowrisc_ip/dv_utils/dv_utils.core
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
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:dv_utils"
|
||||
description: "DV utilities"
|
||||
|
||||
filesets:
|
||||
files_dv:
|
||||
depend:
|
||||
- lowrisc:dv:common_ifs
|
||||
- lowrisc:prim:assert:0.1
|
||||
- lowrisc:constants:top_pkg
|
||||
files:
|
||||
- dv_utils_pkg.sv
|
||||
- dv_macros.svh: {is_include_file: true}
|
||||
- dv_report_server.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- files_dv
|
145
vendor/lowrisc_ip/dv_utils/dv_utils_pkg.sv
vendored
Normal file
145
vendor/lowrisc_ip/dv_utils/dv_utils_pkg.sv
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package dv_utils_pkg;
|
||||
// dep packages
|
||||
import uvm_pkg::*;
|
||||
import top_pkg::*;
|
||||
|
||||
// macro includes
|
||||
`include "dv_macros.svh"
|
||||
`include "uvm_macros.svh"
|
||||
|
||||
// common parameters used across all benches
|
||||
parameter int NUM_MAX_INTERRUPTS = 32;
|
||||
parameter int NUM_MAX_ALERTS = 32;
|
||||
|
||||
// types & variables
|
||||
typedef bit [31:0] uint;
|
||||
typedef bit [7:0] uint8;
|
||||
typedef bit [15:0] uint16;
|
||||
typedef bit [31:0] uint32;
|
||||
typedef bit [63:0] uint64;
|
||||
|
||||
// typedef parameterized pins_if for ease of implementation for interrupts and alerts
|
||||
typedef virtual pins_if #(NUM_MAX_INTERRUPTS) intr_vif;
|
||||
typedef virtual pins_if #(1) devmode_vif;
|
||||
typedef virtual pins_if #(1) tlul_assert_ctrl_vif;
|
||||
|
||||
// interface direction / mode - Host or Device
|
||||
typedef enum bit {
|
||||
Host,
|
||||
Device
|
||||
} if_mode_e;
|
||||
|
||||
// speed for the clock
|
||||
typedef enum int {
|
||||
ClkFreq24Mhz = 24,
|
||||
ClkFreq25Mhz = 25,
|
||||
ClkFreq48Mhz = 48,
|
||||
ClkFreq50Mhz = 50,
|
||||
ClkFreq100Mhz = 100
|
||||
} clk_freq_mhz_e;
|
||||
|
||||
// compare operator types
|
||||
typedef enum {
|
||||
CompareOpEq,
|
||||
CompareOpCaseEq,
|
||||
CompareOpNe,
|
||||
CompareOpCaseNe,
|
||||
CompareOpGt,
|
||||
CompareOpGe,
|
||||
CompareOpLt,
|
||||
CompareOpLe
|
||||
} compare_op_e;
|
||||
|
||||
// mem address struct
|
||||
typedef struct {
|
||||
uvm_reg_addr_t start_addr;
|
||||
uvm_reg_addr_t end_addr;
|
||||
} addr_range_t;
|
||||
|
||||
// Enum representing a bus operation type - read or write.
|
||||
typedef enum bit {
|
||||
BusOpWrite = 1'b0,
|
||||
BusOpRead = 1'b1
|
||||
} bus_op_e;
|
||||
|
||||
// Enum representing a type of host requests - read only, write only or random read & write
|
||||
typedef enum int {
|
||||
HostReqNone = 0,
|
||||
HostReqReadOnly = 1,
|
||||
HostReqWriteOnly = 2,
|
||||
HostReqReadWrite = 3
|
||||
} host_req_type_e;
|
||||
|
||||
string msg_id = "dv_utils_pkg";
|
||||
|
||||
// Simple function to set max errors before quitting sim
|
||||
function automatic void set_max_quit_count(int n);
|
||||
uvm_report_server report_server = uvm_report_server::get_server();
|
||||
report_server.set_max_quit_count(n);
|
||||
endfunction
|
||||
|
||||
// return if uvm_fatal occurred
|
||||
function automatic bit has_uvm_fatal_occurred();
|
||||
uvm_report_server report_server = uvm_report_server::get_server();
|
||||
return report_server.get_severity_count(UVM_FATAL) > 0;
|
||||
endfunction
|
||||
|
||||
// task that waits for the specfied timeout
|
||||
task automatic wait_timeout(input uint timeout_ns,
|
||||
input string error_msg_id = msg_id,
|
||||
input string error_msg = "timeout occurred!",
|
||||
input bit report_fatal = 1);
|
||||
#(timeout_ns * 1ns);
|
||||
if (report_fatal) `uvm_fatal(error_msg_id, error_msg)
|
||||
else `uvm_error(error_msg_id, error_msg)
|
||||
endtask : wait_timeout
|
||||
|
||||
// get masked data based on provided byte mask; if csr reg handle is provided (optional) then
|
||||
// masked bytes from csr's mirrored value are returned, else masked bytes are 0's
|
||||
function automatic bit [TL_DW-1:0] get_masked_data(bit [TL_DW-1:0] data,
|
||||
bit [TL_DBW-1:0] mask,
|
||||
uvm_reg csr = null);
|
||||
bit [TL_DW-1:0] csr_data;
|
||||
csr_data = (csr != null) ? csr.get_mirrored_value() : '0;
|
||||
get_masked_data = data;
|
||||
foreach (mask[i])
|
||||
if (~mask[i]) get_masked_data[i * 8 +: 8] = csr_data[i * 8 +: 8];
|
||||
endfunction
|
||||
|
||||
// get absolute value of the input. Usage: absolute(val) or absolute(a - b)
|
||||
function automatic uint absolute(int val);
|
||||
return val >= 0 ? val : -val;
|
||||
endfunction
|
||||
|
||||
// endian swap
|
||||
function automatic logic [31:0] endian_swap(logic [31:0] data);
|
||||
return {<<8{data}};
|
||||
endfunction
|
||||
|
||||
// create a sequence by name and return the handle of uvm_sequence
|
||||
function automatic uvm_sequence create_seq_by_name(string seq_name);
|
||||
uvm_object obj;
|
||||
uvm_factory factory;
|
||||
uvm_sequence seq;
|
||||
|
||||
factory = uvm_factory::get();
|
||||
obj = factory.create_object_by_name(seq_name, "", seq_name);
|
||||
if (obj == null) begin
|
||||
// print factory overrides to help debug
|
||||
factory.print(1);
|
||||
`uvm_fatal(msg_id, $sformatf("could not create %0s seq", seq_name))
|
||||
end
|
||||
if (!$cast(seq, obj)) begin
|
||||
`uvm_fatal(msg_id, $sformatf("cast failed - %0s is not a uvm_sequence", seq_name))
|
||||
end
|
||||
return seq;
|
||||
endfunction
|
||||
|
||||
// sources
|
||||
`include "dv_report_server.sv"
|
||||
|
||||
endpackage
|
873
vendor/lowrisc_ip/dvsim/Deploy.py
vendored
Normal file
873
vendor/lowrisc_ip/dvsim/Deploy.py
vendored
Normal file
|
@ -0,0 +1,873 @@
|
|||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
r"""
|
||||
Classes
|
||||
"""
|
||||
|
||||
import logging as log
|
||||
import pprint
|
||||
import random
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
|
||||
import hjson
|
||||
from tabulate import tabulate
|
||||
|
||||
from utils import *
|
||||
|
||||
|
||||
class Deploy():
|
||||
"""
|
||||
Abstraction for deploying builds and runs.
|
||||
"""
|
||||
|
||||
# Timer in hours, minutes and seconds.
|
||||
hh = 0
|
||||
mm = 0
|
||||
ss = 0
|
||||
|
||||
# Maintain a list of dispatched items.
|
||||
dispatch_counter = 0
|
||||
|
||||
# Misc common deploy settings.
|
||||
print_legend = True
|
||||
print_interval = 5
|
||||
max_parallel = 16
|
||||
max_odirs = 5
|
||||
|
||||
def __self_str__(self):
|
||||
if log.getLogger().isEnabledFor(VERBOSE):
|
||||
return pprint.pformat(self.__dict__)
|
||||
else:
|
||||
ret = self.cmd
|
||||
if self.sub != []: ret += "\nSub:\n" + str(self.sub)
|
||||
return ret
|
||||
|
||||
def __str__(self):
|
||||
return self.__self_str__()
|
||||
|
||||
def __repr__(self):
|
||||
return self.__self_str__()
|
||||
|
||||
def __init__(self, sim_cfg):
|
||||
# Cross ref the whole cfg object for ease.
|
||||
self.sim_cfg = sim_cfg
|
||||
|
||||
# Common vars
|
||||
self.identifier = ""
|
||||
self.cmd = ""
|
||||
self.odir = ""
|
||||
self.log = ""
|
||||
self.fail_msg = ""
|
||||
|
||||
# Flag to indicate whether to 'overwrite' if odir already exists,
|
||||
# or to backup the existing one and create a new one.
|
||||
# For builds, we want to overwrite existing to leverage the tools'
|
||||
# incremental / partition compile features. For runs, we may want to
|
||||
# create a new one.
|
||||
self.renew_odir = False
|
||||
|
||||
# List of vars required to be exported to sub-shell
|
||||
self.exports = {}
|
||||
|
||||
# Deploy sub commands
|
||||
self.sub = []
|
||||
|
||||
# Process
|
||||
self.process = None
|
||||
self.log_fd = None
|
||||
self.status = None
|
||||
|
||||
# These are command, output directory and log file
|
||||
self.mandatory_misc_attrs.update({
|
||||
"name": False,
|
||||
"build_mode": False,
|
||||
"flow_makefile": False,
|
||||
"exports": False,
|
||||
"dry_run": False
|
||||
})
|
||||
|
||||
# Function to parse a dict and extract the mandatory cmd and misc attrs.
|
||||
def parse_dict(self, ddict):
|
||||
if not hasattr(self, "target"):
|
||||
log.error(
|
||||
"Class %s does not have the mandatory attribute \"target\" defined",
|
||||
self.__class__.__name__)
|
||||
sys.exit(1)
|
||||
|
||||
ddict_keys = ddict.keys()
|
||||
for key in self.mandatory_cmd_attrs.keys():
|
||||
if self.mandatory_cmd_attrs[key] == False:
|
||||
if key in ddict_keys:
|
||||
setattr(self, key, ddict[key])
|
||||
self.mandatory_cmd_attrs[key] = True
|
||||
|
||||
for key in self.mandatory_misc_attrs.keys():
|
||||
if self.mandatory_misc_attrs[key] == False:
|
||||
if key in ddict_keys:
|
||||
setattr(self, key, ddict[key])
|
||||
self.mandatory_misc_attrs[key] = True
|
||||
|
||||
def __post_init__(self):
|
||||
# Ensure all mandatory attrs are set
|
||||
for attr in self.mandatory_cmd_attrs.keys():
|
||||
if self.mandatory_cmd_attrs[attr] is False:
|
||||
log.error("Attribute \"%s\" not found for \"%s\".", attr,
|
||||
self.name)
|
||||
sys.exit(1)
|
||||
|
||||
for attr in self.mandatory_misc_attrs.keys():
|
||||
if self.mandatory_misc_attrs[attr] is False:
|
||||
log.error("Attribute \"%s\" not found for \"%s\".", attr,
|
||||
self.name)
|
||||
sys.exit(1)
|
||||
|
||||
# Recursively search and replace wildcards
|
||||
self.__dict__ = find_and_substitute_wildcards(self.__dict__,
|
||||
self.__dict__)
|
||||
|
||||
# Set identifier.
|
||||
self.identifier = self.sim_cfg.name + ":" + self.name
|
||||
|
||||
# Set the command, output dir and log
|
||||
self.odir = getattr(self, self.target + "_dir")
|
||||
# Set the output dir link name to the basename of odir (by default)
|
||||
self.odir_ln = os.path.basename(os.path.normpath(self.odir))
|
||||
self.log = self.odir + "/" + self.target + ".log"
|
||||
|
||||
# If using LSF, redirect stdout and err to the log file
|
||||
self.cmd = self.construct_cmd()
|
||||
|
||||
def construct_cmd(self):
|
||||
cmd = "make -f " + self.flow_makefile + " " + self.target
|
||||
if self.dry_run is True:
|
||||
cmd += " -n"
|
||||
for attr in self.mandatory_cmd_attrs.keys():
|
||||
value = getattr(self, attr)
|
||||
if type(value) is list:
|
||||
pretty_value = []
|
||||
for item in value:
|
||||
pretty_value.append(item.strip())
|
||||
value = " ".join(pretty_value)
|
||||
if type(value) is bool:
|
||||
value = int(value)
|
||||
if type(value) is str:
|
||||
value = value.strip()
|
||||
cmd += " " + attr + "=\"" + str(value) + "\""
|
||||
|
||||
# TODO: If not running locally, redirect stdout and err to the log file
|
||||
# self.cmd += " > " + self.log + " 2>&1 &"
|
||||
return cmd
|
||||
|
||||
def dispatch_cmd(self):
|
||||
self.exports.update(os.environ)
|
||||
args = shlex.split(self.cmd)
|
||||
try:
|
||||
# If renew_odir flag is True - then move it.
|
||||
if self.renew_odir: self.odir_limiter(odir=self.odir)
|
||||
os.system("mkdir -p " + self.odir)
|
||||
os.system("ln -s " + self.odir + " " + self.sim_cfg.links['D'] +
|
||||
'/' + self.odir_ln)
|
||||
f = open(self.log, "w")
|
||||
self.process = subprocess.Popen(args,
|
||||
bufsize=4096,
|
||||
universal_newlines=True,
|
||||
stdout=f,
|
||||
stderr=f,
|
||||
env=self.exports)
|
||||
self.log_fd = f
|
||||
self.status = "D"
|
||||
Deploy.dispatch_counter += 1
|
||||
except IOError:
|
||||
log.error('IO Error: See %s', self.log)
|
||||
if self.log_fd: self.log_fd.close()
|
||||
self.status = "K"
|
||||
|
||||
def odir_limiter(self, odir, max_odirs=-1):
|
||||
'''Function to backup previously run output directory to maintain a
|
||||
history of a limited number of output directories. It deletes the output
|
||||
directory with the oldest timestamps, if the limit is reached. It returns
|
||||
a list of directories that remain after deletion.
|
||||
Arguments:
|
||||
odir: The output directory to backup
|
||||
max_odirs: Maximum output directories to maintain as history.
|
||||
|
||||
Returns:
|
||||
dirs: Space-separated list of directories that remain after deletion.
|
||||
'''
|
||||
try:
|
||||
# If output directory exists, back it up.
|
||||
if os.path.exists(odir):
|
||||
ts = run_cmd("date '+" + self.sim_cfg.ts_format + "' -d \"" +
|
||||
"$(stat -c '%y' " + odir + ")\"")
|
||||
os.system('mv ' + odir + " " + odir + "_" + ts)
|
||||
except IOError:
|
||||
log.error('Failed to back up existing output directory %s', odir)
|
||||
|
||||
dirs = ""
|
||||
# Delete older directories.
|
||||
try:
|
||||
pdir = os.path.realpath(odir + "/..")
|
||||
# Fatal out if pdir got set to root.
|
||||
if pdir == "/":
|
||||
log.fatal(
|
||||
"Something went wrong while processing \"%s\": odir = \"%s\"",
|
||||
self.name, odir)
|
||||
sys.exit(1)
|
||||
|
||||
if os.path.exists(pdir):
|
||||
find_cmd = "find " + pdir + " -mindepth 1 -maxdepth 1 -type d "
|
||||
dirs = run_cmd(find_cmd)
|
||||
dirs = dirs.replace('\n', ' ')
|
||||
list_dirs = dirs.split()
|
||||
num_dirs = len(list_dirs)
|
||||
if max_odirs == -1: max_odirs = self.max_odirs
|
||||
num_rm_dirs = num_dirs - max_odirs
|
||||
if num_rm_dirs > -1:
|
||||
rm_dirs = run_cmd(find_cmd +
|
||||
"-printf '%T+ %p\n' | sort | head -n " +
|
||||
str(num_rm_dirs + 1) +
|
||||
" | awk '{print $2}'")
|
||||
rm_dirs = rm_dirs.replace('\n', ' ')
|
||||
dirs = dirs.replace(rm_dirs, "")
|
||||
os.system("/bin/rm -rf " + rm_dirs)
|
||||
except IOError:
|
||||
log.error("Failed to delete old run directories!")
|
||||
return dirs
|
||||
|
||||
def set_status(self):
|
||||
self.status = 'P'
|
||||
if self.dry_run is False:
|
||||
for fail_pattern in self.fail_patterns:
|
||||
# Return error messege with the following 4 lines.
|
||||
grep_cmd = "grep -m 1 -A 4 -E \'" + fail_pattern + "\' " + self.log
|
||||
(status, rslt) = subprocess.getstatusoutput(grep_cmd)
|
||||
if rslt:
|
||||
msg = "```\n{}\n```\n".format(rslt)
|
||||
self.fail_msg += msg
|
||||
log.log(VERBOSE, msg)
|
||||
self.status = 'F'
|
||||
break
|
||||
|
||||
# If fail patterns were not encountered, but the job returned with non-zero exit code
|
||||
# for whatever reason, then show the last 10 lines of the log as the failure message,
|
||||
# which might help with the debug.
|
||||
if self.process.returncode != 0 and not self.fail_msg:
|
||||
msg = "Last 10 lines of the log:<br>\n"
|
||||
self.fail_msg += msg
|
||||
log.log(VERBOSE, msg)
|
||||
get_fail_msg_cmd = "tail -n 10 " + self.log
|
||||
msg = run_cmd(get_fail_msg_cmd)
|
||||
msg = "```\n{}\n```\n".format(msg)
|
||||
self.fail_msg += msg
|
||||
log.log(VERBOSE, msg)
|
||||
self.status = "F"
|
||||
|
||||
# Return if status is fail - no need to look for pass patterns.
|
||||
if self.status == 'F': return
|
||||
|
||||
# If fail patterns were not found, ensure pass patterns indeed were.
|
||||
for pass_pattern in self.pass_patterns:
|
||||
grep_cmd = "grep -c -m 1 -E \'" + pass_pattern + "\' " + self.log
|
||||
(status, rslt) = subprocess.getstatusoutput(grep_cmd)
|
||||
if rslt == "0":
|
||||
msg = "Pass pattern \"{}\" not found.<br>\n".format(
|
||||
pass_pattern)
|
||||
self.fail_msg += msg
|
||||
log.log(VERBOSE, msg)
|
||||
self.status = 'F'
|
||||
break
|
||||
|
||||
# Recursively set sub-item's status if parent item fails
|
||||
def set_sub_status(self, status):
|
||||
if self.sub == []: return
|
||||
for sub_item in self.sub:
|
||||
sub_item.status = status
|
||||
sub_item.set_sub_status(status)
|
||||
|
||||
def link_odir(self):
|
||||
if self.status == '.':
|
||||
log.error("Method unexpectedly called!")
|
||||
else:
|
||||
old_link = self.sim_cfg.links['D'] + "/" + self.odir_ln
|
||||
new_link = self.sim_cfg.links[self.status] + "/" + self.odir_ln
|
||||
cmd = "ln -s " + self.odir + " " + new_link + "; "
|
||||
cmd += "rm " + old_link
|
||||
try:
|
||||
os.system(cmd)
|
||||
except Exception as e:
|
||||
log.error("Cmd \"%s\" could not be run", cmd)
|
||||
|
||||
def get_status(self):
|
||||
if self.status != "D": return
|
||||
if self.process.poll() is not None:
|
||||
self.log_fd.close()
|
||||
self.set_status()
|
||||
|
||||
log.debug("Item %s has completed execution: %s", self.name,
|
||||
self.status)
|
||||
Deploy.dispatch_counter -= 1
|
||||
self.link_odir()
|
||||
del self.process
|
||||
|
||||
@staticmethod
|
||||
def increment_timer():
|
||||
# sub function that increments with overflow = 60
|
||||
def _incr_ovf_60(val):
|
||||
if val >= 59:
|
||||
val = 0
|
||||
return val, True
|
||||
else:
|
||||
val += 1
|
||||
return val, False
|
||||
|
||||
incr_hh = False
|
||||
Deploy.ss, incr_mm = _incr_ovf_60(Deploy.ss)
|
||||
if incr_mm: Deploy.mm, incr_hh = _incr_ovf_60(Deploy.mm)
|
||||
if incr_hh: Deploy.hh += 1
|
||||
|
||||
@staticmethod
|
||||
def deploy(items):
|
||||
dispatched_items = []
|
||||
queued_items = []
|
||||
|
||||
# Print timer val in hh:mm:ss.
|
||||
def get_timer_val():
|
||||
return "%02i:%02i:%02i" % (Deploy.hh, Deploy.mm, Deploy.ss)
|
||||
|
||||
# Check if elapsed time has reached the next print interval.
|
||||
def has_print_interval_reached():
|
||||
# Deploy.print_interval is expected to be < 1 hour.
|
||||
return (((Deploy.mm * 60 + Deploy.ss) %
|
||||
Deploy.print_interval) == 0)
|
||||
|
||||
def dispatch_items(items):
|
||||
item_names = OrderedDict()
|
||||
for item in items:
|
||||
if item.target not in item_names.keys():
|
||||
item_names[item.target] = ""
|
||||
if item.status is None:
|
||||
item_names[item.target] += item.identifier + ", "
|
||||
item.dispatch_cmd()
|
||||
|
||||
for target in item_names.keys():
|
||||
if item_names[target] != "":
|
||||
item_names[target] = " [" + item_names[target][:-2] + "]"
|
||||
log.log(VERBOSE, "[%s]: [%s]: [dispatch]:\n%s",
|
||||
get_timer_val(), target, item_names[target])
|
||||
|
||||
# Initialize status for a target, add '_stats_' for the said target
|
||||
# and initialize counters for queued, dispatched, passed, failed,
|
||||
# killed and total to 0. Also adds a boolean key to indicate if all
|
||||
# items in a given target are done.
|
||||
def init_status_target_stats(status, target):
|
||||
status[target] = OrderedDict()
|
||||
status[target]['_stats_'] = OrderedDict()
|
||||
status[target]['_stats_']['Q'] = 0
|
||||
status[target]['_stats_']['D'] = 0
|
||||
status[target]['_stats_']['P'] = 0
|
||||
status[target]['_stats_']['F'] = 0
|
||||
status[target]['_stats_']['K'] = 0
|
||||
status[target]['_stats_']['T'] = 0
|
||||
status[target]['_done_'] = False
|
||||
|
||||
# Update status counter for a newly queued item.
|
||||
def add_status_target_queued(status, item):
|
||||
if item.target not in status.keys():
|
||||
init_status_target_stats(status, item.target)
|
||||
status[item.target][item] = "Q"
|
||||
status[item.target]['_stats_']['Q'] += 1
|
||||
status[item.target]['_stats_']['T'] += 1
|
||||
|
||||
# Update status counters for a target.
|
||||
def update_status_target_stats(status, item):
|
||||
old_status = status[item.target][item]
|
||||
status[item.target]['_stats_'][old_status] -= 1
|
||||
status[item.target]['_stats_'][item.status] += 1
|
||||
status[item.target][item] = item.status
|
||||
|
||||
def check_if_done_and_print_status(status, print_status_flag):
|
||||
all_done = True
|
||||
for target in status.keys():
|
||||
target_done_prev = status[target]['_done_']
|
||||
target_done_curr = ((status[target]['_stats_']["Q"] == 0) and
|
||||
(status[target]['_stats_']["D"] == 0))
|
||||
status[target]['_done_'] = target_done_curr
|
||||
all_done &= target_done_curr
|
||||
|
||||
# Print if flag is set and target_done is not True for two
|
||||
# consecutive times.
|
||||
if not (target_done_prev and
|
||||
target_done_curr) and print_status_flag:
|
||||
stats = status[target]['_stats_']
|
||||
width = "0{}d".format(len(str(stats["T"])))
|
||||
msg = "["
|
||||
for s in stats.keys():
|
||||
msg += s + ": {:{}}, ".format(stats[s], width)
|
||||
msg = msg[:-2] + "]"
|
||||
log.info("[%s]: [%s]: %s", get_timer_val(), target, msg)
|
||||
return all_done
|
||||
|
||||
# Print legend once at the start of the run.
|
||||
if Deploy.print_legend:
|
||||
log.info("[legend]: [Q: queued, D: dispatched, "
|
||||
"P: passed, F: failed, K: killed, T: total]")
|
||||
Deploy.print_legend = False
|
||||
|
||||
status = OrderedDict()
|
||||
print_status_flag = True
|
||||
|
||||
# Queue all items
|
||||
queued_items = items
|
||||
for item in queued_items:
|
||||
add_status_target_queued(status, item)
|
||||
|
||||
all_done = False
|
||||
while not all_done:
|
||||
# Get status of dispatched items.
|
||||
for item in dispatched_items:
|
||||
if item.status == "D": item.get_status()
|
||||
if item.status != status[item.target][item]:
|
||||
print_status_flag = True
|
||||
if item.status != "D":
|
||||
if item.status != "P":
|
||||
# Kill its sub items if item did not pass.
|
||||
item.set_sub_status("K")
|
||||
log.error("[%s]: [%s]: [status] [%s: %s]",
|
||||
get_timer_val(), item.target,
|
||||
item.identifier, item.status)
|
||||
else:
|
||||
log.log(VERBOSE, "[%s]: [%s]: [status] [%s: %s]",
|
||||
get_timer_val(), item.target,
|
||||
item.identifier, item.status)
|
||||
# Queue items' sub-items if it is done.
|
||||
queued_items.extend(item.sub)
|
||||
for sub_item in item.sub:
|
||||
add_status_target_queued(status, sub_item)
|
||||
update_status_target_stats(status, item)
|
||||
|
||||
# Dispatch items from the queue as slots free up.
|
||||
all_done = (len(queued_items) == 0)
|
||||
if not all_done:
|
||||
num_slots = Deploy.max_parallel - Deploy.dispatch_counter
|
||||
if num_slots > 0:
|
||||
if len(queued_items) > num_slots:
|
||||
dispatch_items(queued_items[0:num_slots])
|
||||
dispatched_items.extend(queued_items[0:num_slots])
|
||||
queued_items = queued_items[num_slots:]
|
||||
else:
|
||||
dispatch_items(queued_items)
|
||||
dispatched_items.extend(queued_items)
|
||||
queued_items = []
|
||||
|
||||
# Check if we are done and print the status periodically.
|
||||
all_done &= check_if_done_and_print_status(status,
|
||||
print_status_flag)
|
||||
|
||||
# Advance time by 1s if there is more work to do.
|
||||
if not all_done:
|
||||
time.sleep(1)
|
||||
Deploy.increment_timer()
|
||||
print_status_flag = has_print_interval_reached()
|
||||
|
||||
|
||||
class CompileSim(Deploy):
|
||||
"""
|
||||
Abstraction for building the simulation executable.
|
||||
"""
|
||||
|
||||
# Register all builds with the class
|
||||
items = []
|
||||
|
||||
def __init__(self, build_mode, sim_cfg):
|
||||
self.target = "build"
|
||||
self.pass_patterns = []
|
||||
self.fail_patterns = []
|
||||
|
||||
self.mandatory_cmd_attrs = {
|
||||
# tool srcs
|
||||
"tool_srcs": False,
|
||||
"tool_srcs_dir": False,
|
||||
|
||||
# RAL gen
|
||||
"skip_ral": False,
|
||||
"gen_ral_pkg_cmd": False,
|
||||
"gen_ral_pkg_dir": False,
|
||||
"gen_ral_pkg_opts": False,
|
||||
|
||||
# Flist gen
|
||||
"sv_flist_gen_cmd": False,
|
||||
"sv_flist_gen_dir": False,
|
||||
"sv_flist_gen_opts": False,
|
||||
|
||||
# Build
|
||||
"build_dir": False,
|
||||
"build_cmd": False,
|
||||
"build_opts": False
|
||||
}
|
||||
|
||||
self.mandatory_misc_attrs = {
|
||||
"cov_db_dir": False,
|
||||
"build_pass_patterns": False,
|
||||
"build_fail_patterns": False
|
||||
}
|
||||
|
||||
# Initialize
|
||||
super().__init__(sim_cfg)
|
||||
super().parse_dict(build_mode.__dict__)
|
||||
# Call this method again with the sim_cfg dict passed as the object,
|
||||
# since it may contain additional mandatory attrs.
|
||||
super().parse_dict(sim_cfg.__dict__)
|
||||
self.build_mode = self.name
|
||||
self.pass_patterns = self.build_pass_patterns
|
||||
self.fail_patterns = self.build_fail_patterns
|
||||
self.__post_init__()
|
||||
|
||||
# Start fail message construction
|
||||
self.fail_msg = "\n**BUILD:** {}<br>\n".format(self.name)
|
||||
log_sub_path = self.log.replace(self.sim_cfg.scratch_path + '/', '')
|
||||
self.fail_msg += "**LOG:** $scratch_path/{}<br>\n".format(log_sub_path)
|
||||
|
||||
CompileSim.items.append(self)
|
||||
|
||||
def dispatch_cmd(self):
|
||||
# Delete previous cov_db_dir if it exists before dispatching new build.
|
||||
if os.path.exists(self.cov_db_dir):
|
||||
os.system("rm -rf " + self.cov_db_dir)
|
||||
super().dispatch_cmd()
|
||||
|
||||
|
||||
class CompileOneShot(Deploy):
|
||||
"""
|
||||
Abstraction for building the simulation executable.
|
||||
"""
|
||||
|
||||
# Register all builds with the class
|
||||
items = []
|
||||
|
||||
def __init__(self, build_mode, sim_cfg):
|
||||
self.target = "build"
|
||||
self.pass_patterns = []
|
||||
self.fail_patterns = []
|
||||
|
||||
self.mandatory_cmd_attrs = {
|
||||
# tool srcs
|
||||
"tool_srcs": False,
|
||||
"tool_srcs_dir": False,
|
||||
|
||||
# Build
|
||||
"build_dir": False,
|
||||
"build_cmd": False,
|
||||
"build_opts": False,
|
||||
|
||||
# Report processing
|
||||
"report_cmd": False,
|
||||
"report_opts": False
|
||||
}
|
||||
|
||||
self.mandatory_misc_attrs = {}
|
||||
|
||||
# Initialize
|
||||
super().__init__(sim_cfg)
|
||||
super().parse_dict(build_mode.__dict__)
|
||||
# Call this method again with the sim_cfg dict passed as the object,
|
||||
# since it may contain additional mandatory attrs.
|
||||
super().parse_dict(sim_cfg.__dict__)
|
||||
self.build_mode = self.name
|
||||
self.__post_init__()
|
||||
|
||||
# Start fail message construction
|
||||
self.fail_msg = "\n**BUILD:** {}<br>\n".format(self.name)
|
||||
log_sub_path = self.log.replace(self.sim_cfg.scratch_path + '/', '')
|
||||
self.fail_msg += "**LOG:** $scratch_path/{}<br>\n".format(log_sub_path)
|
||||
|
||||
CompileOneShot.items.append(self)
|
||||
|
||||
|
||||
class RunTest(Deploy):
|
||||
"""
|
||||
Abstraction for running tests. This is one per seed for each test.
|
||||
"""
|
||||
|
||||
# Initial seed values when running tests (if available).
|
||||
seeds = []
|
||||
fixed_seed = None
|
||||
|
||||
# Register all runs with the class
|
||||
items = []
|
||||
|
||||
def __init__(self, index, test, sim_cfg):
|
||||
self.target = "run"
|
||||
self.pass_patterns = []
|
||||
self.fail_patterns = []
|
||||
|
||||
self.mandatory_cmd_attrs = {
|
||||
"proj_root": False,
|
||||
"uvm_test": False,
|
||||
"uvm_test_seq": False,
|
||||
"run_opts": False,
|
||||
"sw_dir": False,
|
||||
"sw_name": False,
|
||||
"sw_build_device": False,
|
||||
"sw_build_dir": False,
|
||||
"run_dir": False,
|
||||
"run_cmd": False,
|
||||
"run_opts": False
|
||||
}
|
||||
|
||||
self.mandatory_misc_attrs = {
|
||||
"run_dir_name": False,
|
||||
"cov_db_test_dir": False,
|
||||
"run_pass_patterns": False,
|
||||
"run_fail_patterns": False
|
||||
}
|
||||
|
||||
self.index = index
|
||||
self.seed = RunTest.get_seed()
|
||||
|
||||
# Initialize
|
||||
super().__init__(sim_cfg)
|
||||
super().parse_dict(test.__dict__)
|
||||
# Call this method again with the sim_cfg dict passed as the object,
|
||||
# since it may contain additional mandatory attrs.
|
||||
super().parse_dict(sim_cfg.__dict__)
|
||||
self.test = self.name
|
||||
self.renew_odir = True
|
||||
self.build_mode = test.build_mode.name
|
||||
self.pass_patterns = self.run_pass_patterns
|
||||
self.fail_patterns = self.run_fail_patterns
|
||||
self.__post_init__()
|
||||
# For output dir link, use run_dir_name instead.
|
||||
self.odir_ln = self.run_dir_name
|
||||
|
||||
# Start fail message construction
|
||||
self.fail_msg = "\n**TEST:** {}, ".format(self.name)
|
||||
self.fail_msg += "**SEED:** {}<br>\n".format(self.seed)
|
||||
log_sub_path = self.log.replace(self.sim_cfg.scratch_path + '/', '')
|
||||
self.fail_msg += "**LOG:** $scratch_path/{}<br>\n".format(log_sub_path)
|
||||
|
||||
RunTest.items.append(self)
|
||||
|
||||
def __post_init__(self):
|
||||
super().__post_init__()
|
||||
# Set identifier.
|
||||
self.identifier = self.sim_cfg.name + ":" + self.run_dir_name
|
||||
|
||||
def get_status(self):
|
||||
'''Override base class get_status implementation for additional post-status
|
||||
actions.'''
|
||||
super().get_status()
|
||||
if self.status not in ["D", "P"]:
|
||||
# Delete the coverage data if available.
|
||||
if os.path.exists(self.cov_db_test_dir):
|
||||
log.log(VERBOSE, "Deleting coverage data of failing test:\n%s",
|
||||
self.cov_db_test_dir)
|
||||
os.system("/bin/rm -rf " + self.cov_db_test_dir)
|
||||
|
||||
@staticmethod
|
||||
def get_seed():
|
||||
# If --seeds option is passed, then those custom seeds are consumed
|
||||
# first. If --fixed-seed <val> is also passed, the subsequent tests
|
||||
# (once the custom seeds are consumed) will be run with the fixed seed.
|
||||
if not RunTest.seeds:
|
||||
if RunTest.fixed_seed: return RunTest.fixed_seed
|
||||
for i in range(1000):
|
||||
seed = random.getrandbits(32)
|
||||
RunTest.seeds.append(seed)
|
||||
return RunTest.seeds.pop(0)
|
||||
|
||||
|
||||
class CovMerge(Deploy):
|
||||
"""
|
||||
Abstraction for merging coverage databases. An item of this class is created AFTER
|
||||
the regression is completed.
|
||||
"""
|
||||
|
||||
# Register all builds with the class
|
||||
items = []
|
||||
|
||||
def __init__(self, sim_cfg):
|
||||
self.target = "cov_merge"
|
||||
self.pass_patterns = []
|
||||
self.fail_patterns = []
|
||||
|
||||
# Construct local 'special' variable from cov directories that need to
|
||||
# be merged.
|
||||
self.cov_db_dirs = ""
|
||||
|
||||
self.mandatory_cmd_attrs = {
|
||||
"cov_merge_cmd": False,
|
||||
"cov_merge_opts": False
|
||||
}
|
||||
|
||||
self.mandatory_misc_attrs = {
|
||||
"cov_merge_dir": False,
|
||||
"cov_merge_db_dir": False
|
||||
}
|
||||
|
||||
# Initialize
|
||||
super().__init__(sim_cfg)
|
||||
super().parse_dict(sim_cfg.__dict__)
|
||||
self.__post_init__()
|
||||
|
||||
# Override standard output and log patterns.
|
||||
self.odir = self.cov_merge_db_dir
|
||||
self.odir_ln = os.path.basename(os.path.normpath(self.odir))
|
||||
|
||||
# Start fail message construction
|
||||
self.fail_msg = "\n**COV_MERGE:** {}<br>\n".format(self.name)
|
||||
log_sub_path = self.log.replace(self.sim_cfg.scratch_path + '/', '')
|
||||
self.fail_msg += "**LOG:** $scratch_path/{}<br>\n".format(log_sub_path)
|
||||
|
||||
CovMerge.items.append(self)
|
||||
|
||||
def __post_init__(self):
|
||||
# Add cov db dirs from all the builds that were kicked off.
|
||||
for bld in self.sim_cfg.builds:
|
||||
self.cov_db_dirs += bld.cov_db_dir + " "
|
||||
|
||||
# Recursively search and replace wildcards, ignoring cov_db_dirs for now.
|
||||
# We need to resolve it later based on cov_db_dirs value set below.
|
||||
self.__dict__ = find_and_substitute_wildcards(
|
||||
self.__dict__, self.__dict__, ignored_wildcards=["cov_db_dirs"])
|
||||
|
||||
# Prune previous merged cov directories.
|
||||
prev_cov_db_dirs = self.odir_limiter(odir=self.cov_merge_db_dir)
|
||||
|
||||
# If a merged cov data base exists from a previous run, then consider
|
||||
# that as well for merging, if the --cov-merge-previous command line
|
||||
# switch is passed.
|
||||
if self.sim_cfg.cov_merge_previous:
|
||||
self.cov_db_dirs += prev_cov_db_dirs
|
||||
|
||||
# Call base class __post_init__ to do checks and substitutions
|
||||
super().__post_init__()
|
||||
|
||||
|
||||
class CovReport(Deploy):
|
||||
"""
|
||||
Abstraction for coverage report generation. An item of this class is created AFTER
|
||||
the regression is completed.
|
||||
"""
|
||||
|
||||
# Register all builds with the class
|
||||
items = []
|
||||
|
||||
def __init__(self, sim_cfg):
|
||||
self.target = "cov_report"
|
||||
self.pass_patterns = []
|
||||
self.fail_patterns = []
|
||||
self.cov_total = ""
|
||||
self.cov_results = ""
|
||||
|
||||
self.mandatory_cmd_attrs = {
|
||||
"cov_report_cmd": False,
|
||||
"cov_report_opts": False
|
||||
}
|
||||
|
||||
self.mandatory_misc_attrs = {
|
||||
"cov_report_dir": False,
|
||||
"cov_merge_db_dir": False,
|
||||
"cov_report_dashboard": False
|
||||
}
|
||||
|
||||
# Initialize
|
||||
super().__init__(sim_cfg)
|
||||
super().parse_dict(sim_cfg.__dict__)
|
||||
self.__post_init__()
|
||||
|
||||
# Start fail message construction
|
||||
self.fail_msg = "\n**COV_REPORT:** {}<br>\n".format(self.name)
|
||||
log_sub_path = self.log.replace(self.sim_cfg.scratch_path + '/', '')
|
||||
self.fail_msg += "**LOG:** $scratch_path/{}<br>\n".format(log_sub_path)
|
||||
|
||||
CovReport.items.append(self)
|
||||
|
||||
def get_status(self):
|
||||
super().get_status()
|
||||
# Once passed, extract the cov results summary from the dashboard.
|
||||
if self.status == "P":
|
||||
try:
|
||||
with open(self.cov_report_dashboard, 'r') as f:
|
||||
for line in f:
|
||||
match = re.match("total coverage summary", line,
|
||||
re.IGNORECASE)
|
||||
if match:
|
||||
results = []
|
||||
# Metrics on the next line.
|
||||
line = f.readline().strip()
|
||||
results.append(line.split())
|
||||
# Values on the next.
|
||||
line = f.readline().strip()
|
||||
# Pretty up the values - add % sign for ease of post
|
||||
# processing.
|
||||
values = []
|
||||
for val in line.split():
|
||||
val += " %"
|
||||
values.append(val)
|
||||
# first row is coverage total
|
||||
self.cov_total = values[0]
|
||||
results.append(values)
|
||||
colalign = (("center", ) * len(values))
|
||||
self.cov_results = tabulate(results,
|
||||
headers="firstrow",
|
||||
tablefmt="pipe",
|
||||
colalign=colalign)
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
ex_msg = "Failed to parse \"{}\":\n{}".format(
|
||||
self.cov_report_dashboard, str(e))
|
||||
self.fail_msg += ex_msg
|
||||
log.error(ex_msg)
|
||||
self.status = "F"
|
||||
|
||||
if self.cov_results == "":
|
||||
nf_msg = "Coverage summary not found in the reports dashboard!"
|
||||
self.fail_msg += nf_msg
|
||||
log.error(nf_msg)
|
||||
self.status = "F"
|
||||
|
||||
if self.status == "P":
|
||||
# Delete the cov report - not needed.
|
||||
os.system("rm -rf " + self.log)
|
||||
|
||||
|
||||
class CovAnalyze(Deploy):
|
||||
"""
|
||||
Abstraction for coverage analysis tool.
|
||||
"""
|
||||
|
||||
# Register all builds with the class
|
||||
items = []
|
||||
|
||||
def __init__(self, sim_cfg):
|
||||
self.target = "cov_analyze"
|
||||
self.pass_patterns = []
|
||||
self.fail_patterns = []
|
||||
|
||||
self.mandatory_cmd_attrs = {
|
||||
"cov_analyze_cmd": False,
|
||||
"cov_analyze_opts": False
|
||||
}
|
||||
|
||||
self.mandatory_misc_attrs = {
|
||||
"cov_analyze_dir": False,
|
||||
"cov_merge_db_dir": False
|
||||
}
|
||||
|
||||
# Initialize
|
||||
super().__init__(sim_cfg)
|
||||
super().parse_dict(sim_cfg.__dict__)
|
||||
self.__post_init__()
|
||||
|
||||
# Start fail message construction
|
||||
self.fail_msg = "\n**COV_ANALYZE:** {}<br>\n".format(self.name)
|
||||
log_sub_path = self.log.replace(self.sim_cfg.scratch_path + '/', '')
|
||||
self.fail_msg += "**LOG:** $scratch_path/{}<br>\n".format(log_sub_path)
|
||||
|
||||
CovAnalyze.items.append(self)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue