hw/ip/usbdev/rtl/usbdev_linkstate.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3: // SPDX-License-Identifier: Apache-2.0
4: //
5: // Link state detection
6: //
7:
8: module usbdev_linkstate (
9: input logic clk_48mhz_i,
10: input logic rst_ni,
11: input logic us_tick_i,
12: input logic usb_sense_i,
13: input logic usb_rx_d_i,
14: input logic usb_rx_se0_i,
15: input logic sof_valid_i,
16: output logic link_disconnect_o, // level
17: output logic link_connect_o, // level
18: output logic link_reset_o, // level
19: output logic link_suspend_o, // level
20: output logic link_resume_o, // pulse
21: output logic host_lost_o, // level
22:
23: output logic [2:0] link_state_o
24: );
25:
26: localparam logic [11:0] SUSPEND_TIMEOUT = 12'd3000; // 3ms by spec
27: localparam logic [2:0] RESET_TIMEOUT = 3'd3; // 3us. Can be 2.5us - 10ms by spec
28:
29: typedef enum logic [2:0] {
30: // Unpowered state
31: LinkDisconnect = 0,
32: // Powered states
33: LinkPowered = 1,
34: LinkPoweredSuspend = 2,
35: // Active states
36: LinkActive = 3,
37: LinkSuspend = 4
38: } link_state_e;
39:
40: typedef enum logic [1:0] {
41: NoRst,
42: RstCnt,
43: RstPend
44: } link_rst_state_e;
45:
46: typedef enum logic [1:0] {
47: Active,
48: InactCnt,
49: InactPend
50: } link_inac_state_e;
51:
52: link_state_e link_state_d, link_state_q;
53: logic link_active;
54: logic line_se0_raw, line_idle_raw;
55: logic see_se0, see_idle, see_pwr_sense;
56:
57: // Reset FSM
58: logic [2:0] link_rst_timer_d, link_rst_timer_q;
59: link_rst_state_e link_rst_state_d, link_rst_state_q;
60: logic link_reset; // reset detected (level)
61:
62: // Link inactivity detection
63: logic monitor_inac; // monitor link inactivity
64: logic [11:0] link_inac_timer_d, link_inac_timer_q;
65: link_inac_state_e link_inac_state_d, link_inac_state_q;
66:
67:
68: // Events that are not triggered by a timeout
69: logic ev_bus_active;
70:
71: // Events that are triggered by timeout
72: logic ev_bus_inactive, ev_reset;
73:
74: assign link_disconnect_o = (link_state_q == LinkDisconnect);
75: assign link_connect_o = (link_state_q != LinkDisconnect);
76: assign link_suspend_o = (link_state_q == LinkSuspend ||
77: link_state_q == LinkPoweredSuspend);
78: assign link_active = (link_state_q == LinkActive);
79: // Link state is stable, so we can output it to the register
80: assign link_state_o = link_state_q;
81:
82: assign line_se0_raw = usb_rx_se0_i;
83: assign line_idle_raw = usb_rx_d_i && !usb_rx_se0_i; // same as J
84:
85: // four ticks is a bit time
86: // Could completely filter out 2-cycle EOP SE0 here but
87: // does not seem needed
88: prim_filter #(.Cycles(6)) filter_se0 (
89: .clk_i (clk_48mhz_i),
90: .rst_ni (rst_ni),
91: .enable_i (1'b1),
92: .filter_i (line_se0_raw),
93: .filter_o (see_se0)
94: );
95:
96: prim_filter #(.Cycles(6)) filter_idle (
97: .clk_i (clk_48mhz_i),
98: .rst_ni (rst_ni),
99: .enable_i (1'b1),
100: .filter_i (line_idle_raw),
101: .filter_o (see_idle)
102: );
103:
104: prim_filter #(.Cycles(6)) filter_pwr_sense (
105: .clk_i (clk_48mhz_i),
106: .rst_ni (rst_ni),
107: .enable_i (1'b1),
108: .filter_i (usb_sense_i),
109: .filter_o (see_pwr_sense)
110: );
111:
112: // Simple events
113: assign ev_bus_active = !see_idle;
114:
115: always_comb begin
116: link_state_d = link_state_q;
117: link_resume_o = 0;
118: monitor_inac = 0;
119:
120: // If VBUS ever goes away the link has disconnected
121: if (!see_pwr_sense) begin
122: link_state_d = LinkDisconnect;
123: end else begin
124: unique case (link_state_q)
125: // No USB supply detected (USB spec: Attached)
126: LinkDisconnect: begin
127: if (see_pwr_sense) begin
128: link_state_d = LinkPowered;
129: end
130: end
131:
132: LinkPowered: begin
133: monitor_inac = 1;
134: if (ev_reset) begin
135: link_state_d = LinkActive;
136: end else if (ev_bus_inactive) begin
137: link_state_d = LinkPoweredSuspend;
138: end
139: end
140:
141: LinkPoweredSuspend: begin
142: if (ev_reset) begin
143: link_state_d = LinkActive;
144: end else if (ev_bus_active) begin
145: link_resume_o = 1;
146: link_state_d = LinkPowered;
147: end
148: end
149:
150: // Active (USB spec: Default / Address / Configured)
151: LinkActive: begin
152: monitor_inac = 1;
153: if (ev_bus_inactive) begin
154: link_state_d = LinkSuspend;
155: end
156: end
157:
158: LinkSuspend: begin
159: if (ev_reset || ev_bus_active) begin
160: link_resume_o = 1;
161: link_state_d = LinkActive;
162: end
163: end
164:
165: default: begin
166: link_state_d = LinkDisconnect;
167: end
168: endcase // case (link_state_q)
169: end
170: end
171:
172: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
173: if (!rst_ni) begin
174: link_state_q <= LinkDisconnect;
175: end else begin
176: link_state_q <= link_state_d;
177: end
178: end
179:
180: /////////////////////
181: // Reset detection //
182: /////////////////////
183: // Here we clean up the SE0 signal and generate a signle ev_reset at
184: // the end of a valid reset
185:
186: always_comb begin : proc_rst_fsm
187: link_rst_state_d = link_rst_state_q;
188: link_rst_timer_d = link_rst_timer_q;
189: ev_reset = 1'b0;
190: link_reset = 1'b0;
191:
192: unique case (link_rst_state_q)
193: // No reset signal detected
194: NoRst: begin
195: if (see_se0) begin
196: link_rst_state_d = RstCnt;
197: link_rst_timer_d = 0;
198: end
199: end
200:
201: // Reset signal detected -> counting
202: RstCnt: begin
203: if (!see_se0) begin
204: link_rst_state_d = NoRst;
205: end else begin
206: if (us_tick_i) begin
207: if (link_rst_timer_q == RESET_TIMEOUT) begin
208: link_rst_state_d = RstPend;
209: end else begin
210: link_rst_timer_d = link_rst_timer_q + 1;
211: end
212: end
213: end
214: end
215:
216: // Detected reset -> wait for falling edge
217: RstPend: begin
218: if (!see_se0) begin
219: link_rst_state_d = NoRst;
220: ev_reset = 1'b1;
221: end
222: link_reset = 1'b1;
223: end
224:
225: default : link_rst_state_d = NoRst;
226: endcase
227: end
228:
229: assign link_reset_o = link_reset;
230:
231: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin : proc_reg_rst
232: if (!rst_ni) begin
233: link_rst_state_q <= NoRst;
234: link_rst_timer_q <= 0;
235: end else begin
236: link_rst_state_q <= link_rst_state_d;
237: link_rst_timer_q <= link_rst_timer_d;
238: end
239: end
240:
241: ////////////////////
242: // Idle detection //
243: ////////////////////
244: // Here we clean up the idle signal and generate a signle ev_bus_inactive
245: // after the timer expires
246: always_comb begin : proc_idle_det
247: link_inac_state_d = link_inac_state_q;
248: link_inac_timer_d = link_inac_timer_q;
249: ev_bus_inactive = 0;
250:
251: unique case (link_inac_state_q)
252: // Active or disabled
253: Active: begin
254: link_inac_timer_d = 0;
255: if (see_idle && monitor_inac) begin
256: link_inac_state_d = InactCnt;
257: end
258: end
259:
260: // Got an inactivity signal -> count duration
261: InactCnt: begin
262: if (!see_idle || !monitor_inac) begin
263: link_inac_state_d = Active;
264: end else if (us_tick_i) begin
265: if (link_inac_timer_q == SUSPEND_TIMEOUT) begin
266: link_inac_state_d = InactPend;
267: ev_bus_inactive = 1;
268: end else begin
269: link_inac_timer_d = link_inac_timer_q + 1;
270: end
271: end
272: end
273:
274: // Counter expired & event sent, wait here
275: InactPend: begin
276: if (!see_idle || !monitor_inac) begin
277: link_inac_state_d = Active;
278: end
279: end
280:
281: default : link_inac_state_d = Active;
282: endcase
283: end
284:
285: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin : proc_reg_idle_det
286: if (!rst_ni) begin
287: link_inac_state_q <= Active;
288: link_inac_timer_q <= 0;
289: end else begin
290: link_inac_state_q <= link_inac_state_d;
291: link_inac_timer_q <= link_inac_timer_d;
292: end
293: end
294:
295: /////////////////////////
296: // Host loss detection //
297: /////////////////////////
298: // host_lost if no sof in 4.096ms (supposed to be every 1ms)
299: // and the link is active
300: logic [12:0] host_presence_timer;
301:
302: assign host_lost_o = host_presence_timer[12];
303: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
304: if (!rst_ni) begin
305: host_presence_timer <= '0;
306: end else begin
307: if (sof_valid_i || !link_active || link_reset) begin
308: host_presence_timer <= '0;
309: end else if (us_tick_i && !host_lost_o) begin
310: host_presence_timer <= host_presence_timer + 1;
311: end
312: end
313: end
314:
315: endmodule
316: