[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:
udinator 2020-03-27 11:02:47 -07:00 committed by GitHub
parent 08f2271c75
commit f69c6fbabd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
161 changed files with 12145 additions and 28 deletions

View file

@ -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.

View file

@ -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

View file

@ -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;

View 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

View 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"]
}
]
}

View 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}"
}

View 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
View 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
View 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

View 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
View 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}"]
}
]
}

View 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

View file

@ -0,0 +1,4 @@
merge = xmerge;
// Turn on xprop for dut only
instance { tb.dut } { xpropOn };

View 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"]
}
]
}

View 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: []
}
]
}

View 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
![Block diagram](tb.svg)
### 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 674 KiB

View 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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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"

View file

@ -0,0 +1,3 @@
# IBEX_ICACHE UVM Agent
IBEX_ICACHE UVM Agent is extended from DV library agent classes.

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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_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

View file

@ -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"

View 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

View 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
View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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
View 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
![Block diagram](pins_if.svg)
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
View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 56 KiB

View 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
View 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.

View 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

View 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

View 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

View 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
View 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

View 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

View 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

View 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

View 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
View 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

View 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

View 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
View 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

View 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
View 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

View 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

View 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

View 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

View 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
View 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

View 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

View 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

View 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
View 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
View 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
View 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
View file

288
vendor/lowrisc_ip/dv_utils/dv_macros.svh vendored Normal file
View 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

View 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

View 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

View 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
View 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