blob: 395a42b3527a3c29bbd8c9e7c133eedf3a1111dd [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include <stdalign.h>
#include <stdbool.h>
#include "sw/device/examples/demos.h"
#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/dif/dif_gpio.h"
#include "sw/device/lib/dif/dif_pinmux.h"
#include "sw/device/lib/dif/dif_spi_device.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/runtime/hart.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_test_config.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.
// These just for the '/' printout
#define USBDEV_BASE_ADDR TOP_EARLGREY_USBDEV_BASE_ADDR
#include "usbdev_regs.h" // Generated.
#define REG32(add) *((volatile uint32_t *)(add))
OTTF_DEFINE_TEST_CONFIG();
/**
* Configuration values for USB.
*/
static 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),
};
/**
* 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_serial0;
static usb_testutils_ss_ctx_t simple_serial1;
/**
* 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 size_t usb_chars_recved_total;
static dif_gpio_t gpio;
static dif_pinmux_t pinmux;
static dif_spi_device_handle_t spi;
static dif_uart_t uart;
/**
* Callbacks for processing USB reciept. The latter increments the
* recieved character by one, to make them distinct.
*/
static void usb_receipt_callback_0(uint8_t c) {
c = make_printable(c, '?');
CHECK_DIF_OK(dif_uart_byte_send_polled(&uart, c));
++usb_chars_recved_total;
}
static void usb_receipt_callback_1(uint8_t c) {
c = make_printable(c + 1, '?');
CHECK_DIF_OK(dif_uart_byte_send_polled(&uart, c));
++usb_chars_recved_total;
}
/**
* USB Send String
*
* Send a 0 terminated string to the USB one byte at a time.
* The send byte code will flush the endpoint if needed.
*
* @param string Zero terminated string to send.
* @param ss_ctx Pointer to simple string endpoint context to send through.
*/
static void usb_send_str(const char *string, usb_testutils_ss_ctx_t *ss_ctx) {
for (int i = 0; string[i] != 0; ++i) {
usb_testutils_simpleserial_send_byte(ss_ctx, string[i]);
}
}
// These GPIO bits control USB PHY configuration
static const uint32_t kPinflipMask = (1 << 8);
static const uint32_t kDiffXcvrMask = (1 << 9);
static const uint32_t kUPhyMask = (1 << 10);
static dif_pinmux_index_t leds[] = {
kTopEarlgreyPinmuxMioOutIor10,
kTopEarlgreyPinmuxMioOutIor11,
kTopEarlgreyPinmuxMioOutIor12,
kTopEarlgreyPinmuxMioOutIor13,
};
static dif_pinmux_index_t switches[] = {
kTopEarlgreyPinmuxInselIob6,
kTopEarlgreyPinmuxInselIob7,
kTopEarlgreyPinmuxInselIob8,
kTopEarlgreyPinmuxInselIob9,
};
void configure_pinmux(void) {
pinmux_testutils_init(&pinmux);
// Hook up some LEDs.
for (size_t i = 0; i < ARRAYSIZE(leds); ++i) {
dif_pinmux_index_t gpio = kTopEarlgreyPinmuxOutselGpioGpio0 + i;
CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, leds[i], gpio));
}
// Hook up DIP switches.
for (size_t i = 0; i < ARRAYSIZE(switches); ++i) {
dif_pinmux_index_t gpio = kTopEarlgreyPinmuxPeripheralInGpioGpio8 + i;
CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, gpio, switches[i]));
}
}
void _ottf_main(void) {
CHECK_DIF_OK(dif_pinmux_init(
mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux));
configure_pinmux();
CHECK_DIF_OK(dif_uart_init(
mmio_region_from_addr(TOP_EARLGREY_UART0_BASE_ADDR), &uart));
CHECK_DIF_OK(
dif_uart_configure(&uart, (dif_uart_config_t){
.baudrate = kUartBaudrate,
.clk_freq_hz = kClockFreqPeripheralHz,
.parity_enable = kDifToggleDisabled,
.parity = kDifUartParityEven,
.tx_enable = kDifToggleEnabled,
.rx_enable = kDifToggleEnabled,
}));
base_uart_stdout(&uart);
CHECK_DIF_OK(dif_spi_device_init_handle(
mmio_region_from_addr(TOP_EARLGREY_SPI_DEVICE_BASE_ADDR), &spi));
dif_spi_device_config_t spi_config = {
.clock_polarity = kDifSpiDeviceEdgePositive,
.data_phase = kDifSpiDeviceEdgeNegative,
.tx_order = kDifSpiDeviceBitOrderMsbToLsb,
.rx_order = kDifSpiDeviceBitOrderMsbToLsb,
.device_mode = kDifSpiDeviceModeGeneric,
.mode_cfg =
{
.generic =
{
.rx_fifo_commit_wait = 63,
.rx_fifo_len = kDifSpiDeviceBufferLen / 2,
.tx_fifo_len = kDifSpiDeviceBufferLen / 2,
},
},
};
CHECK_DIF_OK(dif_spi_device_configure(&spi, spi_config));
CHECK_DIF_OK(
dif_gpio_init(mmio_region_from_addr(TOP_EARLGREY_GPIO_BASE_ADDR), &gpio));
// Enable GPIO: 0-7 and 16 is input; 8-15 is output.
CHECK_DIF_OK(dif_gpio_output_set_enabled_all(&gpio, 0x000ff));
LOG_INFO("Hello, USB!");
LOG_INFO("Built at: " __DATE__ ", " __TIME__);
demo_gpio_startup(&gpio);
// 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.
uint32_t gpio_state;
CHECK_DIF_OK(dif_gpio_read_all(&gpio, &gpio_state));
bool pinflip = gpio_state & kPinflipMask ? true : false;
bool differential_xcvr = gpio_state & kDiffXcvrMask ? true : false;
bool uphy = gpio_state & kUPhyMask ? true : false;
LOG_INFO("PHY settings: pinflip=%d differential_xcvr=%d USB Phy=%d", pinflip,
differential_xcvr, uphy);
// Connect correct VBUS detection pin
if (uphy) {
CHECK_DIF_OK(dif_pinmux_input_select(
&pinmux, kTopEarlgreyPinmuxPeripheralInUsbdevSense,
kTopEarlgreyPinmuxInselIoc7));
} else {
CHECK_DIF_OK(dif_pinmux_input_select(
&pinmux, kTopEarlgreyPinmuxPeripheralInUsbdevSense,
kTopEarlgreyPinmuxInselConstantOne));
}
CHECK_DIF_OK(dif_spi_device_send(&spi, "SPI!", 4, /*bytes_sent=*/NULL));
// The TI phy always uses a differential TX interface
usb_testutils_init(&usbdev, pinflip, differential_xcvr,
differential_xcvr && !uphy);
usb_testutils_controlep_init(&usbdev_control, &usbdev, 0, config_descriptors,
sizeof(config_descriptors), NULL, 0);
while (usbdev_control.device_state != kUsbTestutilsDeviceConfigured) {
usb_testutils_poll(&usbdev);
}
usb_testutils_simpleserial_init(&simple_serial0, &usbdev, 1,
usb_receipt_callback_0);
usb_testutils_simpleserial_init(&simple_serial1, &usbdev, 2,
usb_receipt_callback_1);
bool say_hello = true;
bool pass_signaled = false;
while (true) {
usb_testutils_poll(&usbdev);
gpio_state = demo_gpio_to_log_echo(&gpio, gpio_state);
demo_spi_to_log_echo(&spi);
while (true) {
size_t chars_available;
if (dif_uart_rx_bytes_available(&uart, &chars_available) != kDifOk ||
chars_available == 0) {
break;
}
uint8_t rcv_char;
CHECK_DIF_OK(dif_uart_bytes_receive(&uart, 1, &rcv_char, NULL));
CHECK_DIF_OK(dif_uart_byte_send_polled(&uart, rcv_char));
CHECK_DIF_OK(dif_gpio_write_all(&gpio, rcv_char << 8));
if (rcv_char == '/') {
uint32_t usb_irq_state =
REG32(USBDEV_BASE_ADDR + USBDEV_INTR_STATE_REG_OFFSET);
uint32_t usb_stat = REG32(USBDEV_BASE_ADDR + USBDEV_USBSTAT_REG_OFFSET);
LOG_INFO("I%04x-%08x", usb_irq_state, usb_stat);
} else {
usb_testutils_simpleserial_send_byte(&simple_serial0, rcv_char);
usb_testutils_simpleserial_send_byte(&simple_serial1, rcv_char + 1);
}
}
if (say_hello && usb_chars_recved_total > 2) {
usb_send_str("Hello USB World!!!!", &simple_serial0);
say_hello = false;
}
// Signal that the simulation succeeded.
if (usb_chars_recved_total >= kExpectedUsbCharsRecved && !pass_signaled) {
LOG_INFO("PASS!");
pass_signaled = true;
}
}
LOG_INFO("USB recieved %d characters.", usb_chars_recved_total);
}