Software APIs
sleep_pin_retention_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 
10 #include "sw/device/lib/runtime/irq.h"
12 #include "sw/device/lib/testing/pwrmgr_testutils.h"
13 #include "sw/device/lib/testing/rand_testutils.h"
14 #include "sw/device/lib/testing/rv_plic_testutils.h"
15 #include "sw/device/lib/testing/test_framework/check.h"
17 
19 
20 OTTF_DEFINE_TEST_CONFIG();
21 
22 // PLIC structures
23 static const uint32_t kPlicTarget = 0;
24 static dif_gpio_t gpio;
25 static dif_pwrmgr_t pwrmgr;
26 static dif_pinmux_t pinmux;
27 static dif_pwrmgr_domain_config_t pwrmgr_domain_cfg;
28 static dif_rv_plic_t plic;
29 
30 static const dt_pwrmgr_t kPwrmgrDt = 0;
31 static_assert(kDtPwrmgrCount == 1, "this test expects a pwrmgr");
32 static const dt_rv_plic_t kRvPlicDt = 0;
33 static_assert(kDtRvPlicCount == 1, "this test expects exactly one rv_plic");
34 static const dt_pinmux_t kPinmuxDt = 0;
35 static_assert(kDtPinmuxCount == 1, "this test expects exactly one pinmux");
36 static const dt_gpio_t kGpioDt = 0;
37 static_assert(kDtGpioCount == 1, "this test expects exactly one gpio");
38 
39 // SV randomizes the round of entering/exiting sleep then set this volatile
40 // variable via backdoor_overwrite.
41 static volatile const uint8_t kRounds = 2;
42 
43 // To wakeup and maintain GPIO, for now test enters to normal sleep only.
44 static const bool deepPowerdown = false;
45 
46 // Num of GPIO Pads to test
47 enum { kNumGpioPadsDv = 8, kNumGpioPadsSiVal = 4 };
48 int num_gpio_pads; // One of the above, based on the actual environment.
49 
50 unsigned gpio_mask;
51 
52 static int first_gpio_pin;
53 
54 /**
55  * External interrupt handler.
56  */
57 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
58  dif_rv_plic_irq_id_t irq_id) {
59  if (devid == dt_pwrmgr_instance_id(kPwrmgrDt) &&
60  irq_id == dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup)) {
61  CHECK_DIF_OK(dif_pinmux_wakeup_cause_clear(&pinmux));
62  CHECK_DIF_OK(dif_pwrmgr_irq_acknowledge(&pwrmgr, kDtPwrmgrIrqWakeup));
63  return true;
64  }
65  return false;
66 }
67 
68 /**
69  * A round of GPIO[num_gpio_pads-1:0] retention value test.
70  *
71  * The test sequence is:
72  *
73  * 1. Randomly choose GPIO[num_gpio_pads-1:0] value using rand_testutils.
74  * 2. Drive GPIO with the chosen value.
75  * 3. Send the chosen values to SV/host via LOG_INFO.
76  * 4. Configure PINMUX Retention value opposit to the chosen value for
77  * GPIO[num_gpio_pads-1:0].
78  * 5. Initiate sleep mode (assuming pinmux pin wake up has been configured.)
79  * 6. WFI()
80  * 7. At this point, chip has been waken up by DV/host. Send a log to DV/host
81  * that the chip has waken up.
82  *
83  * DV/host env checks all PIN value. SW simply drives the GPIO and invert the
84  * value for retention.
85  */
86 void gpio_test(dif_pwrmgr_t *pwrmgr, dif_pinmux_t *pinmux, dif_gpio_t *gpio,
87  int round) {
88  uint8_t gpio_val = 0;
89  dif_pinmux_pad_kind_t pad_kind;
90  dif_pinmux_sleep_mode_t pad_mode;
91 
92  LOG_INFO("Current Test Round: %1d", round);
93 
94  // 1. Randomly choose GPIO value
95  gpio_val = (uint8_t)(rand_testutils_gen32_range(0, gpio_mask));
96 
97  // 2. Drive GPIO with the chosen value.
98  CHECK_DIF_OK(dif_gpio_write_masked(gpio, (dif_gpio_mask_t)gpio_mask,
99  (dif_gpio_state_t)gpio_val));
100 
101  // 3. Send the chosen value to SV via LOG_INFO.
102  //
103  // The format is:
104  //
105  // Chosen GPIO value: %2x
106  LOG_INFO("Chosen GPIO value: %2x", gpio_val);
107 
108  // 4. Configure PINMUX Retention value opposite to the chosen value.
109  pad_kind = kDifPinmuxPadKindMio;
110  for (int i = 0; i < num_gpio_pads; i++) {
111  // GPIO are assigned starting from MIO0
112  pad_mode = ((gpio_val >> i) & 0x1) ? kDifPinmuxSleepModeLow
114  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
115  pinmux, (dif_pinmux_index_t)(first_gpio_pin + i), pad_kind, pad_mode));
116  }
117 
118  // 5. Initiate sleep mode
119  dif_pwrmgr_request_sources_t wakeup_sources;
120  CHECK_DIF_OK(dif_pwrmgr_find_request_source(
121  pwrmgr, kDifPwrmgrReqTypeWakeup, dt_pinmux_instance_id(kPinmuxDt),
122  kDtPinmuxWakeupPinWkupReq, &wakeup_sources));
123  CHECK_STATUS_OK(pwrmgr_testutils_enable_low_power(pwrmgr, wakeup_sources,
124  pwrmgr_domain_cfg));
125  // 6. WFI()
126  LOG_INFO("Entering low power mode.");
128 
129  // 7. Turn-off retention.
130  for (int i = 0; i < num_gpio_pads; i++) {
132  pinmux, (dif_pinmux_index_t)(first_gpio_pin + i), pad_kind));
133  }
134 
135  LOG_INFO("Woke up from low power mode.");
136 
137  // When running outside DV, wait until the host is ready to end this round.
138  bool end_round = false;
139  if (kDeviceType != kDeviceSimDV)
140  do {
141  CHECK_DIF_OK(dif_gpio_read(gpio, 8, &end_round));
142  } while (!end_round);
143 }
144 
145 /**
146  * Configure GPIO
147  *
148  * gpio_init() configures first 8 MIO PADs to GPIO[7:0].
149  */
150 void gpio_init(const dif_pinmux_t *pinmux, const dif_gpio_t *gpio) {
151  // Drive GPIO first
152  CHECK_DIF_OK(
154  CHECK_DIF_OK(dif_gpio_write_masked(gpio, (dif_gpio_mask_t)gpio_mask,
155  (dif_gpio_state_t)0x00000000));
156 
157  // Configure PINMUX to GPIO
158  for (int i = 0; i < num_gpio_pads; i++) {
159  CHECK_DIF_OK(dif_pinmux_input_select(
160  pinmux,
162  (dif_pinmux_index_t)(first_gpio_pin + i)));
163  CHECK_DIF_OK(dif_pinmux_output_select(
164  pinmux, (dif_pinmux_index_t)(first_gpio_pin + i),
166  }
167 
168  // Configure PINMUX an additional GPIO pin for the SiVal host to sync with
169  // the device and indicate the round can end.
170  CHECK_DIF_OK(dif_pinmux_input_select(
173 }
174 
175 bool test_main(void) {
176  bool result = true;
177 
178  num_gpio_pads =
179  kDeviceType == kDeviceSimDV ? kNumGpioPadsDv : kNumGpioPadsSiVal;
180  gpio_mask = (1 << num_gpio_pads) - 1;
183 
184  dif_pinmux_wakeup_config_t wakeup_cfg;
185 
186  // Default Deep Power Down
187 
188  // Enable global and external IRQ at Ibex.
189  irq_global_ctrl(true);
190  irq_external_ctrl(true);
191 
192  // Initialize power manager
193  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
194  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kRvPlicDt, &plic));
195  CHECK_DIF_OK(dif_pinmux_init_from_dt(kPinmuxDt, &pinmux));
196  CHECK_DIF_OK(dif_gpio_init_from_dt(kGpioDt, &gpio));
197 
198  // Enable all the AON interrupts used in this test.
199  dif_rv_plic_irq_id_t plic_id =
200  dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup);
201  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, plic_id, plic_id);
202 
203  // Enable pwrmgr interrupt
204  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
205 
206  // Wakeup configs
208  wakeup_cfg.signal_filter = false;
209  wakeup_cfg.pad_type = kDifPinmuxPadKindMio;
210  wakeup_cfg.pad_select = kDeviceType == kDeviceSimDV
213 
214  // Configure Wakeup Detector 0
215  CHECK_DIF_OK(dif_pinmux_wakeup_detector_enable(&pinmux, 0, wakeup_cfg));
216 
217  if (deepPowerdown == false) {
218  // Configure Normal Sleep
219  pwrmgr_domain_cfg = kDifPwrmgrDomainOptionMainPowerInLowPower |
220  kDifPwrmgrDomainOptionUsbClockInActivePower;
221  }
222 
223  LOG_INFO("Num Rounds: %3d", kRounds);
224 
225  // Select the appropriate GPIO pins
226  gpio_init(&pinmux, &gpio);
227 
228  for (int i = kRounds - 1; i >= 0; i--) {
229  gpio_test(&pwrmgr, &pinmux, &gpio, i);
230  }
231 
232  return result;
233 }