blob: 67309933d27058470ec127ec45eb599a00e05030 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// USBDPI -- act as a simple host for usbuart device
// Bits in LOG_LEVEL sets what is output on socket
// 0x01 -- monitor_usb (packet level)
// 0x02 -- more verbose monitor
// 0x08 -- bit level
module usbdpi #(
parameter string NAME = "usb0",
parameter LOG_LEVEL = 1
)(
input logic clk_i,
input logic rst_ni,
input logic clk_48MHz_i,
output logic dp_p2d,
input logic dp_d2p,
input logic dp_en_d2p,
output logic dn_p2d,
input logic dn_d2p,
input logic dn_en_d2p,
output logic d_p2d,
input logic d_d2p,
input logic d_en_d2p,
input logic se0_d2p,
input logic rx_enable_d2p,
input logic tx_use_d_se0_d2p,
output logic sense_p2d,
input logic pullupdp_d2p,
input logic pullupdn_d2p
);
import "DPI-C" function
chandle usbdpi_create(input string name, input int loglevel);
import "DPI-C" function
void usbdpi_device_to_host(input chandle ctx, input bit [10:0] d2p);
import "DPI-C" function
void usbdpi_close(input chandle ctx);
import "DPI-C" function
byte usbdpi_host_to_device(input chandle ctx, input bit [10:0] d2p);
import "DPI-C" function
void usbdpi_diags(input chandle ctx, output bit [95:0] diags);
chandle ctx;
initial begin
ctx = usbdpi_create(NAME, LOG_LEVEL);
sense_p2d = 1'b0;
end
final begin
usbdpi_close(ctx);
end
// USB Packet IDentifier values, for waveform viewing
typedef enum bit [7:0] {
OUT = 8'hE1,
IN = 8'h69,
SOF = 8'hA5,
SETUP = 8'h2D,
DATA0 = 8'hC3,
DATA1 = 8'h4B,
ACK = 8'hD2,
NAK = 8'h5A,
STALL = 8'h1E,
NYET = 8'h96,
PING = 8'hB4
} usb_pid_t;
// Host state
// Note: MUST be kept consistent with usbdpi_host_state_t in usbdpi.h
typedef enum bit [4:0] {
HS_STARTFRAME = 0,
HS_WAITACK = 1,
HS_SET_DATASTAGE = 2,
HS_DS_RXDATA = 3,
HS_DS_SENDACK = 4,
HS_DONEDADR = 5,
HS_REQDATA = 6,
HS_WAITDATA = 7,
HS_SENDACK = 8,
HS_WAIT_PKT = 9,
HS_ACKIFDATA = 10,
HS_SENDHI = 11,
HS_EMPTYDATA = 12,
HS_WAITACK2 = 13,
HS_STREAMOUT = 14,
HS_STREAMIN = 15,
HS_NEXTFRAME = 16
} usbdpi_host_state_t;
// Bus state
// Note: MUST be kept consistent with usbdpi_bus_state in usbdpi.h
typedef enum bit [4:0] {
kUsbIdle = 0,
kUsbControlSetup,
kUsbControlSetupAck,
kUsbControlDataOut,
kUsbControlDataOutAck, // 4
kUsbControlStatusInToken,
kUsbControlStatusInData,
kUsbControlStatusInAck,
kUsbControlDataInToken, // 8
kUsbControlDataInData,
kUsbControlDataInAck,
kUsbControlStatusOut,
kUsbControlStatusOutAck, // 0xc
kUsbIsoToken,
kUsbIsoDataIn,
kUsbIsoDataOut,
kUsbBulkOut, // 0x10
kUsbBulkOutAck,
kUsbBulkInToken,
kUsbBulkInData,
kUsbBulkInAck
} usbdpi_bus_state_t;
// USB monitor state
// Note: MUST be kept consistent with usbdpi_monitor_state_t in usb_monitor.c
typedef enum bit [1:0] {
MS_IDLE = 0,
MS_GET_PID,
MS_GET_BYTES
} usb_monitor_state_t;
// USB driver state
// Note: MUST be kept consistent with usbdpi_drv_state_t in usbdpi.h
typedef enum bit [3:0] {
ST_IDLE = 0,
ST_SEND = 1,
ST_GET = 2,
ST_SYNC = 3,
ST_EOP = 4,
ST_EOP0 = 5
} usbdpi_drv_state_t;
// Test steps
typedef enum bit [6:0] {
STEP_BUS_RESET = 0,
STEP_SET_DEVICE_ADDRESS,
STEP_GET_DEVICE_DESCRIPTOR,
STEP_GET_CONFIG_DESCRIPTOR,
STEP_GET_FULL_CONFIG_DESCRIPTOR,
STEP_SET_DEVICE_CONFIG,
STEP_GET_TEST_CONFIG,
STEP_SET_TEST_STATUS,
// usbdev_test
STEP_FIRST_READ,
STEP_READ_BAUD,
STEP_SECOND_READ,
STEP_SET_BAUD,
STEP_THIRD_READ,
STEP_TEST_ISO1,
STEP_TEST_ISO2,
STEP_ENDPT_UNIMPL_SETUP,
STEP_ENDPT_UNIMPL_OUT,
STEP_ENDPT_UNIMPL_IN,
STEP_DEVICE_UK_SETUP,
STEP_IDLE_START,
STEP_IDLE_END = STEP_IDLE_START + 4,
// usbdev_stream_test
STEP_STREAM_SERVICE = 'h20,
// Disconnect the device and stop
STEP_BUS_DISCONNECT = 'h7f
} usbdpi_test_step_t;
// Make usb_monitor diagnostic information viewable in waveforms
bit [9:0] c_spare1;
usb_monitor_state_t c_mon_state;
bit [3:0] c_mon_bits;
bit [7:0] c_mon_byte;
usb_pid_t c_mon_pid;
// Make usbdpi diagnostic information viewable in waveforms
usbdpi_test_step_t c_step;
usbdpi_bus_state_t c_bus_state;
bit [31:0] c_tickbits;
bit [10:0] c_frame;
usbdpi_host_state_t c_hostSt;
usbdpi_drv_state_t c_state;
always @(posedge clk_48MHz_i)
usbdpi_diags(ctx, {c_spare1, c_mon_state, c_mon_bits, c_mon_byte, c_mon_pid,
c_step, c_bus_state, c_tickbits, c_frame, c_hostSt,
c_state});
logic [10:0] d2p;
logic [10:0] d2p_r;
logic unused_dummy;
logic unused_clk = clk_i;
logic unused_rst = rst_ni;
logic dp_int, dn_int, d_last;
logic flip_detect, pullup_detect, rx_enable;
// Detect a request to flip pins by the DN resistor being applied;
// it's a full speed device so the pullup resistor is on the D+ signal
assign flip_detect = pullupdn_d2p;
assign pullup_detect = pullupdp_d2p || pullupdn_d2p;
assign rx_enable = rx_enable_d2p;
assign d2p = {dp_d2p, dp_en_d2p, dn_d2p, dn_en_d2p, d_d2p, d_en_d2p, se0_d2p, tx_use_d_se0_d2p,
pullupdp_d2p, pullupdn_d2p, rx_enable};
always_ff @(posedge clk_48MHz_i) begin
if (!sense_p2d || pullup_detect) begin
automatic byte p2d = usbdpi_host_to_device(ctx, d2p);
d_last <= d_p2d;
dp_int <= p2d[2];
dn_int <= p2d[1];
sense_p2d <= p2d[0];
unused_dummy <= |p2d[7:4];
d2p_r <= d2p;
if (d2p_r != d2p) begin
usbdpi_device_to_host(ctx, d2p);
end
end else begin // if (pullup_detect)
d_last <= 0;
dp_int <= 0;
dn_int <= 0;
end
end
always_comb begin : proc_data
d_p2d = d_last;
if (rx_enable) begin
// Differential receiver is enabled.
// If host is driving, update d_p2d only if there is a valid differential
// value.
if (d_en_d2p) begin
d_p2d = d_d2p;
end else if (dp_int && !dn_int) begin
d_p2d = 1'b1;
end else if (!dp_int && dn_int) begin
d_p2d = 1'b0;
end
end
if (dp_en_d2p) begin
if (tx_use_d_se0_d2p) begin
dp_p2d = se0_d2p ? 1'b0 : flip_detect ^ d_d2p;
end else begin
dp_p2d = dp_d2p;
end
end else begin
dp_p2d = dp_int;
end
if (dn_en_d2p) begin
if (tx_use_d_se0_d2p) begin
dn_p2d = se0_d2p ? 1'b0 : flip_detect ^ ~d_d2p;
end else begin
dn_p2d = dn_d2p;
end
end else begin
dn_p2d = dn_int;
end
end
endmodule