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