|  | // 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) | 
|  | output usbdev_hw2reg_phy_pins_sense_reg_t sys_hw2reg_sense_o, | 
|  | input  usbdev_reg2hw_phy_pins_drive_reg_t sys_reg2hw_drive_i, | 
|  | 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_dp_o, | 
|  | output logic                          usb_rx_dn_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 cio_usb_d_flipped; | 
|  | logic cio_usb_dp_pullup_en, cio_usb_dn_pullup_en; | 
|  |  | 
|  | logic sys_usb_sense; | 
|  | logic cio_usb_dp, cio_usb_dn, cio_usb_d; | 
|  | logic pinflip; | 
|  | logic unused_eop_single_bit; | 
|  | logic unused_rx_differential_mode; | 
|  | logic unused_usb_ref_disable; | 
|  | logic unused_tx_osc_test_mode; | 
|  |  | 
|  | 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; | 
|  | assign unused_tx_osc_test_mode     = sys_reg2hw_config_i.tx_osc_test_mode.q; | 
|  | assign unused_rx_differential_mode = sys_reg2hw_config_i.rx_differential_mode.q; | 
|  |  | 
|  | ////////// | 
|  | // CDCs // | 
|  | ////////// | 
|  |  | 
|  | // USB pins sense (to sysclk) | 
|  | prim_flop_2sync #( | 
|  | .Width (10) | 
|  | ) cdc_io_to_sys ( | 
|  | .clk_i  (clk_i), | 
|  | .rst_ni (rst_ni), | 
|  | .d_i    ({cio_usb_dp_i, | 
|  | cio_usb_dn_i, | 
|  | cio_usb_d_i, | 
|  | cio_usb_dp_o, | 
|  | cio_usb_dn_o, | 
|  | usb_tx_d_i, | 
|  | usb_tx_se0_i, | 
|  | usb_tx_oe_i, | 
|  | usb_suspend_i, | 
|  | cio_usb_sense_i}), | 
|  | .q_o   ({sys_hw2reg_sense_o.rx_dp_i.d, | 
|  | sys_hw2reg_sense_o.rx_dn_i.d, | 
|  | sys_hw2reg_sense_o.rx_d_i.d, | 
|  | sys_hw2reg_sense_o.tx_dp_o.d, | 
|  | sys_hw2reg_sense_o.tx_dn_o.d, | 
|  | sys_hw2reg_sense_o.tx_d_o.d, | 
|  | sys_hw2reg_sense_o.tx_se0_o.d, | 
|  | sys_hw2reg_sense_o.tx_oe_o.d, | 
|  | sys_hw2reg_sense_o.suspend_o.d, | 
|  | sys_usb_sense}) | 
|  | ); | 
|  |  | 
|  | assign sys_usb_sense_o                = sys_usb_sense; | 
|  | assign sys_hw2reg_sense_o.pwr_sense.d = 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, | 
|  | cio_usb_sense_i}), | 
|  | .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; | 
|  |  | 
|  | prim_clock_mux2 #( | 
|  | .NoFpgaBufG(1) | 
|  | ) i_mux_tx_d_flip ( | 
|  | .clk0_i (usb_tx_d_i), | 
|  | .clk1_i (~usb_tx_d_i), | 
|  | .sel_i  (pinflip), | 
|  | .clk_o  (cio_usb_d_flipped) | 
|  | ); | 
|  | prim_clock_mux2 #( | 
|  | .NoFpgaBufG(1) | 
|  | ) i_mux_dp_pull_flip ( | 
|  | .clk0_i (usb_pullup_en_i), | 
|  | .clk1_i (1'b0), | 
|  | .sel_i  (pinflip), | 
|  | .clk_o  (cio_usb_dp_pullup_en) | 
|  | ); | 
|  | prim_clock_mux2 #( | 
|  | .NoFpgaBufG(1) | 
|  | ) i_mux_dn_pull_flip ( | 
|  | .clk0_i (1'b0), | 
|  | .clk1_i (usb_pullup_en_i), | 
|  | .sel_i  (pinflip), | 
|  | .clk_o  (cio_usb_dn_pullup_en) | 
|  | ); | 
|  |  | 
|  | always_comb begin : proc_drive_out | 
|  | // Defaults | 
|  | cio_usb_dn_o           = 1'b0; | 
|  | cio_usb_dp_o           = 1'b0; | 
|  |  | 
|  | if (sys_reg2hw_drive_i.en.q) begin | 
|  | // Override from registers | 
|  | cio_usb_dp_o           = sys_reg2hw_drive_i.dp_o.q; | 
|  | cio_usb_dn_o           = sys_reg2hw_drive_i.dn_o.q; | 
|  | cio_usb_dp_pullup_en_o = sys_reg2hw_drive_i.dp_pullup_en_o.q; | 
|  | cio_usb_dn_pullup_en_o = sys_reg2hw_drive_i.dn_pullup_en_o.q; | 
|  | cio_usb_tx_mode_se_o   = sys_reg2hw_drive_i.tx_mode_se_o.q; | 
|  | cio_usb_suspend_o      = sys_reg2hw_drive_i.suspend_o.q; | 
|  |  | 
|  | end else begin | 
|  | // Signals from the peripheral core | 
|  | cio_usb_dp_pullup_en_o = cio_usb_dp_pullup_en; | 
|  | cio_usb_dn_pullup_en_o = cio_usb_dn_pullup_en; | 
|  | cio_usb_suspend_o      = usb_suspend_i; | 
|  |  | 
|  | if(sys_reg2hw_config_i.tx_differential_mode.q) begin | 
|  | // Differential TX mode (physical IO takes d and se0) | 
|  | // i.e. expect the "else" logic to be in the physical interface | 
|  | cio_usb_tx_mode_se_o   = 1'b0; | 
|  |  | 
|  | end else begin | 
|  | // Single-ended mode (physical IO takes dp and dn) | 
|  | 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 = cio_usb_d_flipped; | 
|  | cio_usb_dn_o = ~cio_usb_d_flipped; | 
|  | end | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // Use explicit muxes for the critical output signals, we do this | 
|  | // to avoid glitches from synthesized logic on these signals. | 
|  | // Clock muxes should be used here to achieve the best match between | 
|  | // rising and falling edges on an ASIC. This mismatch on the data line | 
|  | // degrades performance in the JK-KJ jitter test. | 
|  | prim_clock_mux2 #( | 
|  | .NoFpgaBufG(1) | 
|  | ) i_mux_tx_d ( | 
|  | .clk0_i (cio_usb_d_flipped), | 
|  | .clk1_i (sys_reg2hw_drive_i.d_o.q), | 
|  | .sel_i  (sys_reg2hw_drive_i.en.q), | 
|  | .clk_o  (cio_usb_d_o) | 
|  | ); | 
|  | prim_clock_mux2 #( | 
|  | .NoFpgaBufG(1) | 
|  | ) i_mux_tx_se0 ( | 
|  | .clk0_i (usb_tx_se0_i), | 
|  | .clk1_i (sys_reg2hw_drive_i.se0_o.q), | 
|  | .sel_i  (sys_reg2hw_drive_i.en.q), | 
|  | .clk_o  (cio_usb_se0_o) | 
|  | ); | 
|  | prim_clock_mux2 #( | 
|  | .NoFpgaBufG(1) | 
|  | ) i_mux_tx_oe ( | 
|  | .clk0_i (usb_tx_oe_i), | 
|  | .clk1_i (sys_reg2hw_drive_i.oe_o.q), | 
|  | .sel_i  (sys_reg2hw_drive_i.en.q), | 
|  | .clk_o  (cio_usb_oe_o) | 
|  | ); | 
|  |  | 
|  | /////////////////////// | 
|  | // USB input pin mux // | 
|  | /////////////////////// | 
|  |  | 
|  | // D+/D- can be swapped based on a config register. | 
|  | assign usb_rx_dp_o = pinflip ?  cio_usb_dn : cio_usb_dp; | 
|  | assign usb_rx_dn_o = pinflip ?  cio_usb_dp : cio_usb_dn; | 
|  | assign usb_rx_d_o  = pinflip ? ~cio_usb_d  : cio_usb_d; | 
|  |  | 
|  | endmodule |