[rom_ctrl/dv] Add coverage plan and implementation

Some basic coverage points are specified to track items not expected to
be caught by code-coverage.

Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
diff --git a/hw/ip/rom_ctrl/data/rom_ctrl_testplan.hjson b/hw/ip/rom_ctrl/data/rom_ctrl_testplan.hjson
index 6760cc5..41d3b77 100644
--- a/hw/ip/rom_ctrl/data/rom_ctrl_testplan.hjson
+++ b/hw/ip/rom_ctrl/data/rom_ctrl_testplan.hjson
@@ -63,4 +63,31 @@
       tests: ["rom_ctrl_ecc"]
     }
   ]
+  covergroups: [
+    {
+      name: rom_ctrl_kmac_cg
+      desc: '''
+            Collect coverage on the rom_ctrl / kmac interface, specifically around
+            stalling and back-pressure behavior.
+
+            The agent needs to cover the case where the kmac returns a digest
+            before the rom_ctrl finishes reading the expected digest from memory,
+            and also after.
+            '''
+    }
+    {
+      name: rom_ctrl_tlul_cg
+      desc: '''
+            Collect coverage on the two TLUL interfaces, specifically checking
+            that we see requests around the same time as the rom check completes.
+            '''
+    }
+    {
+      name: rom_ctrl_check_cg
+      desc: '''
+            Collect coverage on the outputs sent to the power manager to confirm
+            that we see pass and fail resutls.
+            '''
+    }
+  ]
 }
diff --git a/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov.core b/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov.core
new file mode 100644
index 0000000..e8adce5
--- /dev/null
+++ b/hw/ip/rom_ctrl/dv/cov/rom_ctrl_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:rom_ctrl_cov"
+description: "rom_ctrl functional coverage interface & bind."
+
+filesets:
+  files_dv:
+    depend:
+      - lowrisc:dv:dv_utils
+      - lowrisc:ip:rom_ctrl
+    files:
+      - rom_ctrl_cov_if.sv
+      - rom_ctrl_cov_bind.sv
+    file_type: systemVerilogSource
+
+targets:
+  default:
+    filesets:
+      - files_dv
diff --git a/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov_bind.sv b/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov_bind.sv
new file mode 100644
index 0000000..a9e9708
--- /dev/null
+++ b/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov_bind.sv
@@ -0,0 +1,10 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Binds functional coverage interaface to the top level rom_ctrl module.
+module rom_ctrl_cov_bind;
+
+  bind rom_ctrl rom_ctrl_cov_if u_rom_ctrl_cov_if (.*);
+
+endmodule
diff --git a/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov_if.sv b/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov_if.sv
new file mode 100644
index 0000000..a2b3be8
--- /dev/null
+++ b/hw/ip/rom_ctrl/dv/cov/rom_ctrl_cov_if.sv
@@ -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
+//
+// Implements functional coverage for rom_ctrl
+
+interface rom_ctrl_cov_if (
+    input logic                       clk_i,
+    input kmac_pkg::app_rsp_t         kmac_data_i,
+    input kmac_pkg::app_req_t         kmac_data_o,
+    input logic                       exp_digest_de,
+    input tlul_pkg::tl_h2d_t          rom_tl_i,
+    input tlul_pkg::tl_h2d_t          regs_tl_i,
+    input rom_ctrl_pkg::pwrmgr_data_t pwrmgr_data_o
+);
+
+  `include "dv_fcov_macros.svh"
+  import uvm_pkg::*;
+  import dv_utils_pkg::*;
+
+  bit en_full_cov = 1'b1;
+  bit en_intg_cov = 1'b1;
+
+  /////////////////////////////////////
+  // KMAC APP interface cover points //
+  /////////////////////////////////////
+
+  covergroup rom_ctrl_kmac_cg @(posedge clk_i);
+    option.name         = "rom_ctrl_kmac_cg";
+    option.comment      = "KMAC interface behaviors";
+    option.per_instance = 1;
+
+    // Cover some basic stalling behavior on the kmac ready input
+    cp_kmac_ready: coverpoint kmac_data_i.ready iff (kmac_data_o.valid) {
+      bins zero_delay_5 = (1'b1[*5]);
+      bins stall_1      = (1'b1 => 1'b0 => 1'b1);
+      bins stall_long   = (1'b0[*5:10] => 1'b1);
+      bins stall_repeat = (1'b0[*1:10] => 1'b1 => 1'b0[*1:10]);
+    }
+
+    // Cover the different delays on the kmac done signal
+    cp_kmac_done: coverpoint {kmac_data_i.done, exp_digest_de} {
+      bins kmac_first = {2'b11}; // kmac responds while still reading digest
+      bins rom_first  = (2'b00 => 2'b10); // kmac responds after digest read
+      bins same_cycle = (2'b01 => 2'b10); // kmac responds as digest read completes
+    }
+
+  endgroup
+  `DV_FCOV_INSTANTIATE_CG(rom_ctrl_kmac_cg, en_full_cov)
+
+  /////////////////////////////////////
+  // ROM TLUL interface cover points //
+  /////////////////////////////////////
+
+  covergroup rom_ctrl_tlul_cg @(posedge clk_i);
+    option.name         = "rom_ctrl_tlul_cg";
+    option.comment      = "TLUL interface behaviors";
+    option.per_instance = 1;
+
+    // Cover rom requests around the time of check completion
+    cp_rom_req_check: coverpoint {rom_tl_i.a_valid, pwrmgr_data_o.done} {
+      bins req_before_done = (2'b10 => 2'b11);
+      bins req_and_done    = (2'b00 => 2'b11);
+      bins req_after_done  = (2'b01 => 2'b10);
+    }
+
+    // Cover csr requests around the time of check completion
+    cp_regs_req_check: coverpoint {regs_tl_i.a_valid, pwrmgr_data_o.done} {
+      bins req_before_done = (2'b10 => 2'b11);
+      bins req_and_done    = (2'b00 => 2'b11);
+      bins req_after_done  = (2'b01 => 2'b10);
+    }
+
+  endgroup
+  `DV_FCOV_INSTANTIATE_CG(rom_ctrl_tlul_cg, en_full_cov)
+
+  ////////////////////////////
+  // ROM check cover points //
+  ////////////////////////////
+
+  covergroup rom_ctrl_check_cg @(posedge clk_i);
+    option.name         = "rom_ctrl_check_cg";
+    option.comment      = "ROM check coverpoints";
+    option.per_instance = 1;
+
+    // Cover the check pass and fail case
+    cp_rom_check_condition: coverpoint pwrmgr_data_o.good iff (pwrmgr_data_o.done) {
+      bins check_pass = {1'b1};
+      bins check_fail = {1'b0};
+    }
+
+  endgroup
+  `DV_FCOV_INSTANTIATE_CG(rom_ctrl_check_cg, en_full_cov)
+
+endinterface
diff --git a/hw/ip/rom_ctrl/dv/rom_ctrl_sim.core b/hw/ip/rom_ctrl/dv/rom_ctrl_sim.core
index 56a279e..b737913 100644
--- a/hw/ip/rom_ctrl/dv/rom_ctrl_sim.core
+++ b/hw/ip/rom_ctrl/dv/rom_ctrl_sim.core
@@ -13,6 +13,7 @@
     depend:
       - lowrisc:dv:rom_ctrl_test
       - lowrisc:dv:rom_ctrl_sva
+      - lowrisc:dv:rom_ctrl_cov
     files:
       - tb.sv
     file_type: systemVerilogSource
diff --git a/hw/ip/rom_ctrl/dv/rom_ctrl_sim_cfg.hjson b/hw/ip/rom_ctrl/dv/rom_ctrl_sim_cfg.hjson
index 51a65cd..a9d4e37 100644
--- a/hw/ip/rom_ctrl/dv/rom_ctrl_sim_cfg.hjson
+++ b/hw/ip/rom_ctrl/dv/rom_ctrl_sim_cfg.hjson
@@ -35,7 +35,7 @@
                 "{proj_root}/hw/dv/tools/dvsim/tests/stress_tests.hjson"]
 
   // Add additional tops for simulation.
-  sim_tops: ["rom_ctrl_bind"]
+  sim_tops: ["rom_ctrl_bind", "rom_ctrl_cov_bind"]
 
   // Default iterations for all tests - each test entry can override this.
   reseed: 50