Software APIs
usbdev_toggle_restore_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 // USB Device Toggle Restore test
6 //
7 // This is a simple test to exercise the ability to save and restore the
8 // Data Toggle bits within the USB device. That is important for resuming
9 // communication with the USB host controller after returning from a
10 // Deep Sleep state.
11 //
12 // Note that this test does not itself attempt to enter/resume from
13 // Deep Sleep.
14 
15 #include <stdbool.h>
16 #include <stdint.h>
17 
18 #include "sw/device/lib/base/status.h"
22 #include "sw/device/lib/testing/pinmux_testutils.h"
23 #include "sw/device/lib/testing/test_framework/check.h"
25 
26 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
27 
28 #define USBDEV_BASE_ADDR TOP_EARLGREY_USBDEV_BASE_ADDR
29 
30 /**
31  * USB device handle
32  */
33 static dif_usbdev_t usbdev;
34 
35 /**
36  * Pinmux handle
37  */
38 static dif_pinmux_t pinmux;
39 
40 // Wait with timeout until the VBUS/SENSE signal is in the expected state.
41 static status_t vbus_wait(bool expected, uint32_t timeout_micros) {
42  ibex_timeout_t timeout = ibex_timeout_init(timeout_micros);
43  do {
44  // Read the current state of VBUS.
45  bool vbus;
46  TRY(dif_usbdev_status_get_sense(&usbdev, &vbus));
47  if (vbus == expected) {
48  return OK_STATUS();
49  }
50  } while (!ibex_timeout_check(&timeout));
51 
52  return DEADLINE_EXCEEDED();
53 }
54 
55 // Wait with timeout until the link is in the expected state.
56 static status_t link_state_wait(dif_usbdev_link_state_t expected,
57  uint32_t timeout_micros) {
58  ibex_timeout_t timeout = ibex_timeout_init(timeout_micros);
59  do {
60  // Read the current state of the link.
61  dif_usbdev_link_state_t link_state;
62  TRY(dif_usbdev_status_get_link_state(&usbdev, &link_state));
63  if (link_state == expected) {
64  return OK_STATUS();
65  }
66  } while (!ibex_timeout_check(&timeout));
67 
68  return DEADLINE_EXCEEDED();
69 }
70 
71 // Check that the data toggle bits read from the USB device match expectations.
72 static void check_toggles(uint16_t exp_out_toggles, uint16_t exp_in_toggles,
73  bool logging) {
74  uint16_t act_out_toggles;
75  uint16_t act_in_toggles;
76 
77  CHECK_DIF_OK(dif_usbdev_data_toggle_out_read(&usbdev, &act_out_toggles));
78  CHECK_DIF_OK(dif_usbdev_data_toggle_in_read(&usbdev, &act_in_toggles));
79 
80  // Logging could cause this test to fail with the DPI in simulation and even
81  // lead to flaky behaviour with a physical USB host controller because we
82  // ignore the host for too long and it resorts to issuing a second Bus Reset.
83  if (logging) {
84  LOG_INFO("exp_out_toggles 0x%x act_out_toggles 0x%x", exp_out_toggles,
85  act_out_toggles);
86  LOG_INFO("exp_in_toggles 0x%x act_in_toggles 0x%x", exp_in_toggles,
87  act_in_toggles);
88  }
89 
90  CHECK(act_out_toggles == exp_out_toggles);
91  CHECK(act_in_toggles == exp_in_toggles);
92 }
93 
94 OTTF_DEFINE_TEST_CONFIG();
95 
96 bool test_main(void) {
97  // Mask defining the valid endpoints.
98  const uint16_t all_endpoints = (uint16_t)(1u << USBDEV_NUM_ENDPOINTS) - 1u;
99  // Expected state of the Data Toggle registers.
100  uint16_t exp_out_toggles = (uint16_t)(0xAAAAu & all_endpoints);
101  uint16_t exp_in_toggles = (uint16_t)(0x5555u & all_endpoints);
102  uint32_t timeout_micros = 1000u;
103  // Default to no prompting or logging.
104  bool logging = false;
105  bool prompt = false;
106 
107  switch (kDeviceType) {
108  // In simulation the DPI model connects VBUS shortly after reset.
109  case kDeviceSimDV:
110  // logging is harmless for DV sim because of the `sw_logger_if` back door
111  // reducing the simulation time.
112  logging = true;
114  case kDeviceSimVerilator:
115  // avoid logging with Verilator because it does not have the back door.
116  timeout_micros = 1000u;
117  break;
118  default:
119  // FPGA platforms where user intervention may be required.
120  timeout_micros = 30 * 1000 * 1000u;
121  prompt = true;
122  // logging _may_ be used in this case but it could lead to flakiness,
123  // through a failure to respond to the USB host controller promptly.
124  logging = false;
125  break;
126  }
127 
128  // Ensure that the VBUS/SENSE signal is routed through to the usbdev
129  CHECK_DIF_OK(dif_pinmux_init(
131  pinmux_testutils_init(&pinmux);
132  CHECK_DIF_OK(dif_pinmux_input_select(
135 
136  CHECK_DIF_OK(
137  dif_usbdev_init(mmio_region_from_addr(USBDEV_BASE_ADDR), &usbdev));
138 
139  // Firstly, ensure that we are disconnected from the USB; if there's a
140  // physical USB host controller cabled then this will force a reset of the
141  // connection.
142  CHECK_DIF_OK(dif_usbdev_interface_enable(&usbdev, kDifToggleDisabled));
143 
144  // Unfortunately we _do_ need to connect to the USB host controller/DPI model
145  // to ensure that the link comes out of reset.
146  bool vbus;
147  CHECK_DIF_OK(dif_usbdev_status_get_sense(&usbdev, &vbus));
148  if (!vbus) {
149  if (prompt) {
150  LOG_INFO("Connect or power up the USB");
151  }
152  CHECK_STATUS_OK(vbus_wait(true, timeout_micros));
153  }
154 
155  // Now connect to the bus and wait for the link to come out of reset.
156  CHECK_DIF_OK(dif_usbdev_interface_enable(&usbdev, kDifToggleEnabled));
157  CHECK_STATUS_OK(
158  link_state_wait(kDifUsbdevLinkStateActiveNoSof, timeout_micros));
159  if (prompt) {
160  LOG_INFO("Link out of reset; testing...");
161  }
162 
163  // We must then quickly perform the testing before any communication from the
164  // USB host controller occurs, because any communication will interfere with
165  // the data toggle bits.
166 
167  // Set up a test pattern in the registers, putting all data toggle bits into
168  // a defined state.
169  CHECK_DIF_OK(dif_usbdev_data_toggle_out_write(&usbdev, all_endpoints,
170  exp_out_toggles));
171  CHECK_DIF_OK(
172  dif_usbdev_data_toggle_in_write(&usbdev, all_endpoints, exp_in_toggles));
173 
174  // Check that they read as expected.
175  check_toggles(exp_out_toggles, exp_in_toggles, logging);
176 
177  // Write an inverted test pattern.
178  exp_out_toggles ^= all_endpoints;
179  exp_in_toggles ^= all_endpoints;
180  CHECK_DIF_OK(dif_usbdev_data_toggle_out_write(&usbdev, all_endpoints,
181  exp_out_toggles));
182  CHECK_DIF_OK(
183  dif_usbdev_data_toggle_in_write(&usbdev, all_endpoints, exp_in_toggles));
184 
185  // Check that they read as expected.
186  check_toggles(exp_out_toggles, exp_in_toggles, logging);
187 
188  // Walk through each of the bits within each register, setting and resetting
189  // each bit individually.
190  for (uint8_t ep = 0u; ep < USBDEV_NUM_ENDPOINTS; ++ep) {
191  uint16_t ep_mask = (uint16_t)(1u << ep);
192 
193  exp_out_toggles |= ep_mask;
194  exp_in_toggles |= ep_mask;
195  CHECK_DIF_OK(dif_usbdev_data_toggle_out_write(&usbdev, ep_mask, ep_mask));
196  CHECK_DIF_OK(dif_usbdev_data_toggle_in_write(&usbdev, ep_mask, ep_mask));
197 
198  check_toggles(exp_out_toggles, exp_in_toggles, logging);
199 
200  exp_out_toggles &= ~ep_mask;
201  exp_in_toggles &= ~ep_mask;
202  CHECK_DIF_OK(dif_usbdev_data_toggle_out_write(&usbdev, ep_mask, 0u));
203  CHECK_DIF_OK(dif_usbdev_data_toggle_in_write(&usbdev, ep_mask, 0u));
204 
205  check_toggles(exp_out_toggles, exp_in_toggles, logging);
206  }
207 
208  // Tidy up by disconnecting from the USB.
209  CHECK_DIF_OK(dif_usbdev_interface_enable(&usbdev, kDifToggleDisabled));
210 
211  return true;
212 }