Software APIs
otbn_randomness_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 #include "dt/dt_otbn.h" // Generated
6 #include "dt/dt_rv_plic.h" // Generated
11 #include "sw/device/lib/runtime/irq.h"
13 #include "sw/device/lib/testing/clkmgr_testutils.h"
14 #include "sw/device/lib/testing/entropy_testutils.h"
16 #include "sw/device/lib/testing/rv_plic_testutils.h"
17 #include "sw/device/lib/testing/test_framework/check.h"
19 #include "sw/device/tests/otbn_randomness_impl.h"
20 
21 OTTF_DEFINE_TEST_CONFIG();
22 
23 static const uint32_t kPlicTarget = 0;
24 
25 static dif_clkmgr_t clkmgr;
26 static const dif_clkmgr_hintable_clock_t kOtbnClock =
27 #if defined(OPENTITAN_IS_EARLGREY)
29 #elif defined(OPENTITAN_IS_DARJEELING)
30  kTopDarjeelingHintableClocksMainOtbn
31 #else
32 #error Unsupported top
33 #endif
34  ;
35 
36 static dif_rv_plic_t plic;
37 static dif_otbn_t otbn;
38 /**
39  * These variables are used for ISR communication hence they are volatile.
40  */
41 static volatile dt_instance_id_t plic_peripheral;
42 static volatile dt_otbn_irq_t irq;
43 
44 /**
45  * Provides external IRQ handling for otbn tests.
46  *
47  * This function overrides the default OTTF external ISR.
48  *
49  * It performs the following:
50  * 1. Claims the IRQ fired (finds PLIC IRQ index).
51  * 2. Compute the OTBN peripheral.
52  * 3. Compute the otbn irq.
53  * 4. Clears the IRQ at the peripheral.
54  * 5. Completes the IRQ service at PLIC.
55  */
56 void ottf_external_isr(uint32_t *exc_info) {
57  dif_rv_plic_irq_id_t irq_id;
58  CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget,
59  (dif_rv_plic_irq_id_t *)&irq_id));
60 
61  plic_peripheral = dt_plic_id_to_instance_id(irq_id);
62  if (plic_peripheral == dt_otbn_instance_id(kDtOtbn)) {
63  irq = dt_otbn_irq_from_plic_id(kDtOtbn, irq_id);
64  }
65 
66  // Otbn clock is disabled, so we can not acknowledge the irq. Disabling it to
67  // avoid an infinite loop here.
68  irq_global_ctrl(false);
69  irq_external_ctrl(false);
70 
71  // Complete the IRQ by writing the IRQ source to the Ibex register.
72  CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic, kPlicTarget, irq_id));
73 }
74 
75 static void otbn_wait_for_done_irq(dif_otbn_t *otbn) {
76  // Clear the otbn irq variable: we'll set it in the interrupt handler when
77  // we see the Done interrupt fire.
78  irq = UINT32_MAX;
79  plic_peripheral = kDtInstanceIdUnknown;
80  CHECK_DIF_OK(
81  dif_otbn_irq_set_enabled(otbn, kDifOtbnIrqDone, kDifToggleEnabled));
82 
83  // OTBN should be running. Wait for an interrupt that says
84  // it's done.
85  ATOMIC_WAIT_FOR_INTERRUPT(plic_peripheral != kDtInstanceIdUnknown);
86  CHECK(plic_peripheral == dt_otbn_instance_id(kDtOtbn),
87  "Interrupt from incorrect peripheral: (exp: %d, obs: %s)",
88  dt_otbn_instance_id(kDtOtbn), plic_peripheral);
89 
90  // Check this is the interrupt we expected.
91  CHECK(irq == kDtOtbnIrqDone);
92 }
93 
94 static void otbn_init_irq(void) {
95  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kDtRvPlic, &plic));
96 
97  // Set interrupt priority to be positive.
98  dif_rv_plic_irq_id_t irq_id = dt_otbn_irq_to_plic_id(kDtOtbn, kDtOtbnIrqDone);
99  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(&plic, irq_id, 0x1));
100 
101  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(&plic, irq_id, kPlicTarget,
103 
104  CHECK_DIF_OK(dif_rv_plic_target_set_threshold(&plic, kPlicTarget, 0x0));
105 
106  irq_global_ctrl(true);
107  irq_external_ctrl(true);
108 }
109 
110 status_t initialize_clkmgr(void) {
111  CHECK_DIF_OK(dif_clkmgr_init_from_dt(kDtClkmgrAon, &clkmgr));
112 
113  // Get initial hint and enable for OTBN clock and check both are enabled.
114  dif_toggle_t clock_hint_state;
115  CHECK_DIF_OK(dif_clkmgr_hintable_clock_get_hint(&clkmgr, kOtbnClock,
116  &clock_hint_state));
117  CHECK(clock_hint_state == kDifToggleEnabled);
118  return CLKMGR_TESTUTILS_CHECK_CLOCK_HINT(clkmgr, kOtbnClock,
120 }
121 
122 status_t execute_test(void) {
123  // Write the OTBN clk hint to 0 within clkmgr to indicate OTBN clk can be
124  // gated and verify that the OTBN clk hint status within clkmgr reads 0 (OTBN
125  // is idle).
126  CLKMGR_TESTUTILS_SET_AND_CHECK_CLOCK_HINT(
127  clkmgr, kOtbnClock, kDifToggleDisabled, kDifToggleDisabled);
128 
129  // Write the OTBN clk hint to 1 within clkmgr to indicate OTBN clk can be
130  // enabled.
131  CLKMGR_TESTUTILS_SET_AND_CHECK_CLOCK_HINT(
132  clkmgr, kOtbnClock, kDifToggleEnabled, kDifToggleEnabled);
133 
134  // Start an OTBN operation, write the OTBN clk hint to 0 within clkmgr and
135  // verify that the OTBN clk hint status within clkmgr reads 1 (OTBN is not
136  // idle).
137  otbn_randomness_test_start(&otbn, /*iters=*/0);
138 
139  CLKMGR_TESTUTILS_SET_AND_CHECK_CLOCK_HINT(
140  clkmgr, kOtbnClock, kDifToggleDisabled, kDifToggleEnabled);
141 
142  // After the OTBN operation is complete, verify that the OTBN clk hint status
143  // within clkmgr now reads 0 again (OTBN is idle).
144  otbn_wait_for_done_irq(&otbn);
145  CLKMGR_TESTUTILS_CHECK_CLOCK_HINT(clkmgr, kOtbnClock, kDifToggleDisabled);
146 
147  // Write the OTBN clk hint to 1, read and check the OTBN output for
148  // correctness.
149  CLKMGR_TESTUTILS_SET_AND_CHECK_CLOCK_HINT(
150  clkmgr, kOtbnClock, kDifToggleEnabled, kDifToggleEnabled);
151 
152  otbn_randomness_test_log_results(&otbn);
153 
154  // Check for successful test execution (self-reported).
155  TRY_CHECK(otbn_randomness_test_end(&otbn, /*skip_otbn_done_check=*/true));
156  return OK_STATUS();
157 }
158 
159 bool test_main(void) {
160  // Initialize EDN in auto mode.
161  CHECK_STATUS_OK(entropy_testutils_auto_mode_init());
162  CHECK_STATUS_OK(initialize_clkmgr());
163 
164  CHECK_DIF_OK(dif_otbn_init_from_dt(kDtOtbn, &otbn));
165 
166  otbn_init_irq();
167  return status_ok(execute_test());
168 }