//-----------------------------------------------------------------
//                       USB Serial Port
//                            V0.1
//                     Ultra-Embedded.com
//                       Copyright 2020
//
//                 Email: admin@ultra-embedded.com
//
//                         License: LGPL
//
// Modified by H. Peter Anvin for the MAX80 project
//-----------------------------------------------------------------
//
// This source file may be used and distributed without
// restriction provided that this copyright statement is not
// removed from the file and that any derivative work contains
// the original copyright notice and the associated disclaimer.
//
// This source file is free software; you can redistribute it
// and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any
// later version.
//
// This source is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General
// Public License along with this source; if not, write to the
// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
// Boston, MA  02111-1307  USA
//-----------------------------------------------------------------

// Multiport CDC interface
//
// Each channel contains the following registers seen from the CPU:
//
// 0 - RW - FIFO read/write data register
// 1 - RW - watermark control
//         3:1 - transmit FIFO low watermark  (resets to 1/4)
//         7:5 - transmit FIFO high watermark (resets to 3/4)
//        11:9 - receive FIFO low watermark   (resets to 1/4)
//       15:13 - receive FIFO high watermark  (resets to 3/4)
// 2 - RO - status register (some bits W1C)
//           0 - transmit FIFO empty (write 1 to flush Tx data)
//           1 - transmit FIFO <= low watermark
//           2 - transmit FIFO >= high watermark
//           3 - transmit FIFO full
//           4 - receive FIFO empty (write 1 to flush Rx data)
//           5 - receive FIFO <= low watermark
//           6 - receive FIFO >= high watermark
//           7 - receive FIFO full
//           8 - receive FIFO not empty after two USB frames
//           9 - BREAK received (sticky, W1C)
//          10 - USB device configured
//	    11 - DTR input active
//          12 - RTS input active
// 3 - RW - interrupt enable register
//        15:0 - mask of corresponding status bits
//       31:16 - invert polarity (0 = irq on status 1, 1 = irq on status 0)

module usb_cdc_channel
  #(parameter [3:0] data_ep_num = 4'd1,
    parameter [3:0] intr_ep_num = data_ep_num+1'b1)
  (
   // CPU bus
   input	 rst_n,
   input	 sys_clk,
   input	 cpu_valid,
   input [1:0]	 cpu_addr,
   output [31:0] cpu_rdata,
   input [31:0]  cpu_wdata,
   input [3:0]	 cpu_wstrb,
   output	 irq,

   // Core FIFO interface
   input	 clk_i,
   input	 rst_i,
		 usb_endpoint.dstr data_ep,
		 usb_endpoint.dstr intr_ep,

   // Control signals
   input	 recv_break_i,	// in the USB clock domain
   output	 recv_break_o,  // sys_clk domain, reflects the status register

   // Control lines (RTS, DTR) in the USB clock domain
   input	 set_control_line,
   input [1:0]	 control_lines,

   // USB configuration status
   input	 usb_configured,	// in the USB clock domain
   input	 usb_configured_s,	// in the sys_clk domain

   // Top of frame strobe *in the sys_clk domain*
   input	 start_of_frame_s
   );

   localparam fifo_bits   = 10;
   localparam fifo_size   = 1 << fifo_bits;
   localparam packet_bits = 6;
   localparam packet_size = 1 << packet_bits;

   // Control line status in the USB domain
   reg [1:0]   control_lines_q;
   wire        inport_dtr_w = control_lines_q[0];
   wire        inport_rts_w = control_lines_q[1];

   // Up to four bits per watermark, but all are not necessarily
   // implemented
   localparam water_bits  = 3;

   wire		 fifo_access = cpu_valid & cpu_addr == 2'b00;
   reg		 fifo_access_q;
   reg		 fifo_read_q;

   always @(posedge sys_clk)
     begin
	fifo_access_q <= fifo_access;
	fifo_read_q   <= fifo_access & ~|cpu_wstrb;
     end

   wire		        txempty;
   wire		        txfull;
   wire [fifo_bits-1:0]  txused;
   wire [water_bits-1:0] txused_msb = txused[fifo_bits-1:fifo_bits-water_bits];
   wire		        inport_empty_w;
   reg			inport_valid_q;
   reg			flush_tx_data;

   // cdc_txfifo does not have lookahead data output,
   // aclr assumed synchronous with wrclk
   cdc_txfifo txfifo (
		    .aclr    ( flush_tx_data ),

		    .wrclk   ( sys_clk ),
		    .data    ( cpu_wdata[7:0] ),
		    .wrreq   ( fifo_access & ~fifo_access_q & cpu_wstrb[0] ),
		    .wrempty ( txempty ),
		    .wrfull  ( txfull ),
		    .wrusedw ( txused ),

		    .rdclk   ( clk_i ),
		    .q       ( data_ep.d.tx_data ),
		    .rdreq   ( inport_valid_q
			       ? data_ep.u.tx_data_accept
			       : ~inport_empty_w ),
		    .rdempty ( inport_empty_w ),
		    .rdfull  ( ),
		    .rdusedw ( )
		    );

   // If RTS is not asserted, suspend data transmit
   wire inport_has_data = ~inport_empty_w & inport_rts_w;

   always @(posedge clk_i or posedge rst_i)
     if (rst_i)
       inport_valid_q <= 1'b0;
     else if ( inport_has_data )
       inport_valid_q <= 1'b1;
     else if ( data_ep.u.tx_data_accept )
       inport_valid_q <= 1'b0;

   assign data_ep.d.tx_data_valid = inport_valid_q;
   assign data_ep.d.tx_ready      = inport_valid_q;
   assign data_ep.d.tx_data_strb  = inport_valid_q;

   // Must terminate a transfer at the max packet size
   reg [packet_bits-1:0] inport_cnt_q;
   wire			 inport_last_w = ~inport_has_data | &inport_cnt_q;

   always @(posedge clk_i or posedge rst_i)
     if (rst_i)
       inport_cnt_q <= 'd0;
     else if (inport_last_w & data_ep.u.tx_data_accept)
       inport_cnt_q <= 'd0;
     else if (inport_valid_q & data_ep.u.tx_data_accept)
       inport_cnt_q <= inport_cnt_q + 1'b1;

   assign data_ep.d.tx_data_last = inport_last_w;

   wire [7:0]	        rdata_fifo;
   wire		        rxempty;
   wire		        rxfull;
   reg			flush_rx_data;
   wire [fifo_bits-1:0] rxused;
   wire [water_bits-1:0] rxused_msb = rxused[fifo_bits-1:fifo_bits-water_bits];
   wire [fifo_bits-1:0] outport_used_w;
   wire			outport_full_w;

   wire			outport_valid_w = data_ep.u.rx_valid & data_ep.uc.rx_strb;

   // Should be space for a max-sized packet before allowing input
   assign data_ep.d.rx_space = ~outport_full_w &
			       (outport_used_w < fifo_size - packet_size);

   // cdc_rxfifo has lookahead data output, aclr assumed synchronous with
   // rdclk
   cdc_rxfifo rxfifo (
		    .aclr    ( flush_rx_data ),

		    .rdclk   ( sys_clk ),
		    .q       ( rdata_fifo ),
		    .rdreq   ( ~fifo_access & fifo_read_q ),
		    .rdempty ( rxempty ),
		    .rdfull  ( rxfull ),
		    .rdusedw ( rxused ),

		    .wrclk   ( clk_i ),
		    .data    ( data_ep.uc.rx_data ),
		    .wrreq   ( outport_valid_w ),
		    .wrempty ( ),
		    .wrfull  ( outport_full_w ),
		    .wrusedw ( outport_used_w )
		    );

   // Queued data wakeup
   reg [1:0]		had_rxdata;

   always @(posedge sys_clk)
     begin
	if (rxempty)
	  had_rxdata <= 2'b00;
	else if (start_of_frame_s)
	  had_rxdata <= { had_rxdata[0], 1'b1 };
     end

   wire [15:0] status_mask = 16'b0001_1111_1111_1111; // Implemented bit mask
   reg  [15:0] irq_mask;
   reg  [15:0] irq_pol;
   wire [ 3:0] water_mask     = 4'b1111 << (4-water_bits);
   wire [15:0] water_ctl_mask = {4{water_mask}};
   reg  [15:0] water_ctl;

   wire        recv_break_s;
   reg	       recv_break_q;
   synchronizer #(.width(1)) break_synchro
     (
      .rst_n ( rst_n ),
      .clk   ( sys_clk ),
      .d     ( recv_break_i ),
      .q     ( recv_break_s )
      );

   wire [1:0]  control_lines_s;
   always @(posedge rst_i or posedge clk_i)
     if (rst_i)
       control_lines_q <= 2'b00;
     else if (~usb_configured)
       control_lines_q <= 2'b00;
     else if (set_control_line)
       control_lines_q <= control_lines;

   synchronizer #(.width(2)) ctl_synchro
     (
      .rst_n ( rst_n ),
      .clk   ( sys_clk ),
      .d     ( control_lines_q ),
      .q     ( control_lines_s )
      );

   always @(negedge rst_n or posedge sys_clk)
     if (~rst_n)
       begin
	  water_ctl     <= 16'hc3c3 & water_ctl_mask;
	  irq_mask      <= 16'b0;
	  irq_pol       <= 16'b0;
	  recv_break_q  <= 1'b0;
	  flush_rx_data <= 1'b0;
	  flush_tx_data <= 1'b0;
       end
     else
       begin
	  flush_rx_data <= flush_rx_data & ~rxempty;
	  flush_tx_data <= flush_tx_data & ~txempty;

	  recv_break_q  <= recv_break_q | recv_break_s;

	  if (cpu_valid)
	    case (cpu_addr)
	      2'b01: begin
		   if (cpu_wstrb[0])
		     water_ctl[7:0] <= cpu_wdata[7:0] & water_ctl_mask[7:0];
		   if (cpu_wstrb[1])
		     water_ctl[15:8] <= cpu_wdata[15:8] & water_ctl_mask[15:8];
		end
	      2'b10: begin
		 if (cpu_wstrb[0] & cpu_wdata[0])
		   flush_tx_data <= ~txempty;
		 if (cpu_wstrb[0] & cpu_wdata[4])
		   flush_rx_data <= ~rxempty;

		 if (cpu_wstrb[1] & cpu_wdata[9])
		   recv_break_q <= recv_break_s;
	      end
	      2'b11: begin
		 if (cpu_wstrb[0])
		   irq_mask[7:0]  <= cpu_wdata[7:0] & status_mask[7:0];
		 if (cpu_wstrb[1])
		   irq_mask[15:8] <= cpu_wdata[15:8] & status_mask[15:8];
		 if (cpu_wstrb[2])
		   irq_pol[7:0] <= cpu_wdata[23:16] & status_mask[7:0];
		 if (cpu_wstrb[3])
		   irq_pol[15:8] <= cpu_wdata[31:24] & status_mask[15:8];
	      end
	      default: begin
		 /* do nothing */
	      end
	    endcase // case (cpu_addr)
       end // else: !if(~rst_n)

   assign recv_break_o = recv_break_q; // Available to external logic

   tri0 [15:0] status;
   assign status[0]     = txempty;
   assign status[1]     = txempty | (txused_msb <= water_ctl[3:4-water_bits]);
   assign status[2]     = txfull  | (txused_msb >= water_ctl[7:8-water_bits]);
   assign status[3]     = txfull;

   assign status[4]     = rxempty;
   assign status[5]     = rxempty | (rxused_msb <= water_ctl[11:12-water_bits]);
   assign status[6]     = rxfull  | (rxused_msb >= water_ctl[15:16-water_bits]);
   assign status[7]     = rxfull;

   assign status[8]     = had_rxdata[1];
   assign status[9]     = recv_break_q;
   assign status[10]    = usb_configured_s;
   assign status[12:11] = control_lines_s;

   reg	       irq_q;
   always @(negedge rst_n or posedge sys_clk)
     if (~rst_n)
       irq_q <= 1'b0;
     else
       irq_q <= |((status ^ irq_pol) & irq_mask & status_mask);

   assign irq = irq_q;

   always @(*)
     case (cpu_addr)
       2'b00:   cpu_rdata = { 24'b0, rdata_fifo };
       2'b01:   cpu_rdata = { 16'b0, water_ctl };
       2'b10:   cpu_rdata = { 16'b0, status };
       2'b11:   cpu_rdata = { irq_pol, irq_mask };
       default: cpu_rdata = 32'bx;
     endcase // case (cpu_addr)

   // Channel configurations
   assign data_ep.c.ep_num  = data_ep_num;
   assign data_ep.c.ep_mask = 4'b1111;
   assign intr_ep.c.ep_num  = intr_ep_num;
   assign intr_ep.c.ep_mask = 4'b1111;

   // Data channel endpoint: unused signals
   assign data_ep.d.ep_iso        = 1'b0;
   assign data_ep.d.cfg_int_rx    = 1'b0;
   assign data_ep.d.cfg_int_tx    = 1'b0;
   assign data_ep.d.ep_stall      = 1'b0;

   // Notification channel endpoint: currently never used at all
   assign intr_ep.d.ep_iso        = 1'b0;
   assign intr_ep.d.cfg_int_rx    = 1'b0;
   assign intr_ep.d.cfg_int_tx    = 1'b0;
   assign intr_ep.d.ep_stall      = 1'b0;
   assign intr_ep.d.tx_ready      = 1'b0;
   assign intr_ep.d.tx_data_valid = 1'b0;
   assign intr_ep.d.tx_data_strb  = 1'b0;
   assign intr_ep.d.tx_data       = 8'bx;
   assign intr_ep.d.tx_data_last  = 1'b0;
   assign intr_ep.d.rx_space      = 1'b0;

endmodule // usb_cdc_channel

module usb_cdc_core
  #( parameter [2:0] channels = 3'd1 )
  (
   // Inputs
   input		 clk_i,
   input		 rst_i,
   input		 enable_i,
   input [ 7:0]		 utmi_data_in_i,
   input		 utmi_txready_i,
   input		 utmi_rxvalid_i,
   input		 utmi_rxactive_i,
   input		 utmi_rxerror_i,
   input [ 1:0]		 utmi_linestate_i,
   //input		 inport_valid_i,
   //input [ 7:0]	 inport_data_i,
   //input		 outport_accept_i,

   // Outputs
   output [ 7:0]	 utmi_data_out_o,
   output		 utmi_txvalid_o,
   output [ 1:0]	 utmi_op_mode_o,
   output [ 1:0]	 utmi_xcvrselect_o,
   output		 utmi_termselect_o,
   output		 utmi_dppulldown_o,
   output		 utmi_dmpulldown_o,
   output [channels-1:0] recv_break_o,

   //output		 inport_accept_o,
   //output		 outport_valid_o,
   //output [ 7:0]	 outport_data_o,
   //output		 outport_break_o,

   // CPU bus
   input		 rst_n,
   input		 sys_clk,
   input		 cpu_valid_cdc,
   input		 cpu_valid_usbdesc,
   input  [31:0]	 cpu_addr,
   output [31:0]	 cpu_rdata_cdc,
   output [31:0]	 cpu_rdata_usbdesc,
   input  [31:0]	 cpu_wdata,
   input   [3:0]	 cpu_wstrb,
   output [channels-1:0] irq
   );

   parameter USB_SPEED_HS = "False"; // True or False

   //-----------------------------------------------------------------
   // Defines
   //-----------------------------------------------------------------
   // Device class
`define DEV_CLASS_RESERVED              8'h00
`define DEV_CLASS_AUDIO                 8'h01
`define DEV_CLASS_COMMS                 8'h02
`define DEV_CLASS_HID                   8'h03
`define DEV_CLASS_MONITOR               8'h04
`define DEV_CLASS_PHY_IF                8'h05
`define DEV_CLASS_POWER                 8'h06
`define DEV_CLASS_PRINTER               8'h07
`define DEV_CLASS_STORAGE               8'h08
`define DEV_CLASS_HUB                   8'h09
`define DEV_CLASS_TMC                   8'hFE
`define DEV_CLASS_VENDOR_CUSTOM         8'hFF

   // Standard requests (via SETUP packets)
`define REQ_GET_STATUS                  8'h00
`define REQ_CLEAR_FEATURE               8'h01
`define REQ_SET_FEATURE                 8'h03
`define REQ_SET_ADDRESS                 8'h05
`define REQ_GET_DESCRIPTOR              8'h06
`define REQ_SET_DESCRIPTOR              8'h07
`define REQ_GET_CONFIGURATION           8'h08
`define REQ_SET_CONFIGURATION           8'h09
`define REQ_GET_INTERFACE               8'h0A
`define REQ_SET_INTERFACE               8'h0B
`define REQ_SYNC_FRAME                  8'h0C

   // Descriptor types
`define DESC_DEVICE                     8'h01
`define DESC_CONFIGURATION              8'h02
`define DESC_STRING                     8'h03
`define DESC_INTERFACE                  8'h04
`define DESC_ENDPOINT                   8'h05
`define DESC_DEV_QUALIFIER              8'h06
`define DESC_OTHER_SPEED_CONF           8'h07
`define DESC_IF_POWER                   8'h08

   // Endpoints
`define ENDPOINT_DIR_MASK               8'h80
`define ENDPOINT_DIR_R                  7
`define ENDPOINT_DIR_IN                 1'b1
`define ENDPOINT_DIR_OUT                1'b0
`define ENDPOINT_ADDR_MASK              8'h7F
`define ENDPOINT_TYPE_MASK              8'h3
`define ENDPOINT_TYPE_CONTROL           0
`define ENDPOINT_TYPE_ISO               1
`define ENDPOINT_TYPE_BULK              2
`define ENDPOINT_TYPE_INTERRUPT         3

   // Device Requests (bmRequestType)
`define USB_RECIPIENT_MASK              8'h1F
`define USB_RECIPIENT_DEVICE            8'h00
`define USB_RECIPIENT_INTERFACE         8'h01
`define USB_RECIPIENT_ENDPOINT          8'h02
`define USB_REQUEST_TYPE_MASK           8'h60
`define USB_STANDARD_REQUEST            8'h00
`define USB_CLASS_REQUEST               8'h20
`define USB_VENDOR_REQUEST              8'h40

   // USB device addresses are 7-bits
`define USB_ADDRESS_MASK                8'h7F

   // USB Feature Selectors
`define USB_FEATURE_ENDPOINT_STATE      16'h0000
`define USB_FEATURE_REMOTE_WAKEUP       16'h0001
`define USB_FEATURE_TEST_MODE           16'h0002

   // String Descriptors
`define UNICODE_LANGUAGE_STR_ID         8'd0
`define MANUFACTURER_STR_ID             8'd1
`define PRODUCT_NAME_STR_ID             8'd2
`define SERIAL_NUM_STR_ID               8'd3

`define CDC_ENDPOINT_BULK_OUT           1
`define CDC_ENDPOINT_BULK_IN            2
`define CDC_ENDPOINT_INTR_IN            3

`define CDC_SEND_ENCAPSULATED_COMMAND   8'h00
`define CDC_GET_ENCAPSULATED_RESPONSE   8'h01
`define CDC_GET_LINE_CODING             8'h21
`define CDC_SET_LINE_CODING             8'h20
`define CDC_SET_CONTROL_LINE_STATE      8'h22
`define CDC_SEND_BREAK                  8'h23

   //-----------------------------------------------------------------
   // Wires
   //-----------------------------------------------------------------

   wire  usb_reset_w;
   reg [6:0] device_addr_q;

   localparam endpoint_channels = channels*2 + 1;
   usb_endpoint usb_ep [0:endpoint_channels-1] ();

   wire       utmi_chirp_en_w;
   wire       usb_hs_w;

   //-----------------------------------------------------------------
   // Transceiver Control (high speed)
   //-----------------------------------------------------------------
   generate
      if (USB_SPEED_HS == "True")
	begin

	   localparam STATE_W                       = 3;
	   localparam STATE_IDLE                    = 3'd0;
	   localparam STATE_WAIT_RST                = 3'd1;
	   localparam STATE_SEND_CHIRP_K            = 3'd2;
	   localparam STATE_WAIT_CHIRP_JK           = 3'd3;
	   localparam STATE_FULLSPEED               = 3'd4;
	   localparam STATE_HIGHSPEED               = 3'd5;
	   reg [STATE_W-1:0] state_q;
	   reg [STATE_W-1:0] next_state_r;

	   // 60MHz clock rate
`define USB_RST_W  20
	   reg [`USB_RST_W-1:0] usb_rst_time_q;
	   reg [7:0]		chirp_count_q;
	   reg [1:0]		last_linestate_q;

	   localparam DETACH_TIME    = 20'd60000;  // 1ms -> T0
	   localparam ATTACH_FS_TIME = 20'd180000; // T0 + 3ms = T1
	   localparam CHIRPK_TIME    = 20'd246000; // T1 + ~1ms
	   localparam HS_RESET_TIME  = 20'd600000; // T0 + 10ms = T9
	   localparam HS_CHIRP_COUNT = 8'd5;

	   reg [  1:0]		utmi_op_mode_r;
	   reg [  1:0]		utmi_xcvrselect_r;
	   reg			utmi_termselect_r;
	   reg			utmi_dppulldown_r;
	   reg			utmi_dmpulldown_r;

	   always @ *
	     begin
		next_state_r = state_q;

		// Default - disconnect
		utmi_op_mode_r    = 2'd1;
		utmi_xcvrselect_r = 2'd0;
		utmi_termselect_r = 1'b0;
		utmi_dppulldown_r = 1'b0;
		utmi_dmpulldown_r = 1'b0;

		case (state_q)
		  STATE_IDLE:
		    begin
		       // Detached
		       if (enable_i && usb_rst_time_q >= DETACH_TIME)
			 next_state_r = STATE_WAIT_RST;
		    end
		  STATE_WAIT_RST:
		    begin
		       // Assert FS mode, check for SE0 (T0)
		       utmi_op_mode_r    = 2'd0;
		       utmi_xcvrselect_r = 2'd1;
		       utmi_termselect_r = 1'b1;
		       utmi_dppulldown_r = 1'b0;
		       utmi_dmpulldown_r = 1'b0;

		       // Wait for SE0 (T1), send device chirp K
		       if (usb_rst_time_q >= ATTACH_FS_TIME)
			 next_state_r = STATE_SEND_CHIRP_K;
		    end
		  STATE_SEND_CHIRP_K:
		    begin
		       // Send chirp K
		       utmi_op_mode_r    = 2'd2;
		       utmi_xcvrselect_r = 2'd0;
		       utmi_termselect_r = 1'b1;
		       utmi_dppulldown_r = 1'b0;
		       utmi_dmpulldown_r = 1'b0;

		       // End of device chirp K (T2)
		       if (usb_rst_time_q >= CHIRPK_TIME)
			 next_state_r = STATE_WAIT_CHIRP_JK;
		    end
		  STATE_WAIT_CHIRP_JK:
		    begin
		       // Stop sending chirp K and wait for downstream port chirps
		       utmi_op_mode_r    = 2'd2;
		       utmi_xcvrselect_r = 2'd0;
		       utmi_termselect_r = 1'b1;
		       utmi_dppulldown_r = 1'b0;
		       utmi_dmpulldown_r = 1'b0;

		       // Required number of chirps detected, move to HS mode (T7)
		       if (chirp_count_q >= HS_CHIRP_COUNT)
			 next_state_r = STATE_HIGHSPEED;
		       // Time out waiting for chirps, fallback to FS mode
		       else if (usb_rst_time_q >= HS_RESET_TIME)
			 next_state_r = STATE_FULLSPEED;
		    end
		  STATE_FULLSPEED:
		    begin
		       utmi_op_mode_r    = 2'd0;
		       utmi_xcvrselect_r = 2'd1;
		       utmi_termselect_r = 1'b1;
		       utmi_dppulldown_r = 1'b0;
		       utmi_dmpulldown_r = 1'b0;

		       // USB reset detected...
		       if (usb_rst_time_q >= HS_RESET_TIME && usb_reset_w)
			 next_state_r = STATE_WAIT_RST;
		    end
		  STATE_HIGHSPEED:
		    begin
		       // Enter HS mode
		       utmi_op_mode_r    = 2'd0;
		       utmi_xcvrselect_r = 2'd0;
		       utmi_termselect_r = 1'b0;
		       utmi_dppulldown_r = 1'b0;
		       utmi_dmpulldown_r = 1'b0;

		       // Long SE0 - could be reset or suspend
		       // TODO: Should revert to FS mode and check...
		       if (usb_rst_time_q >= HS_RESET_TIME && usb_reset_w)
			 next_state_r = STATE_WAIT_RST;
		    end
		  default:
		    ;
		endcase
	     end

	   // Update state
	   always @ (posedge clk_i or posedge rst_i)
	     if (rst_i)
	       state_q   <= STATE_IDLE;
	     else
	       state_q   <= next_state_r;

	   // Time since T0 (start of HS reset)
	   always @ (posedge clk_i or posedge rst_i)
	     if (rst_i)
	       usb_rst_time_q <= `USB_RST_W'b0;
	   // Entering wait for reset state
	     else if (next_state_r == STATE_WAIT_RST && state_q != STATE_WAIT_RST)
	       usb_rst_time_q <=  `USB_RST_W'b0;
	   // Waiting for reset, reset count on line state toggle
	     else if (state_q == STATE_WAIT_RST && (utmi_linestate_i != 2'b00))
	       usb_rst_time_q <=  `USB_RST_W'b0;
	     else if (usb_rst_time_q != {(`USB_RST_W){1'b1}})
	       usb_rst_time_q <= usb_rst_time_q + `USB_RST_W'd1;

	   always @ (posedge clk_i or posedge rst_i)
	     if (rst_i)
	       last_linestate_q   <= 2'b0;
	     else
	       last_linestate_q   <= utmi_linestate_i;

	   // Chirp counter
	   always @ (posedge clk_i or posedge rst_i)
	     if (rst_i)
	       chirp_count_q   <= 8'b0;
	     else if (state_q == STATE_SEND_CHIRP_K)
	       chirp_count_q   <= 8'b0;
	     else if (state_q == STATE_WAIT_CHIRP_JK && (last_linestate_q != utmi_linestate_i) && chirp_count_q != 8'hFF)
	       chirp_count_q   <= chirp_count_q + 8'd1;

	   assign utmi_op_mode_o    = utmi_op_mode_r;
	   assign utmi_xcvrselect_o = utmi_xcvrselect_r;
	   assign utmi_termselect_o = utmi_termselect_r;
	   assign utmi_dppulldown_o = utmi_dppulldown_r;
	   assign utmi_dmpulldown_o = utmi_dmpulldown_r;

	   assign utmi_chirp_en_w   = (state_q == STATE_SEND_CHIRP_K);
	   assign usb_hs_w          = (state_q == STATE_HIGHSPEED);

	end
      else
	begin
	   //-----------------------------------------------------------------
	   // Transceiver Control
	   //-----------------------------------------------------------------
	   reg [  1:0]  utmi_op_mode_r;
	   reg [  1:0]	utmi_xcvrselect_r;
	   reg          utmi_termselect_r;
	   reg          utmi_dppulldown_r;
	   reg          utmi_dmpulldown_r;

	   always @ *
	     begin
		if (enable_i)
		  begin
		     utmi_op_mode_r    = 2'd0;
		     utmi_xcvrselect_r = 2'd1;
		     utmi_termselect_r = 1'b1;
		     utmi_dppulldown_r = 1'b0;
		     utmi_dmpulldown_r = 1'b0;
		  end
		else
		  begin
		     utmi_op_mode_r    = 2'd1;
		     utmi_xcvrselect_r = 2'd0;
		     utmi_termselect_r = 1'b0;
		     utmi_dppulldown_r = 1'b0;
		     utmi_dmpulldown_r = 1'b0;
		  end
	     end

	   assign utmi_op_mode_o    = utmi_op_mode_r;
	   assign utmi_xcvrselect_o = utmi_xcvrselect_r;
	   assign utmi_termselect_o = utmi_termselect_r;
	   assign utmi_dppulldown_o = utmi_dppulldown_r;
	   assign utmi_dmpulldown_o = utmi_dmpulldown_r;

	   assign utmi_chirp_en_w   = 1'b0;
	   assign usb_hs_w          = 1'b0;

	end
   endgenerate

   //-----------------------------------------------------------------
   // Core
   //-----------------------------------------------------------------
   wire start_of_frame_w;		// Or reset, but close enough

   usbf_device_core #(.endpoint_channels(endpoint_channels))
     u_core
       (
	.clk_i(clk_i),
	.rst_i(rst_i),

	.intr_o(start_of_frame_w),

	// UTMI interface
	.utmi_data_o(utmi_data_out_o),
	.utmi_data_i(utmi_data_in_i),
	.utmi_txvalid_o(utmi_txvalid_o),
	.utmi_txready_i(utmi_txready_i),
	.utmi_rxvalid_i(utmi_rxvalid_i),
	.utmi_rxactive_i(utmi_rxactive_i),
	.utmi_rxerror_i(utmi_rxerror_i),
	.utmi_linestate_i(utmi_linestate_i),

	.reg_chirp_en_i(utmi_chirp_en_w),
	.reg_int_en_sof_i(1'b1),

	.reg_dev_addr_i(device_addr_q),

	// Endpoint interfaces array
	.ep (usb_ep),

	// Status
	.reg_sts_rst_clr_i(1'b1),
	.reg_sts_rst_o(usb_reset_w),
	.reg_sts_frame_num_o()
	);

   // USB: Setup packet capture (limited to 8 bytes for USB-FS)
   //-----------------------------------------------------------------
   reg [7:0] setup_packet_q[0:7];
   reg [2:0] setup_wr_idx_q;
   reg       setup_frame_q;
   reg       setup_valid_q;
   reg       setup_data_q;
   reg       status_ready_q; // STATUS response received

   assign usb_ep[0].c.ep_num   = 4'd0;
   assign usb_ep[0].c.ep_mask  = 4'b1111;

   assign usb_ep[0].d.rx_space = 1'b1;

   always @ (posedge clk_i or posedge rst_i)
     if (rst_i)
       begin
	  setup_packet_q[0]  <= 8'b0;
	  setup_packet_q[1]  <= 8'b0;
	  setup_packet_q[2]  <= 8'b0;
	  setup_packet_q[3]  <= 8'b0;
	  setup_packet_q[4]  <= 8'b0;
	  setup_packet_q[5]  <= 8'b0;
	  setup_packet_q[6]  <= 8'b0;
	  setup_packet_q[7]  <= 8'b0;
	  setup_wr_idx_q     <= 3'b0;
	  setup_valid_q      <= 1'b0;
	  setup_frame_q      <= 1'b0;
	  setup_data_q       <= 1'b0;
	  status_ready_q     <= 1'b0;
       end
   // SETUP token received
     else if (usb_ep[0].uc.rx_setup)
       begin
	  setup_packet_q[0]  <= 8'b0;
	  setup_packet_q[1]  <= 8'b0;
	  setup_packet_q[2]  <= 8'b0;
	  setup_packet_q[3]  <= 8'b0;
	  setup_packet_q[4]  <= 8'b0;
	  setup_packet_q[5]  <= 8'b0;
	  setup_packet_q[6]  <= 8'b0;
	  setup_packet_q[7]  <= 8'b0;
	  setup_wr_idx_q     <= 3'b0;
	  setup_valid_q      <= 1'b0;
	  setup_frame_q      <= 1'b1;
	  setup_data_q       <= 1'b0;
	  status_ready_q     <= 1'b0;
       end
   // Valid DATA for setup frame
     else if (usb_ep[0].u.rx_valid && usb_ep[0].uc.rx_strb)
       begin
	  setup_packet_q[setup_wr_idx_q] <= usb_ep[0].uc.rx_data;
	  setup_wr_idx_q      <= setup_wr_idx_q + 3'd1;
	  setup_valid_q       <=  setup_frame_q && usb_ep[0].uc.rx_last;
	  setup_data_q        <= !setup_frame_q && usb_ep[0].uc.rx_last;
	  if (usb_ep[0].uc.rx_last)
            setup_frame_q   <= 1'b0;
       end
   // Detect STATUS stage (ACK for SETUP GET requests)
   // TODO: Not quite correct ....
     else if (usb_ep[0].u.rx_valid && !usb_ep[0].uc.rx_strb &&
	      usb_ep[0].uc.rx_last)
       begin
	  setup_valid_q       <= 1'b0;
	  status_ready_q      <= 1'b1;
       end
     else
       setup_valid_q       <= 1'b0;

   //-----------------------------------------------------------------
   // SETUP request decode
   //-----------------------------------------------------------------
   wire [7:0]  bmRequestType_w = setup_packet_q[0];
   wire [7:0]  bRequest_w      = setup_packet_q[1];
   wire [15:0] wValue_w        = {setup_packet_q[3], setup_packet_q[2]};
   wire [15:0] wIndex_w        = {setup_packet_q[5], setup_packet_q[4]};
   wire [15:0] wLength         = {setup_packet_q[7], setup_packet_q[6]};

   wire        setup_get_w            = setup_valid_q && (bmRequestType_w[`ENDPOINT_DIR_R] == `ENDPOINT_DIR_IN);
   wire        setup_set_w            = setup_valid_q && (bmRequestType_w[`ENDPOINT_DIR_R] == `ENDPOINT_DIR_OUT);
   wire        setup_no_data_w        = setup_set_w && (wLength == 16'b0);

   // For REQ_GET_DESCRIPTOR
   wire [7:0]  bDescriptorType_w  = setup_packet_q[3];
   wire [7:0]  bDescriptorIndex_w = setup_packet_q[2];

   //-----------------------------------------------------------------
   // Process setup request
   //-----------------------------------------------------------------
   reg	       ctrl_stall_r; // Send STALL
   reg	       ctrl_ack_r;   // Send STATUS (ZLP)
   reg	       ctrl_send_data_r;

   reg [15:0]  desc_base_addr_r;
   reg [15:0]  desc_len_r;

   reg         rx_break_r;
   reg         rx_set_control_line_r;
   reg [1:0]   rx_control_lines_r;
   reg [2:0]   rx_channel_r;

   reg	       addressed_q;
   reg	       addressed_r;
   reg [6:0]   device_addr_r;

   reg	       configured_q;
   reg	       configured_r;

   reg	       set_with_data_q;
   reg	       set_with_data_r;
   wire        data_status_zlp_w;

   usb_desc_index
     u_index (
	      .dtype  (bDescriptorType_w),
	      .dindex (bDescriptorIndex_w),
	      .windex (wIndex_w),
	      .additional (bmRequestType_w[5]), // CLASS request

	      .addr (desc_base_addr_r),
	      .len  (desc_len_r)
	      );

   always @ *
     begin
	ctrl_stall_r          = 1'b0;
	ctrl_ack_r            = 1'b0;
	ctrl_send_data_r      = 1'b0;
	device_addr_r         = device_addr_q;
	addressed_r           = addressed_q;
	configured_r          = configured_q;
	set_with_data_r       = set_with_data_q;
	rx_break_r            = 1'b0;
	rx_set_control_line_r = 1'b0;
	rx_channel_r          = wIndex_w[3:1];
	rx_control_lines_r    = wValue_w[1:0];

	if (setup_valid_q)
	  begin
             set_with_data_r = 1'b0;

             case (bmRequestType_w & `USB_REQUEST_TYPE_MASK)
               `USB_STANDARD_REQUEST:
		 begin
		    case (bRequest_w)
		      `REQ_GET_STATUS:
			begin
			   $display("GET_STATUS");
			end
		      `REQ_CLEAR_FEATURE:
			begin
			   $display("CLEAR_FEATURE");
			   ctrl_ack_r = setup_set_w && setup_no_data_w;
			end
		      `REQ_SET_FEATURE:
			begin
			   $display("SET_FEATURE");
			   ctrl_ack_r = setup_set_w && setup_no_data_w;
			end
		      `REQ_SET_ADDRESS:
			begin
			   $display("SET_ADDRESS: Set device address %d", wValue_w[6:0]);
			   ctrl_ack_r    = setup_set_w && setup_no_data_w;
			   device_addr_r = wValue_w[6:0];
			   addressed_r   = 1'b1;
			end
		      `REQ_GET_DESCRIPTOR:
			begin
			   $display("GET_DESCRIPTOR: Type %d", bDescriptorType_w);
			   ctrl_send_data_r = 1'b1;
			end
		      `REQ_GET_CONFIGURATION:
			begin
			   $display("GET_CONF");
			end
		      `REQ_SET_CONFIGURATION:
			begin
			   $display("SET_CONF: Configuration %x", wValue_w);

			   // Only support one configuration for now
			   if (wValue_w < 16'd2)
			     begin
				configured_r = wValue_w[0];
				ctrl_ack_r   = setup_set_w && setup_no_data_w;
			     end
			   else
			     ctrl_stall_r = 1'b1;
			end
		      `REQ_GET_INTERFACE:
			begin
			   $display("GET_INTERFACE");
			   ctrl_stall_r = 1'b1;
			end
//`ifdef HANDLE_SET_INTERFACE
		      `REQ_SET_INTERFACE:
			begin
			   $display("SET_INTERFACE: %x %x", wValue_w, wIndex_w);
			   if (wValue_w == 16'd0 && wIndex_w < (channels << 1))
			     ctrl_ack_r   = setup_set_w && setup_no_data_w;
			   else
			     ctrl_stall_r = 1'b1;
			end
//`endif
		      default:
			begin
			   ctrl_stall_r = 1'b1;
			end
		    endcase
		 end
               `USB_VENDOR_REQUEST:
		 begin
		    // None supported
		    ctrl_stall_r = 1'b1;
		 end
               `USB_CLASS_REQUEST:
		 begin
		    ctrl_ack_r      = setup_set_w && setup_no_data_w;
		    set_with_data_r = setup_set_w && !setup_no_data_w;

		    case (bRequest_w)
		      `CDC_GET_LINE_CODING:
			begin
			   $display("CDC_GET_LINE_CODING");
			   ctrl_send_data_r = 1'b1;
			end
		      `CDC_SEND_BREAK:
			begin
			   rx_break_r = |wValue_w;
			end
		      `CDC_SET_CONTROL_LINE_STATE:
			begin
			   rx_set_control_line_r = 1'b1;
			end
		      default:
			begin
			   // Do nothing
			end
		    endcase
		 end
               default:
		 begin
		    ctrl_stall_r = 1'b1;
		 end
             endcase
	  end
	else if (data_status_zlp_w)
          set_with_data_r = 1'b0;
     end

   always @ (posedge clk_i or posedge rst_i)
     if (rst_i)
       begin
	  device_addr_q   <= 7'b0;
	  addressed_q     <= 1'b0;
	  configured_q    <= 1'b0;
	  set_with_data_q <= 1'b0;
       end
     else if (usb_reset_w)
       begin
	  device_addr_q   <= 7'b0;
	  addressed_q     <= 1'b0;
	  configured_q    <= 1'b0;
	  set_with_data_q <= 1'b0;
       end
     else
       begin
	  device_addr_q   <= device_addr_r;
	  addressed_q     <= addressed_r;
	  configured_q    <= configured_r;
	  set_with_data_q <= set_with_data_r;
       end

   //-----------------------------------------------------------------
   // SETUP response
   //-----------------------------------------------------------------
   reg        ctrl_sending_q;
   reg [15:0] ctrl_send_idx_q;

   reg        ctrl_sending_r;
   reg [15:0] ctrl_send_idx_r;

   reg	      ctrl_sending_zlp_r;
   reg	      ctrl_sending_zlp_q;

   reg        ctrl_txvalid_q;
   reg [7:0]  ctrl_txdata_q;
   reg        ctrl_txstrb_q;
   reg        ctrl_txlast_q;
   reg        ctrl_txstall_q;

   reg        ctrl_txvalid_r;
   reg [7:0]  ctrl_txdata_r;
   reg        ctrl_txstrb_r;
   reg        ctrl_txlast_r;
   reg        ctrl_txstall_r;

   wire       ctrl_send_accept_w = usb_ep[0].u.tx_data_accept || !usb_ep[0].d.tx_data_valid;

   reg  [15:0] desc_addr_r;
   reg  [15:0] desc_addr_q;
   wire [7:0] desc_data_w;

   always @ *
     begin
	ctrl_sending_r     = ctrl_sending_q;
	ctrl_send_idx_r    = ctrl_send_idx_q;
	ctrl_sending_zlp_r = ctrl_sending_zlp_q;

	ctrl_txvalid_r  = ctrl_txvalid_q;
	ctrl_txdata_r   = ctrl_txdata_q;
	ctrl_txstrb_r   = ctrl_txstrb_q;
	ctrl_txlast_r   = ctrl_txlast_q;
	ctrl_txstall_r  = ctrl_txstall_q;

	desc_addr_r     = desc_addr_q;

	// New SETUP request
	if (setup_valid_q)
	  begin
             // Send STALL
             if (ctrl_stall_r)
               begin
		  ctrl_txvalid_r  = 1'b1;
		  ctrl_txstrb_r   = 1'b0;
		  ctrl_txlast_r   = 1'b1;
		  ctrl_txstall_r  = 1'b1;
               end
             // Send STATUS response (ZLP)
             else if (ctrl_ack_r)
               begin
		  ctrl_txvalid_r  = 1'b1;
		  ctrl_txstrb_r   = 1'b0;
		  ctrl_txlast_r   = 1'b1;
		  ctrl_txstall_r  = 1'b0;
               end
             else if (ctrl_send_data_r)
               begin
		  ctrl_txvalid_r     = 1'b1;
		  ctrl_txdata_r      = desc_data_w;
		  ctrl_txstall_r     = setup_get_w & ~|desc_len_r;
		  ctrl_txstrb_r      = setup_get_w && !ctrl_txstall_r;
		  ctrl_sending_r     = ctrl_txstrb_r;
		  ctrl_txlast_r      = !ctrl_txstrb_r;
		  ctrl_sending_zlp_r = 1'b0;
		  desc_addr_r        = desc_addr_r     + 1'b1;
		  ctrl_send_idx_r    = ctrl_send_idx_r + 1'b1;
               end
	  end
	// Abort control send when STATUS received
	else if (status_ready_q)
	  begin
             ctrl_sending_r  = 1'b0;
             ctrl_txvalid_r  = 1'b0;
	  end
	// Send STATUS response (ZLP)
	else if (set_with_data_q && setup_data_q)
	  begin
             ctrl_txvalid_r  = 1'b1;
             ctrl_txstrb_r   = 1'b0;
             ctrl_txlast_r   = 1'b1;
             ctrl_txstall_r  = 1'b0;
	  end
	else if (ctrl_sending_r && ctrl_send_accept_w)
	  begin
             ctrl_txvalid_r  = 1'b1;
             ctrl_txdata_r   = desc_data_w;
	     ctrl_txstall_r  = 1'b0;

	     if (ctrl_sending_zlp_r)
	       begin
		  ctrl_sending_r = 1'b0;
		  ctrl_txlast_r  = 1'b1;
		  ctrl_txstrb_r  = 1'b0;
	       end
	     else
	       begin
		  ctrl_txstrb_r  = 1'b1;
		  ctrl_txlast_r  = &(ctrl_send_idx_r[5:0]
				     | { {3{~usb_hs_w}}, 3'b0 });

		  // Advance to next data item
		  ctrl_send_idx_r = ctrl_send_idx_r + 16'd1;
		  desc_addr_r     = desc_addr_r + 1'b1;

		  if (ctrl_send_idx_r == wLength ||
		      ctrl_send_idx_r == desc_len_r)
		    begin
		       if (ctrl_txlast_r)
			 ctrl_sending_zlp_r = 1'b1;
		       else
			 ctrl_sending_r = 1'b0;

		       ctrl_txlast_r = 1'b1;
		    end
	       end // else: !if(ctrl_sending_zlp_r)
	  end
	else if (ctrl_send_accept_w)
          ctrl_txvalid_r  = 1'b0;

	if (!ctrl_sending_r)
	  begin
	     // Prepare for the next SETUP
	     desc_addr_r        = desc_base_addr_r;
	     ctrl_sending_zlp_r = 1'b0;
	     ctrl_send_idx_r    = 16'd0;
	  end
     end

   assign data_status_zlp_w = set_with_data_q && setup_data_q && ctrl_send_accept_w;

   always @ (posedge clk_i or posedge rst_i)
     if (rst_i)
       begin
	  ctrl_sending_q  <= 1'b0;
	  ctrl_send_idx_q <= 16'b0;
	  ctrl_sending_zlp_q <= 1'b0;
	  ctrl_txvalid_q  <= 1'b0;
	  ctrl_txdata_q   <= 8'b0;
	  ctrl_txstrb_q   <= 1'b0;
	  ctrl_txlast_q   <= 1'b0;
	  ctrl_txstall_q  <= 1'b0;
	  desc_addr_q     <= 8'b0;
       end
     else if (usb_reset_w)
       begin
	  ctrl_sending_q  <= 1'b0;
	  ctrl_send_idx_q <= 16'b0;
	  ctrl_sending_zlp_q <= 1'b0;
	  ctrl_txvalid_q  <= 1'b0;
	  ctrl_txdata_q   <= 8'b0;
	  ctrl_txstrb_q   <= 1'b0;
	  ctrl_txlast_q   <= 1'b0;
	  ctrl_txstall_q  <= 1'b0;
	  desc_addr_q     <= 8'b0;
       end
     else
       begin
	  ctrl_sending_q  <= ctrl_sending_r;
	  ctrl_send_idx_q <= ctrl_send_idx_r;
	  ctrl_sending_zlp_q <= ctrl_sending_zlp_r;
	  ctrl_txvalid_q  <= ctrl_txvalid_r;
	  ctrl_txdata_q   <= ctrl_txdata_r;
	  ctrl_txstrb_q   <= ctrl_txstrb_r;
	  ctrl_txlast_q   <= ctrl_txlast_r;
	  ctrl_txstall_q  <= ctrl_txstall_r;
	  desc_addr_q     <= desc_addr_r;
       end

   assign usb_ep[0].d.tx_ready      = ctrl_txvalid_q;
   assign usb_ep[0].d.tx_data_valid = ctrl_txvalid_q;
   assign usb_ep[0].d.tx_data_strb  = ctrl_txstrb_q;
   assign usb_ep[0].d.tx_data       = ctrl_txdata_q;
   assign usb_ep[0].d.tx_data_last  = ctrl_txlast_q;
   assign usb_ep[0].d.ep_stall      = ctrl_txstall_q;

   // Unused endpoint 0 signals
   assign usb_ep[0].d.ep_iso        = 1'b0;
   assign usb_ep[0].d.cfg_int_rx    = 1'b0;
   assign usb_ep[0].d.cfg_int_tx    = 1'b0;

   //-----------------------------------------------------------------
   // Descriptor ROM
   //-----------------------------------------------------------------
   wire [7:0] rdata_usbdesc;

   usb_desc_rom
     u_rom
       (
	.clk		( clk_i ),
	.usb_addr	( desc_addr_r ),
	.usb_rdata	( desc_data_w ),

	// CPU interface for modifications
	.cpu_clk        ( sys_clk ),
	.cpu_addr	( cpu_addr[17:2] ),
	.cpu_rdata	( cpu_rdata_usbdesc[7:0] ),
	.cpu_wdata	( cpu_wdata[7:0] ),
	.cpu_wren	( cpu_valid_usbdesc & cpu_wstrb[0] )
	);

   assign cpu_rdata_usbdesc[31:8] = 24'b0;

   //-----------------------------------------------------------------
   // Channel streaming interfaces and CPU interfaces
   // Some signals need to be synchronized to the CPU clock, and
   // there is no reason to do it in each channel.
   //-----------------------------------------------------------------

   wire		       start_of_frame_ws;
   reg		       start_of_frame_q;

   synchronizer #(.width(1))
   sof_sync (
	     .rst_n ( 1'b1 ),
	     .clk   ( sys_clk ),
	     .d     ( start_of_frame_w ),
	     .q     ( start_of_frame_ws )
	     );

   always @(posedge sys_clk)
     start_of_frame_q <= start_of_frame_ws;

   wire start_of_frame_s = start_of_frame_ws & ~start_of_frame_q;

   wire configured_s;
   synchronizer #(.width(1))
   configured_sync (
		    .rst_n ( 1'b1 ),
		    .clk   ( sys_clk ),
		    .d     ( configured_q ),
		    .q     ( configured_s )
		    );

   wire [31:0] rdata_chan[0:7];

   generate
      genvar  cn;
      for (cn = 0; cn < channels; cn++)
	begin : chan
	   usb_cdc_channel #(.data_ep_num ( cn*2+1 ),
			     .intr_ep_num ( cn*2+2 ))
	   cchan (
		  .rst_n            ( rst_n ),
		  .sys_clk          ( sys_clk ),
		  .cpu_valid        ( cpu_valid_cdc & (cpu_addr[6:4] == cn) ),
		  .cpu_addr         ( cpu_addr[3:2] ),
		  .cpu_rdata        ( rdata_chan[cn] ),
		  .cpu_wdata        ( cpu_wdata ),
		  .cpu_wstrb        ( cpu_wstrb ),
		  .irq              ( irq[cn] ),

		  .clk_i            ( clk_i ),
		  .rst_i            ( rst_i ),
		  .data_ep          ( usb_ep[cn*2+1].dstr ),
		  .intr_ep          ( usb_ep[cn*2+2].dstr ),

		  .usb_configured   ( configured_q ),
		  .usb_configured_s ( configured_s ),

		  .recv_break_i     ( rx_break_r & (rx_channel_r == cn) ),
		  .recv_break_o     ( recv_break_o[cn] ),

		  .set_control_line ( rx_set_control_line_r &
				      (rx_channel_r == cn) ),
		  .control_lines    ( rx_control_lines_r ),

		 .start_of_frame_s ( start_of_frame_s )
		 );
	end // block: chan
      for (cn = channels; cn < 8; cn++)
	begin : nochan
	   assign rdata_chan[cn] = 32'bx;
	end
   endgenerate

   assign cpu_rdata_cdc = rdata_chan[cpu_addr[6:4]];

endmodule
