Software APIs
sleep_pin_mio_dio_val_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 
8 #include "sw/device/lib/runtime/irq.h"
10 #include "sw/device/lib/testing/pwrmgr_testutils.h"
11 #include "sw/device/lib/testing/rand_testutils.h"
12 #include "sw/device/lib/testing/rv_plic_testutils.h"
13 #include "sw/device/lib/testing/test_framework/check.h"
15 
16 OTTF_DEFINE_TEST_CONFIG();
17 
18 /**
19  * Test: chip_sw_sleep_pin_mio_dio_val
20  *
21  * chip_sw_sleep_pin_mio_dio_val test is to check the PADs assert desired
22  * values. When the chip enters deep powerdown (turning off power domains
23  * except AON), PADs cannot get the data from off domain logics.
24  *
25  * To not confuse the devices outside of the logic, usually isolation cells are
26  * placed in between (off and on). OpenTitan, on top of the isolation, provides
27  * a functionality to control the PADs output value among **0**, **1**,
28  * **High-Z**, and **Passthrough**. Refer the PINMUX technical specification
29  * for the details.
30  *
31  * In this test, the code randomizes the output values except the
32  * **passthrough**, as **passthrough** is tested in other chip level test. Then
33  * the chosen values are given to the UVM testbenches. UVM testbenches compare
34  * the expected and measured values.
35  */
36 
37 #if defined(OPENTITAN_IS_EARLGREY)
38 static const dt_pad_t kOptOut[] = {
39  kDtPadSpiDeviceSck,
40  kDtPadSpiDeviceCsb,
41 };
42 #elif defined(OPENTITAN_IS_DARJEELING)
43 static const dt_pad_t kOptOut[] = {};
44 #else
45 #error Unsupported top
46 #endif
47 
48 static uint8_t kPads[kDtPadCount] = {0};
49 
50 // PLIC structures
51 static dif_pwrmgr_t pwrmgr;
52 static dif_pinmux_t pinmux;
53 static dif_rv_plic_t plic;
54 
55 /**
56  * External interrupt handler.
57  */
58 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
59  dif_rv_plic_irq_id_t irq_id) {
60  if (devid != dt_pwrmgr_instance_id(kDtPwrmgrAon)) {
61  return false;
62  }
63 
64  dt_pwrmgr_irq_t irq = dt_pwrmgr_irq_from_plic_id(kDtPwrmgrAon, irq_id);
65  CHECK(irq == kDtPwrmgrIrqWakeup, "IRQ ID: %d is incorrect", irq);
66  CHECK_DIF_OK(dif_pwrmgr_irq_acknowledge(&pwrmgr, irq));
67  return true;
68 }
69 
70 /** Configure pinmux retention value.
71  *
72  * Each gen32 can cover 16 PADs randomization. When each pad ret val draws
73  * **3**, the code calls gen32_range to choose from 0 to 2.
74  */
75 void draw_pinmux_ret(uint32_t num_pins, uint8_t *arr, const dt_pad_t *optout,
76  size_t num_optout) {
77  for (uint32_t i = 0; i < num_pins; i += 16) {
78  uint32_t val = rand_testutils_gen32();
79  uint32_t min_idx = (i + 16 < num_pins) ? i + 16 : num_pins;
80 
81  for (uint32_t j = i; j < min_idx; j++) {
82  /* Bit slice 2b at a time and if it is 3, redraw */
83  arr[j] = (val >> ((j & 0xF) * 2)) & 0x3;
84  if (arr[j] == 3) {
85  arr[j] = (uint8_t)rand_testutils_gen32_range(0, 2);
86  }
87  }
88  }
89 
90  // OptOut processing after draw.
91  for (int i = 0; i < num_optout; i++) {
92  arr[optout[i]] = 2; // High-Z always
93  }
94 }
95 
96 /**
97  * Send the chosen values to UVM tb.
98  *
99  * Format:
100  *
101  * "{DIO/MIO} [i]: {}"
102  */
103 void print_chosen_values(void) {
104  LOG_INFO("BEGIN Chosen Retention Types");
105 
106  for (dt_pad_t pad = 0; pad < kDtPadCount; ++pad) {
107  dif_pinmux_index_t index;
109  CHECK_DIF_OK(dif_pinmux_pad_from_dt_pad(pad, &index, &type));
110  if (type == kDifPinmuxPadKindDio) {
111  LOG_INFO("DIO [%d]: %x", index, kPads[pad]);
112  } else {
113  LOG_INFO("MIO [%d]: %x", index, kPads[pad]);
114  }
115  }
116 
117  LOG_INFO("END Chosen Retention Types");
118 }
119 
120 /**
121  * Configure PADs retention types.
122  *
123  * @param pinmux Pinmux handle.
124  */
125 void configure_pad_retention_types(dif_pinmux_t *pinmux) {
126  LOG_INFO("Configuring PADs retention types in PINMUX...");
127 
128  for (dt_pad_t pad = 0; pad < kDtPadCount; pad++) {
129  dif_pinmux_index_t index;
131  CHECK_DIF_OK(dif_pinmux_pad_from_dt_pad(pad, &index, &type));
132  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(pinmux, index, type, kPads[pad]));
133  }
134 
135  LOG_INFO("PADs retention modes are configured.");
136 }
137 
138 bool lowpower_prep(dif_pwrmgr_t *pwrmgr, dif_pinmux_t *pinmux, bool deepsleep) {
139  bool result = false;
140  dif_pwrmgr_domain_config_t pwrmgr_domain_cfg = 0;
141 
142  LOG_INFO("Selecting PADs retention modes...");
143 
144  draw_pinmux_ret(kDtPadCount, kPads, kOptOut, ARRAYSIZE(kOptOut));
145 
146  print_chosen_values();
147 
148  // Configure pwrmgr to deep powerdown.
149  configure_pad_retention_types(pinmux);
150 
151  CHECK_DIF_OK(dif_pwrmgr_get_domain_config(pwrmgr, &pwrmgr_domain_cfg));
152  if (deepsleep) {
153  pwrmgr_domain_cfg &= ~kDifPwrmgrDomainOptionMainPowerInLowPower;
154  } else {
155  pwrmgr_domain_cfg |= kDifPwrmgrDomainOptionMainPowerInLowPower;
156  }
157  CHECK_DIF_OK(dif_pwrmgr_set_domain_config(pwrmgr, pwrmgr_domain_cfg,
161 
162  LOG_INFO("Going to sleep");
163  wait_for_interrupt(); // Entering deep power down.
164 
165  return result;
166 }
167 
168 bool test_main(void) {
169  bool result = false;
170 
171  // Enable global and external IRQ at Ibex.
172  irq_global_ctrl(true);
173  irq_external_ctrl(true);
174 
175  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kDtPwrmgrAon, &pwrmgr));
176  CHECK_DIF_OK(dif_pinmux_init_from_dt(kDtPinmuxAon, &pinmux));
177  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kDtRvPlic, &plic));
178 
179  if (UNWRAP(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 0)) == true) {
180  uint32_t deep_powerdown_en = rand_testutils_gen32_range(0, 1);
181  bool deepsleep = (deep_powerdown_en) ? true : false;
182 
183 #if defined(OPENTITAN_IS_EARLGREY)
184  // TODO(lowrisc/opentitan#15889): The weak pull on IOC3 needs to be
185  // disabled for this test. Remove this later.
186  dif_pinmux_pad_attr_t out_attr;
187  dif_pinmux_pad_attr_t in_attr = {0};
188  CHECK_DIF_OK(
189  dif_pinmux_pad_write_attrs_dt(&pinmux, kDtPadIoc3, in_attr, &out_attr));
190 #elif defined(OPENTITAN_IS_DARJEELING)
191  // Nothing to be done
192 #else
193 #error Unsupported top
194 #endif
195 
196  if (!deepsleep) {
197  // Enable all the AON interrupts used in this test.
198  static const uint32_t kPlicTarget = 0;
199  rv_plic_testutils_irq_range_enable(
200  &plic, kPlicTarget,
201  dt_pwrmgr_irq_to_plic_id(kDtPwrmgrAon, kDtPwrmgrIrqWakeup),
202  dt_pwrmgr_irq_to_plic_id(kDtPwrmgrAon, kDtPwrmgrIrqWakeup));
203  // Enable pwrmgr interrupt
204  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
205  }
206 
207  result = lowpower_prep(&pwrmgr, &pinmux, deepsleep);
208  }
209 
210  if (UNWRAP(pwrmgr_testutils_is_wakeup_reason(
211  &pwrmgr, kDifPwrmgrWakeupRequestSourceThree)) == true) {
212  // TODO: change PINMUX wakeup, not pin detector
213  /**
214  * Usually this part won't be hit. UVM testbench checks the PAD output
215  * values and raises an error if failed.
216  */
217  } else {
218  // Other wakeup. This is a failure.
219  dif_pwrmgr_wakeup_reason_t wakeup_reason;
220  CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_get(&pwrmgr, &wakeup_reason));
221  LOG_ERROR("Unexpected wakeup detected: type = %d, request_source = %d",
222  wakeup_reason.types, wakeup_reason.request_sources);
223  result = false;
224  }
225 
226  return result;
227 }