| // 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; |
| } |