Software APIs
usbdev_mixed_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 Mixed streaming data test
6 //
7 // This test requires interaction with the USB DPI model or a test application
8 // on the USB host. The test initializes the USB device and configures a set of
9 // endpoints for data streaming using bulk transfers.
10 //
11 // The DPI model mimicks a USB host. After device initialization, it detects
12 // the assertion of the pullup and first assigns an address to the device.
13 // For this test it will then repeatedly fetch data via IN requests to
14 // each stream and propagate that data to the corresponding OUT endpoints.
15 //
16 // The data itself is pseudo-randomly generated by the sender and,
17 // independently, by the receiving code to check that the data has been
18 // propagated unmodified and without data loss, corruption, replication etc.
19 
20 /*
21 Thoughts:
22 
23  randomize the input and output endpoints for each stream; pick without
24  replacement(!)
25 
26  randomize the stream types
27 
28  randomize the sending, trying, receiving and max_packets
29 */
30 
34 #include "sw/device/lib/testing/pinmux_testutils.h"
35 #include "sw/device/lib/testing/test_framework/check.h"
37 #include "sw/device/lib/testing/usb_testutils.h"
38 #include "sw/device/lib/testing/usb_testutils_controlep.h"
39 #include "sw/device/lib/testing/usb_testutils_diags.h"
40 #include "sw/device/lib/testing/usb_testutils_streams.h"
41 
42 #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" // Generated.
43 
44 // Number of streams to be tested
45 #ifndef NUM_STREAMS
46 #define NUM_STREAMS USBUTILS_STREAMS_MAX
47 #endif
48 
49 #define TRANSFER_BYTES_SILICON (4U << 20)
50 #define TRANSFER_BYTES_FPGA (8U << 16)
51 
52 // This is appropriate for a Verilator chip simulation with 15 min timeout
53 #define TRANSFER_BYTES_VERILATOR 0x2400U
54 
55 // For top-level DV simulation (regression runs, deterministic behavior)
56 #define TRANSFER_BYTES_DVSIM 0x800U
57 
58 /**
59  * Indexed by usb_testutils_transfer_type_t
60  */
61 static const char *xfr_name[] = {
62  "Control",
63  "Isochronous",
64  "Bulk",
65  "Interrupt",
66 };
67 
68 // The transfer types of the streams
69 //
70 // Note: because Isochronous and Interrupt streams are guaranteed the requested
71 // amount of bus bandwidth, the number of concurrent streams of these types is
72 // limited to four if connected through a hub (six without hub(s)).
73 static const usb_testutils_transfer_type_t xfr_types[USBUTILS_STREAMS_MAX] = {
74  kUsbTransferTypeIsochronous, kUsbTransferTypeBulk,
75  kUsbTransferTypeBulk, kUsbTransferTypeBulk,
76 
77  kUsbTransferTypeIsochronous, kUsbTransferTypeInterrupt,
78  kUsbTransferTypeBulk, kUsbTransferTypeBulk,
79 
80  kUsbTransferTypeInterrupt, kUsbTransferTypeBulk,
81  kUsbTransferTypeBulk,
82 };
83 
84 // Total length of the configuration descriptor.
85 #define CFG_DSCR_TOTAL_LEN \
86  (USB_CFG_DSCR_LEN + \
87  NUM_STREAMS * (USB_INTERFACE_DSCR_LEN + 2 * USB_EP_DSCR_LEN))
88 
89 /**
90  * Configuration values for USB.
91  */
92 static uint8_t config_descriptors[CFG_DSCR_TOTAL_LEN] = {
93  USB_CFG_DSCR_HEAD(CFG_DSCR_TOTAL_LEN, NUM_STREAMS)
94  // Followed by programmatically-generated list of interface descriptors
95 };
96 
97 /**
98  * Test flags specifying the nature and direction of the data stream(s)
99  */
100 static usbdev_stream_flags_t test_flags;
101 
102 /**
103  * Test descriptor
104  */
105 static uint8_t test_descriptor[USB_TESTUTILS_TEST_DSCR_LEN];
106 
107 /**
108  * USB device context types.
109  */
110 static usb_testutils_ctx_t usbdev;
111 static usb_testutils_controlep_ctx_t usbdev_control;
112 
113 /**
114  * Pinmux handle
115  */
116 static dif_pinmux_t pinmux;
117 
118 /**
119  * State information for streaming data test
120  */
121 static usb_testutils_streams_ctx_t stream_test;
122 
123 /**
124  * Specify whether to perform verbose logging, for visibility
125  * (Note that this substantially alters the timing of interactions with the
126  * DPI model and will increase the simulation time)
127  */
128 static const bool kVerbose = false;
129 
130 /*
131  * These switches may be modified manually to experimentally measure the
132  * performance of IN traffic in isolation, or IN/OUT together etc.
133  *
134  * Are we sending data?
135  */
136 static const bool kSending = true;
137 
138 /**
139  * Are we generating a valid byte sequence?
140  */
141 static const bool kGenerating = true;
142 
143 /**
144  * Do we want the host to retry transmissions? (DPI model only; we cannot
145  * instruct a physical host to fake delivery failure/packet corruption etc)
146  */
147 static const bool kRetrying = true;
148 
149 /**
150  * Are we expecting to receive data?
151  */
152 static const bool kRecving = true;
153 
154 /**
155  * Send only maximal length packets?
156  * (important for performance measurements on the USB, but obviously undesirable
157  * for testing reliability/function)
158  */
159 static const bool kMaxPackets = false;
160 
161 /**
162  * Number of streams to be created
163  */
164 static const unsigned nstreams = NUM_STREAMS;
165 
166 OTTF_DEFINE_TEST_CONFIG();
167 
168 bool test_main(void) {
169  // Context state for streaming test
170  usb_testutils_streams_ctx_t *ctx = &stream_test;
171 
172  LOG_INFO("Running USBDEV MIXED Test");
173 
174  // Check we can support the requested number of streams
175  CHECK(nstreams && nstreams < USBDEV_NUM_ENDPOINTS);
176 
177  // Decide upon the number of bytes to be transferred for the entire test
178  uint32_t transfer_bytes = TRANSFER_BYTES_FPGA;
179  switch (kDeviceType) {
180  case kDeviceSimVerilator:
181  transfer_bytes = TRANSFER_BYTES_VERILATOR;
182  break;
183  case kDeviceSimDV:
184  transfer_bytes = TRANSFER_BYTES_DVSIM;
185  break;
186  case kDeviceSilicon:
187  transfer_bytes = TRANSFER_BYTES_SILICON;
188  break;
189  case kDeviceFpgaCw340:
190  break;
191  default:
192  CHECK(kDeviceType == kDeviceFpgaCw310);
193  break;
194  }
195  transfer_bytes = (transfer_bytes + nstreams - 1) / nstreams;
196  LOG_INFO(" - %u stream(s), 0x%x bytes each", nstreams, transfer_bytes);
197 
198  CHECK_DIF_OK(dif_pinmux_init(
200  pinmux_testutils_init(&pinmux);
201  CHECK_DIF_OK(dif_pinmux_input_select(
204 
205  // Construct the test/stream flags to be used
206  test_flags = (kSending ? kUsbdevStreamFlagRetrieve : 0U) |
207  (kGenerating ? kUsbdevStreamFlagCheck : 0U) |
208  (kRetrying ? kUsbdevStreamFlagRetry : 0U) |
209  (kRecving ? kUsbdevStreamFlagSend : 0U) |
210  // Note: the 'max packets' test flag is not required by the DPI
211  (kMaxPackets ? kUsbdevStreamFlagMaxPackets : 0U);
212 
213  // Remember context state for usb_testutils context
214  ctx->usbdev = &usbdev;
215 
216  // Call `usbdev_init` here so that DPI will not start until the
217  // simulation has finished all of the printing, which takes a while
218  // if `--trace` was passed in.
219  CHECK_STATUS_OK(usb_testutils_init(ctx->usbdev, /*pinflip=*/false,
220  /*en_diff_rcvr=*/false,
221  /*tx_use_d_se0=*/false));
222 
223  // Initialize the state of each of the streams in turn;
224  // we do this before setting up the control endpoint so that we can log the
225  // configuration of each stream before the DPI model is activated by
226  // enabling the interface
227  uint32_t mixed_types = 0U;
228  uint8_t *cfg = &config_descriptors[USB_CFG_DSCR_LEN];
229  for (uint8_t s = 0U; s < nstreams; s++) {
230  usb_testutils_transfer_type_t xfr_type = xfr_types[s];
231 
232  // Indicate to the DPI model the transfer type of each stream in turn.
233  mixed_types |= xfr_type << (s * 2U);
234 
235  // TODO: we shall also want some Control endpoints at some point, not just
236  // Endpoint Zero, but this requires more thought.
237  // TODO: some of the streams should probably be undirectional, and it could
238  // be a good idea to cross endpoints for a few of the streams, just in case.
239  uint8_t ep_in = (uint8_t)(s + 1U);
240  uint8_t ep_out = (uint8_t)(s + 1U);
241 
242  // Isochronous endpoints require a bInterval value of 1.
243  uint8_t bInterval = (xfr_type == kUsbTransferTypeIsochronous ||
244  xfr_type == kUsbTransferTypeInterrupt);
245 
246  // Description of a single interface
247  uint8_t int_dscr[USB_INTERFACE_DSCR_LEN + 2 * USB_EP_DSCR_LEN] = {
248  VEND_INTERFACE_DSCR(s, 2, 0x50, 1),
249  USB_EP_DSCR(0, ep_out, (uint8_t)xfr_type, USBDEV_MAX_PACKET_SIZE,
250  bInterval),
251  USB_EP_DSCR(1, ep_in, (uint8_t)xfr_type, USBDEV_MAX_PACKET_SIZE,
252  bInterval),
253  };
254  // Append interface descriptor to the configuration descriptor.
255  memcpy(cfg, int_dscr, sizeof(int_dscr));
256  cfg += sizeof(int_dscr);
257 
258  CHECK_STATUS_OK(usb_testutils_stream_init(
259  ctx, s, xfr_type, ep_in, ep_out, transfer_bytes, test_flags, kVerbose));
260  LOG_INFO("S%u: IN %u:OUT %u : %s - 0x%x bytes flags 0x%x", s, ep_in, ep_out,
261  xfr_name[xfr_type], transfer_bytes, test_flags);
262  }
263 
264  // Inform the testutils layer of the total number of streams
265  CHECK(usb_testutils_streams_count_set(ctx, nstreams));
266 
267  // Initialize the test descriptor
268  // Note: the 'max packets' test flag is not required by the DPI model
269  const uint8_t desc[] = {USB_TESTUTILS_TEST_DSCR(
270  kUsbTestNumberMixed, NUM_STREAMS | (uint8_t)test_flags,
271  (uint8_t)mixed_types, (uint8_t)(mixed_types >> 8),
272  (uint8_t)(mixed_types >> 16))};
273  memcpy(test_descriptor, desc, sizeof(test_descriptor));
274 
275  // This also ctivates the DPI model by asserting the pull up, indicating
276  // device presence
277  CHECK_STATUS_OK(usb_testutils_controlep_init(
278  &usbdev_control, ctx->usbdev, 0, config_descriptors,
279  sizeof(config_descriptors), test_descriptor, sizeof(test_descriptor)));
280 
281  // Proceed only when the device has been configured; this allows host-side
282  // software to establish communication.
283  CHECK_STATUS_OK(
284  usb_testutils_controlep_config_wait(&usbdev_control, &usbdev));
285 
286  USBUTILS_USER_PROMPT("Start host-side streaming software");
287 
288  // Streaming loop; most of the work is done by the usb_testutils_streams base
289  // code and we don't need to specialize its behavior for this test.
290  bool done = false;
291  do {
292  CHECK_STATUS_OK(usb_testutils_streams_service(ctx));
293 
294  // See whether any streams still have more work to do
295  done = usb_testutils_streams_completed(ctx);
296  } while (!done);
297 
298  // Determine the total counts of bytes sent and received
299  uint32_t tx_bytes = 0U;
300  uint32_t rx_bytes = 0U;
301  for (uint8_t s = 0U; s < nstreams; s++) {
302  uint32_t tx, rx;
303  CHECK_STATUS_OK(usb_testutils_stream_status(ctx, s, NULL, &tx, &rx));
304  tx_bytes += tx;
305  rx_bytes += rx;
306  }
307 
308  LOG_INFO("USB sent 0x%x byte(s), received and checked 0x%x byte(s)", tx_bytes,
309  rx_bytes);
310 
311  // Note: since some streams are Isochronous, packet dropping can lead to the
312  // byte count exceeding expectations.
313  if (kSending) {
314  CHECK(tx_bytes >= nstreams * transfer_bytes,
315  "Unexpected count of byte(s) sent to USB host");
316  }
317  if (kRecving) {
318  CHECK(rx_bytes >= nstreams * transfer_bytes,
319  "Unexpected count of byte(s) received from USB host");
320  }
321 
322  return true;
323 }