../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: