[dv/alert_handler] Add a ping timeout sequence
This PR adds a ping timeout sequence in alert_handler.
The previous ping_corner_case sequence was moved because it tries to
cross ping request with escalation request.
This new sequence will focus on ping timeout, and the cross case is
covered in prim_esc testbench.
Signed-off-by: Cindy Chen <chencindy@opentitan.org>
diff --git a/hw/dv/sv/alert_esc_agent/esc_monitor.sv b/hw/dv/sv/alert_esc_agent/esc_monitor.sv
index a633e8c..d4883d6 100644
--- a/hw/dv/sv/alert_esc_agent/esc_monitor.sv
+++ b/hw/dv/sv/alert_esc_agent/esc_monitor.sv
@@ -49,7 +49,7 @@
!cfg.probe_vif.get_esc_en() &&
!under_reset) begin
@(cfg.vif.monitor_cb);
- check_esc_resp(.req(req), .is_ping(1));
+ check_esc_resp(.req(req), .is_ping(1), .ping_triggered(1));
ping_cnter ++;
end
if (under_reset) continue;
@@ -64,7 +64,7 @@
while (!cfg.probe_vif.get_esc_en() &&
!(req.esc_handshake_sta inside {EscIntFail, EscRespComplete, EscReceived})) begin
@(cfg.vif.monitor_cb);
- check_esc_resp(.req(req), .is_ping(1));
+ check_esc_resp(.req(req), .is_ping(1), .ping_triggered(1));
end
end
// wait a clk cycle to enter the esc_p/n mode
@@ -132,13 +132,19 @@
end
endtask : sig_int_fail_thread
- // this task checks if resp_p/n is correct by:
- // if ping is interrupt by real escalation, abort checking and goes to next expected stage
- // if it is not a ping_response, it should follow: low -> high .. until esc_p goes low
- // if it is a ping_response, it should follow: low -> high -> low -> high
- // if any clock cycle resp_p/n does not match the expected pattern, reset back to "low" state
- // if any clock cycle resp_p/n are not complement, reset back to "low" state
- virtual task check_esc_resp(alert_esc_seq_item req, bit is_ping);
+ // This task checks if resp_p/n is correct.
+ //
+ // Check conditions:
+ // - If ping is interrupt by real escalation, abort checking and goes to next expected stage.
+ // - If it is not a ping_response, it should follow: low -> high .. until esc_p goes low.
+ // - If it is a ping_response, it should follow: low -> high -> low -> high.
+ // - If any clock cycle resp_p/n does not match the expected pattern, reset back to "low" state.
+ // - If any clock cycle resp_p/n are not complement, reset back to "low" state.
+ // The `ping_triggered` input is added to cover a corner case when escalation ping has integrity
+ // error and FSM goes back to EscReceived case. Because escalation ping is an one cycle pulse, so
+ // design does not know this is ping request thus stays in this EscReceived case.
+ // TODO: maybe use separate esc and ping enum to avoid adding this `ping_triggered` input.
+ virtual task check_esc_resp(alert_esc_seq_item req, bit is_ping, bit ping_triggered = 0);
case (req.esc_handshake_sta)
EscIntFail, EscReceived: begin
if (cfg.vif.monitor_cb.esc_rx.resp_p !== 0) begin
@@ -146,8 +152,13 @@
`downcast(req_clone, req.clone());
req_clone.esc_handshake_sta = EscIntFail;
alert_esc_port.write(req_clone);
+ `uvm_info("esc_monitor", $sformatf("[%s]: EscReceived has integrity error",
+ req.alert_esc_type.name()), UVM_HIGH)
end
- if (!cfg.probe_vif.get_esc_en() && req.esc_handshake_sta == EscIntFail && !is_ping) begin
+ // If there is signal integrity error or it is not the first ping request or escalation
+ // request, stay in this case for one more clock cycle.
+ if (!cfg.probe_vif.get_esc_en() &&
+ (ping_triggered || (req.esc_handshake_sta == EscIntFail && !is_ping))) begin
req.esc_handshake_sta = EscReceived;
end else begin
req.esc_handshake_sta = EscRespHi;
@@ -159,6 +170,8 @@
end else if (cfg.vif.monitor_cb.esc_rx.resp_p !== 1) begin
req.esc_handshake_sta = EscIntFail;
alert_esc_port.write(req);
+ `uvm_info("esc_monitor", $sformatf("[%s]: EscRespHi has integrity error",
+ req.alert_esc_type.name()), UVM_HIGH)
end else begin
req.esc_handshake_sta = EscRespLo;
end
@@ -169,6 +182,8 @@
end else if (cfg.vif.monitor_cb.esc_rx.resp_p !== 0) begin
req.esc_handshake_sta = EscIntFail;
alert_esc_port.write(req);
+ `uvm_info("esc_monitor", $sformatf("[%s]: EscRespLow has integrity error",
+ req.alert_esc_type.name()), UVM_HIGH)
end else begin
if (is_ping) req.esc_handshake_sta = EscRespPing0;
else req.esc_handshake_sta = EscRespHi;
@@ -180,6 +195,8 @@
end else if (cfg.vif.monitor_cb.esc_rx.resp_p !== 1) begin
req.esc_handshake_sta = EscIntFail;
alert_esc_port.write(req);
+ `uvm_info("esc_monitor", $sformatf("[%s]: EscRespPing0 has integrity error",
+ req.alert_esc_type.name()), UVM_HIGH)
end else begin
req.esc_handshake_sta = EscRespPing1;
end
@@ -190,6 +207,8 @@
end else if (cfg.vif.monitor_cb.esc_rx.resp_p !== 0) begin
req.esc_handshake_sta = EscIntFail;
alert_esc_port.write(req);
+ `uvm_info("esc_monitor", $sformatf("[%s]: EscRespPing1 has integrity error",
+ req.alert_esc_type.name()), UVM_HIGH)
end else begin
req.esc_handshake_sta = EscRespComplete;
end
diff --git a/hw/ip/alert_handler/data/alert_handler_testplan.hjson b/hw/ip/alert_handler/data/alert_handler_testplan.hjson
index 0a1a9d0..3b61b19 100644
--- a/hw/ip/alert_handler/data/alert_handler_testplan.hjson
+++ b/hw/ip/alert_handler/data/alert_handler_testplan.hjson
@@ -81,6 +81,20 @@
tests: ["alert_handler_random_classes"]
}
{
+ name: ping_timeout
+ desc: '''
+ Based on entropy test, this test request alert_sender and esc_receiver drivers to
+ randomly create ping requests timeout stimulus.
+
+ Checks:
+ - Verify interrupt pin and states.
+ - Verify alert and local alert causes.
+ - Verify escalation states and counts.
+ '''
+ milestone: V2
+ tests: ["alert_handler_ping_timeout"]
+ }
+ {
name: stress_all
desc: '''
Combine above sequences in one test to run sequentially with the following exclusions:
diff --git a/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson b/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson
index f06ee05..364ef96 100644
--- a/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson
+++ b/hw/ip/alert_handler/dv/alert_handler_generic_sim_cfg.hjson
@@ -82,6 +82,11 @@
}
{
+ name: alert_handler_ping_timeout
+ uvm_test_seq: alert_handler_ping_timeout_vseq
+ }
+
+ {
name: alert_handler_stress_all
run_opts: ["+test_timeout_ns=15_000_000_000"]
}
diff --git a/hw/ip/alert_handler/dv/env/alert_handler_env.core b/hw/ip/alert_handler/dv/env/alert_handler_env.core
index 7810828..c29d53c 100644
--- a/hw/ip/alert_handler/dv/env/alert_handler_env.core
+++ b/hw/ip/alert_handler/dv/env/alert_handler_env.core
@@ -26,6 +26,7 @@
- seq_lib/alert_handler_esc_alert_accum_vseq.sv: {is_include_file: true}
- seq_lib/alert_handler_sig_int_fail_vseq.sv: {is_include_file: true}
- seq_lib/alert_handler_entropy_vseq.sv: {is_include_file: true}
+ - seq_lib/alert_handler_ping_timeout_vseq.sv: {is_include_file: true}
- seq_lib/alert_handler_stress_all_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource
diff --git a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
index dc9fb75..5d86ed9 100644
--- a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
+++ b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_base_vseq.sv
@@ -268,7 +268,7 @@
virtual task wr_ping_timeout_cycle(bit[TL_DW-1:0] timeout_val);
csr_wr(.ptr(ral.ping_timeout_cyc_shadowed), .value(timeout_val));
- if (!config_locked) begin
+ if (`gmv(ral.ping_timer_regwen)) begin
if (timeout_val == 0) timeout_val = 1;
foreach (cfg.alert_host_cfg[i]) cfg.alert_host_cfg[i].ping_timeout_cycle = timeout_val;
foreach (cfg.esc_device_cfg[i]) cfg.esc_device_cfg[i].ping_timeout_cycle = timeout_val;
diff --git a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_ping_timeout_vseq.sv b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_ping_timeout_vseq.sv
new file mode 100644
index 0000000..c83a131
--- /dev/null
+++ b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_ping_timeout_vseq.sv
@@ -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
+
+// this sequence test corner cases for alert or escalation pings:
+// 1). ping integrity fail or timeout
+// 2). ping interrupted by a reset signal
+// 3). escalation ping interrupted by real escalation signal (this could happen because escalation
+// ping and real escalation share the same esc_p/n signals)
+
+class alert_handler_ping_timeout_vseq extends alert_handler_entropy_vseq;
+ `uvm_object_utils(alert_handler_ping_timeout_vseq)
+
+ `uvm_object_new
+
+ constraint num_trans_c {
+ num_trans inside {[1:10]};
+ }
+
+ constraint alert_trigger_c {
+ alert_trigger == 0;
+ }
+
+ constraint intr_en_c {
+ intr_en == '1;
+ }
+
+ constraint sig_int_c {
+ alert_int_err == 0;
+ esc_int_err == 0;
+ esc_standalone_int_err == 0;
+ }
+
+ constraint loc_alert_en_c {
+ local_alert_en[LocalEscPingFail:LocalAlertPingFail] > 0;
+ }
+
+ constraint ping_fail_c {
+ alert_ping_timeout == '1;
+ esc_ping_timeout == '1;
+ }
+
+ constraint ping_timeout_cyc_c {
+ ping_timeout_cyc inside {[1:MAX_PING_TIMEOUT_CYCLE]};
+ }
+
+ // disable interrupt timeout
+ constraint esc_intr_timeout_c {
+ foreach (intr_timeout_cyc[i]) {intr_timeout_cyc[i] == 0;}
+ }
+
+ function void pre_randomize();
+ this.enable_classa_only_c.constraint_mode(0);
+ endfunction
+
+ // In this sequence, because we disable all external alerts, so to ensure local alerts are
+ // triggerd, we wait for interrupt pins to fire then wait for alert and escalation handshake
+ // to finish.
+ virtual task wait_alert_esc_done();
+ wait (cfg.intr_vif.pins[NUM_ALERT_CLASSES-1:0]);
+ // Wait two clock cycles to avoid building a cycle-accurate scb.
+ cfg.clk_rst_vif.wait_clks(2);
+ `uvm_info(`gfn, $sformatf("Interrupt pin = %0h", cfg.intr_vif.pins[NUM_ALERT_CLASSES-1:0]),
+ UVM_LOW)
+ check_alert_interrupts();
+ super.wait_alert_esc_done();
+ endtask
+
+endclass : alert_handler_ping_timeout_vseq
diff --git a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_smoke_vseq.sv b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_smoke_vseq.sv
index c1aea42..2ff03b9 100644
--- a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_smoke_vseq.sv
+++ b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_smoke_vseq.sv
@@ -163,15 +163,12 @@
if ((esc_int_err == 0) && (esc_ping_timeout == 0)) check_alert_interrupts();
// if ping timeout enabled, wait for ping timeout done before checking escalation phases
- if ((esc_int_err | alert_ping_timeout) > 0) cfg.clk_rst_vif.wait_clks(MAX_PING_TIMEOUT_CYCLE);
+ if ((esc_int_err | alert_ping_timeout) > 0) begin
+ cfg.clk_rst_vif.wait_clks(MAX_PING_TIMEOUT_CYCLE);
+ end
// wait escalation done, and random interrupt with clear_esc
- wait_alert_handshake_done();
- if ($urandom_range(0, 1) && (esc_int_err == 0)) begin
- cfg.clk_rst_vif.wait_clks($urandom_range(0, max_wait_phases_cyc));
- clear_esc();
- end
- wait_esc_handshake_done();
+ wait_alert_esc_done();
read_alert_cause();
read_esc_status();
@@ -180,4 +177,13 @@
end
endtask
+ virtual task wait_alert_esc_done();
+ wait_alert_handshake_done();
+ if ($urandom_range(0, 1) && (esc_int_err == 0)) begin
+ cfg.clk_rst_vif.wait_clks($urandom_range(0, max_wait_phases_cyc));
+ clear_esc();
+ end
+ wait_esc_handshake_done();
+ endtask
+
endclass : alert_handler_smoke_vseq
diff --git a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
index cede15e..14b176f 100644
--- a/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
+++ b/hw/ip/alert_handler/dv/env/seq_lib/alert_handler_vseq_list.sv
@@ -11,4 +11,5 @@
`include "alert_handler_esc_alert_accum_vseq.sv"
`include "alert_handler_sig_int_fail_vseq.sv"
`include "alert_handler_entropy_vseq.sv"
+`include "alert_handler_ping_timeout_vseq.sv"
`include "alert_handler_stress_all_vseq.sv"