../src/lowrisc_ip_usb_fs_nb_pe_0.1/rtl/usb_fs_nb_in_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 IN endpoints
7: //
8: // Decode IN requests from host and respond with data if there is any
9: //
10: // Based on usb_fs_in_pe.v from the TinyFPGA-Bootloader project but
11: // this version contains no packet buffers
12:
13: `include "prim_assert.sv"
14:
15: module usb_fs_nb_in_pe #(
16: parameter logic [4:0] NumInEps = 12,
17: parameter int unsigned MaxInPktSizeByte = 32,
18: localparam int unsigned InEpW = $clog2(NumInEps), // derived parameter
19: localparam int unsigned PktW = $clog2(MaxInPktSizeByte) // derived parameter
20: ) (
21: input logic clk_48mhz_i,
22: input logic rst_ni,
23: input logic link_reset_i,
24: input logic [6:0] dev_addr_i,
25:
26:
27: ////////////////////
28: // endpoint interface
29: ////////////////////
30: output logic [3:0] in_ep_current_o, // Other signals addressed to this ep
31: output logic in_ep_rollback_o, // Bad termination, rollback transaction
32: output logic in_ep_acked_o, // good termination, transaction complete
33: output logic [PktW - 1:0] in_ep_get_addr_o, // Offset requested (0..pktlen)
34: output logic in_ep_data_get_o, // Accept data (get_addr advances too)
35: output logic in_ep_newpkt_o, // New IN packet starting (updates in_ep_current_o)
36: input logic [NumInEps-1:0] in_ep_stall_i, // Endpoint in a stall state
37: input logic [NumInEps-1:0] in_ep_has_data_i, // Endpoint has data to supply
38: input logic [7:0] in_ep_data_i, // Data for current get_addr
39: input logic [NumInEps-1:0] in_ep_data_done_i, // Set when out of data
40: input logic [NumInEps-1:0] in_ep_iso_i, // Configure endpoint in isochronous mode
41:
42: input logic [NumInEps-1:0] data_toggle_clear_i, // Clear the data toggles for an EP
43:
44: ////////////////////
45: // rx path
46: ////////////////////
47:
48: // Strobed on reception of packet.
49: input logic rx_pkt_start_i,
50: input logic rx_pkt_end_i,
51: input logic rx_pkt_valid_i,
52:
53: // Most recent packet received.
54: input logic [3:0] rx_pid_i,
55: input logic [6:0] rx_addr_i,
56: input logic [3:0] rx_endp_i,
57:
58: ////////////////////
59: // tx path
60: ////////////////////
61:
62: // Strobe to send new packet.
63: output logic tx_pkt_start_o,
64: input logic tx_pkt_end_i,
65: // Packet type to send
66: output logic [3:0] tx_pid_o,
67:
68: // Data payload to send if any
69: output logic tx_data_avail_o,
70: input logic tx_data_get_i,
71: output logic [7:0] tx_data_o
72: );
73: // suppress warnings
74: logic unused_1, unused_2;
75: assign unused_1 = tx_pkt_end_i;
76: assign unused_2 = rx_pkt_start_i;
77:
78: ////////////////////////////////////////////////////////////////////////////////
79: // in transfer state machine
80: ////////////////////////////////////////////////////////////////////////////////
81:
82: import usb_consts_pkg::*;
83:
84: typedef enum logic [1:0] {
85: StIdle,
86: StRcvdIn,
87: StSendData,
88: StWaitAck
89: } state_in_e;
90:
91: state_in_e in_xfr_state;
92: state_in_e in_xfr_state_next;
93:
94: logic in_xfr_end;
95:
96: assign in_ep_acked_o = in_xfr_end;
97:
98: // data toggle state
99: logic [NumInEps-1:0] data_toggle_q, data_toggle_d;
100:
101: // endpoint data buffer
102: logic token_received, setup_token_received, in_token_received, ack_received;
103: logic more_data_to_send;
104: logic ep_impl;
105: logic [3:0] in_ep_current_d;
106:
107: // More syntax so can compare with enum
108: usb_pid_type_e rx_pid_type;
109: usb_pid_e rx_pid;
110: assign rx_pid_type = usb_pid_type_e'(rx_pid_i[1:0]);
111: assign rx_pid = usb_pid_e'(rx_pid_i);
112:
113: // Is the specified endpoint actually implemented?
114: assign ep_impl = {1'b0, rx_endp_i} < NumInEps;
115:
116: // If the specified endpoint is not implemented, we don't process the token and just ignore it.
117: assign token_received =
118: rx_pkt_end_i &&
119: rx_pkt_valid_i &&
120: rx_pid_type == UsbPidTypeToken &&
121: rx_addr_i == dev_addr_i &&
122: ep_impl;
123:
124: assign setup_token_received =
125: token_received &&
126: rx_pid == UsbPidSetup;
127:
128: assign in_token_received =
129: token_received &&
130: rx_pid == UsbPidIn;
131:
132: assign ack_received =
133: rx_pkt_end_i &&
134: rx_pkt_valid_i &&
135: rx_pid == UsbPidAck;
136:
137: assign in_ep_current_d = ep_impl ? rx_endp_i : '0;
138:
139: // Make widths work - in_ep_current_d/in_ep_current_o only hold implemented endpoint IDs.
140: // These signals can be used to index signals of NumInEps width.
141: logic [InEpW-1:0] in_ep_index;
142: logic [InEpW-1:0] in_ep_index_d;
143: assign in_ep_index = in_ep_current_o[0 +: InEpW];
144: assign in_ep_index_d = in_ep_current_d[0 +: InEpW];
145:
146: assign more_data_to_send = in_ep_has_data_i[in_ep_index] & ~in_ep_data_done_i[in_ep_index];
147:
148: assign tx_data_avail_o = logic'(in_xfr_state == StSendData) & more_data_to_send;
149:
150: ////////////////////////////////////////////////////////////////////////////////
151: // in transfer state machine
152: ////////////////////////////////////////////////////////////////////////////////
153:
154: logic rollback_in_xfr;
155:
156: always_comb begin
157: in_xfr_state_next = in_xfr_state;
158: in_xfr_end = 1'b0;
159: tx_pkt_start_o = 1'b0;
160: tx_pid_o = 4'b0000;
161: rollback_in_xfr = 1'b0;
162: unique case (in_xfr_state)
163: StIdle: begin
164: if (in_token_received) begin
165: in_xfr_state_next = StRcvdIn;
166: end else begin
167: in_xfr_state_next = StIdle;
168: end
169: end
170:
171: StRcvdIn: begin
172: tx_pkt_start_o = 1'b1; // Need to transmit NACK/STALL or DATA
173:
174: if (in_ep_stall_i[in_ep_index]) begin
175: in_xfr_state_next = StIdle;
176: tx_pid_o = {UsbPidStall}; // STALL
177: end else if (in_ep_iso_i[in_ep_index]) begin
178: // ISO endpoint
179: // We always need to transmit. When no data is available, we send
180: // a zero-length packet
181: in_xfr_state_next = StSendData;
182: tx_pid_o = {data_toggle_q[in_ep_index], 1'b0, {UsbPidTypeData}}; // DATA0/1
183:
184: end else if (in_ep_has_data_i[in_ep_index]) begin
185: in_xfr_state_next = StSendData;
186: tx_pid_o = {data_toggle_q[in_ep_index], 1'b0, {UsbPidTypeData}}; // DATA0/1
187: end else begin
188: in_xfr_state_next = StIdle;
189: tx_pid_o = {UsbPidNak}; // NAK
190: end
191: end
192:
193: StSendData: begin
194: // Use &in_ep_get_addr so width can vary, looking for all ones
195: if ((!more_data_to_send) || ((&in_ep_get_addr_o) && tx_data_get_i)) begin
196: if (in_ep_iso_i[in_ep_index]) begin
197: in_xfr_state_next = StIdle; // no ACK for ISO EPs
198: end else begin
199: in_xfr_state_next = StWaitAck;
200: end
201: end else begin
202: in_xfr_state_next = StSendData;
203: end
204: end
205:
206: StWaitAck: begin
207: // FIXME: need to handle smash/timeout
208: if (ack_received) begin
209: in_xfr_state_next = StIdle;
210: in_xfr_end = 1'b1;
211: end else if (in_token_received) begin
212: in_xfr_state_next = StRcvdIn;
213: rollback_in_xfr = 1'b1;
214: end else if (rx_pkt_end_i) begin
215: in_xfr_state_next = StIdle;
216: rollback_in_xfr = 1'b1;
217: end else begin
218: in_xfr_state_next = StWaitAck;
219: end
220: end
221:
222: default: in_xfr_state_next = StIdle;
223: endcase
224: end
225:
226: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
227: if (!rst_ni) begin
228: tx_data_o <= '0;
229: end else begin
230: tx_data_o <= in_ep_data_i;
231: end
232:
233: end
234:
235: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
236: if (!rst_ni) begin
237: in_xfr_state <= StIdle;
238: in_ep_rollback_o <= 1'b0;
239: end else if (link_reset_i) begin
240: in_xfr_state <= StIdle;
241: in_ep_rollback_o <= 1'b0;
242: end else begin
243: in_xfr_state <= in_xfr_state_next;
244: in_ep_rollback_o <= rollback_in_xfr;
245: end
246: end
247:
248: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
249: if (!rst_ni) begin
250: in_ep_get_addr_o <= '0;
251: end else begin
252: if (in_xfr_state == StIdle) begin
253: in_ep_get_addr_o <= '0;
254: end else if ((in_xfr_state == StSendData) && tx_data_get_i) begin
255: in_ep_get_addr_o <= in_ep_get_addr_o + 1'b1;
256: end
257: end
258: end
259:
260: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
261: if (!rst_ni) begin
262: in_ep_newpkt_o <= 1'b0;
263: in_ep_current_o <= '0;
264: end else begin
265: if (in_token_received) begin
266: in_ep_current_o <= in_ep_current_d;
267: in_ep_newpkt_o <= 1'b1;
268: end else begin
269: in_ep_newpkt_o <= 1'b0;
270: end
271: end
272: end
273:
274: always_comb begin : proc_data_toggle_d
275: data_toggle_d = data_toggle_q;
276:
277: if (setup_token_received) begin
278: data_toggle_d[in_ep_index_d] = 1'b1;
279: end else if ((in_xfr_state == StWaitAck) && ack_received) begin
280: data_toggle_d[in_ep_index] = ~data_toggle_q[in_ep_index];
281: end
282:
283: data_toggle_d = data_toggle_d & ~data_toggle_clear_i;
284: end
285:
286: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
287: if (!rst_ni) begin
288: data_toggle_q <= '0; // Clear for all endpoints
289: end else if (link_reset_i) begin
290: data_toggle_q <= '0; // Clear for all endpoints
291: end else begin
292: data_toggle_q <= data_toggle_d;
293: end
294: end
295:
296: always_ff @(posedge clk_48mhz_i or negedge rst_ni) begin
297: if (!rst_ni) begin
298: in_ep_data_get_o <= 1'b0;
299: end else begin
300: if ((in_xfr_state == StSendData) && tx_data_get_i) begin
301: in_ep_data_get_o <= 1'b1;
302: end else begin
303: in_ep_data_get_o <= 1'b0;
304: end
305: end
306: end
307:
308: ////////////////
309: // Assertions //
310: ////////////////
311:
312: // We receive a token for an endpoint that is not implemented.
313: `ASSERT(UsbInEndPImpl,
314: (rx_pkt_end_i &&
315: rx_pkt_valid_i &&
316: rx_pid_type == UsbPidTypeToken &&
317: rx_addr_i == dev_addr_i) |-> ep_impl, clk_48mhz_i)
318:
319: endmodule
320: