Software APIs
flash_ctrl_idle_low_power_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_adc_ctrl.h"
13 #include "sw/device/lib/runtime/irq.h"
15 #include "sw/device/lib/testing/flash_ctrl_testutils.h"
16 #include "sw/device/lib/testing/pwrmgr_testutils.h"
17 #include "sw/device/lib/testing/rand_testutils.h"
18 #include "sw/device/lib/testing/rstmgr_testutils.h"
19 #include "sw/device/lib/testing/test_framework/check.h"
21 
22 OTTF_DEFINE_TEST_CONFIG();
23 
24 static dif_rv_plic_t plic;
25 static dif_aon_timer_t aon;
26 static dif_rv_core_ibex_t rv_core_ibex;
27 
28 enum {
29  kPlicTarget = 0,
30 };
31 
32 static const dt_adc_ctrl_t kAdcCtrlDt = 0;
33 static_assert(kDtAdcCtrlCount == 1, "this test expects a adc_ctrl");
34 static const dt_rstmgr_t kRstmgrDt = 0;
35 static_assert(kDtRstmgrCount == 1, "this test expects a rstmgr");
36 static const dt_pwrmgr_t kPwrmgrDt = 0;
37 static_assert(kDtPwrmgrCount == 1, "this test expects a pwrmgr");
38 static const dt_aon_timer_t kAonTimerDt = 0;
39 static_assert(kDtAonTimerCount == 1, "this test expects an aon_timer");
40 static const dt_rv_plic_t kRvPlicDt = 0;
41 static_assert(kDtRvPlicCount == 1, "this test expects exactly one rv_plic");
42 static const dt_rv_core_ibex_t kRvCoreIbexDt = 0;
43 static_assert(kDtRvCoreIbexCount == 1,
44  "this test expects exactly one rv_core_ibex");
45 static const dt_flash_ctrl_t kFlashCtrlDt = 0;
46 static_assert(kDtFlashCtrlCount >= 1,
47  "this test expects at least one flash_ctrl");
48 
49 static volatile bool irq_serviced = false;
50 
51 enum {
52  kFlashDataRegion = 0,
53  kRegionBasePageIndex = 256, // First page in bank 1 (avoids program code.)
54  kPartitionId = 0,
55  kRegionSize = 1,
56  kNumWords = 128,
57  kAONBarkTh = 64,
58  kAONBiteTh = 256,
59 };
60 
61 /**
62  * External interrupt handler.
63  */
64 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
65  dif_rv_plic_irq_id_t irq_id) {
66  if (devid == dt_aon_timer_instance_id(kAonTimerDt) &&
67  irq_id == dt_aon_timer_irq_to_plic_id(kAonTimerDt,
68  kDtAonTimerIrqWdogTimerBark)) {
69  CHECK_DIF_OK(
70  dif_aon_timer_irq_acknowledge(&aon, kDtAonTimerIrqWdogTimerBark));
71  irq_serviced = true;
72  return true;
73  } else {
74  return false;
75  }
76 }
77 
78 /**
79  * OTTF external NMI internal IRQ handler.
80  * The ROM configures the watchdog to generates a NMI at bark, so we clean the
81  * NMI and wait the external irq handler next.
82  */
83 void ottf_external_nmi_handler(void) {
84  bool is_pending;
85  // The watchdog bark external interrupt is also connected to the NMI input
86  // of rv_core_ibex. We therefore expect the interrupt to be pending on the
87  // peripheral side (the check is done later in the test function).
88  CHECK_DIF_OK(dif_aon_timer_irq_is_pending(&aon, kDifAonTimerIrqWdogTimerBark,
89  &is_pending));
90  // In order to handle the NMI we need to acknowledge the interrupt status
91  // bit it at the peripheral side.
92  CHECK_DIF_OK(
93  dif_aon_timer_irq_acknowledge(&aon, kDifAonTimerIrqWdogTimerBark));
94 
95  CHECK_DIF_OK(dif_rv_core_ibex_clear_nmi_state(&rv_core_ibex,
96  kDifRvCoreIbexNmiSourceAll));
97 }
98 
99 static void enable_irqs(void) {
100  // Enable the AON bark interrupt.
101  dif_rv_plic_irq_id_t plic_id =
102  dt_aon_timer_irq_to_plic_id(kAonTimerDt, kDtAonTimerIrqWdogTimerBark);
103  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(&plic, plic_id, kPlicTarget,
105  CHECK_DIF_OK(
107  CHECK_DIF_OK(dif_rv_plic_target_set_threshold(&plic, kPlicTarget, 0x0));
108  // Enable the external IRQ at Ibex.
109  irq_global_ctrl(true);
110  irq_external_ctrl(true);
111 }
112 
113 bool test_main(void) {
115  dif_pwrmgr_t pwrmgr;
116  dif_rstmgr_t rstmgr;
117 
118  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kRvPlicDt, &plic));
119  CHECK_DIF_OK(dif_flash_ctrl_init_state_from_dt(&flash, kFlashCtrlDt));
120  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
121  CHECK_DIF_OK(dif_rstmgr_init_from_dt(kRstmgrDt, &rstmgr));
122  CHECK_DIF_OK(dif_aon_timer_init_from_dt(kAonTimerDt, &aon));
123  CHECK_DIF_OK(dif_rv_core_ibex_init_from_dt(kRvCoreIbexDt, &rv_core_ibex));
124  enable_irqs();
125 
126  dif_pwrmgr_request_sources_t wakeup_sources;
127  CHECK_DIF_OK(dif_pwrmgr_find_request_source(
128  &pwrmgr, kDifPwrmgrReqTypeWakeup, dt_adc_ctrl_instance_id(kAdcCtrlDt),
129  kDtAdcCtrlWakeupWkupReq, &wakeup_sources));
130 
131  dif_pwrmgr_request_sources_t reset_sources;
132  CHECK_DIF_OK(dif_pwrmgr_find_request_source(
133  &pwrmgr, kDifPwrmgrReqTypeReset, dt_aon_timer_instance_id(kAonTimerDt),
134  kDtAonTimerResetReqAonTimer, &reset_sources));
135 
136  CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&aon));
137  CHECK_DIF_OK(dif_pwrmgr_set_request_sources(
138  &pwrmgr, kDifPwrmgrReqTypeReset, reset_sources, kDifToggleEnabled));
139 
140  dif_rstmgr_reset_info_bitfield_t rstmgr_reset_info;
141  rstmgr_reset_info = rstmgr_testutils_reason_get();
142 
143  uint32_t address = 0;
144  CHECK_STATUS_OK(flash_ctrl_testutils_data_region_setup(
145  &flash, kRegionBasePageIndex, kFlashDataRegion, kRegionSize, &address));
146 
147  if (rstmgr_reset_info == kDifRstmgrResetInfoPor) {
148  // Create data. Random data will be different than
149  // the 0xFFFFFFFF that is created with an erase.
150  uint32_t data[kNumWords];
151  for (int i = 0; i < kNumWords; ++i) {
152  data[i] = rand_testutils_gen32();
153  }
154 
155  // Erasing the page and writing data to it followed
156  // by a read back and compare to sanity check basic operation.
157  CHECK_STATUS_OK(flash_ctrl_testutils_erase_and_write_page(
158  &flash, address, kPartitionId, data, kDifFlashCtrlPartitionTypeData,
159  kNumWords));
160  uint32_t readback_data[kNumWords];
161  CHECK_STATUS_OK(flash_ctrl_testutils_read(
162  &flash, address, kPartitionId, readback_data,
163  kDifFlashCtrlPartitionTypeData, kNumWords, 0));
164  CHECK_ARRAYS_EQ(data, readback_data, kNumWords);
165 
166  // Setting up low power hint and starting watchdog timer followed by
167  // a flash operation (page erase) and WFI. This will create a bark
168  // interrupt at some time following the start of the flash operation.
169  CHECK_STATUS_OK(
170  pwrmgr_testutils_enable_low_power(&pwrmgr, wakeup_sources, 0));
171 
172  CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_clear(&pwrmgr));
173 
174  CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&aon));
175 
176  uint32_t bark_th = kAONBarkTh;
177  uint32_t bite_th = kAONBiteTh;
178 
179  // Update bark and bite threshold in case of silicon test
180  if (kDeviceType == kDeviceSilicon) {
181  bark_th = 4000;
182  bite_th = 4 * bark_th;
183  }
184  CHECK_DIF_OK(dif_aon_timer_watchdog_start(
185  &aon /* aon */, bark_th /* bark_threshold */,
186  bite_th /* bite_threshold */, false /* pause_in_sleep */,
187  false /* lock */));
188 
189  dif_flash_ctrl_transaction_t transaction = {
190  .byte_address = address,
192  .partition_type = kDifFlashCtrlPartitionTypeData,
193  .partition_id = kPartitionId,
194  .word_count = 0x0};
195 
196  CHECK_DIF_OK(dif_flash_ctrl_start(&flash, transaction));
197  // Do not put any print here.
198  // That will cause interrupt miss and spurious test failure
200 
201  // Return from interrupt. Stop the watchdog. Check the reset info
202  // is still POR and the interrupt came from the correct source.
203  // Check the erase operation completed successfully.
204  CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&aon));
205 
207  CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_get(&pwrmgr, &reason));
208  CHECK(reason.types == kDifPwrmgrWakeupTypeAbort,
209  "Unexpected wakeup reason: types=%x, srcs=%x", reason.types,
210  reason.request_sources);
211 
212  CHECK(irq_serviced);
213 
214  CHECK_STATUS_OK(flash_ctrl_testutils_wait_transaction_end(&flash));
215 
216  CHECK_STATUS_OK(flash_ctrl_testutils_read(
217  &flash, address, kPartitionId, readback_data,
218  kDifFlashCtrlPartitionTypeData, kNumWords, 0));
219  uint32_t expected_data[kNumWords];
220  memset(expected_data, 0xff, sizeof(expected_data));
221  CHECK_ARRAYS_EQ(readback_data, expected_data, kNumWords);
222 
223  rstmgr_testutils_reason_clear();
224  } else {
225  LOG_ERROR("Unexepected reset type detected. Reset info = %08x",
226  rstmgr_reset_info);
227  return false;
228  }
229 
230  return true;
231 }