Software APIs
data_integrity_escalation_reset_test.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 
5 // This test triggers a ram read uncorrectable error created from the SV side
6 // for a specific address. The alert handler is programmed to trigger a specific
7 // alert for the ibex. The injection address is randomly selected by the SV
8 // sequence and can either be in the main SRAM or in the retention SRAM. For the
9 // main SRAM the SV sequence also randomly selects whether it injects a data
10 // error, or an instruction error in a test function residing in the main SRAM.
11 //
12 // The test checks that the alert handler state indicates the correct alert
13 // prior to reset, which is checked in the alert triggered interrupt. The
14 // test also checks that the alert handler cleared that state after reset. It
15 // also checks that the corresponding bit in the ibex fatal error CSR is set in
16 // the interrupt, and it is also cleared after reset.
17 //
18 // As a backup the aon timer is programmed to bark and bite, but these are
19 // expected not to happen since the escalation takes precedence. In particular,
20 // the timeouts are set so that bark and bite happen during the escalation phase
21 // where lc_escalate_en is asserted, which halts the watchdog timers.
22 //
23 // The alert_handler is programmed to escalate on the first alert event it
24 // registers. It is also programmed to do nothing in its first escalation phase
25 // so that the exception handlers and regular ISRs can execute before the NMI is
26 // triggered in the subsequent escalation phase.
27 //
28 // In summary, the test comprises the following steps:
29 //
30 // 0) SV sequence waits until test is running and the test function has been
31 // loaded to SRAM. It then randomly decides where to inject a fault, and
32 // communicates this to the program via symbol overwrites.
33 //
34 // 1) Test sets up peripherals. Randomly choose the alert class to use for the
35 // test.
36 //
37 // 2) Either read a corrupted memory location in main or retention SRAM, or
38 // jump to a corrupted test function in SRAM.
39 //
40 // 3) Alert handler escalates right away when seeing the first alert
41 // event. It enters phase 0 where it just waits for 200us without triggering any
42 // actions so that regular exception handlers can execute (these would
43 // otherwise be masked by the NMI).
44 //
45 // 4a) Regular ISR acknowledges alert handler has indeed escalated.
46 //
47 // 4b) Exception handler acknowledges either load integrity fault or instruction
48 // access fault.
49 //
50 // 5) Alert handler moves on to the next escalation phase and triggers an NMI.
51 //
52 // 6) NMI handler executes and checks that NMI is not due to Wdog, due to alert
53 // handler escalation. Since we do not clear the escalation, the NMI handler may
54 // execute several times, which will be reflected by a > 1 NMI count in the
55 // retention sram counters.
56 //
57 // 7) Alert handler moves on to the next escalation phase and asserts
58 // lc_escalate_en, which renders the chip inert. The Wdog should stop running,
59 // which is checked implicitly, since both bite and bark have been programmed to
60 // trigger within this escalation phase.
61 //
62 // 8) Alert handler moves on to the last excalation phase and resets the chip.
63 //
64 // 9) The chip reboots, and the test checks the alert handler state and the
65 // various interrupt counts maintained in retention sram.
66 //
67 // Throughout the entire test, we do not expect any Wdog bark or bite events.
68 
69 #include <assert.h>
70 #include <limits.h>
71 #include <stdbool.h>
72 #include <stdint.h>
73 
78 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
81 #include "sw/device/lib/runtime/irq.h"
83 #include "sw/device/lib/testing/alert_handler_testutils.h"
84 #include "sw/device/lib/testing/aon_timer_testutils.h"
85 #include "sw/device/lib/testing/flash_ctrl_testutils.h"
86 #include "sw/device/lib/testing/rand_testutils.h"
87 #include "sw/device/lib/testing/ret_sram_testutils.h"
88 #include "sw/device/lib/testing/rstmgr_testutils.h"
89 #include "sw/device/lib/testing/rv_plic_testutils.h"
90 #include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
91 #include "sw/device/lib/testing/test_framework/check.h"
93 
95 
96 OTTF_DEFINE_TEST_CONFIG();
97 
98 // This location will be update from SV to contain the address which will
99 // trigger the fault on a read.
100 static volatile const uint32_t kErrorRamAddress = 0;
101 
102 // This location will be update from SV to indicate the fault target for this
103 // test. The SV side randomly selects between data errors on main and retention
104 // SRAM, and instruction errors in a program that is loaded in the main SRAM.
105 static volatile const uint32_t kFaultTarget = 0;
106 
107 static const uint32_t kExpectedAlertNumber =
109 
110 // Alert class to use for the test. Will be chosen randomly by the test SW.
111 static volatile dif_alert_handler_class_t alert_class_to_use;
112 
113 enum {
114  // Counter for resets.
115  kCounterReset,
116  // Counter for regular interrupts.
117  kCounterInterrupt,
118  // Counter for NMIs.
119  kCounterNmi,
120  // Counter for exceptions
121  kCounterException
122 };
123 
124 /**
125  * Enums for identifying the fault injection target.
126  */
127 enum {
128  kFaultTargetMainSramData,
129  kFaultTargetRetSramData,
130  kFaultTargetMainSramInstr
131 };
132 
133 /**
134  * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset).
135  * Also program the aon timer with:
136  * - bark after escalation starts, so the interrupt is suppressed by escalation,
137  * - bite after escalation reset, so we should not get timer reset.
138  */
139 enum {
140  // Note that the escalation phase times below define the length of each phase,
141  // not when they start.
142  // The starting time is given by the aggregate of previous phase lengths, and
143  // is noted with @ below.
144  // @0 us -> in this phase we will not do anything so that the exception
145  // handlers have time to execute.
146  kEscalationPhase0Micros = 400,
147  // @200 us -> in this phase we will raise an NMI
148  kEscalationPhase1Micros = 400,
149  // @400 us -> in this phase we will assert lc_escalate_en
150  kEscalationPhase2Micros = 200,
151  // @600 us -> in this phase we will reset the chip
152  kEscalationPhase3Micros = 200,
153  // These are set so that both events happen in Phase2 of the escalation
154  // protocol, which asserts lc_escalate_en. That should prevent the Wdog
155  // from running and sending out an NMI on its own (we check in the NMI
156  // handler below that this does not happen).
157  kWdogBarkMicros = 850,
158  kWdogBiteMicros = 900,
159  // We expect exactly one reset
160  kMaxResets = 1,
161 };
162 
163 static_assert(
164  kWdogBarkMicros < kWdogBiteMicros &&
165  kWdogBarkMicros > (kEscalationPhase0Micros + kEscalationPhase1Micros),
166  "The wdog bite shall after the NMI phase when lc_escalate_en is asserted.");
167 /**
168  * Main SRAM addresses used in the sram_function_test.
169  */
170 enum {
175 };
176 
177 /**
178  * Program in main SRAM to jump to for testing corruption of instruction
179  * integrity.
180  *
181  * Using this program is convenient, since the sram_program.ld script ensures
182  * that the entry point will be mapped to the start of the main SRAM.
183  */
184 OT_SECTION(".data.sram_function_test")
186 static void sram_function_test(void) {
187  uint32_t pc = 0;
188  asm("auipc %[pc], 0;" : [pc] "=r"(pc));
189  LOG_INFO("PC: %p, SRAM: [%p, %p)", pc, kSramStart, kSramEnd);
190  CHECK(pc >= kSramStart && pc < kSramEnd, "PC is outside the main SRAM");
191 }
192 // Make the address of this function available via a global constant that SV can
193 // read.
194 volatile static const uint32_t kSramFunctionTestAddress =
195  (uint32_t)sram_function_test;
196 
197 /**
198  * Objects to access the peripherals used in this test via dif API.
199  */
200 static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
201 static dif_alert_handler_t alert_handler;
202 static dif_aon_timer_t aon_timer;
203 static dif_flash_ctrl_state_t flash_ctrl_state;
204 static dif_rstmgr_t rstmgr;
205 static dif_rv_core_ibex_t rv_core_ibex;
206 static dif_rv_plic_t plic;
207 
208 static void rv_core_ibex_fault_checker(bool enable) {
209  dif_rv_core_ibex_error_status_t codes;
210  CHECK_DIF_OK(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &codes));
211 
212  CHECK(codes == (enable ? kDifRvCoreIbexErrorStatusFatalResponseIntegrity : 0),
213  "Unexpected ibex error status");
214 }
215 
216 /**
217  * External ISR.
218  *
219  * Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
220  * line to the CPU, which results in a call to this OTTF ISR. This ISR
221  * overrides the default OTTF implementation.
222  */
223 void ottf_external_isr(uint32_t *exc_info) {
224  dif_rv_plic_irq_id_t irq_id;
225 
226  LOG_INFO("At regular external ISR");
227 
228  // There may be multiple interrupts due to the alert firing, so this keeps an
229  // interrupt counter and errors-out if there are too many interrupts.
230  CHECK_STATUS_OK(ret_sram_testutils_counter_increment(kCounterInterrupt));
231 
232  CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id));
233 
236 
237  if (peripheral == kTopEarlgreyPlicPeripheralAonTimerAon) {
238  uint32_t irq =
239  (irq_id - (dif_rv_plic_irq_id_t)
241 
242  // We should not get aon timer interrupts since escalation suppresses them.
243  CHECK(false, "Unexpected aon timer interrupt %d", irq);
244  } else if (peripheral == kTopEarlgreyPlicPeripheralAlertHandler) {
245  CHECK(
246  irq_id == kTopEarlgreyPlicIrqIdAlertHandlerClassa + alert_class_to_use,
247  "Unexpected irq_id, expected %d, got %d",
248  kTopEarlgreyPlicIrqIdAlertHandlerClassa + alert_class_to_use, irq_id);
249 
250  // Disable these interrupts from alert_handler so they don't keep happening
251  // until NMI.
252  uint32_t irq =
253  (irq_id -
255  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(&alert_handler, irq,
257 
258  // Disable this interrupt to prevent it from continuously firing. This
259  // should not prevent escalation from continuing.
260  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(&plic, irq_id, kPlicTarget,
262  }
263 
264  // Complete the IRQ by writing the IRQ source to the Ibex specific CC
265  // register.
266  CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic, kPlicTarget, irq_id));
267  LOG_INFO("Regular external ISR exiting");
268 }
269 
270 /**
271  * Load integrity error exception handler.
272  *
273  * Handles load integrity error exceptions on Ibex.
274  */
275 void ottf_load_integrity_error_handler(uint32_t *exc_info) {
276  LOG_INFO("At load integrity error handler");
277 
278  CHECK(kFaultTarget != kFaultTargetMainSramInstr,
279  "Expected fault target 0 or 1, got %d", kFaultTarget);
280 
281  uint32_t mtval = ibex_mtval_read();
282  CHECK(mtval == kErrorRamAddress, "Unexpected mtval: expected 0x%x, got 0x%x",
283  kErrorRamAddress, mtval);
284 
285  CHECK_STATUS_OK(ret_sram_testutils_counter_increment(kCounterException));
286 
287  rv_core_ibex_fault_checker(true);
288  LOG_INFO("Load integrity error handler exiting");
289 }
290 
291 /**
292  * Instruction access exception handler.
293  *
294  * Handles instruction access faults on Ibex.
295  */
296 void ottf_instr_access_fault_handler(uint32_t *exc_info) {
297  LOG_INFO("At instr access fault handler");
298 
299  CHECK(kFaultTargetMainSramInstr == 2, "Expected fault target 2, got %d",
300  kFaultTarget);
301  CHECK_STATUS_OK(ret_sram_testutils_counter_increment(kCounterException));
302 
303  rv_core_ibex_fault_checker(true);
304  LOG_INFO("Instr access fault handler exiting");
305 }
306 
307 /**
308  * External NMI ISR.
309  *
310  * Handles NMI interrupts on Ibex for either escalation or watchdog.
311  */
312 void ottf_external_nmi_handler(uint32_t *exc_info) {
314  LOG_INFO("At NMI handler");
315 
316  CHECK_STATUS_OK(ret_sram_testutils_counter_increment(kCounterNmi));
317 
318  // Check that this NMI was due to an alert handler escalation, and not due
319  // to a watchdog bark, since escalation suppresses the watchdog.
320  CHECK_DIF_OK(dif_rv_core_ibex_get_nmi_state(
321  &rv_core_ibex, (dif_rv_core_ibex_nmi_state_t *)&nmi_state));
322  CHECK(nmi_state.alert_enabled && nmi_state.alert_raised,
323  "Alert handler NMI state not expected:\n\t"
324  "alert_enable:%x\n\talert_raised:%x\n",
325  nmi_state.alert_enabled, nmi_state.alert_raised);
326  CHECK(nmi_state.wdog_enabled && !nmi_state.wdog_barked,
327  "Watchdog NMI state not expected:\n\t"
328  "wdog_enabled:%x\n\twdog_barked:%x\n",
329  nmi_state.wdog_enabled, nmi_state.wdog_barked);
330 
331  // Check the class.
333  CHECK_DIF_OK(dif_alert_handler_get_class_state(&alert_handler,
334  alert_class_to_use, &state));
335  CHECK(state == kDifAlertHandlerClassStatePhase1, "Wrong phase %d", state);
336 
337  // Check this gets the expected alert.
338  bool is_cause = false;
340  &alert_handler, kExpectedAlertNumber, &is_cause));
341  CHECK(is_cause);
342 
343  // Acknowledge the cause, which doesn't affect escalation.
344  CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler,
345  kExpectedAlertNumber));
346  LOG_INFO("NMI handler exiting");
347 }
348 
349 /**
350  * Initialize the peripherals used in this test.
351  */
352 static void init_peripherals(void) {
353  CHECK_DIF_OK(dif_alert_handler_init(
355  &alert_handler));
356 
357  CHECK_DIF_OK(dif_aon_timer_init(
359 
360  CHECK_DIF_OK(dif_flash_ctrl_init_state(
361  &flash_ctrl_state,
363 
364  CHECK_DIF_OK(dif_rstmgr_init(
366 
367  CHECK_DIF_OK(dif_rv_core_ibex_init(
369  &rv_core_ibex));
370 
371  CHECK_DIF_OK(dif_rv_plic_init(
373 }
374 
375 /**
376  * Program the alert handler to escalate on alerts and reset on phase 3,
377  * and to start escalation after observing 1 alert event.
378  */
379 static void alert_handler_config(void) {
380  alert_class_to_use = (dif_alert_handler_class_t)rand_testutils_gen32_range(
381  kDifAlertHandlerClassA, kDifAlertHandlerClassD);
382  dif_alert_handler_alert_t alerts[] = {kExpectedAlertNumber};
383  dif_alert_handler_class_t alert_classes[] = {alert_class_to_use};
384 
385  uint32_t cycles[4] = {0};
386  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
387  kEscalationPhase0Micros, &cycles[0]));
388  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
389  kEscalationPhase1Micros, &cycles[1]));
390  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
391  kEscalationPhase2Micros, &cycles[2]));
392  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
393  kEscalationPhase3Micros, &cycles[3]));
394 
395  dif_alert_handler_escalation_phase_t esc_phases[] = {
397  .signal = 0xFFFFFFFF, // do not trigger any signal, just wait.
398  .duration_cycles = cycles[0]},
400  .signal = 0, // NMI
401  .duration_cycles = cycles[1]},
403  .signal = 1, // lc_escalate_en
404  .duration_cycles = cycles[2]},
406  .signal = 3, // reset
407  .duration_cycles = cycles[3]}};
408 
409  // This test does not leverage the IRQ timeout feature of the alert
410  // handler, hence deadline_cycles is set to zero. Rather, it triggers
411  // escalation right away if an alert event is seen, hence threshold = 0;
412  uint32_t deadline_cycles = 0;
413  uint16_t threshold = 0;
414  LOG_INFO("Configuring class %d with %d cycles and %d occurrences",
415  alert_class_to_use, deadline_cycles, threshold);
416  dif_alert_handler_class_config_t class_config[] = {{
418  .accumulator_threshold = threshold,
419  .irq_deadline_cycles = deadline_cycles,
420  .escalation_phases = esc_phases,
421  .escalation_phases_len = ARRAYSIZE(esc_phases),
422  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase3,
423  }};
424 
425  dif_alert_handler_class_t classes[] = {alert_class_to_use};
426  dif_alert_handler_config_t config = {
427  .alerts = alerts,
428  .alert_classes = alert_classes,
429  .alerts_len = ARRAYSIZE(alerts),
430  .classes = classes,
431  .class_configs = class_config,
432  .classes_len = ARRAYSIZE(class_config),
433  .ping_timeout = kAlertHandlerTestutilsDefaultPingTimeout,
434  };
435 
436  CHECK_STATUS_OK(alert_handler_testutils_configure_all(&alert_handler, config,
438 
439  // Enables all alert handler irqs. This allows us to implicitly check that
440  // we do not get spurious IRQs from the classes that are unused.
441  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
442  &alert_handler, kDifAlertHandlerIrqClassa, kDifToggleEnabled));
443  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
444  &alert_handler, kDifAlertHandlerIrqClassb, kDifToggleEnabled));
445  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
446  &alert_handler, kDifAlertHandlerIrqClassc, kDifToggleEnabled));
447  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
448  &alert_handler, kDifAlertHandlerIrqClassd, kDifToggleEnabled));
449 }
450 
451 static void set_aon_timers(void) {
452  uint32_t bark_cycles = 0;
453  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(kWdogBarkMicros,
454  &bark_cycles));
455  uint32_t bite_cycles = 0;
456  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(kWdogBiteMicros,
457  &bite_cycles));
458 
459  LOG_INFO(
460  "Wdog will bark after %u us (%u cycles) and bite after %u us (%u cycles)",
461  (uint32_t)kWdogBarkMicros, bark_cycles, (uint32_t)kWdogBiteMicros,
462  bite_cycles);
463 
464  // Setup the wdog bark and bite timeouts.
465  CHECK_STATUS_OK(
466  aon_timer_testutils_watchdog_config(&aon_timer, bark_cycles, bite_cycles,
467  /*pause_in_sleep=*/false));
468 }
469 
470 /**
471  * Setup aon timer interrupts and trigger fault.
472  *
473  * The aon timers should never trigger actions because escalation should take
474  * precedence.
475  */
476 static void execute_test(void) {
477  alert_handler_config();
478 
479  // Make sure we can receive both the watchdog and alert NMIs.
480  CHECK_DIF_OK(
481  dif_rv_core_ibex_enable_nmi(&rv_core_ibex, kDifRvCoreIbexNmiSourceAlert));
482  CHECK_DIF_OK(
483  dif_rv_core_ibex_enable_nmi(&rv_core_ibex, kDifRvCoreIbexNmiSourceWdog));
484 
485  set_aon_timers();
486 
487  // 2 is an instruction fault on main SRAM.
488  if (kFaultTarget == kFaultTargetMainSramInstr) {
489  LOG_INFO("Jump to corrupted SRAM test function at %x",
490  kSramFunctionTestAddress);
491  sram_function_test();
492  CHECK(false,
493  "Since the SRAM test function is corrupted, this should not be "
494  "reached");
495  // 0/1 are data faults on ret/main SRAM.
496  } else {
497  // We run this to make sure the program is callable and working correctly
498  // when no instruction faults are inserted.
499  LOG_INFO("Jump to SRAM test function at %x", kSramFunctionTestAddress);
500  sram_function_test();
501  LOG_INFO("Returned from the SRAM test function");
502 
503  LOG_INFO("Reading corrupted address 0x%x, expecting alert %d",
504  kErrorRamAddress, kExpectedAlertNumber);
505 
506  // The SV side injects error when test starts.
507  uint32_t data = *((uint32_t *)kErrorRamAddress);
508 
509  // This normally should not run.
510  LOG_INFO("Read from address 0x%0x with expected error gets 0x%x",
511  kErrorRamAddress, data);
512  }
513 
515  CHECK(false, "This should not be reached");
516 }
517 
518 bool test_main(void) {
519  LOG_INFO("Entered test_main");
520 
521  // Enable global and external IRQ at Ibex.
522  irq_global_ctrl(true);
523  irq_external_ctrl(true);
524 
525  init_peripherals();
526 
527  ret_sram_testutils_init();
528 
529  // Enable all the interrupts used in this test.
530  rv_plic_testutils_irq_range_enable(
533  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
536  // Enable access to flash for storing info across resets.
537  LOG_INFO("Setting default region accesses");
538  CHECK_STATUS_OK(
539  flash_ctrl_testutils_default_region_access(&flash_ctrl_state,
540  /*rd_en*/ true,
541  /*prog_en*/ true,
542  /*erase_en*/ true,
543  /*scramble_en*/ false,
544  /*ecc_en*/ false,
545  /*he_en*/ false));
546 
547  // Check if there was a HW reset caused by the escalation.
549  rst_info = rstmgr_testutils_reason_get();
550  rstmgr_testutils_reason_clear();
551 
552  CHECK(rst_info == kDifRstmgrResetInfoPor ||
553  rst_info == kDifRstmgrResetInfoEscalation,
554  "Wrong reset reason %02X", rst_info);
555 
556  if (rst_info == kDifRstmgrResetInfoPor) {
557  LOG_INFO("Booting for the first time, starting test");
558  CHECK_STATUS_OK(ret_sram_testutils_counter_clear(kCounterException));
559  CHECK_STATUS_OK(ret_sram_testutils_counter_clear(kCounterInterrupt));
560  CHECK_STATUS_OK(ret_sram_testutils_counter_clear(kCounterNmi));
561  CHECK_STATUS_OK(ret_sram_testutils_counter_clear(kCounterReset));
562  execute_test();
563  } else if (rst_info == kDifRstmgrResetInfoEscalation) {
564  uint32_t reset_count;
565  LOG_INFO("Booting for the second time due to escalation reset");
566  CHECK_STATUS_OK(ret_sram_testutils_counter_get(kCounterReset,
567  (uint32_t *)&reset_count));
568  LOG_INFO("Reset counter value: %u", reset_count);
569  if (reset_count > kMaxResets) {
570  CHECK(false, "Got too many resets (%d)", reset_count);
571  }
572 
573  // Increment reset counter to know where we are.
574  CHECK_STATUS_OK(ret_sram_testutils_counter_increment(kCounterReset));
575 
576  // Get the counts from flash.
577  uint32_t interrupt_count = 0;
578  CHECK_STATUS_OK(
579  ret_sram_testutils_counter_get(kCounterInterrupt, &interrupt_count));
580  uint32_t nmi_count = 0;
581  CHECK_STATUS_OK(ret_sram_testutils_counter_get(kCounterNmi, &nmi_count));
582  uint32_t exception_count = 0;
583  CHECK_STATUS_OK(
584  ret_sram_testutils_counter_get(kCounterException, &exception_count));
585 
586  LOG_INFO("Interrupt count %d", interrupt_count);
587  LOG_INFO("NMI count %d", nmi_count);
588  LOG_INFO("Exception count %d", exception_count);
589 
590  CHECK(interrupt_count > 0, "Expected at least one regular interrupt");
591  // The NMI handler may execute multiple times during the corresponding
592  // escalation phase since we do not clear/stop the escalation.
593  CHECK(nmi_count > 0, "Expected at least one nmi");
594  CHECK(exception_count > 0, "Expected at least one exception");
595 
596  // Check the alert handler cause is cleared.
597  bool is_cause = true;
599  &alert_handler, kExpectedAlertNumber, &is_cause));
600  CHECK(!is_cause);
601 
602  // Check the fault register is clear.
603  rv_core_ibex_fault_checker(false);
604  return true;
605  } else {
606  LOG_ERROR("Unexpected rst_info=0x%x", rst_info);
607  return false;
608  }
609 
610  return false;
611 }