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 #include "sw/device/lib/testing/autogen/isr_testutils.h"
20 
21 OTTF_DEFINE_TEST_CONFIG();
22 
23 // PLIC structures
24 static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
25 static dif_gpio_t gpio;
26 static dif_pwrmgr_t pwrmgr;
27 static dif_pinmux_t pinmux;
28 static dif_pwrmgr_domain_config_t pwrmgr_domain_cfg;
29 static dif_rv_plic_t plic;
30 
31 static plic_isr_ctx_t plic_ctx = {.rv_plic = &plic, .hart_id = kPlicTarget};
32 static pwrmgr_isr_ctx_t pwrmgr_isr_ctx = {
33  .pwrmgr = &pwrmgr,
34  .plic_pwrmgr_start_irq_id = kTopEarlgreyPlicIrqIdPwrmgrAonWakeup,
35  .expected_irq = kDifPwrmgrIrqWakeup,
36  .is_only_irq = true};
37 
38 // SV randomizes the round of entering/exiting sleep then set this volatile
39 // variable via backdoor_overwrite.
40 static volatile const uint8_t kRounds = 2;
41 
42 // To wakeup and maintain GPIO, for now test enters to normal sleep only.
43 static const bool deepPowerdown = false;
44 
45 // Num of GPIO Pads to test
46 enum { kNumGpioPadsDv = 8, kNumGpioPadsSiVal = 4 };
47 int num_gpio_pads; // One of the above, based on the actual environment.
48 
49 unsigned gpio_mask;
50 
51 static int first_gpio_pin;
52 
53 /**
54  * External interrupt handler.
55  */
56 void ottf_external_isr(uint32_t *exc_info) {
57  dif_pwrmgr_irq_t irq_id;
59 
60  isr_testutils_pwrmgr_isr(plic_ctx, pwrmgr_isr_ctx, &peripheral, &irq_id);
61 
62  // Check that both the peripheral and the irq id is correct
63  CHECK(peripheral == kTopEarlgreyPlicPeripheralPwrmgrAon,
64  "IRQ peripheral: %d is incorrect", peripheral);
65  CHECK(irq_id == kDifPwrmgrIrqWakeup, "IRQ ID: %d is incorrect", irq_id);
66 
67  // Clear PINMUX WKUP_CAUSE reg
68  CHECK_DIF_OK(dif_pinmux_wakeup_cause_clear(&pinmux));
69 }
70 
71 /**
72  * A round of GPIO[num_gpio_pads-1:0] retention value test.
73  *
74  * The test sequence is:
75  *
76  * 1. Randomly choose GPIO[num_gpio_pads-1:0] value using rand_testutils.
77  * 2. Drive GPIO with the chosen value.
78  * 3. Send the chosen values to SV/host via LOG_INFO.
79  * 4. Configure PINMUX Retention value opposit to the chosen value for
80  * GPIO[num_gpio_pads-1:0].
81  * 5. Initiate sleep mode (assuming pinmux pin wake up has been configured.)
82  * 6. WFI()
83  * 7. At this point, chip has been waken up by DV/host. Send a log to DV/host
84  * that the chip has waken up.
85  *
86  * DV/host env checks all PIN value. SW simply drives the GPIO and invert the
87  * value for retention.
88  */
89 void gpio_test(dif_pwrmgr_t *pwrmgr, dif_pinmux_t *pinmux, dif_gpio_t *gpio,
90  int round) {
91  uint8_t gpio_val = 0;
92  dif_pinmux_pad_kind_t pad_kind;
93  dif_pinmux_sleep_mode_t pad_mode;
94 
95  LOG_INFO("Current Test Round: %1d", round);
96 
97  // 1. Randomly choose GPIO value
98  gpio_val = (uint8_t)(rand_testutils_gen32_range(0, gpio_mask));
99 
100  // 2. Drive GPIO with the chosen value.
101  CHECK_DIF_OK(dif_gpio_write_masked(gpio, (dif_gpio_mask_t)gpio_mask,
102  (dif_gpio_state_t)gpio_val));
103 
104  // 3. Send the chosen value to SV via LOG_INFO.
105  //
106  // The format is:
107  //
108  // Chosen GPIO value: %2x
109  LOG_INFO("Chosen GPIO value: %2x", gpio_val);
110 
111  // 4. Configure PINMUX Retention value opposite to the chosen value.
112  pad_kind = kDifPinmuxPadKindMio;
113  for (int i = 0; i < num_gpio_pads; i++) {
114  // GPIO are assigned starting from MIO0
115  pad_mode = ((gpio_val >> i) & 0x1) ? kDifPinmuxSleepModeLow
117  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
118  pinmux, (dif_pinmux_index_t)(first_gpio_pin + i), pad_kind, pad_mode));
119  }
120 
121  // 5. Initiate sleep mode
122  CHECK_STATUS_OK(pwrmgr_testutils_enable_low_power(
123  pwrmgr, kDifPwrmgrWakeupRequestSourceThree, pwrmgr_domain_cfg));
124  // 6. WFI()
125  LOG_INFO("Entering low power mode.");
127 
128  // 7. Turn-off retention.
129  for (int i = 0; i < num_gpio_pads; i++) {
131  pinmux, (dif_pinmux_index_t)(first_gpio_pin + i), pad_kind));
132  }
133 
134  LOG_INFO("Woke up from low power mode.");
135 
136  // When running outside DV, wait until the host is ready to end this round.
137  bool end_round = false;
138  if (kDeviceType != kDeviceSimDV)
139  do {
140  CHECK_DIF_OK(dif_gpio_read(gpio, 8, &end_round));
141  } while (!end_round);
142 }
143 
144 /**
145  * Configure GPIO
146  *
147  * gpio_init() configures first 8 MIO PADs to GPIO[7:0].
148  */
149 void gpio_init(const dif_pinmux_t *pinmux, const dif_gpio_t *gpio) {
150  // Drive GPIO first
151  CHECK_DIF_OK(
153  CHECK_DIF_OK(dif_gpio_write_masked(gpio, (dif_gpio_mask_t)gpio_mask,
154  (dif_gpio_state_t)0x00000000));
155 
156  // Configure PINMUX to GPIO
157  for (int i = 0; i < num_gpio_pads; i++) {
158  CHECK_DIF_OK(dif_pinmux_input_select(
159  pinmux,
161  (dif_pinmux_index_t)(first_gpio_pin + i)));
162  CHECK_DIF_OK(dif_pinmux_output_select(
163  pinmux, (dif_pinmux_index_t)(first_gpio_pin + i),
165  }
166 
167  // Configure PINMUX an additional GPIO pin for the SiVal host to sync with
168  // the device and indicate the round can end.
169  CHECK_DIF_OK(dif_pinmux_input_select(
172 }
173 
174 bool test_main(void) {
175  bool result = true;
176 
177  num_gpio_pads =
178  kDeviceType == kDeviceSimDV ? kNumGpioPadsDv : kNumGpioPadsSiVal;
179  gpio_mask = (1 << num_gpio_pads) - 1;
182 
183  dif_pinmux_wakeup_config_t wakeup_cfg;
184 
185  // Default Deep Power Down
186 
187  // Enable global and external IRQ at Ibex.
188  irq_global_ctrl(true);
189  irq_external_ctrl(true);
190 
191  // Initialize power manager
192  CHECK_DIF_OK(dif_pwrmgr_init(
194  CHECK_DIF_OK(dif_rv_plic_init(
196  CHECK_DIF_OK(dif_pinmux_init(
198  CHECK_DIF_OK(
199  dif_gpio_init(mmio_region_from_addr(TOP_EARLGREY_GPIO_BASE_ADDR), &gpio));
200 
201  // Enable all the AON interrupts used in this test.
202  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
205  // Enable pwrmgr interrupt
206  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
207 
208  // Wakeup configs
210  wakeup_cfg.signal_filter = false;
211  wakeup_cfg.pad_type = kDifPinmuxPadKindMio;
212  wakeup_cfg.pad_select = kDeviceType == kDeviceSimDV
215 
216  // Configure Wakeup Detector 0
217  CHECK_DIF_OK(dif_pinmux_wakeup_detector_enable(&pinmux, 0, wakeup_cfg));
218 
219  if (deepPowerdown == false) {
220  // Configure Normal Sleep
221  pwrmgr_domain_cfg = kDifPwrmgrDomainOptionMainPowerInLowPower |
222  kDifPwrmgrDomainOptionUsbClockInActivePower;
223  }
224 
225  LOG_INFO("Num Rounds: %3d", kRounds);
226 
227  // Select the appropriate GPIO pins
228  gpio_init(&pinmux, &gpio);
229 
230  for (int i = kRounds - 1; i >= 0; i--) {
231  gpio_test(&pwrmgr, &pinmux, &gpio, i);
232  }
233 
234  return result;
235 }