Software APIs
soc_proxy_external_alerts.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
8 #include "sw/device/lib/testing/alert_handler_testutils.h"
10 
11 #include "hw/top_darjeeling/sw/autogen/top_darjeeling.h"
12 #include "soc_proxy_regs.h"
13 
14 OTTF_DEFINE_TEST_CONFIG();
15 
16 static const dif_alert_handler_escalation_phase_t kEscProfiles[] = {
17  {// Phase 0: non-maskable interrupt (NMI)
19  .signal = 0,
20  .duration_cycles = 1000},
21  {// Phase 1: life cycle scrap / lc_escalate_en
23  .signal = 1,
24  .duration_cycles = 1000},
25  {// Phase 2: reset
27  .signal = 3,
28  .duration_cycles = 300}};
29 
30 static const dif_alert_handler_class_config_t kConfigProfiles[] = {
31  // Profile 0: Fatal alerts
32  {
33  // Lock accumulation counter as soon as escalation is triggered.
35  // Trigger escalation on first event.
36  .accumulator_threshold = 0,
37  // Don't generate an IRQ.
38  .irq_deadline_cycles = 0,
39  // Escalation phases as defined above.
40  .escalation_phases = kEscProfiles,
41  .escalation_phases_len = 3,
42  // Snapshot Alert Handler CSRs for crashdump in phase 1.
43  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
44  },
45  // Profile 1: Recoverable alerts
46  {
47  // Lock accumulation counter as soon as escalation is triggered.
48  .auto_lock_accumulation_counter = kDifToggleEnabled,
49  // Delay escalation until more recoverable alerts than there are
50  // externally are seen (so that this doesn't escalate within this test).
51  .accumulator_threshold = 10000,
52  // Don't generate an IRQ.
53  .irq_deadline_cycles = 0,
54  // Escalation phases as defined above.
55  .escalation_phases = kEscProfiles,
56  .escalation_phases_len = 3,
57  // Snapshot Alert Handler CSRs for crashdump in phase 1.
58  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
59  },
60 };
61 
62 bool test_main(void) {
63  dif_alert_handler_t alert_handler;
64  dif_rstmgr_t rstmgr;
65 
66  // Initialize DIF handles.
67  CHECK_DIF_OK(dif_alert_handler_init(
68  mmio_region_from_addr(TOP_DARJEELING_ALERT_HANDLER_BASE_ADDR),
69  &alert_handler));
70  CHECK_DIF_OK(dif_rstmgr_init(
71  mmio_region_from_addr(TOP_DARJEELING_RSTMGR_AON_BASE_ADDR), &rstmgr));
72 
73  // The first external alert is Fatal Alert External 0.
74  const top_darjeeling_alert_id_t FIRST_SOC_PROXY_FATAL_ALERT_ID =
75  kTopDarjeelingAlertIdSocProxyFatalAlertExternal0;
76  // Keep this in sync with HW the last fatal external alert defined in
77  // `soc_proxy.hjson`.
78  const top_darjeeling_alert_id_t LAST_SOC_PROXY_FATAL_ALERT_ID =
79  kTopDarjeelingAlertIdSocProxyFatalAlertExternal23;
80  // The last external alert is a recoverable alert, and its ID is that of the
81  // first external alert plus the number of alerts minus the one internal
82  // alert.
83  const top_darjeeling_alert_id_t LAST_SOC_PROXY_RECOV_ALERT_ID =
84  FIRST_SOC_PROXY_FATAL_ALERT_ID + SOC_PROXY_PARAM_NUM_ALERTS - 1;
85 
86  // Read out previous alert crash dump (if it exists).
87  bool alert_before_reset = false;
88  top_darjeeling_alert_id_t previous_alert_id;
89  size_t rstmgr_alert_info_size;
90  CHECK_DIF_OK(
91  dif_rstmgr_alert_info_get_size(&rstmgr, &rstmgr_alert_info_size));
92  if (rstmgr_alert_info_size > 0) {
94  alert_info_dump[DIF_RSTMGR_ALERT_INFO_MAX_SIZE];
95  size_t rstmgr_alert_info_dump_segments;
97  &rstmgr, alert_info_dump, rstmgr_alert_info_size,
98  &rstmgr_alert_info_dump_segments));
100  CHECK_STATUS_OK(alert_handler_testutils_info_parse(
101  alert_info_dump, (int)rstmgr_alert_info_size, &alert_info));
102 
103  for (top_darjeeling_alert_id_t alert_id = FIRST_SOC_PROXY_FATAL_ALERT_ID;
104  alert_id <= LAST_SOC_PROXY_FATAL_ALERT_ID; alert_id++) {
105  if (alert_info.alert_cause[alert_id]) {
106  LOG_INFO("Alert ID %0d registered before latest reset.", alert_id);
107  alert_before_reset = true;
108  previous_alert_id = alert_id;
109  }
110  }
111 
112  for (unsigned class = 0; class < ALERT_HANDLER_PARAM_N_CLASSES; class ++) {
113  alert_handler_class_state_t state = alert_info.class_esc_state[class];
114  if (state != kCstateIdle) {
115  LOG_INFO("Alert class %0d: esc_state=%0d.", class, state);
116  }
117  }
118  }
119 
120  // Configure Reset Manager to capture alert crash dumps.
122 
123  // Determine expected ID of alert: First all the fatal alerts, then all the
124  // recoverable alerts.
125  top_darjeeling_alert_id_t expected_alert_id;
126  if (alert_before_reset) {
127  expected_alert_id = previous_alert_id + 1;
128  } else {
129  expected_alert_id = FIRST_SOC_PROXY_FATAL_ALERT_ID;
130  }
131 
132  // Loop over the remaining alerts.
133  while (expected_alert_id <= LAST_SOC_PROXY_RECOV_ALERT_ID) {
134  // Determine profile and configuration lock for alert, depending on whether
135  // it's fatal or recoverable.
136  dif_toggle_t locked;
138  if (expected_alert_id > LAST_SOC_PROXY_FATAL_ALERT_ID) {
139  // Recoverable
140  locked = kDifToggleDisabled;
141  profile = kConfigProfiles[1];
142  } else {
143  // Fatal
144  locked = kDifToggleEnabled;
145  profile = kConfigProfiles[0];
146  }
147 
148  // Set the alert we care about to class A.
150  &alert_handler, expected_alert_id, kDifAlertHandlerClassA,
151  /*enabled=*/kDifToggleEnabled, /*locked=*/locked));
152 
153  // Configure class A.
155  &alert_handler, kDifAlertHandlerClassA, profile,
156  /*enabled=*/kDifToggleEnabled, /*locked=*/locked));
157 
158  LOG_INFO("Alert handler configured.");
159 
160  if (expected_alert_id > LAST_SOC_PROXY_FATAL_ALERT_ID) {
161  // Recoverable alert: poll alert handler register to confirm alert.
162  while (true) {
163  bool is_cause;
165  &alert_handler, expected_alert_id, &is_cause));
166  if (is_cause) {
167  break;
168  }
169  busy_spin_micros(5);
170  }
171  LOG_INFO("Alert ID %0d is cause.", expected_alert_id);
172  CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler,
173  expected_alert_id));
174 
175  // Continue with the next recoverable alert.
176  expected_alert_id++;
177  } else {
178  // Fatal alert: break loop, because a reset is expected before the next
179  // fatal alert can be tested.
180  break;
181  }
182  }
183 
184  // As far as SW is concerned, the test has passed when we reach this code, as
185  // there are two scenarios:
186  // a) Fatal alert: SW has just configured alert handler for a fatal alert.
187  // Until the fatal alert is triggered and the escalation led to a reset of
188  // the CPU, SW has no further tasks. After the reset this function will be
189  // invoked again.
190  // b) Recoverable alert: SW has looped over all recoverable alerts and has
191  // seen and acknowledged each of them.
192  return true;
193 }