[dv/rstmgr] Add dv environment for rstmgr_cnsty_chk

Enable better testability of this V2S module.

Signed-off-by: Guillermo Maturana <maturana@google.com>
diff --git a/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/data/rstmgr_cnsty_chk_testplan.hjson b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/data/rstmgr_cnsty_chk_testplan.hjson
new file mode 100644
index 0000000..e3413ec
--- /dev/null
+++ b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/data/rstmgr_cnsty_chk_testplan.hjson
@@ -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
+{
+  name: "rstmgr_cnsty_chk"
+  testpoints: [
+    {
+      name: unexpected_child_reset_activity
+      desc: '''Verify unexpected child_reset activity flags an error.
+            '''
+      milestone: V2S
+      tests: ["rstmgr_cnsty_chk_smoke"]
+    }
+    {
+      name: child_reset_asserts_late
+      desc: '''Verify error triggered if child reset asserts late.
+            '''
+      milestone: V2S
+      tests: []
+    }
+    {
+      name: child_reset_releases_late
+      desc: '''Verify error triggered if child reset releases late.
+            '''
+      milestone: V2S
+      tests: []
+    }
+    {
+      name: parent_reset_asserts_late
+      desc: '''Verify error triggered if parent reset asserts late.
+            '''
+      milestone: V2S
+      tests: []
+    }
+    {
+      name: parent_reset_releases_late
+      desc: '''Verify error triggered if parent reset releases late.
+            '''
+      milestone: V2S
+      tests: []
+    }
+  ]
+}
diff --git a/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/rstmgr_cnsty_chk_sim.core b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/rstmgr_cnsty_chk_sim.core
new file mode 100644
index 0000000..da8d642
--- /dev/null
+++ b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/rstmgr_cnsty_chk_sim.core
@@ -0,0 +1,32 @@
+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:rstmgr_cnsty_chk_sim:0.1"
+description: "Rstmgr_cnsty_chk DV sim target"
+filesets:
+  files_rtl:
+    depend:
+      - lowrisc:ip:rstmgr_cnsty_chk
+    file_type: systemVerilogSource
+
+  files_dv:
+    depend:
+      - lowrisc:dv:dv_utils
+      - lowrisc:dv:dv_test_status
+      - lowrisc:dv:common_ifs
+    files:
+      - tb.sv
+    file_type: systemVerilogSource
+
+targets:
+  sim: &sim_target
+    toplevel: tb
+    filesets:
+      - files_rtl
+      - files_dv
+    default_tool: vcs
+
+  lint:
+    <<: *sim_target
+
diff --git a/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/rstmgr_cnsty_chk_sim_cfg.hjson b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/rstmgr_cnsty_chk_sim_cfg.hjson
new file mode 100644
index 0000000..eecf954
--- /dev/null
+++ b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/rstmgr_cnsty_chk_sim_cfg.hjson
@@ -0,0 +1,49 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+  // Name of the sim cfg - typically same as the name of the DUT.
+  name: rstmgr_cnsty_chk
+
+  // Top level dut name (sv module).
+  dut: rstmgr_cnsty_chk
+
+  // 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:rstmgr_cnsty_chk_sim:0.1
+
+  // Testplan hjson file.
+  testplan: "{proj_root}/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/data/rstmgr_cnsty_chk_testplan.hjson"
+
+  // Import additional common sim cfg files.
+  import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"]
+
+  // Default iterations for all tests - each test entry can override this.
+  reseed: 10
+
+  // List of test specifications.
+  tests: [
+    {
+      name: rstmgr_cnsty_chk_test
+    }
+  ]
+  // List of regressions.
+  regressions: [
+    {
+      name: smoke
+      tests: ["rstmgr_cnsty_chk_test"]
+    }
+  ]
+  overrides: [
+    // This override is in order to pick the autogen rstmgr packages.
+    {
+      name: design_level
+      value: "top"
+    }
+  ]
+}
diff --git a/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/tb.sv b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/tb.sv
new file mode 100644
index 0000000..a76a404
--- /dev/null
+++ b/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/tb.sv
@@ -0,0 +1,250 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Testbench module for rstmgr_cnsty_chk.
+//
+// Checks that errors are flagged for the following cases.
+// - Child reset going active with inactive reset requests.
+// - Child reset going inactive with active reset requests.
+//
+// NB: The checks are triggered by the reference clock.
+interface rstmgr_cnsty_chk_if;
+  logic child_rst_ni;
+  logic parent_rst_ni;
+  logic sw_rst_req_i;
+  logic sw_rst_req_clr_o;
+  logic err_o;
+  logic fsm_err_o;
+endinterface
+
+class reset_class;
+
+  parameter time ClkPeriod = 10_000;
+  parameter time ChildFastClkPeriod = 6_000;
+  parameter time ChildSlowClkPeriod = 25_000;
+
+  import uvm_pkg::*;
+
+  typedef enum int {
+    OrderChildLags,
+    OrderChildLeads
+  } order_e;
+
+  typedef enum int {
+    TimingOkay,
+    TimingSlow
+  } timing_e;
+
+  typedef enum int {
+    ChildClkFaster,
+    ChildClkSlower
+  } child_clk_e;
+
+  typedef struct packed {
+    order_e order;
+    timing_e timing;
+  } reset_op_t;
+
+  virtual clk_rst_if clk_rst_vif;
+  virtual clk_rst_if child_clk_rst_vif;
+  virtual rstmgr_cnsty_chk_if reset_vif;
+
+  logic error = 0;
+  int cycles_to_check = 7;
+
+  rand int cycles_reset_width;
+  rand int cycles_child_reset_width;
+  rand int cycles_in_apply_resets;
+  rand int cycles_to_child_reset;
+  rand int cycles_to_child_release;
+  rand int cycles_to_parent_reset;
+  rand int cycles_to_parent_release;
+  rand int cycles_to_sw_reset;
+  rand int cycles_to_sw_release;
+
+  constraint cycles_reset_width_c {cycles_reset_width inside {[2 : 10]};}
+  constraint cycles_child_reset_width_c {cycles_child_reset_width inside {[2 : 10]};}
+  constraint cycles_in_apply_resets_c {cycles_in_apply_resets inside {[5 : 25]};}
+  constraint cycles_to_child_reset_c {cycles_to_child_reset inside {[3 : 8]};}
+  constraint cycles_to_child_release_c {cycles_to_child_release inside {[3 : 6]};}
+  constraint cycles_to_parent_reset_c {cycles_to_parent_reset inside {[2 : 8]};}
+  constraint cycles_to_parent_release_c {cycles_to_parent_release inside {[3 : 6]};}
+  constraint cycles_to_sw_reset_c {cycles_to_sw_reset inside {[2 : 8]};}
+  constraint cycles_to_sw_release_c {cycles_to_sw_release inside {[3 : 6]};}
+
+  function new(virtual clk_rst_if clk_vif, virtual clk_rst_if child_clk_vif,
+               virtual rstmgr_cnsty_chk_if rst_vif);
+    clk_rst_vif = clk_vif;
+    child_clk_rst_vif = child_clk_vif;
+    reset_vif = rst_vif;
+  endfunction
+
+  function string get_full_name();
+    return "reset_class";
+  endfunction
+
+  task set_child_period(child_clk_e child_clk);
+    if (child_clk == ChildClkFaster) begin
+      `uvm_info(`gfn, $sformatf(
+                "Setting child clk (%0d ps) faster than reference (%0d ps)",
+                ChildFastClkPeriod,
+                ClkPeriod
+                ), UVM_LOW)
+      child_clk_rst_vif.set_period_ps(ChildFastClkPeriod);
+    end else begin
+      `uvm_info(`gfn, $sformatf(
+                "Setting child clk (%0d ps) slower than reference (%0d ps)",
+                ChildSlowClkPeriod,
+                ClkPeriod
+                ), UVM_LOW)
+      child_clk_rst_vif.set_period_ps(ChildSlowClkPeriod);
+    end
+  endtask
+
+  task apply_resets();
+    fork
+      clk_rst_vif.apply_reset(.reset_width_clks(cycles_reset_width));
+      child_clk_rst_vif.apply_reset(.reset_width_clks(cycles_child_reset_width));
+      begin
+        reset_vif.parent_rst_ni = 1'b0;
+        clk_rst_vif.wait_clks(cycles_in_apply_resets);
+        reset_vif.parent_rst_ni = 1'b1;
+      end
+    join
+    clk_rst_vif.wait_clks(4);
+  endtask
+
+  task set_quiescent();
+    `uvm_info(`gfn, "Setting quiescent inputs", UVM_MEDIUM)
+    reset_vif.parent_rst_ni = 1'b1;
+    reset_vif.sw_rst_req_i  = 1'b0;
+    reset_vif.child_rst_ni  = 1'b1;
+  endtask
+
+  task set_parent_reset(logic value, int cycles);
+    clk_rst_vif.wait_clks(cycles_to_parent_reset);
+    `uvm_info(`gfn, $sformatf("Setting parent_rst_ni=%b", value), UVM_MEDIUM)
+    reset_vif.parent_rst_ni = value;
+  endtask
+
+  task set_sw_reset(logic value, int cycles);
+    clk_rst_vif.wait_clks(cycles_to_sw_reset);
+    `uvm_info(`gfn, $sformatf("Setting sw_rst_req_i=%b", value), UVM_MEDIUM)
+    reset_vif.sw_rst_req_i = value;
+  endtask
+
+  task set_child_reset(logic value, int cycles);
+    clk_rst_vif.wait_clks(cycles);
+    `uvm_info(`gfn, $sformatf("Setting child_rst_ni=%b", value), UVM_MEDIUM)
+    reset_vif.child_rst_ni = value;
+  endtask
+
+  task send_unexpected_child_resets();
+    `uvm_info(`gfn, "check unexpected active child reset", UVM_MEDIUM)
+    set_child_reset(.value(0), .cycles(cycles_to_child_reset));
+    clk_rst_vif.wait_clks(cycles_to_check);
+    `DV_CHECK_EQ(reset_vif.err_o, 1'b1, "expected error for unexpected active child reset")
+    clk_rst_vif.wait_clks(cycles_to_child_release);
+    reset_vif.child_rst_ni = 1'b1;
+  endtask
+
+  task send_unexpected_child_release();
+    `uvm_info(`gfn, "check unexpected inactive child reset", UVM_MEDIUM)
+    fork
+      set_parent_reset(.value(0), .cycles(cycles_to_parent_reset));
+      set_sw_reset(.value(1), .cycles(cycles_to_sw_reset));
+      set_child_reset(.value(0), .cycles(cycles_to_child_reset));
+    join
+    clk_rst_vif.wait_clks(cycles_to_child_release);
+    reset_vif.child_rst_ni = 1'b1;
+    clk_rst_vif.wait_clks(cycles_to_check);
+    `uvm_info(`gfn, "Checking err_o output", UVM_MEDIUM)
+    `DV_CHECK_EQ(reset_vif.err_o, 1'b1, "expected error for unexpected inactive child reset")
+  endtask
+
+  task unexpected_child_activity();
+    `uvm_info(`gfn, "checking unexpected child activity", UVM_LOW)
+    for (int i = 0; i < 20; ++i) begin
+      set_quiescent();
+      `DV_CHECK_RANDOMIZE_FATAL(this);
+
+      send_unexpected_child_resets();
+      // Expect an error, so reset the dut.
+      apply_resets();
+
+      send_unexpected_child_release();
+      apply_resets();
+    end
+  endtask
+
+  task body();
+    clk_rst_vif.set_period_ps(ClkPeriod);
+    clk_rst_vif.set_active();
+    child_clk_rst_vif.set_period_ps(ChildFastClkPeriod);
+    child_clk_rst_vif.set_active();
+    `DV_CHECK_RANDOMIZE_FATAL(this);
+
+    apply_resets();
+    set_quiescent();
+
+    // Run with child clock faster than reference.
+    set_child_period(ChildClkFaster);
+    clk_rst_vif.wait_clks(20);
+
+    unexpected_child_activity();
+
+    // Run with child clock slower than reference.
+    set_child_period(ChildClkSlower);
+    clk_rst_vif.wait_clks(20);
+
+    unexpected_child_activity();
+  endtask
+endclass
+
+module tb;
+
+  import uvm_pkg::*;
+
+  reset_class reset_cl;
+
+  wire clk_i;
+  wire rst_ni;
+  wire child_clk_i;
+  wire unused_child_rst_ni;
+
+  clk_rst_if clk_rst_if (
+    .clk  (clk_i),
+    .rst_n(rst_ni)
+  );
+  clk_rst_if child_clk_rst_if (
+    .clk  (child_clk_i),
+    .rst_n(unused_child_rst_ni)
+  );
+  rstmgr_cnsty_chk_if rstmgr_cnsty_chk_if ();
+
+  logic error = 0;
+
+  rstmgr_cnsty_chk dut (
+    .clk_i(clk_i),
+    .rst_ni(rst_ni),
+    .child_clk_i(child_clk_i),
+    .child_rst_ni(rstmgr_cnsty_chk_if.child_rst_ni),
+    .parent_rst_ni(rstmgr_cnsty_chk_if.parent_rst_ni),
+    .sw_rst_req_i(rstmgr_cnsty_chk_if.sw_rst_req_i),
+    .sw_rst_req_clr_o(rstmgr_cnsty_chk_if.sw_rst_req_clr_o),
+    .err_o(rstmgr_cnsty_chk_if.err_o),
+    .fsm_err_o(rstmgr_cnsty_chk_if.fsm_err_o)
+  );
+
+  initial begin
+    automatic dv_utils_pkg::dv_report_server dv_report_server = new();
+    $timeformat(-12, 0, " ps", 12);
+    uvm_report_server::set_server(dv_report_server);
+    reset_cl = new(clk_rst_if, child_clk_rst_if, rstmgr_cnsty_chk_if);
+    reset_cl.body();
+    dv_report_server.report_summarize();
+    $finish();
+  end
+
+endmodule : tb
diff --git a/hw/ip/rstmgr/rstmgr.core b/hw/ip/rstmgr/rstmgr.core
index 2bfcf46..7339482 100644
--- a/hw/ip/rstmgr/rstmgr.core
+++ b/hw/ip/rstmgr/rstmgr.core
@@ -18,12 +18,12 @@
       - lowrisc:prim:sparse_fsm
       - "fileset_ip     ? (lowrisc:ip:rstmgr_pkg)"
       - "fileset_top    ? (lowrisc:systems:rstmgr_pkg)"
+      - lowrisc:ip:rstmgr_cnsty_chk
       - "fileset_top    ? (lowrisc:systems:rstmgr)"
       - "fileset_topgen ? (lowrisc:systems:topgen)"
     files:
       - rtl/rstmgr_ctrl.sv
       - rtl/rstmgr_por.sv
-      - rtl/rstmgr_cnsty_chk.sv
       - rtl/rstmgr_crash_info.sv
       - rtl/rstmgr_leaf_rst.sv
       - "fileset_ip ? (rtl/rstmgr.sv)"
diff --git a/hw/ip/rstmgr/rstmgr_cnsty_chk.core b/hw/ip/rstmgr/rstmgr_cnsty_chk.core
new file mode 100644
index 0000000..de70135
--- /dev/null
+++ b/hw/ip/rstmgr/rstmgr_cnsty_chk.core
@@ -0,0 +1,43 @@
+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:ip:rstmgr_cnsty_chk"
+description: "Rstmgr consistency checker"
+filesets:
+  files_rtl:
+    depend:
+      - lowrisc:prim:assert
+      - lowrisc:prim:sparse_fsm
+      - lowrisc:ip:rv_core_ibex_pkg
+      - "fileset_ip     ? (lowrisc:ip:rstmgr_pkg)"
+      - "fileset_top    ? (lowrisc:systems:rstmgr_pkg)"
+      - "fileset_top    ? (lowrisc:systems:rstmgr)"
+      - "fileset_topgen ? (lowrisc:systems:topgen)"
+    files:
+      - rtl/rstmgr_cnsty_chk.sv
+    file_type: systemVerilogSource
+
+  files_verilator_waiver:
+    depend:
+      # common waivers
+      - lowrisc:lint:common
+
+  files_ascentlint_waiver:
+    depend:
+      # common waivers
+      - lowrisc:lint:common
+
+  files_veriblelint_waiver:
+    depend:
+      # common waivers
+      - lowrisc:lint:common
+
+targets:
+  default:
+    filesets:
+      - tool_verilator   ? (files_verilator_waiver)
+      - tool_ascentlint  ? (files_ascentlint_waiver)
+      - tool_veriblelint ? (files_veriblelint_waiver)
+      - files_rtl
diff --git a/hw/ip/rstmgr/rtl/rstmgr_cnsty_chk.sv b/hw/ip/rstmgr/rtl/rstmgr_cnsty_chk.sv
index d178786..d439d7f 100644
--- a/hw/ip/rstmgr/rtl/rstmgr_cnsty_chk.sv
+++ b/hw/ip/rstmgr/rtl/rstmgr_cnsty_chk.sv
@@ -114,7 +114,8 @@
   prim_sparse_fsm_flop #(
     .StateEnumT(state_e),
     .Width(StateWidth),
-    .ResetValue(StateWidth'(Reset))
+    .ResetValue(StateWidth'(Reset)),
+    .EnableAlertTriggerSVA(0)
   ) u_state_regs (
     .clk_i,
     .rst_ni,
@@ -148,6 +149,7 @@
 
   logic sync_child_ack;
 
+  // TODO - make this a sparse fsm
   always_comb begin
     state_d = state_q;
     err_o = '0;
diff --git a/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson b/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
index 4d6fbc1..ff99dc8 100644
--- a/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
+++ b/hw/top_earlgrey/dv/top_earlgrey_sim_cfgs.hjson
@@ -44,6 +44,7 @@
              "{proj_root}/hw/ip/pwm/dv/pwm_sim_cfg.hjson",
              "{proj_root}/hw/ip/pwrmgr/dv/pwrmgr_sim_cfg.hjson",
              "{proj_root}/hw/ip/rom_ctrl/dv/rom_ctrl_sim_cfg.hjson",
+             "{proj_root}/hw/ip/rstmgr/dv/rstmgr_cnsty_chk/rstmgr_cnsty_chk_sim_cfg.hjson",
              "{proj_root}/hw/ip/rstmgr/dv/rstmgr_sim_cfg.hjson",
              "{proj_root}/hw/ip/rv_timer/dv/rv_timer_sim_cfg.hjson",
              "{proj_root}/hw/ip/spi_host/dv/spi_host_sim_cfg.hjson",