blob: fd1baa67a54ec02d64a0b3122e1ffdd7297dc681 [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
// and swaps D+/D- if configured. 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,
// Register interface (system clk, quasi-static)
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_dp_pullup_en_o,
output logic cio_usb_dn_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, sys_usb_sense;
logic cio_usb_dp, cio_usb_dn, cio_usb_d;
logic usb_rx_dp, usb_rx_dn, usb_rx_d;
logic pinflip;
logic unused_eop_single_bit;
logic unused_usb_ref_disable;
assign unused_eop_single_bit = sys_reg2hw_config_i.eop_single_bit.q;
assign unused_usb_ref_disable = sys_reg2hw_config_i.usb_ref_disable.q;
//////////
// CDCs //
//////////
// USB sense pin (to sysclk)
prim_flop_2sync #(
.Width (1)
) cdc_io_to_sys (
.clk_i (clk_i),
.rst_ni (rst_ni),
.d_i ({cio_usb_sense_i}),
.q_o ({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_i ({cio_usb_dp_i,
cio_usb_dn_i,
cio_usb_d_i,
async_pwr_sense}),
.q_o ({cio_usb_dp,
cio_usb_dn,
cio_usb_d,
usb_pwr_sense_o})
);
////////////////////////
// USB output pin mux //
////////////////////////
// D+/D- can be swapped based on a config register.
assign pinflip = sys_reg2hw_config_i.pinflip.q;
assign cio_usb_d_o = pinflip ? ~usb_tx_d_i : usb_tx_d_i;
assign cio_usb_dp_pullup_en_o = pinflip ? 1'b0 : usb_pullup_en_i;
assign cio_usb_dn_pullup_en_o = pinflip ? usb_pullup_en_i : 1'b0;
always_comb begin : proc_diff_se_mux_out
// Defaults
cio_usb_dn_o = 1'b0;
cio_usb_dp_o = 1'b0;
// The single-ended signals are only driven in single-ended mode.
if (sys_reg2hw_config_i.tx_differential_mode.q) begin
// Differential TX mode
cio_usb_tx_mode_se_o = 1'b0;
end else begin
// Single-ended TX 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 = pinflip ? ~usb_tx_d_i : usb_tx_d_i;
cio_usb_dn_o = pinflip ? usb_tx_d_i : ~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_oe_o = usb_tx_oe_i;
assign cio_usb_se0_o = usb_tx_se0_i;
assign cio_usb_suspend_o = usb_suspend_i;
///////////////////////
// USB input pin mux //
///////////////////////
// Note that while transmitting, we fix the receive line to 1. If the receive line isn't fixed,
// we are trying to regenerate the bit clock from the bit clock we are regenerating, rather than
// just holding the phase.
// D+/D- can be swapped based on a config register.
assign usb_rx_dp = usb_tx_oe_i ? 1'b1 : (pinflip ? cio_usb_dn : cio_usb_dp);
assign usb_rx_dn = usb_tx_oe_i ? 1'b0 : (pinflip ? cio_usb_dp : cio_usb_dn);
assign usb_rx_d = usb_tx_oe_i ? 1'b1 : (pinflip ? ~cio_usb_d : cio_usb_d);
always_comb begin : proc_diff_se_mux_in
usb_rx_se0_o = ~usb_rx_dp & ~usb_rx_dn;
if (sys_reg2hw_config_i.rx_differential_mode.q) 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_sense
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