[cov methodology] Functional coverage prototype - Implements functional coverage for UART in an interface - Proof of concept to drive a designer-led functional coverage implementation methodology - See link below for more details: https://docs.google.com/presentation/d/1rUFpWzktORuBZNyG6eSG68kWxEWmxMkMaWdSCadhbQI Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/hw/dv/sv/dv_utils/dv_macros.svh b/hw/dv/sv/dv_utils/dv_macros.svh index f169faf..e9413f8 100644 --- a/hw/dv/sv/dv_utils/dv_macros.svh +++ b/hw/dv/sv/dv_utils/dv_macros.svh
@@ -492,4 +492,29 @@ end `endif +// Instantiates a covergroup in an interface or module. +// +// This macro assumes that a covergroup of the same name as the __CG_NAME arg is defined in the +// interface or module. It just adds some extra signals and logic to control the creation of the +// covergroup instance with ~bit en_<cg_name>~. This defaults to 0. It is ORed with the external +// __COND signal. The testbench can modify it at t = 0 based on the test being run. +// NOTE: This is not meant to be invoked inside a class. +// +// __CG_NAME : Name of the covergroup. +// __COND : External condition / expr that controls the creation of the covergroup. +// __CG_ARGS : Arguments to covergroup instance, if any. Args MUST BE wrapped in (..). +`ifndef DV_INSTANTIATE_CG +`define DV_INSTANTIATE_CG(__CG_NAME, __COND = 1'b1, __CG_ARGS = ()) \ + bit en_``__CG_NAME = 1'b0; \ + __CG_NAME __CG_NAME``_inst; \ + initial begin \ + /* The #1 delay below allows any part of the tb to control the conditions first at t = 0. */ \ + #1; \ + if ((en_``__CG_NAME) || (__COND)) begin \ + `DV_INFO({"Creating covergroup ", `"__CG_NAME`"}, UVM_MEDIUM) \ + __CG_NAME``_inst = new``__CG_ARGS; \ + end \ + end +`endif + `endif // __DV_MACROS_SVH__
diff --git a/hw/dv/sv/dv_utils/dv_utils.core b/hw/dv/sv/dv_utils/dv_utils.core index 4751513..5efce93 100644 --- a/hw/dv/sv/dv_utils/dv_utils.core +++ b/hw/dv/sv/dv_utils/dv_utils.core
@@ -15,6 +15,7 @@ files: - dv_utils_pkg.sv - dv_report_server.sv: {is_include_file: true} + - dv_vif_wrap.sv: {is_include_file: true} file_type: systemVerilogSource targets:
diff --git a/hw/dv/sv/dv_utils/dv_utils_pkg.sv b/hw/dv/sv/dv_utils/dv_utils_pkg.sv index 46e2cc9..2c99462 100644 --- a/hw/dv/sv/dv_utils/dv_utils_pkg.sv +++ b/hw/dv/sv/dv_utils/dv_utils_pkg.sv
@@ -152,9 +152,27 @@ endfunction `endif + // Returns the hierarchical path to the interface / module N levels up. + // + // Meant to be invoked inside a module or interface. + // hier: String input of the interface / module, typically $sformatf("%m"). + // n_levels_up: Integer number of levels up the hierarchy to omit. + // Example: if (hier = tb.dut.foo.bar, n_levels_up = 2), then return tb.dut + function automatic string get_parent_hier(string hier, int n_levels_up = 1); + int idx; + int level; + if (n_levels_up <= 0) return hier; + for (idx = hier.len() - 1; idx >= 0; idx--) begin + if (hier[idx] == ".") level++; + if (level == n_levels_up) break; + end + return (hier.substr(0, idx - 1)); + endfunction + // sources `ifdef UVM `include "dv_report_server.sv" + `include "dv_vif_wrap.sv" `endif endpackage
diff --git a/hw/dv/sv/dv_utils/dv_vif_wrap.sv b/hw/dv/sv/dv_utils/dv_vif_wrap.sv new file mode 100644 index 0000000..61d9947 --- /dev/null +++ b/hw/dv/sv/dv_utils/dv_vif_wrap.sv
@@ -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 + +// Abstract class meant to hold arbitrary virtual interface handles. +// +// Written primarily for an interface which implements functional coverage, this could be used +// for other purposes as well. This abstract class provides utilities & macros to retrieve +// virtual interface handles that are bound to a DUT's sub-modules. These sub-module interfaces must +// self-register using the `DV_VIF_WRAP_SET_VIF()` macro (see details below). The extended class +// then implements the `get_vifs()` method using the `DV_VIF_WRAP_GET_VIF*` macros below to retrieve +// the sub-module interface handles it maintains. +virtual class dv_vif_wrap; + string hier; // Represents the hierarchy of the parent module or interface. + string name; // Name of the class instance. + + function new(string hier, string name = ""); + this.hier = hier; + this.name = name; + endfunction + + // Abstract method implemented by the extended class. It is recommended to invoke the helper + // macros below rather than manually define it. + pure virtual task get_vifs(); + +endclass + +// Helper macros. +// +// These are defined in the file itself since they are tightly coupled to the class definition +// above. These are scoped globally so that extended classes can invoke them. + +// Enable an interface to register itself (set its handle into the config db). +// +// Meant to be invoked from an interface. The macros invocation causes the interface to register +// itself into the uvm_resource_db pool. The derived class of dv_vif_wrap retrieves the handle to +// that interface handle from the uvm_resource db pool. +// +// SV LRM does not yet allow referencing the instance of an interface within itself as one +// would in case of a class using the ~this~ keyword. However, most major simulators actually +// support this in their own custom way. On VCS, a reference to self within the interface can be set +// using `interface::self()` construct. On Xcelium, this is achieved by simply referencing the +// interface name. +// +// _IF: The SV interface +// _VIF: The corresponding virtual interface handle name +// _LEVEL: # of hierarchical levels the module to which the SV interface is bound, starting at the +// top level DUT instance. +`define DV_VIF_WRAP_SET_VIF(_IF, _VIF, _LEVEL = 0) \ + import uvm_pkg::*; \ + function automatic void self_register(); \ + string path; \ + virtual _IF vif; \ + /* Initial block adds another level in the path hierarchy which needs to be split out. */ \ + /* Add one more to go one level up the interface instance. */ \ + /* Example: tb.dut.core.u_foo_if.self_register -> tb.dut.core. */ \ + path = dv_utils_pkg::get_parent_hier(.hier($sformatf("%m")), .n_levels_up(2 + _LEVEL)); \ +`ifdef VCS \ + vif = interface::self(); \ +`elsif XCELIUM \ + vif = _IF; \ +`else \ + vif = _IF; \ +`endif \ + uvm_pkg::uvm_resource_db#(virtual _IF)::set(path, `"_VIF`", vif); \ + endfunction \ + initial self_register(); + +// Enables the retrieval of individual vifs. +// +// The three macros below go together to define the _get_vifs() task in the extended class. +`define DV_VIF_WRAP_GET_VIFS_BEGIN \ + task get_vifs(); \ + fork \ + +// To avoid race condition between the instant when an interface handle is set into the config db +// and the instant when it is retrieved (in the same time step, at t = 0), the macro below invokes +// uvm_config_db#(..)::wait_modified() to ensure that the retrieval is done only after the set. +`define DV_VIF_WRAP_GET_VIF(_IF, _VIF) \ + begin \ + bit vif_found; \ + /* At most 2 retries. */ \ + repeat (2) begin \ + /* Force the evaluation at the end of the time step. */ \ + #0; \ + if (uvm_pkg::uvm_resource_db#(virtual _IF)::read_by_name(hier, `"_VIF`", _VIF)) begin \ + vif_found = 1'b1; \ + break; \ + end \ + end \ + `DV_CHECK_FATAL(vif_found, {`"_VIF`", " not found in the resource db"}, hier) \ + end + +`define DV_VIF_WRAP_GET_VIFS_END \ + join \ + endtask
diff --git a/hw/dv/tools/dvsim/vcs.hjson b/hw/dv/tools/dvsim/vcs.hjson index 6193f0c..812eae8 100644 --- a/hw/dv/tools/dvsim/vcs.hjson +++ b/hw/dv/tools/dvsim/vcs.hjson
@@ -47,7 +47,9 @@ // - Write (deposit) capability on registers and variables // - Force capability on registers, variables, and nets "-debug_access+f", - + // Use this to conditionally compile for VCS (example: LRM interpretations differ + // across tools). + "+define+VCS", // Upgrade below warnings to errors to make VCS more strict on syntax to avoid // having issue with other simulators but passing with VCS //
diff --git a/hw/dv/tools/dvsim/xcelium.hjson b/hw/dv/tools/dvsim/xcelium.hjson index 29de0a6..c61101b 100644 --- a/hw/dv/tools/dvsim/xcelium.hjson +++ b/hw/dv/tools/dvsim/xcelium.hjson
@@ -20,8 +20,9 @@ "-xmlibdirname {build_dir}/xcelium.d", // for uvm_hdl_* used by csr backdoor "-access +rw", - "+define+XCELIUM", - ] + // Use this to conditionally compile for Xcelium (example: LRM interpretations differ + // across tools). + "+define+XCELIUM"] run_opts: ["-input {run_script}", "-licqueue",
diff --git a/hw/ip/uart/dv/cov/uart_core_cov_if.sv b/hw/ip/uart/dv/cov/uart_core_cov_if.sv new file mode 100644 index 0000000..8ae2068 --- /dev/null +++ b/hw/ip/uart/dv/cov/uart_core_cov_if.sv
@@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Implements functional coverage for uart_core.sv sub-module. +interface uart_core_cov_if ( + input logic tx_enable, + input logic rx_enable +); + + // This interface is bound to `uart_core` which is 1 level underneath `uart` which is the DUT top + // level module. + `DV_VIF_WRAP_SET_VIF(uart_core_cov_if, uart_core_cov_vif, 1) + +endinterface
diff --git a/hw/ip/uart/dv/cov/uart_cov.core b/hw/ip/uart/dv/cov/uart_cov.core new file mode 100644 index 0000000..06d9f3c --- /dev/null +++ b/hw/ip/uart/dv/cov/uart_cov.core
@@ -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:uart_cov" +description: "UART functional coverage interface & bind." + +filesets: + files_dv: + depend: + - lowrisc:dv:dv_utils + files: + - uart_core_cov_if.sv + - uart_cov_if.sv + - uart_cov_bind.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_dv
diff --git a/hw/ip/uart/dv/cov/uart_cov_bind.sv b/hw/ip/uart/dv/cov/uart_cov_bind.sv new file mode 100644 index 0000000..5179124 --- /dev/null +++ b/hw/ip/uart/dv/cov/uart_cov_bind.sv
@@ -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 +// +// Binds UART functional coverage interaface to the top level UART module. +module uart_cov_bind; + + bind uart uart_cov_if u_uart_cov_if (.*); + + bind uart_core uart_core_cov_if u_uart_core_cov_if (.*); + +endmodule
diff --git a/hw/ip/uart/dv/cov/uart_cov_if.sv b/hw/ip/uart/dv/cov/uart_cov_if.sv new file mode 100644 index 0000000..823e897 --- /dev/null +++ b/hw/ip/uart/dv/cov/uart_cov_if.sv
@@ -0,0 +1,55 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Implements functional coverage for UART. +interface uart_cov_if ( + input logic clk_i +); + + import uvm_pkg::*; + import dv_utils_pkg::*; + + bit en_full_cov = 1'b1; + bit en_intg_cov = 1'b1; + + // If en_full_cov is set, then en_intg_cov must also be set since it is a subset. + bit en_intg_cov_loc; + assign en_intg_cov_loc = en_full_cov | en_intg_cov; + + // References to cov interfaces bound to sub-modules need to be held inside a class. + class uart_cov_vifs_wrap extends dv_vif_wrap; + virtual uart_core_cov_if uart_core_cov_vif; + + function new(string hier, string name); + super.new(hier, name); + endfunction + + // The section below constructs the `get_vifs()` task using the helper macros. It retrieves the + // VIF handles bound to sub-modules from the uvm_resource_db. + `DV_VIF_WRAP_GET_VIFS_BEGIN + `DV_VIF_WRAP_GET_VIF(uart_core_cov_if, uart_core_cov_vif) + `DV_VIF_WRAP_GET_VIFS_END + + endclass + + uart_cov_vifs_wrap m_uart_cov_vifs_wrap; + + initial begin + m_uart_cov_vifs_wrap = new(dv_utils_pkg::get_parent_hier(.hier($sformatf("%m"))), + "m_uart_cov_vifs_wrap"); + m_uart_cov_vifs_wrap.get_vifs(); + end + + covergroup uart_op_cg @(posedge clk_i); + option.name = "uart_op_cg"; + option.comment = "UART TX and RX operations"; + option.per_instance = 1; + + cp_tx_enable: coverpoint uart_core.tx_enable; + cp_rx_enable: coverpoint m_uart_cov_vifs_wrap.uart_core_cov_vif.rx_enable; + cr_tx_rx_enable: cross cp_tx_enable, cp_rx_enable; + endgroup + `DV_INSTANTIATE_CG(uart_op_cg, en_full_cov) + +endinterface
diff --git a/hw/ip/uart/dv/uart_sim.core b/hw/ip/uart/dv/uart_sim.core index a279900..3ae0370 100644 --- a/hw/ip/uart/dv/uart_sim.core +++ b/hw/ip/uart/dv/uart_sim.core
@@ -13,6 +13,7 @@ depend: - lowrisc:dv:uart_test - lowrisc:dv:uart_sva + - lowrisc:dv:uart_cov files: - tb/tb.sv file_type: systemVerilogSource
diff --git a/hw/ip/uart/dv/uart_sim_cfg.hjson b/hw/ip/uart/dv/uart_sim_cfg.hjson index 573b3db..9e1fdd4 100644 --- a/hw/ip/uart/dv/uart_sim_cfg.hjson +++ b/hw/ip/uart/dv/uart_sim_cfg.hjson
@@ -33,7 +33,7 @@ "{proj_root}/hw/dv/tools/dvsim/tests/stress_tests.hjson"] // Add additional tops for simulation. - sim_tops: ["-top uart_bind"] + sim_tops: ["-top uart_bind", "-top uart_cov_bind"] // Default iterations for all tests - each test entry can override this. reseed: 50