blob: 19ec094d3d715e047101956dee9dc5a7287247b2 [file] [log] [blame]
// Copyright lowRISC contributors.
// Copyright ETH Zurich.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// USB IO Mux
//
// Muxes the USB IO signals from: register access, differential signaling,
// single-ended signaling. The incomming signals are also muxed and synchronized
// to the corresponding clock domain.
module usbdev_iomux
import usbdev_reg_pkg::*;
(
input logic clk_i,
input logic rst_ni,
input logic clk_usb_48mhz_i, // use usb_ prefix for signals in this clk
input logic rst_usb_48mhz_ni,
// Configuration (quasi-static)
input logic rx_differential_mode_i,
input logic tx_differential_mode_i,
// Register interface (system clk)
input usbdev_reg2hw_phy_config_reg_t sys_reg2hw_config_i,
output logic sys_usb_sense_o,
// External USB Interface(s) (async)
input logic cio_usb_d_i,
input logic cio_usb_dp_i,
input logic cio_usb_dn_i,
output logic cio_usb_d_o,
output logic cio_usb_se0_o,
output logic cio_usb_dp_o,
output logic cio_usb_dn_o,
output logic cio_usb_oe_o,
output logic cio_usb_tx_mode_se_o,
input logic cio_usb_sense_i,
output logic cio_usb_pullup_en_o,
output logic cio_usb_suspend_o,
// Internal USB Interface (usb clk)
output logic usb_rx_d_o,
output logic usb_rx_se0_o,
input logic usb_tx_d_i,
input logic usb_tx_se0_i,
input logic usb_tx_oe_i,
output logic usb_pwr_sense_o,
input logic usb_pullup_en_i,
input logic usb_suspend_i
);
logic async_pwr_sense;
logic sys_usb_sense;
logic usb_rx_d;
logic usb_rx_dp;
logic usb_rx_dn;
//////////
// CDCs //
//////////
// USB pins sense (to sysclk)
prim_flop_2sync #(
.Width (1)
) cdc_io_to_sys (
.clk_i (clk_i),
.rst_ni (rst_ni),
.d ({cio_usb_sense_i}),
.q ({sys_usb_sense})
);
assign sys_usb_sense_o = sys_usb_sense;
// USB input pins (to usbclk)
prim_flop_2sync #(
.Width (4)
) cdc_io_to_usb (
.clk_i (clk_usb_48mhz_i),
.rst_ni (rst_usb_48mhz_ni),
.d ({cio_usb_dp_i,
cio_usb_dn_i,
cio_usb_d_i,
async_pwr_sense}),
.q ({usb_rx_dp,
usb_rx_dn,
usb_rx_d,
usb_pwr_sense_o})
);
///////////////////////////////
// USB output pins drive mux //
///////////////////////////////
always_comb begin : proc_drive_out
// Defaults
cio_usb_dn_o = 1'b0;
cio_usb_dp_o = 1'b0;
// Signals from the peripheral core
cio_usb_pullup_en_o = usb_pullup_en_i;
cio_usb_suspend_o = usb_suspend_i;
if (tx_differential_mode_i) begin
// Differential mode
cio_usb_tx_mode_se_o = 1'b0;
end else begin
// Single-ended mode
cio_usb_tx_mode_se_o = 1'b1;
if (usb_tx_se0_i) begin
cio_usb_dp_o = 1'b0;
cio_usb_dn_o = 1'b0;
end else begin
cio_usb_dp_o = usb_tx_d_i;
cio_usb_dn_o = !usb_tx_d_i;
end
end
end
// It would be possible to insert explicit controllability muxes here.
// For now, we just pass the signal through
assign cio_usb_d_o = usb_tx_d_i;
assign cio_usb_se0_o = usb_tx_se0_i;
assign cio_usb_oe_o = usb_tx_oe_i;
///////////////////////
// USB input pin mux //
///////////////////////
always_comb begin : proc_mux_data_input
usb_rx_se0_o = ~usb_rx_dp & ~usb_rx_dn;
if (rx_differential_mode_i) begin
// Differential RX mode
usb_rx_d_o = usb_rx_d;
end else begin
// Single-ended RX mode
usb_rx_d_o = usb_rx_dp; // SE1 is interpreted as differential 1
end
end
// Power sense mux
always_comb begin : proc_mux_pwr_input
if (sys_reg2hw_config_i.override_pwr_sense_en.q) begin
async_pwr_sense = sys_reg2hw_config_i.override_pwr_sense_val.q;
end else begin
async_pwr_sense = cio_usb_sense_i;
end
end
endmodule