[otbn, dv] Added a testcase to verify *_STACK.ADDR.INTEGRITY

This commit adds a new testcase called otbn_stack_addr_integ_chk to
verify OTBN.CALL_STACK.ADDR.INTEGRITY and OTBN.LOOP_STACK.ADDR.INTEGRITY
countermeasures.

Signed-off-by: Prajwala Puttappa <prajwalaputtappa@lowrisc.org>
diff --git a/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson b/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson
index c725ed7..fb303a0 100644
--- a/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson
+++ b/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson
@@ -181,13 +181,13 @@
       name: sec_cm_loop_stack_addr_integrity
       desc: "Verify the countermeasure(s) LOOP_STACK.ADDR.INTEGRITY."
       stage: V2S
-      tests: []
+      tests: ["otbn_stack_addr_integ_chk"]
     }
     {
       name: sec_cm_call_stack_addr_integrity
       desc: "Verify the countermeasure(s) CALL_STACK.ADDR.INTEGRITY."
       stage: V2S
-      tests: []
+      tests: ["otbn_stack_addr_integ_chk"]
     }
     {
       name: sec_cm_start_stop_ctrl_state_consistency
diff --git a/hw/ip/otbn/dv/uvm/env/otbn_env.core b/hw/ip/otbn/dv/uvm/env/otbn_env.core
index 1dc46f2..c3fb0ba 100644
--- a/hw/ip/otbn/dv/uvm/env/otbn_env.core
+++ b/hw/ip/otbn/dv/uvm/env/otbn_env.core
@@ -59,6 +59,7 @@
       - seq_lib/otbn_urnd_err_vseq.sv: {is_include_file: true}
       - seq_lib/otbn_sw_no_acc_vseq.sv: {is_include_file: true}
       - seq_lib/otbn_mem_gnt_acc_err_vseq.sv: {is_include_file: true}
+      - seq_lib/otbn_stack_addr_integ_chk_vseq.sv: {is_include_file: true}
     file_type: systemVerilogSource
 
 generate:
diff --git a/hw/ip/otbn/dv/uvm/env/seq_lib/otbn_stack_addr_integ_chk_vseq.sv b/hw/ip/otbn/dv/uvm/env/seq_lib/otbn_stack_addr_integ_chk_vseq.sv
new file mode 100644
index 0000000..43a1b1f
--- /dev/null
+++ b/hw/ip/otbn/dv/uvm/env/seq_lib/otbn_stack_addr_integ_chk_vseq.sv
@@ -0,0 +1,132 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// A sequence to verify the countermeasure(s) OTBN.*_STACK.ADDR.INTEGRITY
+
+class otbn_stack_addr_integ_chk_vseq extends otbn_single_vseq;
+  `uvm_object_utils(otbn_stack_addr_integ_chk_vseq)
+  `uvm_object_new
+
+  bit end_test;
+
+  task body();
+    do_end_addr_check = 0;
+    fork
+      begin
+        super.body();
+      end
+      begin
+        inject_integ_err();
+      end
+    join
+  endtask: body
+
+  task inject_integ_err();
+    bit err_type;
+    string err_path;
+    string top_valid_path;
+    string stack_read_path;
+    string stack_wr_idx_path;
+    string stack_write_path;
+    bit stack_write;
+    bit top_valid;
+    bit stack_read;
+    bit [31:0] err_val = 32'd1 << 20;
+    `DV_CHECK_STD_RANDOMIZE_FATAL(err_type)
+    if (err_type) begin // err_type = 1 -> call stack error injection
+        top_valid_path = "tb.dut.u_otbn_core.u_otbn_rf_base.u_call_stack.top_valid_o";
+        stack_wr_idx_path = "tb.dut.u_otbn_core.u_otbn_rf_base.u_call_stack.stack_wr_idx";
+        stack_read_path =
+        "tb.dut.u_otbn_core.u_otbn_instruction_fetch.ctrl_flow_predec_o.call_stack_pop";
+        stack_write_path = "tb.dut.u_otbn_core.u_otbn_rf_base.u_call_stack.stack_write_o";
+    end else begin //  err_type = 0 -> loop stack error injection
+        top_valid_path =
+    "tb.dut.u_otbn_core.u_otbn_controller.u_otbn_loop_controller.loop_info_stack.top_valid_o";
+        stack_wr_idx_path =
+    "tb.dut.u_otbn_core.u_otbn_controller.u_otbn_loop_controller.loop_info_stack.stack_wr_idx[2:0]";
+        stack_read_path =
+    "tb.dut.u_otbn_core.u_otbn_controller.u_otbn_loop_controller.loop_info_stack.stack_read_o";
+        stack_write_path =
+    "tb.dut.u_otbn_core.u_otbn_controller.u_otbn_loop_controller.loop_info_stack.stack_write";
+    end
+    `DV_SPINWAIT(
+      do begin
+        @(cfg.clk_rst_vif.cb);
+        uvm_hdl_read(top_valid_path, top_valid);
+      end while (!top_valid);
+    )
+    cfg.clk_rst_vif.wait_clks($urandom_range(10, 100));
+    uvm_hdl_read(top_valid_path, top_valid);
+    if (top_valid) begin
+      fork
+        begin: isolation_fork
+          fork
+            begin
+              if (err_type) begin
+                `DV_SPINWAIT(
+                  do begin
+                    @(cfg.clk_rst_vif.cb);
+                    uvm_hdl_read(stack_read_path, stack_read);
+                  end while (!stack_read);
+                )
+                cfg.model_agent_cfg.vif.send_err_escalation(err_val);
+              end else begin
+                // error is injected in the corrupt_stack task.
+                // We wait here till the otbn is locked so that we don't exit the fork early.
+                `DV_WAIT(end_test)
+                @(cfg.clk_rst_vif.cb);
+              end
+            end
+            begin
+              corrupt_stack(stack_wr_idx_path, err_type, err_path);
+              forever begin
+                `DV_SPINWAIT(
+                  do begin
+                    @(cfg.clk_rst_vif.cb);
+                    uvm_hdl_read(stack_write_path, stack_write);
+                  end while (!stack_write);
+                )
+                @(cfg.clk_rst_vif.cb);
+                corrupt_stack(stack_wr_idx_path, err_type, err_path);
+              end // forever begin
+            end // fork begin
+          join_any
+          disable fork;
+        end: isolation_fork
+      join
+      `DV_WAIT(cfg.model_agent_cfg.vif.status == otbn_pkg::StatusLocked)
+      `DV_CHECK_FATAL(uvm_hdl_release(err_path) == 1);
+      reset_if_locked();
+    end
+  endtask: inject_integ_err
+
+  task corrupt_stack(string stack_wr_idx_path, bit err_type, output string err_path);
+    bit [2:0] stack_wr_idx;
+    bit [38:0] good_data;
+    bit [38:0] bad_data;
+    bit [31:0] mask;
+    bit [31:0] err_val = 32'd1 << 20;
+    uvm_hdl_read(stack_wr_idx_path, stack_wr_idx);
+    if (stack_wr_idx != 0) begin
+      if (err_type) begin
+        $sformat(err_path, "tb.dut.u_otbn_core.u_otbn_rf_base.u_call_stack.stack_storage[%0d]",
+                 (stack_wr_idx-1));
+      end else begin
+        uvm_hdl_read(stack_wr_idx_path, stack_wr_idx);
+        $sformat(err_path,
+    "tb.dut.u_otbn_core.u_otbn_controller.u_otbn_loop_controller.loop_info_stack.stack_storage[%0d]"
+                 , (stack_wr_idx - 1));
+      end
+      uvm_hdl_read(err_path, good_data);
+      `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(mask, $countones(mask) inside {[1:2]};)
+      bad_data = good_data ^ mask;
+      `DV_CHECK_FATAL(uvm_hdl_force(err_path, bad_data))
+      if (!err_type) begin
+        cfg.model_agent_cfg.vif.send_err_escalation(err_val);
+        `DV_WAIT(cfg.model_agent_cfg.vif.status == otbn_pkg::StatusLocked)
+        end_test = 1;
+      end
+    end
+  endtask: corrupt_stack
+endclass : otbn_stack_addr_integ_chk_vseq
diff --git a/hw/ip/otbn/dv/uvm/env/seq_lib/otbn_vseq_list.sv b/hw/ip/otbn/dv/uvm/env/seq_lib/otbn_vseq_list.sv
index 54a6fc4..e95b071 100644
--- a/hw/ip/otbn/dv/uvm/env/seq_lib/otbn_vseq_list.sv
+++ b/hw/ip/otbn/dv/uvm/env/seq_lib/otbn_vseq_list.sv
@@ -27,3 +27,4 @@
 `include "otbn_urnd_err_vseq.sv"
 `include "otbn_sw_no_acc_vseq.sv"
 `include "otbn_mem_gnt_acc_err_vseq.sv"
+`include "otbn_stack_addr_integ_chk_vseq.sv"
diff --git a/hw/ip/otbn/dv/uvm/otbn_sim_cfg.hjson b/hw/ip/otbn/dv/uvm/otbn_sim_cfg.hjson
index c9e2897..feef3ee 100644
--- a/hw/ip/otbn/dv/uvm/otbn_sim_cfg.hjson
+++ b/hw/ip/otbn/dv/uvm/otbn_sim_cfg.hjson
@@ -316,6 +316,12 @@
       en_run_modes: ["build_otbn_rig_binary_mode"]
       reseed: 2
     }
+    {
+      name: "otbn_stack_addr_integ_chk"
+      uvm_test_seq: "otbn_stack_addr_integ_chk_vseq"
+      en_run_modes: ["build_otbn_rig_binary_mode"]
+      reseed: 5
+    }
   ]
 
   // List of regressions.