Software APIs
alert_handler_lpg_reset_toggle.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 #include <assert.h>
6 #include <limits.h>
7 #include <stdbool.h>
8 #include <stdint.h>
9 
15 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
20 #include "sw/device/lib/runtime/irq.h"
22 #include "sw/device/lib/testing/alert_handler_testutils.h"
23 #include "sw/device/lib/testing/ret_sram_testutils.h"
24 #include "sw/device/lib/testing/rstmgr_testutils.h"
25 #include "sw/device/lib/testing/rv_plic_testutils.h"
26 #include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
27 #include "sw/device/lib/testing/test_framework/check.h"
29 
30 #include "alert_handler_regs.h"
32 #include "i2c_regs.h"
33 #include "spi_device_regs.h"
34 #include "spi_host_regs.h"
35 #include "sw/device/lib/testing/autogen/isr_testutils.h"
36 #include "usbdev_regs.h"
37 
38 OTTF_DEFINE_TEST_CONFIG();
39 
40 static dif_rv_plic_t plic;
41 static dif_alert_handler_t alert_handler;
42 static dif_rstmgr_t rstmgr;
43 static dif_spi_device_handle_t spi_dev;
44 static dif_spi_host_t spi_host0;
45 static dif_spi_host_t spi_host1;
46 static dif_usbdev_t usbdev;
47 static dif_i2c_t i2c0;
48 static dif_i2c_t i2c1;
49 static dif_i2c_t i2c2;
50 static dif_rv_core_ibex_t ibex;
51 static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
52 
53 /**
54  * Initialize the peripherals used in this test.
55  */
56 static void init_peripherals(void) {
57  mmio_region_t base_addr =
59  CHECK_DIF_OK(dif_rv_plic_init(base_addr, &plic));
60 
62  CHECK_DIF_OK(dif_alert_handler_init(base_addr, &alert_handler));
63 
64  CHECK_DIF_OK(dif_rstmgr_init(
66 
67  CHECK_DIF_OK(dif_usbdev_init(
69 
70  CHECK_DIF_OK(dif_spi_device_init(
72 
73  CHECK_DIF_OK(dif_spi_host_init(
75 
76  CHECK_DIF_OK(dif_spi_host_init(
78 
79  CHECK_DIF_OK(
81 
82  CHECK_DIF_OK(
84 
85  CHECK_DIF_OK(
87 
88  mmio_region_t ibex_addr =
90  CHECK_DIF_OK(dif_rv_core_ibex_init(ibex_addr, &ibex));
91 
92  // Enable all the alert_handler interrupts used in this test.
93  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
96 }
97 
98 // List of alerts
99 static const uint32_t spidev_alerts[] = {
101 static const uint32_t spihost0_alerts[] = {
103 static const uint32_t spihost1_alerts[] = {
105 static const uint32_t usbdev_alerts[] = {kTopEarlgreyAlertIdUsbdevFatalFault};
106 static const uint32_t i2c0_alerts[] = {kTopEarlgreyAlertIdI2c0FatalFault};
107 static const uint32_t i2c1_alerts[] = {kTopEarlgreyAlertIdI2c1FatalFault};
108 static const uint32_t i2c2_alerts[] = {kTopEarlgreyAlertIdI2c2FatalFault};
109 
110 static const uint32_t num_spihost0_alerts = ARRAYSIZE(spihost0_alerts);
111 static const uint32_t num_spihost1_alerts = ARRAYSIZE(spihost1_alerts);
112 static const uint32_t num_usbdev_alerts = ARRAYSIZE(usbdev_alerts);
113 static const uint32_t num_spidev_alerts = ARRAYSIZE(spidev_alerts);
114 static const uint32_t num_i2c0_alerts = ARRAYSIZE(i2c0_alerts);
115 static const uint32_t num_i2c1_alerts = ARRAYSIZE(i2c1_alerts);
116 static const uint32_t num_i2c2_alerts = ARRAYSIZE(i2c2_alerts);
117 
118 static const size_t num_alerts =
119  num_spihost0_alerts + num_spihost1_alerts + num_usbdev_alerts +
120  num_i2c0_alerts + num_i2c1_alerts + num_i2c2_alerts + num_spidev_alerts;
121 
122 /**
123  * A structure to keep the info for peripheral IPs
124  */
125 typedef struct test {
126  /**
127  * Name of the peripheral.
128  */
129  const char *name;
130  /**
131  * Base address for the peripheral.
132  */
133  uintptr_t base;
134  /**
135  * Handle to the DIF object for this peripheral.
136  */
137  void *dif;
138  /**
139  * List of Alert IDs for the peripheral
140  */
141  const uint32_t *alert_ids;
142  /**
143  * number of alerts for the peripheral
144  */
145  const uint32_t num_alert_peri;
146  /**
147  * The index of this device in the reset manager.
148  */
149  uint32_t reset_index;
150 } test_t;
151 
152 // The array of the peripherals
153 static const test_t kPeripherals[] = {
154  {
155  .name = "SPI_HOST0",
157  .dif = &spi_host0,
158  .alert_ids = spihost0_alerts,
159  .num_alert_peri = num_spihost0_alerts,
160  .reset_index = kTopEarlgreyResetManagerSwResetsSpiHost0,
161  },
162  {
163  .name = "SPI_HOST1",
165  .dif = &spi_host1,
166  .alert_ids = spihost1_alerts,
167  .num_alert_peri = num_spihost1_alerts,
168  .reset_index = kTopEarlgreyResetManagerSwResetsSpiHost1,
169  },
170  {
171  .name = "USB",
173  .dif = &usbdev,
174  .alert_ids = usbdev_alerts,
175  .num_alert_peri = num_usbdev_alerts,
176  .reset_index = kTopEarlgreyResetManagerSwResetsUsb,
177  },
178  {
179  .name = "SPI_DEVICE",
181  .dif = &spi_dev.dev,
182  .alert_ids = spidev_alerts,
183  .num_alert_peri = num_spidev_alerts,
184  .reset_index = kTopEarlgreyResetManagerSwResetsSpiDevice,
185  },
186  {
187  .name = "I2C0",
189  .dif = &i2c0,
190  .alert_ids = i2c0_alerts,
191  .num_alert_peri = num_i2c0_alerts,
192  .reset_index = kTopEarlgreyResetManagerSwResetsI2c0,
193  },
194  {
195  .name = "I2C1",
197  .dif = &i2c1,
198  .alert_ids = i2c1_alerts,
199  .num_alert_peri = num_i2c1_alerts,
200  .reset_index = kTopEarlgreyResetManagerSwResetsI2c1,
201  },
202  {
203  .name = "I2C2",
205  .dif = &i2c2,
206  .alert_ids = i2c2_alerts,
207  .num_alert_peri = num_i2c2_alerts,
208  .reset_index = kTopEarlgreyResetManagerSwResetsI2c2,
209  },
210 };
211 
212 /**
213  * Configure and enable alerts of `num_peripheral` peripherals starting from the
214  * `first_peripheral`.
215  * @param num_peripherals The number of peripherals, of which alerts will be
216  * enabled
217  * @param first_peripheral The address of the first peripheral in the range
218  * @param ping_timeout The timeout value for the ping timer object.
219  */
220 static void alert_handler_config_peripherals(uint32_t num_peripherals,
221  const test_t *first_peripheral,
222  uint32_t ping_timeout) {
223  dif_alert_handler_alert_t alerts[num_alerts];
224  dif_alert_handler_class_t alert_classes[num_alerts];
225 
226  // Enable all alerts coming from the chosen peripherals
227  // Configure them as Class A.
228  size_t array_idx = 0;
229  test_t *peri_ptr;
230  for (int ii = 0; ii < num_peripherals; ii++) {
231  peri_ptr = (test_t *)first_peripheral + ii;
232  for (int jj = 0; jj < peri_ptr->num_alert_peri; jj++) {
233  alerts[array_idx] = peri_ptr->alert_ids[jj];
234  alert_classes[array_idx] = kDifAlertHandlerClassA;
235  array_idx++;
236  }
237  }
238 
239  // Enable the alert_ping_fail local alert and configure that as Class B.
240  dif_alert_handler_local_alert_t loc_alerts[] = {
241  kDifAlertHandlerLocalAlertAlertPingFail};
242  dif_alert_handler_class_t loc_alert_classes[] = {kDifAlertHandlerClassB};
243 
244  dif_alert_handler_escalation_phase_t esc_phases[] = {
246  .signal = 0,
247  .duration_cycles = 2000}};
248 
249  dif_alert_handler_class_config_t class_config = {
251  .accumulator_threshold = 0,
252  .irq_deadline_cycles = 10000,
253  .escalation_phases = esc_phases,
254  .escalation_phases_len = ARRAYSIZE(esc_phases),
255  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
256  };
257 
258  dif_alert_handler_class_config_t class_configs[] = {class_config,
259  class_config};
260 
261  dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA,
262  kDifAlertHandlerClassB};
263  dif_alert_handler_config_t config = {
264  .alerts = alerts,
265  .alert_classes = alert_classes,
266  .alerts_len = array_idx,
267  .local_alerts = loc_alerts,
268  .local_alert_classes = loc_alert_classes,
269  .local_alerts_len = ARRAYSIZE(loc_alerts),
270  .classes = classes,
271  .class_configs = class_configs,
272  .classes_len = ARRAYSIZE(class_configs),
273  .ping_timeout = ping_timeout,
274  };
275 
276  CHECK_STATUS_OK(alert_handler_testutils_configure_all(&alert_handler, config,
278  // Enables alert handler irq.
279  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
280  &alert_handler, kDifAlertHandlerIrqClassa, kDifToggleEnabled));
281 
282  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
283  &alert_handler, kDifAlertHandlerIrqClassb, kDifToggleEnabled));
284 }
285 
286 /**
287  * A utility function to wait enough until the alert handler pings a peripheral
288  * alert
289  */
290 void wait_enough_for_alert_ping(void) {
291  // wait enough
292  if (kDeviceType == kDeviceFpgaCw310) {
293  // NUM_ALERTS*2*margin_of_safety*(2**DW)*(1/kClockFreqPeripheralHz)
294  // (2**6)*2*4*(2**16)*(400ns) = 12.8s
295  busy_spin_micros(1000 * 12800);
296  } else if (kDeviceType == kDeviceSimDV) {
297  // NUM_ALERTS*2*margin_of_safety*(2**DW)*(1/kClockFreqPeripheralHz)
298  // (2**6)*2*4*(2**3)*(40ns) = 160us
299  busy_spin_micros(160);
300  } else {
301  // Verilator
302  // NUM_ALERTS*2*margin_of_safety*(2**DW)*(1/kClockFreqPeripheralHz)
303  // (2**6)*2*4*(2**16)*(8us) = 256s
304  // This seems to be impractical for the current clock frequency config
305  // of the Verilator tests (kClockFreqPeripheralHz = 125K).
306  LOG_FATAL("SUPPORTED PLATFORMS: DV and FPGA");
307  LOG_FATAL("TO SUPPORT THE PLATFORM %d, COMPUTE THE RIGHT WAIT-TIME",
308  kDeviceType);
309  }
310 }
311 
312 /**
313  * Get `num` distinct random numbers in the range [0, `max`] from
314  * RV_CORE_IBEX_RND_DATA.
315  *
316  * @param ibex The Ibex DIF object.
317  * @param num The number of random numbers to get.
318  * @param[out] rnd_buf Pointer to the buffer to write the random numbers to.
319  * @param max The maximum random value returned.
320  */
321 static void get_rand_words(dif_rv_core_ibex_t *ibex, int num, uint32_t *rnd_buf,
322  uint32_t max) {
323  uint32_t rnd_word;
324  for (int i = 0; i < num; ++i) {
325  bool found = false;
326  while (found == false) {
327  // Get a new random number.
328  CHECK_DIF_OK(dif_rv_core_ibex_read_rnd_data(ibex, &rnd_word));
329  rnd_word = rnd_word % max;
330  // Check if the number is unique.
331  found = true;
332  for (int j = 0; j < i; ++j) {
333  if (rnd_buf[j] == rnd_word) {
334  // Start over.
335  found = false;
336  break;
337  }
338  }
339  }
340  // Add the number to the buffer.
341  rnd_buf[i] = rnd_word;
342  }
343 }
344 
345 enum {
346  // The counter ID for the non-volatile counter keeping the test steps.
347  kCounterTestSteps = 0,
348 };
349 
350 /**
351  * External ISR.
352  *
353  * Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
354  * line to the CPU, which results in a call to this OTTF ISR. This ISR
355  * overrides the default OTTF implementation.
356  */
357 void ottf_external_isr(uint32_t *exc_info) {
358  // We don't expect any interrupt to be fired.
359  // If an interrupt is fired, the test will be ended.
360  CHECK(false, "Unexpected external interrupt triggered.");
361  // top_earlgrey_plic_peripheral_t peripheral_serviced;
362  // dif_alert_handler_irq_t irq_serviced;
363  // isr_testutils_alert_handler_isr(plic_ctx, alert_handler_ctx,
364  // &peripheral_serviced, &irq_serviced);
365  // CHECK(peripheral_serviced == kTopEarlgreyPlicPeripheralAlertHandler,
366  // "Interurpt from unexpected peripheral: %d", peripheral_serviced);
367 }
368 
369 bool test_main(void) {
370  init_peripherals();
371 
372  ret_sram_testutils_init();
373 
374  // To keep the test results
375  bool is_cause;
376  // To keep the random wait time
377  uint32_t rnd_wait_time;
378 
379  // The test consists of multiple test phases
380  // Each test phase consists of ARRAYSIZE(kPeripherals) steps
381  // At every step, a specific peripheral is tested
382  size_t test_phase;
383  size_t test_step_cnt;
384  size_t peri_idx;
385 
386  // Need a retention sram counter to keep the test-step info
387  // between resets on the FPGA.
389  rst_info = rstmgr_testutils_reason_get();
390  rstmgr_testutils_reason_clear();
391  if (rst_info & kDifRstmgrResetInfoPor) {
392  // Initialize retention sram counter if running on Cw310.
393  if (kDeviceType == kDeviceFpgaCw310) {
394  CHECK_STATUS_OK(ret_sram_testutils_counter_clear(kCounterTestSteps));
395  }
396  }
397  if (kDeviceType == kDeviceFpgaCw310) {
398  CHECK_STATUS_OK(
399  ret_sram_testutils_counter_get(kCounterTestSteps, &test_step_cnt));
400  } else {
401  // Initialize the test_step counter to zero for the simulation
402  test_step_cnt = 0;
403  }
404  /* TEST PHASE #0: NEGATIVE TESTING (FPGA-ONLY)
405 
406  The timeout value is set to 2
407  All of the alerts should trigger ping_timeout_alert
408  if they are not in reset
409  TEST: for_each_peripheral
410  1- Hold the reset of the IP
411  2- Enable the corresponding alerts
412  3- Wait long enough
413  4- Confirm that ping_timeout has not been triggered
414  5- Release the reset of the IP
415  6- Reset the device to test the next peripheral
416  */
417  while (test_step_cnt < 1 * ARRAYSIZE(kPeripherals)) {
418  // Run the negative test only on the FPGA
419  // This will save the test time in the simulation
420  if (kDeviceType == kDeviceFpgaCw310) {
421  // Read the test_step_cnt and compute the test phase
422  // amd the peripheral ID to test
423  CHECK_STATUS_OK(
424  ret_sram_testutils_counter_get(kCounterTestSteps, &test_step_cnt));
425  test_phase = test_step_cnt / ARRAYSIZE(kPeripherals);
426  peri_idx = test_step_cnt - (test_phase)*ARRAYSIZE(kPeripherals);
427 
428  // Wait for a random time <= 1024us
429  get_rand_words(&ibex, /*number of words*/ 1, &rnd_wait_time,
430  /*max*/ 1 << 10);
431  busy_spin_micros(rnd_wait_time);
432 
433  // Hold the reset of the peripheral
434  CHECK_DIF_OK(dif_rstmgr_software_reset(&rstmgr,
435  kPeripherals[peri_idx].reset_index,
437 
438  alert_handler_config_peripherals(
439  /*num_peripherals*/ 1, /* *first_peripheral*/ &kPeripherals[peri_idx],
440  /*ping_timeout*/ 2);
441 
442  // wait enough until the alert handler pings the peripheral
443  wait_enough_for_alert_ping();
444 
445  // Check the ping_timeout alert status
447  &alert_handler, kDifAlertHandlerLocalAlertAlertPingFail, &is_cause));
448  CHECK(!is_cause,
449  "Expected response is ping_timeout_fail= 0! But we got "
450  "ping_timrout_fail = 1 for peripheral[%d]",
451  peri_idx);
452 
453  // Release the reset
454  CHECK_DIF_OK(dif_rstmgr_software_reset(&rstmgr,
455  kPeripherals[peri_idx].reset_index,
457 
458  // Increment the test_step counter
459  CHECK_STATUS_OK(ret_sram_testutils_counter_increment(kCounterTestSteps));
460  // Request system reset to unlock the alert handler config and test the
461  // next peripheral
462  CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
463  } else {
464  // For the simulation, only increment the test_step counter
465  // to procced to the next phase without resetting the device
466  test_step_cnt++;
467  }
468  }
469 
470  /* TEST PHASE #1: TEST THE REGULAR CONFIG
471 
472  The timeout value is set to 256
473  None of the alerts should trigger ping_timeout_alert
474  TEST:
475  enable_alerts (all_peripherals, timeout=256)
476  for_each_peripheral
477  1- wait for random time
478  2- Hold the IP's reset
479  3- Wait long enough
480  4- Release the IP's reset
481  5- Confirm that ping_timeout has not been triggered
482  */
483 
484  // Enable and lock the all peripherals' alerts at the beginning
485  // of the PHASE #1
486  if (test_step_cnt == 1 * ARRAYSIZE(kPeripherals)) {
487  alert_handler_config_peripherals(
488  /*num_peripherals*/ ARRAYSIZE(kPeripherals),
489  /* *first_peripheral*/ &kPeripherals[0], /*ping_timeout*/ 256);
490  }
491 
492  while (test_step_cnt < 2 * ARRAYSIZE(kPeripherals)) {
493  // Read the test_step_cnt and compute the test phase
494  // amd the peripheral ID to test
495  test_phase = test_step_cnt / ARRAYSIZE(kPeripherals);
496  peri_idx = test_step_cnt - (test_phase)*ARRAYSIZE(kPeripherals);
497 
498  // Wait for a random time <= 1024us
499  get_rand_words(&ibex, /*number of words*/ 1, &rnd_wait_time,
500  /*max*/ 1 << 10);
501  busy_spin_micros(rnd_wait_time);
502 
503  // Hold the reset of the peripheral
504  CHECK_DIF_OK(dif_rstmgr_software_reset(&rstmgr,
505  kPeripherals[peri_idx].reset_index,
507 
508  // wait enough until the alert handler pings the peripheral
509  wait_enough_for_alert_ping();
510 
511  // Check the alert status
513  &alert_handler, kDifAlertHandlerLocalAlertAlertPingFail, &is_cause));
514  CHECK(!is_cause, "Expected response: No alert_ping_fail! but we got %d",
515  is_cause);
516 
517  // Release the reset of the peripheral
518  CHECK_DIF_OK(dif_rstmgr_software_reset(&rstmgr,
519  kPeripherals[peri_idx].reset_index,
521 
522  // Increment the test counter
523  test_step_cnt++;
524  }
525 
526  return true;
527 }