Software APIs
ast_clk_rst_inputs.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 
15 #include "sw/device/lib/runtime/irq.h"
17 #include "sw/device/lib/testing/alert_handler_testutils.h"
18 #include "sw/device/lib/testing/aon_timer_testutils.h"
19 #include "sw/device/lib/testing/clkmgr_testutils.h"
20 #include "sw/device/lib/testing/entropy_src_testutils.h"
21 #include "sw/device/lib/testing/entropy_testutils.h"
22 #include "sw/device/lib/testing/pwrmgr_testutils.h"
23 #include "sw/device/lib/testing/rstmgr_testutils.h"
24 #include "sw/device/lib/testing/rv_plic_testutils.h"
25 #include "sw/device/lib/testing/test_framework/check.h"
27 
29 #include "sensor_ctrl_regs.h"
30 #include "sw/device/lib/testing/autogen/isr_testutils.h"
31 
32 #define kAlertSet true
33 #define kAlertClear false
34 #define kAlertVal7 7
35 #define kAlertVal8 8
36 #define kDifNoWakeup 0
37 
38 OTTF_DEFINE_TEST_CONFIG();
39 
40 static volatile const uint8_t kNumLowPowerSamples;
41 static volatile const uint8_t kNumNormalPowerSamples;
42 static volatile const uint8_t kWakeUpTimeInUs;
43 
44 static volatile const uint8_t kChannel0MaxLowByte;
45 static volatile const uint8_t kChannel0MaxHighByte;
46 static volatile const uint8_t kChannel0MinLowByte;
47 static volatile const uint8_t kChannel0MinHighByte;
48 
49 static volatile const uint8_t kChannel1MaxLowByte;
50 static volatile const uint8_t kChannel1MaxHighByte;
51 static volatile const uint8_t kChannel1MinLowByte;
52 static volatile const uint8_t kChannel1MinHighByte;
53 
54 static dif_sensor_ctrl_t sensor_ctrl;
55 static dif_alert_handler_t alert_handler;
56 static dif_aon_timer_t aon_timer;
57 static dif_rv_plic_t rv_plic;
58 static dif_rv_plic_t plic;
59 static dif_pwrmgr_t pwrmgr;
60 static dif_rstmgr_t rstmgr;
61 static dif_entropy_src_t entropy_src;
62 
63 static dif_clkmgr_t clkmgr;
64 static dif_adc_ctrl_t adc_ctrl;
65 
66 static volatile bool interrupt_serviced = false;
67 static bool first_adc_setup = true;
68 
69 enum {
70  /**
71  * The size of the buffer used in firmware to process the entropy bits in
72  * firmware override mode.
73  */
74  kEntropyFifoBufferSize = 16,
75 };
76 
77 enum {
78  kPowerUpTimeInUs = 30,
79 };
80 
81 static uint32_t read_fifo_depth(dif_entropy_src_t *entropy) {
82  uint32_t fifo_depth = 0;
83  CHECK_DIF_OK(dif_entropy_src_get_fifo_depth(entropy, &fifo_depth));
84  return fifo_depth;
85 }
86 
87 static uint32_t get_events(dif_toggle_t fatal) {
88  dif_sensor_ctrl_events_t events = 0;
89  if (dif_toggle_to_bool(fatal)) {
90  CHECK_DIF_OK(dif_sensor_ctrl_get_fatal_events(&sensor_ctrl, &events));
91  } else {
92  CHECK_DIF_OK(dif_sensor_ctrl_get_recov_events(&sensor_ctrl, &events));
93  }
94  return events;
95 }
96 
97 /**
98  * Clear event trigger and recoverable status.
99  */
100 static void clear_event(uint32_t idx, dif_toggle_t fatal) {
101  CHECK_DIF_OK(dif_sensor_ctrl_set_ast_event_trigger(&sensor_ctrl, idx,
103  if (!dif_toggle_to_bool(fatal)) {
104  CHECK_DIF_OK(dif_sensor_ctrl_clear_recov_event(&sensor_ctrl, idx));
105  }
106 }
107 
108 /**
109  * Check alert cause registers are correctly set
110  */
111 static void check_alert_state(dif_toggle_t fatal) {
112  bool fatal_cause = false;
113  bool recov_cause = false;
114 
117  &fatal_cause));
118 
121  &recov_cause));
122 
123  if (dif_toggle_to_bool(fatal)) {
124  CHECK(fatal_cause & !recov_cause,
125  "Fatal alert not correctly observed in alert handler");
126  } else {
127  CHECK(recov_cause & !fatal_cause,
128  "Recov alert not correctly observed in alert handler");
129  }
130 
135 }
136 
137 /**
138  * First configure fatality of the desired event.
139  * Then trigger the event from sensor_ctrl to ast.
140  * Next poll for setting of correct events inside sensor_ctrl status.
141  * When a recoverable event is triggerd, make sure only recoverable
142  * status is seen, likewise for fatal events.
143  * Finally, check for correct capture of cause in alert handler.
144  */
145 static void test_event(uint32_t idx, dif_toggle_t fatal, bool set_event) {
146  if (set_event) {
147  // Enable the alert on the sensor_ctrl side
148  CHECK_DIF_OK(
149  dif_sensor_ctrl_set_alert_en(&sensor_ctrl, idx, kDifToggleEnabled));
150 
151  // Configure event fatality
152  CHECK_DIF_OK(dif_sensor_ctrl_set_alert_fatal(&sensor_ctrl, idx, fatal));
153 
154  // Trigger event
155  CHECK_DIF_OK(dif_sensor_ctrl_set_ast_event_trigger(&sensor_ctrl, idx,
157  // wait for events to set
158  IBEX_SPIN_FOR(get_events(fatal) > 0, 1);
159 
160  // Check for the event in ast sensor_ctrl
161  // if the event is not set, error
162  CHECK(((get_events(fatal) >> idx) & 0x1) == 1,
163  "Event %d not observed in AST", idx);
164 
165  // check the opposite fatality setting, should not be set
166  CHECK(((get_events(!fatal) >> idx) & 0x1) == 0,
167  "Event %d observed in AST when it should not be", idx);
168  } else {
169  // clear event trigger
170  clear_event(idx, fatal);
171 
172  // check whether alert handler captured the event
173  check_alert_state(fatal);
174 
175  // Disable the alert on the sensor_ctrl side
176  CHECK_DIF_OK(
178  }
179 }
180 
181 void init_units(void) {
182  CHECK_DIF_OK(dif_pwrmgr_init(
184  CHECK_DIF_OK(dif_rstmgr_init(
186  CHECK_DIF_OK(dif_entropy_src_init(
188  CHECK_DIF_OK(dif_aon_timer_init(
190  CHECK_DIF_OK(dif_rv_plic_init(
192  CHECK_DIF_OK(dif_sensor_ctrl_init(
194  &sensor_ctrl));
195  CHECK_DIF_OK(dif_alert_handler_init(
197  &alert_handler));
198  CHECK_DIF_OK(dif_rv_plic_init(
200  CHECK_DIF_OK(dif_clkmgr_init(
202  CHECK_DIF_OK(dif_adc_ctrl_init(
204 }
205 
206 /**
207  * configure adc module
208  */
209 static void configure_adc_ctrl(const dif_adc_ctrl_t *adc_ctrl) {
210  uint32_t wake_up_time_aon_cycles = 0;
211  uint32_t power_up_time_aon_cycles = 0;
212 
213  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(
214  kPowerUpTimeInUs, &power_up_time_aon_cycles));
215  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(
216  kWakeUpTimeInUs, &wake_up_time_aon_cycles));
217  CHECK_DIF_OK(dif_adc_ctrl_set_enabled(adc_ctrl, kDifToggleDisabled));
218  CHECK_DIF_OK(dif_adc_ctrl_reset(adc_ctrl));
219  CHECK_DIF_OK(dif_adc_ctrl_configure(
220  adc_ctrl,
223  .num_low_power_samples = kNumLowPowerSamples,
224  .num_normal_power_samples = kNumNormalPowerSamples,
225  .power_up_time_aon_cycles = (uint8_t)power_up_time_aon_cycles + 1,
226  .wake_up_time_aon_cycles = wake_up_time_aon_cycles}));
227 }
228 
229 static void en_plic_irqs(dif_rv_plic_t *plic) {
230  top_earlgrey_plic_irq_id_t plic_irqs[] = {
232 
233  for (uint32_t i = 0; i < ARRAYSIZE(plic_irqs); ++i) {
234  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
235  plic, plic_irqs[i], kTopEarlgreyPlicTargetIbex0, kDifToggleEnabled));
236 
237  // Assign a default priority
238  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(plic, plic_irqs[i], 0x1));
239  }
240 
241  // Enable the external IRQ at Ibex.
242  irq_global_ctrl(true);
243  irq_external_ctrl(true);
244 }
245 
246 void adc_setup(bool first_adc_setup) {
247  // Enable adc interrupts.
248  CHECK_DIF_OK(dif_adc_ctrl_irq_set_enabled(
249  &adc_ctrl, kDifAdcCtrlIrqMatchPending, kDifToggleEnabled));
250 
251  uint16_t channel0_filter0_max =
252  ((uint16_t)(kChannel0MaxHighByte << 8)) | kChannel0MaxLowByte;
253  uint16_t channel0_filter0_min =
254  ((uint16_t)(kChannel0MinHighByte << 8)) | kChannel0MinLowByte;
255  uint16_t channel1_filter0_max =
256  ((uint16_t)(kChannel1MaxHighByte << 8)) | kChannel1MaxLowByte;
257  uint16_t channel1_filter0_min =
258  ((uint16_t)(kChannel1MinHighByte << 8)) | kChannel1MinLowByte;
259 
260  if (first_adc_setup) {
261  // Setup ADC configuration.
262  configure_adc_ctrl(&adc_ctrl);
263  } else {
264  CHECK_DIF_OK(dif_adc_ctrl_reset(&adc_ctrl));
265  }
266 
267  en_plic_irqs(&plic);
268  // Setup ADC filters. There is one filter for each channel.
269  CHECK_DIF_OK(dif_adc_ctrl_configure_filter(
270  &adc_ctrl, kDifAdcCtrlChannel0,
271  (dif_adc_ctrl_filter_config_t){.filter = kDifAdcCtrlFilter0,
272  .generate_irq_on_match = true,
273  .generate_wakeup_on_match = true,
274  .in_range = true,
275  .max_voltage = channel0_filter0_max,
276  .min_voltage = channel0_filter0_min},
278  CHECK_DIF_OK(dif_adc_ctrl_configure_filter(
279  &adc_ctrl, kDifAdcCtrlChannel1,
280  (dif_adc_ctrl_filter_config_t){.filter = kDifAdcCtrlFilter0,
281  .generate_irq_on_match = true,
282  .generate_wakeup_on_match = true,
283  .in_range = true,
284  .max_voltage = channel1_filter0_max,
285  .min_voltage = channel1_filter0_min},
287 
288  // enable filters.
289  CHECK_DIF_OK(dif_adc_ctrl_filter_set_enabled(
290  &adc_ctrl, kDifAdcCtrlChannel0, kDifAdcCtrlFilter0, kDifToggleEnabled));
291  CHECK_DIF_OK(dif_adc_ctrl_filter_set_enabled(
292  &adc_ctrl, kDifAdcCtrlChannel1, kDifAdcCtrlFilter0, kDifToggleEnabled));
293 
294  if (first_adc_setup) {
295  CHECK_DIF_OK(dif_adc_ctrl_set_enabled(&adc_ctrl, kDifToggleEnabled));
296  }
297 }
298 
299 void ast_enter_sleep_states_and_check_functionality(
300  dif_pwrmgr_domain_config_t pwrmgr_config, uint32_t alert_idx) {
301  bool deepsleep;
302  uint32_t read_fifo_depth_val = 0;
303  uint32_t unhealthy_fifos, errors, alerts;
304 
305  const dif_edn_t edn0 = {
307  const dif_edn_t edn1 = {
309 
310  if ((pwrmgr_config & (~kDifPwrmgrDomainOptionUsbClockInActivePower)) == 0) {
311  deepsleep = true;
312  } else {
313  deepsleep = false;
314  }
315 
316  if (UNWRAP(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, kDifNoWakeup)) ==
317  true) {
318  // Make sure ENTROPY_SRC is enabled and then empty the observe FIFO to
319  // restart the entropy collection. Note that this is more efficient than
320  // restarting the entire block.
321  CHECK_DIF_OK(dif_entropy_src_set_enabled(&entropy_src, kDifToggleEnabled));
322  CHECK_STATUS_OK(entropy_src_testutils_drain_observe_fifo(&entropy_src));
323 
324  // Verify that the FIFO depth is non-zero via SW - indicating the reception
325  // of data over the AST RNG interface.
326  IBEX_SPIN_FOR(read_fifo_depth(&entropy_src) > 0, 1000);
327 
328  // test recoverable event
329  test_event(alert_idx, kDifToggleDisabled, kAlertSet);
330 
331  // Enable all the AON interrupts used in this test.
332  rv_plic_testutils_irq_range_enable(
333  &rv_plic, kTopEarlgreyPlicTargetIbex0,
336  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
337 
338  // Setup low power.
339  CHECK_STATUS_OK(rstmgr_testutils_pre_reset(&rstmgr));
340 
341  if (!deepsleep) {
342  // read fifo depth before enter sleep mode
343  read_fifo_depth_val = read_fifo_depth(&entropy_src);
344  }
345 
346  // Configure ADC
347  adc_setup(first_adc_setup);
348 
349  // set sleep mode
350  CHECK_STATUS_OK(pwrmgr_testutils_enable_low_power(
351  &pwrmgr, kDifPwrmgrWakeupRequestSourceTwo, pwrmgr_config));
352 
353  // Enter low power mode.
354  LOG_INFO("Issued WFI to enter sleep.");
355 
357 
358  // Interrupt should have been serviced.
359  CHECK(interrupt_serviced);
360 
361  interrupt_serviced = false;
362 
363  } else if (UNWRAP(pwrmgr_testutils_is_wakeup_reason(
364  &pwrmgr, kDifPwrmgrWakeupRequestSourceTwo)) == true) {
365  if (deepsleep) {
366  first_adc_setup = false;
367  // Configure ADC after deep sleep
368  adc_setup(first_adc_setup);
369  }
370 
371  // Make sure ENTROPY_SRC is enabled and then empty the observe FIFO to
372  // restart the entropy collection. Note that this is more efficient than
373  // restarting the entire block.
374  CHECK_DIF_OK(dif_entropy_src_set_enabled(&entropy_src, kDifToggleEnabled));
375  CHECK_STATUS_OK(entropy_src_testutils_drain_observe_fifo(&entropy_src));
376 
377  IBEX_SPIN_FOR(read_fifo_depth(&entropy_src) > 0, 1000);
378  }
379 
380  if (!deepsleep) {
381  if (read_fifo_depth_val >= read_fifo_depth(&entropy_src))
382  LOG_ERROR(
383  "read_fifo_depth after exit from idle=%0d should be equal/greater "
384  "than previous read value (%0d)",
385  read_fifo_depth(&entropy_src), read_fifo_depth_val);
386  }
387 
388  IBEX_SPIN_FOR(read_fifo_depth(&entropy_src) > 0, 1000);
389 
390  test_event(alert_idx, kDifToggleDisabled, kAlertClear);
391 
392  CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_clear(&pwrmgr));
393 
394  // test event after exit from low power
395  test_event(alert_idx, kDifToggleDisabled, kAlertSet);
396  test_event(alert_idx, kDifToggleDisabled, kAlertClear);
397 
398  // verify there are no any edn alerts/errors
399  CHECK_DIF_OK(dif_edn_get_errors(&edn0, &unhealthy_fifos, &errors));
400  CHECK_DIF_OK(dif_edn_get_recoverable_alerts(&edn0, &alerts));
401  if (unhealthy_fifos != 0 || errors != 0 || alerts != 0)
402  LOG_ERROR("edn0: error=0x%x, unhealthy_fifos=0x%x, alerts=0x%x", errors,
403  unhealthy_fifos, alerts);
404 
405  CHECK_DIF_OK(dif_edn_get_errors(&edn1, &unhealthy_fifos, &errors));
406  CHECK_DIF_OK(dif_edn_get_recoverable_alerts(&edn1, &alerts));
407  if (unhealthy_fifos != 0 || errors != 0 || alerts != 0)
408  LOG_ERROR("edn1: error=0x%x, unhealthy_fifos=0x%x, alerts=0x%x", errors,
409  unhealthy_fifos, alerts);
410 }
411 
412 /**
413  * set edn auto mode
414  */
415 void set_edn_auto_mode(void) {
416  const dif_csrng_t csrng = {
418  const dif_edn_t edn0 = {
420  const dif_edn_t edn1 = {
422 
423  // Disable the entropy complex
424  CHECK_STATUS_OK(entropy_testutils_stop_all());
425 
426  // Configure ENTROPY_SRC in Firmware Override: Observe mode and enable it.
427  // In this mode, the entropy received from the RNG inside AST gets collected
428  // in the Observe FIFO AND continues to flow through the hardware pipeline to
429  // eventually reach the hardware interface.
430  const dif_entropy_src_fw_override_config_t fw_override_config = {
431  .entropy_insert_enable = false,
432  .buffer_threshold = kEntropyFifoBufferSize,
433  };
435  &entropy_src, fw_override_config, kDifToggleEnabled));
436  CHECK_STATUS_OK(entropy_testutils_entropy_src_init());
437 
438  // Enable CSRNG
439  CHECK_DIF_OK(dif_csrng_configure(&csrng));
440 
441  // Enable EDNs in auto request mode
442  // Re-enable EDN0 in auto mode.
443  const dif_edn_auto_params_t edn0_params = {
444  // EDN0 provides lower-quality entropy. Let one generate command return 8
445  // blocks, and reseed every 32 generates.
446  .instantiate_cmd =
447  {
448  .cmd = 0x00000001 | // Reseed from entropy source only.
449  kMultiBitBool4False << 8,
450  .seed_material =
451  {
452  .len = 0,
453  },
454  },
455  .reseed_cmd =
456  {
457  .cmd = 0x00008002 | // One generate returns 8 blocks, reseed
458  // from entropy source only.
459  kMultiBitBool4False << 8,
460  .seed_material =
461  {
462  .len = 0,
463  },
464  },
465  .generate_cmd =
466  {
467  .cmd = 0x00008003, // One generate returns 8 blocks.
468  .seed_material =
469  {
470  .len = 0,
471  },
472  },
473  .reseed_interval = 32, // Reseed every 32 generates.
474  };
475  CHECK_DIF_OK(dif_edn_set_auto_mode(&edn0, edn0_params));
476 
477  // Re-enable EDN1 in auto mode.
478  const dif_edn_auto_params_t edn1_params = {
479  // EDN1 provides highest-quality entropy. Let one generate command
480  // return 1 block, and reseed after every generate.
481  .instantiate_cmd =
482  {
483  .cmd = 0x00000001 | // Reseed from entropy source only.
484  kMultiBitBool4False << 8,
485  .seed_material =
486  {
487  .len = 0,
488  },
489  },
490  .reseed_cmd =
491  {
492  .cmd = 0x00001002 | // One generate returns 1 block, reseed
493  // from entropy source only.
494  kMultiBitBool4False << 8,
495  .seed_material =
496  {
497  .len = 0,
498  },
499  },
500  .generate_cmd =
501  {
502  .cmd = 0x00001003, // One generate returns 1 block.
503  .seed_material =
504  {
505  .len = 0,
506  },
507  },
508  .reseed_interval = 4, // Reseed after every 4 generates.
509  };
510  CHECK_DIF_OK(dif_edn_set_auto_mode(&edn1, edn1_params));
511 
512  // The Observe FIFO has already been filled while producing the seeds for the
513  // EDNs. Empty the FIFO to restart the collection for the actual test.
514  CHECK_STATUS_OK(entropy_src_testutils_drain_observe_fifo(&entropy_src));
515 }
516 
517 void ottf_external_isr(uint32_t *exc_info) {
518  plic_isr_ctx_t plic_ctx = {.rv_plic = &plic,
519  .hart_id = kTopEarlgreyPlicTargetIbex0};
520 
521  adc_ctrl_isr_ctx_t adc_ctrl_ctx = {
522  .adc_ctrl = &adc_ctrl,
523  .plic_adc_ctrl_start_irq_id = kTopEarlgreyPlicIrqIdAdcCtrlAonMatchPending,
524  .expected_irq = 0,
525  .is_only_irq = true};
526 
528  dif_adc_ctrl_irq_t adc_ctrl_irq;
529  isr_testutils_adc_ctrl_isr(plic_ctx, adc_ctrl_ctx, false, &peripheral,
530  &adc_ctrl_irq);
531 
532  CHECK(peripheral == kTopEarlgreyPlicPeripheralAdcCtrlAon);
533  CHECK(adc_ctrl_irq == kDifAdcCtrlIrqMatchPending);
534  interrupt_serviced = true;
535 }
536 
537 bool test_main(void) {
538  dif_pwrmgr_domain_config_t pwrmgr_config;
539 
540  init_units();
541 
542  set_edn_auto_mode();
543  CHECK_DIF_OK(dif_clkmgr_jitter_set_enabled(&clkmgr, kDifToggleEnabled));
544 
545  // Enable both recoverable and fatal alerts
548  kDifAlertHandlerClassA, kDifToggleEnabled, kDifToggleEnabled));
551  kDifAlertHandlerClassA, kDifToggleEnabled, kDifToggleEnabled));
552 
553  LOG_INFO("1 test alert/rng after Deep sleep 1");
554  pwrmgr_config = kDifPwrmgrDomainOptionUsbClockInActivePower;
555  ast_enter_sleep_states_and_check_functionality(pwrmgr_config, kAlertVal7);
556 
557  LOG_INFO("2 test alert/rng after regular sleep (usb clk enabled)");
558  LOG_INFO("force new adc conv set");
559  pwrmgr_config = kDifPwrmgrDomainOptionUsbClockInActivePower |
560  kDifPwrmgrDomainOptionUsbClockInLowPower |
561  kDifPwrmgrDomainOptionMainPowerInLowPower;
562  ast_enter_sleep_states_and_check_functionality(pwrmgr_config, kAlertVal8);
563 
564  LOG_INFO("3 test alert/rng after regular sleep (all clk disabled in lp)");
565  LOG_INFO("force new adc conv set");
566  pwrmgr_config = kDifPwrmgrDomainOptionMainPowerInLowPower |
567  kDifPwrmgrDomainOptionUsbClockInActivePower;
568  ast_enter_sleep_states_and_check_functionality(pwrmgr_config, kAlertVal7);
569 
570  LOG_INFO("c code is finished");
571 
572  return true;
573 }