../src/lowrisc_ip_usbdev_0.1/rtl/usbdev_iomux.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Copyright ETH Zurich.
3: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
4: // SPDX-License-Identifier: Apache-2.0
5: //
6: // USB IO Mux
7: //
8: // Muxes the USB IO signals from register access, differential signaling, single-ended signaling
9: // and swaps D+/D- if configured. The incomming signals are also muxed and synchronized to the
10: // corresponding clock domain.
11:
12: module usbdev_iomux
13: import usbdev_reg_pkg::*;
14: (
15: input logic clk_i,
16: input logic rst_ni,
17: input logic clk_usb_48mhz_i, // use usb_ prefix for signals in this clk
18: input logic rst_usb_48mhz_ni,
19:
20: // Register interface (system clk, quasi-static)
21: input usbdev_reg2hw_phy_config_reg_t sys_reg2hw_config_i,
22: output logic sys_usb_sense_o,
23:
24: // External USB Interface(s) (async)
25: input logic cio_usb_d_i,
26: input logic cio_usb_dp_i,
27: input logic cio_usb_dn_i,
28:
29: output logic cio_usb_d_o,
30: output logic cio_usb_se0_o,
31: output logic cio_usb_dp_o,
32: output logic cio_usb_dn_o,
33: output logic cio_usb_oe_o,
34:
35: output logic cio_usb_tx_mode_se_o,
36: input logic cio_usb_sense_i,
37: output logic cio_usb_dp_pullup_en_o,
38: output logic cio_usb_dn_pullup_en_o,
39: output logic cio_usb_suspend_o,
40:
41: // Internal USB Interface (usb clk)
42: output logic usb_rx_d_o,
43: output logic usb_rx_se0_o,
44:
45: input logic usb_tx_d_i,
46: input logic usb_tx_se0_i,
47: input logic usb_tx_oe_i,
48:
49: output logic usb_pwr_sense_o,
50: input logic usb_pullup_en_i,
51: input logic usb_suspend_i
52: );
53:
54: logic async_pwr_sense, sys_usb_sense;
55: logic cio_usb_dp, cio_usb_dn, cio_usb_d;
56: logic usb_rx_dp, usb_rx_dn, usb_rx_d;
57: logic pinflip;
58: logic unused_eop_single_bit;
59: logic unused_usb_ref_disable;
60:
61: assign unused_eop_single_bit = sys_reg2hw_config_i.eop_single_bit.q;
62: assign unused_usb_ref_disable = sys_reg2hw_config_i.usb_ref_disable.q;
63:
64: //////////
65: // CDCs //
66: //////////
67:
68: // USB sense pin (to sysclk)
69: prim_flop_2sync #(
70: .Width (1)
71: ) cdc_io_to_sys (
72: .clk_i (clk_i),
73: .rst_ni (rst_ni),
74: .d ({cio_usb_sense_i}),
75: .q ({sys_usb_sense})
76: );
77:
78: assign sys_usb_sense_o = sys_usb_sense;
79:
80: // USB input pins (to usbclk)
81: prim_flop_2sync #(
82: .Width (4)
83: ) cdc_io_to_usb (
84: .clk_i (clk_usb_48mhz_i),
85: .rst_ni (rst_usb_48mhz_ni),
86: .d ({cio_usb_dp_i,
87: cio_usb_dn_i,
88: cio_usb_d_i,
89: async_pwr_sense}),
90: .q ({cio_usb_dp,
91: cio_usb_dn,
92: cio_usb_d,
93: usb_pwr_sense_o})
94: );
95:
96: ////////////////////////
97: // USB output pin mux //
98: ////////////////////////
99:
100: // D+/D- can be swapped based on a config register.
101: assign pinflip = sys_reg2hw_config_i.pinflip.q;
102:
103: assign cio_usb_d_o = pinflip ? ~usb_tx_d_i : usb_tx_d_i;
104: assign cio_usb_dp_pullup_en_o = pinflip ? 1'b0 : usb_pullup_en_i;
105: assign cio_usb_dn_pullup_en_o = pinflip ? usb_pullup_en_i : 1'b0;
106:
107: always_comb begin : proc_diff_se_mux_out
108: // Defaults
109: cio_usb_dn_o = 1'b0;
110: cio_usb_dp_o = 1'b0;
111:
112: // The single-ended signals are only driven in single-ended mode.
113: if (sys_reg2hw_config_i.tx_differential_mode.q) begin
114: // Differential TX mode
115: cio_usb_tx_mode_se_o = 1'b0;
116:
117: end else begin
118: // Single-ended TX mode
119: cio_usb_tx_mode_se_o = 1'b1;
120: if (usb_tx_se0_i) begin
121: cio_usb_dp_o = 1'b0;
122: cio_usb_dn_o = 1'b0;
123: end else begin
124: cio_usb_dp_o = pinflip ? ~usb_tx_d_i : usb_tx_d_i;
125: cio_usb_dn_o = pinflip ? usb_tx_d_i : ~usb_tx_d_i;
126: end
127: end
128: end
129:
130: // It would be possible to insert explicit controllability muxes here.
131: // For now, we just pass the signal through
132: assign cio_usb_oe_o = usb_tx_oe_i;
133: assign cio_usb_se0_o = usb_tx_se0_i;
134: assign cio_usb_suspend_o = usb_suspend_i;
135:
136: ///////////////////////
137: // USB input pin mux //
138: ///////////////////////
139:
140: // Note that while transmitting, we fix the receive line to 1. If the receive line isn't fixed,
141: // we are trying to regenerate the bit clock from the bit clock we are regenerating, rather than
142: // just holding the phase.
143: // D+/D- can be swapped based on a config register.
144: assign usb_rx_dp = usb_tx_oe_i ? 1'b1 : (pinflip ? cio_usb_dn : cio_usb_dp);
145: assign usb_rx_dn = usb_tx_oe_i ? 1'b0 : (pinflip ? cio_usb_dp : cio_usb_dn);
146: assign usb_rx_d = usb_tx_oe_i ? 1'b1 : (pinflip ? ~cio_usb_d : cio_usb_d);
147:
148: always_comb begin : proc_diff_se_mux_in
149: usb_rx_se0_o = ~usb_rx_dp & ~usb_rx_dn;
150:
151: if (sys_reg2hw_config_i.rx_differential_mode.q) begin
152: // Differential RX mode
153: usb_rx_d_o = usb_rx_d;
154:
155: end else begin
156: // Single-ended RX mode
157: usb_rx_d_o = usb_rx_dp; // SE1 is interpreted as differential 1
158: end
159: end
160:
161: // Power sense mux
162: always_comb begin : proc_mux_pwr_sense
163: if (sys_reg2hw_config_i.override_pwr_sense_en.q) begin
164: async_pwr_sense = sys_reg2hw_config_i.override_pwr_sense_val.q;
165: end else begin
166: async_pwr_sense = cio_usb_sense_i;
167: end
168: end
169:
170: endmodule
171: