| // Copyright lowRISC contributors. | 
 | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | // SPDX-License-Identifier: Apache-2.0 | 
 | // | 
 | // USB device test | 
 | // | 
 | // This test is a stripped down version of the hello_usbdev example application. | 
 | // It requires interaction with the USB DPI model mimicking the host and thus | 
 | // can only be run in the Verilator simulation. The test initializes the USB | 
 | // device and configures USB Endpoint 1 as a simpleserial endpoint. The test | 
 | // then starts polling the USB device for data sent by the host. Any data | 
 | // received on Endpoint 1 is stored in a buffer and printed via UART. | 
 | // | 
 | // The DPI model mimicks the USB host. After device initialization, it detects | 
 | // the assertion of the pullup and first assigns an address to the device. It | 
 | // then sends various USB transactions to the device including two OUT | 
 | // transactions with a data payload of "Hi!" to Endpoint 1. If these two OUT | 
 | // transactions are succesfully received by the device, the test passes. | 
 |  | 
 | #include "sw/device/lib/dif/dif_pinmux.h" | 
 | #include "sw/device/lib/runtime/log.h" | 
 | #include "sw/device/lib/runtime/print.h" | 
 | #include "sw/device/lib/testing/pinmux_testutils.h" | 
 | #include "sw/device/lib/testing/test_framework/check.h" | 
 | #include "sw/device/lib/testing/test_framework/ottf_main.h" | 
 | #include "sw/device/lib/testing/usb_testutils.h" | 
 | #include "sw/device/lib/testing/usb_testutils_controlep.h" | 
 | #include "sw/device/lib/testing/usb_testutils_simpleserial.h" | 
 |  | 
 | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"  // Generated. | 
 |  | 
 | /** | 
 |  * Configuration values for USB. | 
 |  */ | 
 | static const uint8_t config_descriptors[] = { | 
 |     USB_CFG_DSCR_HEAD( | 
 |         USB_CFG_DSCR_LEN + 2 * (USB_INTERFACE_DSCR_LEN + 2 * USB_EP_DSCR_LEN), | 
 |         2), | 
 |     VEND_INTERFACE_DSCR(0, 2, 0x50, 1), | 
 |     USB_BULK_EP_DSCR(0, 1, 32, 0), | 
 |     USB_BULK_EP_DSCR(1, 1, 32, 4), | 
 |     VEND_INTERFACE_DSCR(1, 2, 0x50, 1), | 
 |     USB_BULK_EP_DSCR(0, 2, 32, 0), | 
 |     USB_BULK_EP_DSCR(1, 2, 32, 4), | 
 | }; | 
 |  | 
 | /** | 
 |  * Test descriptor | 
 |  */ | 
 | static const uint8_t test_descriptor[] = { | 
 |     USB_TESTUTILS_TEST_DSCR(0, 0, 0, 0, 0)}; | 
 |  | 
 | /** | 
 |  * USB device context types. | 
 |  */ | 
 | static usb_testutils_ctx_t usbdev; | 
 | static usb_testutils_controlep_ctx_t usbdev_control; | 
 | static usb_testutils_ss_ctx_t simple_serial; | 
 |  | 
 | /** | 
 |  * Pinmux handle | 
 |  */ | 
 | static dif_pinmux_t pinmux; | 
 |  | 
 | /** | 
 |  * Makes `c` into a printable character, replacing it with `replacement` | 
 |  * as necessary. | 
 |  */ | 
 | static char make_printable(char c, char replacement) { | 
 |   if (c == 0xa || c == 0xd) { | 
 |     return c; | 
 |   } | 
 |  | 
 |   if (c < ' ' || c > '~') { | 
 |     c = replacement; | 
 |   } | 
 |   return c; | 
 | } | 
 |  | 
 | static const size_t kExpectedUsbCharsRecved = 6; | 
 | static const char kExpectedUsbRecved[7] = "Hi!Hi!"; | 
 | static size_t usb_chars_recved_total; | 
 | static char buffer[7]; | 
 |  | 
 | /** | 
 |  * Callback for processing USB reciept. | 
 |  */ | 
 | static void usb_receipt_callback(uint8_t c) { | 
 |   c = make_printable(c, '?'); | 
 |   base_printf("%c", c); | 
 |   if (usb_chars_recved_total < kExpectedUsbCharsRecved) { | 
 |     buffer[usb_chars_recved_total] = c; | 
 |     ++usb_chars_recved_total; | 
 |   } | 
 | } | 
 |  | 
 | OTTF_DEFINE_TEST_CONFIG(); | 
 |  | 
 | bool test_main(void) { | 
 |   CHECK(kDeviceType == kDeviceSimVerilator || kDeviceType == kDeviceFpgaCw310, | 
 |         "This test is not expected to run on platforms other than the " | 
 |         "Verilator simulation or CW310 FPGA. It needs the USB DPI model " | 
 |         "or host application."); | 
 |  | 
 |   LOG_INFO("Running USBDEV test"); | 
 |  | 
 |   CHECK_DIF_OK(dif_pinmux_init( | 
 |       mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux)); | 
 |   pinmux_testutils_init(&pinmux); | 
 |   CHECK_DIF_OK(dif_pinmux_input_select( | 
 |       &pinmux, kTopEarlgreyPinmuxPeripheralInUsbdevSense, | 
 |       kTopEarlgreyPinmuxInselIoc7)); | 
 |  | 
 |   // Call `usbdev_init` here so that DPI will not start until the | 
 |   // simulation has finished all of the printing, which takes a while | 
 |   // if `--trace` was passed in. | 
 |   usb_testutils_init(&usbdev, /*pinflip=*/false, /*en_diff_rcvr=*/false, | 
 |                      /*tx_use_d_se0=*/false); | 
 |   usb_testutils_controlep_init(&usbdev_control, &usbdev, 0, config_descriptors, | 
 |                                sizeof(config_descriptors), test_descriptor, | 
 |                                sizeof(test_descriptor)); | 
 |   while (usbdev_control.device_state != kUsbTestutilsDeviceConfigured) { | 
 |     usb_testutils_poll(&usbdev); | 
 |   } | 
 |   usb_testutils_simpleserial_init(&simple_serial, &usbdev, 1, | 
 |                                   usb_receipt_callback); | 
 |  | 
 |   while (usb_chars_recved_total < kExpectedUsbCharsRecved) { | 
 |     usb_testutils_poll(&usbdev); | 
 |   } | 
 |  | 
 |   base_printf("\r\n"); | 
 |   for (int i = 0; i < kExpectedUsbCharsRecved; i++) { | 
 |     CHECK(buffer[i] == kExpectedUsbRecved[i], | 
 |           "Received char #%d mismatched: exp = %x, actual = %x", i, | 
 |           kExpectedUsbRecved[i], buffer[i]); | 
 |   } | 
 |   LOG_INFO("USB received %d characters: %s", usb_chars_recved_total, buffer); | 
 |  | 
 |   return true; | 
 | } |