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