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