../src/lowrisc_ip_usb_fs_nb_pe_0.1/rtl/usb_fs_nb_out_pe.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Copyright Luke Valenty (TinyFPGA project)
3: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
4: // SPDX-License-Identifier: Apache-2.0
5:
6: // USB Full Speed Non-Buffered Protocol Engine for OUT/SETUP endpoints
7: //
8: // Decode OUT/SETUP requests from host and accept data unless buffers are full
9: // (SETUP is a special form of OUT and starts a transaction sequence)
10: //
11: // Based on usb_fs_out_pe.v from the TinyFPGA-Bootloader project but
12: // this version contains no packet buffers
13:
14: `include "prim_assert.sv"
15:
16: module usb_fs_nb_out_pe #(
17: parameter logic [4:0] NumOutEps = 2,
18: parameter int unsigned MaxOutPktSizeByte = 32,
19: localparam int unsigned OutEpW = $clog2(NumOutEps), // derived parameter
20: localparam int unsigned PktW = $clog2(MaxOutPktSizeByte) // derived parameter
21: ) (
22: input logic clk_48mhz_i,
23: input logic rst_ni,
24: input logic link_reset_i,
25: input logic [6:0] dev_addr_i,
26:
27: ////////////////////////
28: // endpoint interface //
29: ////////////////////////
30: output logic [3:0] out_ep_current_o, // Other signals address to this ep,
31: // stable for several cycles
32: output logic out_ep_data_put_o, // put the data (put addr advances after)
33: output logic [PktW - 1:0] out_ep_put_addr_o, // Offset to put data (0..pktlen)
34: output logic [7:0] out_ep_data_o,
35: output logic out_ep_newpkt_o, // new packed, current was set
36: output logic out_ep_acked_o, // good termination, device has acked
37: output logic out_ep_rollback_o, // bad termination, discard data
38: output logic [NumOutEps-1:0] out_ep_setup_o,
39: input logic [NumOutEps-1:0] out_ep_full_i, // Cannot accept data
40: input logic [NumOutEps-1:0] out_ep_stall_i, // Stalled
41: input logic [NumOutEps-1:0] out_ep_iso_i, // Configure endpoint in isochronous mode
42:
43: input logic [NumOutEps-1:0] data_toggle_clear_i, // Clear the data toggles for an EP
44:
45: /////////////
46: // rx path //
47: /////////////
48:
49: // Strobed on reception of packet.
50: input logic rx_pkt_start_i,
51: input logic rx_pkt_end_i,
52: input logic rx_pkt_valid_i,
53:
54: // Most recent packet received.
55: input logic [3:0] rx_pid_i,
56: input logic [6:0] rx_addr_i,
57: input logic [3:0] rx_endp_i,
58:
59: // rx_data is pushed into endpoint controller.
60: input logic rx_data_put_i,
61: input logic [7:0] rx_data_i,
62:
63:
64: /////////////
65: // tx path //
66: /////////////
67:
68: // Strobe to send new packet.
69: output logic tx_pkt_start_o,
70: input logic tx_pkt_end_i,
71: output logic [3:0] tx_pid_o
72: );
73:
74: // suppress warnings
75: logic unused_1;
76: assign unused_1 = tx_pkt_end_i;
77:
78: ////////////////////////////////
79: // out transfer state machine //
80: ////////////////////////////////
81: import usb_consts_pkg::*;
82:
83: typedef enum logic [2:0] {
84: StIdle,
85: StRcvdOut,
86: StRcvdDataStart,
87: StRcvdDataEnd,
88: StRcvdIsoDataEnd
89: } state_out_e;
90:
91: state_out_e out_xfr_state;
92: state_out_e out_xfr_state_next;
93:
94: logic out_xfr_start;
95: logic new_pkt_end;
96: logic rollback_data;
97:
98: // set when the endpoint buffer is unable to receive the out transfer
99: logic nak_out_transfer;
100:
101: // data toggle state
102: logic [NumOutEps - 1:0] data_toggle_q, data_toggle_d;
103:
104: // Decode the rx token
105: logic token_received, out_token_received, setup_token_received;
106: logic invalid_packet_received, data_packet_received, non_data_packet_received;
107: logic bad_data_toggle;
108: logic ep_impl;
109: logic [3:0] out_ep_current_d;
110:
111: // 1: If the current transfer is a SETUP, 0: OUT
112: logic current_xfer_setup_q;
113:
114: // More syntax so can compare with enum
115: usb_pid_type_e rx_pid_type;
116: usb_pid_e rx_pid;
117: assign rx_pid_type = usb_pid_type_e'(rx_pid_i[1:0]);
118: assign rx_pid = usb_pid_e'(rx_pid_i);
119:
120: // Is the specified endpoint actually implemented?
121: assign ep_impl = {1'b0, rx_endp_i} < NumOutEps;
122:
123: // If the specified endpoint is not implemented, we don't process the token and just ignore it.
124: assign token_received =
125: rx_pkt_end_i &&
126: rx_pkt_valid_i &&
127: rx_pid_type == UsbPidTypeToken &&
128: rx_addr_i == dev_addr_i &&
129: ep_impl;
130:
131: assign out_token_received =
132: token_received &&
133: rx_pid == UsbPidOut;
134:
135: assign setup_token_received =
136: token_received &&
137: rx_pid == UsbPidSetup;
138:
139: assign invalid_packet_received =
140: rx_pkt_end_i &&
141: !rx_pkt_valid_i;
142:
143: assign data_packet_received =
144: rx_pkt_end_i &&
145: rx_pkt_valid_i &&
146: ((rx_pid == UsbPidData0) || (rx_pid == UsbPidData1));
147:
148: assign non_data_packet_received =
149: rx_pkt_end_i &&
150: rx_pkt_valid_i &&
151: !((rx_pid == UsbPidData0) || (rx_pid == UsbPidData1));
152:
153: assign out_ep_current_d = ep_impl ? rx_endp_i : '0;
154:
155: // Make widths work - out_ep_current_d/out_ep_current_o only hold implemented endpoint IDs.
156: // These signals can be used to index signals of NumOutEps width.
157: logic [OutEpW-1:0] out_ep_index;
158: logic [OutEpW-1:0] out_ep_index_d;
159: assign out_ep_index = out_ep_current_o[0 +: OutEpW];
160: assign out_ep_index_d = out_ep_current_d[0 +: OutEpW];
161:
162: assign bad_data_toggle =
163: data_packet_received &&
164: rx_pid_i[3] != data_toggle_q[out_ep_index_d];
165:
166: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
167: if (!rst_ni) begin
168: out_ep_setup_o <= '0;
169: end else begin
170: if (setup_token_received) begin
171: out_ep_setup_o[out_ep_index_d] <= 1'b1;
172: end else if (out_token_received) begin
173: out_ep_setup_o[out_ep_index_d] <= 1'b0;
174: end
175: end
176: end
177:
178: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
179: if (!rst_ni) begin
180: out_ep_data_o <= 0;
181: end else begin
182: if (rx_data_put_i) begin
183: out_ep_data_o <= rx_data_i;
184: end
185: end
186: end
187:
188: ////////////////////////////////
189: // out transfer state machine //
190: ////////////////////////////////
191:
192: always_comb begin
193: out_ep_acked_o = 1'b0;
194: out_xfr_start = 1'b0;
195: out_xfr_state_next = out_xfr_state;
196: tx_pkt_start_o = 1'b0;
197: tx_pid_o = 4'b0000;
198: new_pkt_end = 1'b0;
199: rollback_data = 1'b0;
200:
201: unique case (out_xfr_state)
202: StIdle: begin
203: if (out_token_received || setup_token_received) begin
204: out_xfr_state_next = StRcvdOut;
205: out_xfr_start = 1'b1;
206: end else begin
207: out_xfr_state_next = StIdle;
208: end
209: end
210:
211: StRcvdOut: begin
212: if (rx_pkt_start_i) begin
213: out_xfr_state_next = StRcvdDataStart;
214: end else begin
215: out_xfr_state_next = StRcvdOut;
216: end
217: end
218:
219: StRcvdDataStart: begin
220: if (out_ep_iso_i[out_ep_index] && data_packet_received) begin
221: // ISO endpoint: Don't send a handshake, ignore toggle
222: out_xfr_state_next = StRcvdIsoDataEnd;
223: end else if (bad_data_toggle) begin
224: out_xfr_state_next = StIdle;
225: rollback_data = 1'b1;
226: tx_pkt_start_o = 1'b1;
227: tx_pid_o = {UsbPidAck}; // ACK by spec because this is most likely previous ACK was lost
228: end else if (invalid_packet_received || non_data_packet_received) begin
229: // in these cases eg bad CRC, send no response (not a NAK)
230: out_xfr_state_next = StIdle;
231: rollback_data = 1'b1;
232: end else if (data_packet_received) begin
233: out_xfr_state_next = StRcvdDataEnd;
234: end else begin
235: out_xfr_state_next = StRcvdDataStart;
236: end
237: end
238:
239: StRcvdDataEnd: begin
240: out_xfr_state_next = StIdle;
241: tx_pkt_start_o = 1'b1;
242:
243: if (out_ep_stall_i[out_ep_index] && !current_xfer_setup_q) begin
244: // We only send STALL for OUT transfers, not for SETUP transfers
245: tx_pid_o = {UsbPidStall}; // STALL
246: end else if (nak_out_transfer) begin
247: tx_pid_o = {UsbPidNak}; // NAK -- the endpoint could not accept the data at the moment
248: rollback_data = 1'b1;
249: end else begin
250: tx_pid_o = {UsbPidAck}; // ACK
251: new_pkt_end = 1'b1;
252: out_ep_acked_o = 1'b1;
253: end
254: end
255:
256: StRcvdIsoDataEnd: begin
257: out_xfr_state_next = StIdle;
258:
259: if (out_ep_stall_i[out_ep_index] && !current_xfer_setup_q) begin
260: // Send a STALL (something bad happened and the host needs to resolve it)
261: tx_pkt_start_o = 1'b1;
262: tx_pid_o = {UsbPidStall}; // STALL
263: end else if (nak_out_transfer) begin
264: // We got a valid packet, but can't store it (error that the software must resolve)
265: rollback_data = 1'b1;
266: end else begin
267: // We got a valid packet, but we don't send an ACK on the bus
268: new_pkt_end = 1'b1;
269: out_ep_acked_o = 1'b1;
270: end
271:
272: end
273:
274: default: out_xfr_state_next = StIdle;
275: endcase
276: end
277:
278: // could flop this if needed
279: assign out_ep_rollback_o = rollback_data;
280:
281: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
282: if (!rst_ni) begin
283: out_xfr_state <= StIdle;
284: end else begin
285: out_xfr_state <= link_reset_i ? StIdle : out_xfr_state_next;
286: end
287: end
288:
289: always_comb begin : proc_data_toggle_d
290: data_toggle_d = data_toggle_q;
291:
292: if (setup_token_received) begin
293: data_toggle_d[out_ep_index_d] = 1'b0;
294: end else if (new_pkt_end) begin
295: data_toggle_d[out_ep_index] = ~data_toggle_q[out_ep_index];
296: end
297:
298: data_toggle_d = data_toggle_d & ~data_toggle_clear_i;
299: end
300:
301: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
302: if (!rst_ni) begin
303: data_toggle_q <= '0; // All endpoints
304: end else if (link_reset_i) begin
305: data_toggle_q <= '0; // All endpoints
306: end else begin
307: data_toggle_q <= data_toggle_d;
308: end
309: end
310:
311: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
312: if (!rst_ni) begin
313: out_ep_newpkt_o <= 1'b0;
314: out_ep_current_o <= '0;
315: current_xfer_setup_q <= 1'b0;
316: end else begin
317: if (out_xfr_start) begin // ep_impl factored in already
318: out_ep_newpkt_o <= 1'b1;
319: out_ep_current_o <= out_ep_current_d;
320: current_xfer_setup_q <= setup_token_received;
321: end else begin
322: out_ep_newpkt_o <= 1'b0;
323: end
324: end
325: end
326:
327: // put data strobe follows the rx strobe (which will latch the data)
328: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
329: if (!rst_ni) begin
330: out_ep_data_put_o <= 1'b0;
331: end else begin
332: out_ep_data_put_o <= ((out_xfr_state == StRcvdDataStart) && rx_data_put_i);
333: end
334: end
335:
336: // nack an OUT if any data comes in with the endpoint full
337: // Note that if there is a full size packet buffer this will only be all or nothing
338: // but in the case there was a FIFO with less than a max packet size free you
339: // could get lucky and the packet received be small and fit with no need to NAK
340: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
341: if (!rst_ni) begin
342: nak_out_transfer <= 1'b0;
343: end else begin
344: if ((out_xfr_state == StIdle) || (out_xfr_state == StRcvdOut)) begin
345: nak_out_transfer <= 1'b0;
346: end else if (out_ep_data_put_o && out_ep_full_i[out_ep_index]) begin
347: nak_out_transfer <= 1'b1;
348: end
349: end
350: end
351:
352: // address increment whenever there is a data put unless
353: // -- already going to NAK transaction and replay things
354: // -- the address is at max packet size
355: // NOTE if more than max packet size received then data is lost
356: logic increment_addr;
357: assign increment_addr = !nak_out_transfer && (~&out_ep_put_addr_o) && out_ep_data_put_o;
358:
359: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
360: if (!rst_ni) begin
361: out_ep_put_addr_o <= '0;
362: end else begin
363: if (out_xfr_state == StRcvdOut) begin
364: out_ep_put_addr_o <= '0;
365: end else if ((out_xfr_state == StRcvdDataStart) && increment_addr) begin
366: out_ep_put_addr_o <= out_ep_put_addr_o + 1;
367: end
368: end
369: end
370:
371: ////////////////
372: // Assertions //
373: ////////////////
374:
375: // We receive a token for an endpoint that is not implemented.
376: `ASSERT(UsbOutEndPImpl,
377: (rx_pkt_end_i &&
378: rx_pkt_valid_i &&
379: rx_pid_type == UsbPidTypeToken &&
380: rx_addr_i == dev_addr_i) |-> ep_impl, clk_48mhz_i)
381:
382: endmodule
383: