|
@@ -0,0 +1,1116 @@
|
|
|
+//-----------------------------------------------------------------
|
|
|
+// USB Serial Port
|
|
|
+// V0.1
|
|
|
+// Ultra-Embedded.com
|
|
|
+// Copyright 2020
|
|
|
+//
|
|
|
+// Email: admin@ultra-embedded.com
|
|
|
+//
|
|
|
+// License: LGPL
|
|
|
+//-----------------------------------------------------------------
|
|
|
+//
|
|
|
+// 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
|
|
|
+//-----------------------------------------------------------------
|
|
|
+
|
|
|
+//-----------------------------------------------------------------
|
|
|
+// Generated File
|
|
|
+//-----------------------------------------------------------------
|
|
|
+
|
|
|
+module usb_cdc_core
|
|
|
+(
|
|
|
+ // 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 inport_accept_o
|
|
|
+ ,output outport_valid_o
|
|
|
+ ,output [ 7:0] outport_data_o
|
|
|
+);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+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
|
|
|
+
|
|
|
+// Descriptor ROM offsets / sizes
|
|
|
+`define ROM_DESC_DEVICE_ADDR 8'd0
|
|
|
+`define ROM_DESC_DEVICE_SIZE 16'd18
|
|
|
+`define ROM_DESC_CONF_ADDR 8'd18
|
|
|
+`define ROM_DESC_CONF_SIZE 16'd67
|
|
|
+`define ROM_DESC_STR_LANG_ADDR 8'd85
|
|
|
+`define ROM_DESC_STR_LANG_SIZE 16'd4
|
|
|
+`define ROM_DESC_STR_MAN_ADDR 8'd89
|
|
|
+`define ROM_DESC_STR_MAN_SIZE 16'd30
|
|
|
+`define ROM_DESC_STR_PROD_ADDR 8'd119
|
|
|
+`define ROM_DESC_STR_PROD_SIZE 16'd30
|
|
|
+`define ROM_DESC_STR_SERIAL_ADDR 8'd149
|
|
|
+`define ROM_DESC_STR_SERIAL_SIZE 16'd14
|
|
|
+`define ROM_CDC_LINE_CODING_ADDR 8'd163
|
|
|
+`define ROM_CDC_LINE_CODING_SIZE 16'd7
|
|
|
+
|
|
|
+//-----------------------------------------------------------------
|
|
|
+// Wires
|
|
|
+//-----------------------------------------------------------------
|
|
|
+wire usb_reset_w;
|
|
|
+reg [6:0] device_addr_q;
|
|
|
+
|
|
|
+wire usb_ep0_tx_rd_w;
|
|
|
+wire [7:0] usb_ep0_tx_data_w;
|
|
|
+wire usb_ep0_tx_empty_w;
|
|
|
+
|
|
|
+wire usb_ep0_rx_wr_w;
|
|
|
+wire [7:0] usb_ep0_rx_data_w;
|
|
|
+wire usb_ep0_rx_full_w;
|
|
|
+wire usb_ep1_tx_rd_w;
|
|
|
+wire [7:0] usb_ep1_tx_data_w;
|
|
|
+wire usb_ep1_tx_empty_w;
|
|
|
+
|
|
|
+wire usb_ep1_rx_wr_w;
|
|
|
+wire [7:0] usb_ep1_rx_data_w;
|
|
|
+wire usb_ep1_rx_full_w;
|
|
|
+wire usb_ep2_tx_rd_w;
|
|
|
+wire [7:0] usb_ep2_tx_data_w;
|
|
|
+wire usb_ep2_tx_empty_w;
|
|
|
+
|
|
|
+wire usb_ep2_rx_wr_w;
|
|
|
+wire [7:0] usb_ep2_rx_data_w;
|
|
|
+wire usb_ep2_rx_full_w;
|
|
|
+wire usb_ep3_tx_rd_w;
|
|
|
+wire [7:0] usb_ep3_tx_data_w;
|
|
|
+wire usb_ep3_tx_empty_w;
|
|
|
+
|
|
|
+wire usb_ep3_rx_wr_w;
|
|
|
+wire [7:0] usb_ep3_rx_data_w;
|
|
|
+wire usb_ep3_rx_full_w;
|
|
|
+
|
|
|
+// Rx SIE Interface (shared)
|
|
|
+wire rx_strb_w;
|
|
|
+wire [7:0] rx_data_w;
|
|
|
+wire rx_last_w;
|
|
|
+wire rx_crc_err_w;
|
|
|
+
|
|
|
+// EP0 Rx SIE Interface
|
|
|
+wire ep0_rx_space_w;
|
|
|
+wire ep0_rx_valid_w;
|
|
|
+wire ep0_rx_setup_w;
|
|
|
+
|
|
|
+// EP0 Tx SIE Interface
|
|
|
+wire ep0_tx_ready_w;
|
|
|
+wire ep0_tx_data_valid_w;
|
|
|
+wire ep0_tx_data_strb_w;
|
|
|
+wire [7:0] ep0_tx_data_w;
|
|
|
+wire ep0_tx_data_last_w;
|
|
|
+wire ep0_tx_data_accept_w;
|
|
|
+wire ep0_tx_stall_w;
|
|
|
+// EP1 Rx SIE Interface
|
|
|
+wire ep1_rx_space_w;
|
|
|
+wire ep1_rx_valid_w;
|
|
|
+wire ep1_rx_setup_w;
|
|
|
+
|
|
|
+// EP1 Tx SIE Interface
|
|
|
+wire ep1_tx_ready_w;
|
|
|
+wire ep1_tx_data_valid_w;
|
|
|
+wire ep1_tx_data_strb_w;
|
|
|
+wire [7:0] ep1_tx_data_w;
|
|
|
+wire ep1_tx_data_last_w;
|
|
|
+wire ep1_tx_data_accept_w;
|
|
|
+wire ep1_tx_stall_w;
|
|
|
+// EP2 Rx SIE Interface
|
|
|
+wire ep2_rx_space_w;
|
|
|
+wire ep2_rx_valid_w;
|
|
|
+wire ep2_rx_setup_w;
|
|
|
+
|
|
|
+// EP2 Tx SIE Interface
|
|
|
+wire ep2_tx_ready_w;
|
|
|
+wire ep2_tx_data_valid_w;
|
|
|
+wire ep2_tx_data_strb_w;
|
|
|
+wire [7:0] ep2_tx_data_w;
|
|
|
+wire ep2_tx_data_last_w;
|
|
|
+wire ep2_tx_data_accept_w;
|
|
|
+wire ep2_tx_stall_w;
|
|
|
+// EP3 Rx SIE Interface
|
|
|
+wire ep3_rx_space_w;
|
|
|
+wire ep3_rx_valid_w;
|
|
|
+wire ep3_rx_setup_w;
|
|
|
+
|
|
|
+// EP3 Tx SIE Interface
|
|
|
+wire ep3_tx_ready_w;
|
|
|
+wire ep3_tx_data_valid_w;
|
|
|
+wire ep3_tx_data_strb_w;
|
|
|
+wire [7:0] ep3_tx_data_w;
|
|
|
+wire ep3_tx_data_last_w;
|
|
|
+wire ep3_tx_data_accept_w;
|
|
|
+wire ep3_tx_stall_w;
|
|
|
+
|
|
|
+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
|
|
|
+//-----------------------------------------------------------------
|
|
|
+usbf_device_core
|
|
|
+u_core
|
|
|
+(
|
|
|
+ .clk_i(clk_i),
|
|
|
+ .rst_i(rst_i),
|
|
|
+
|
|
|
+ .intr_o(),
|
|
|
+
|
|
|
+ // 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'b0),
|
|
|
+
|
|
|
+ .reg_dev_addr_i(device_addr_q),
|
|
|
+
|
|
|
+ // Rx SIE Interface (shared)
|
|
|
+ .rx_strb_o(rx_strb_w),
|
|
|
+ .rx_data_o(rx_data_w),
|
|
|
+ .rx_last_o(rx_last_w),
|
|
|
+ .rx_crc_err_o(rx_crc_err_w),
|
|
|
+
|
|
|
+ // EP0 Config
|
|
|
+ .ep0_iso_i(1'b0),
|
|
|
+ .ep0_stall_i(ep0_tx_stall_w),
|
|
|
+ .ep0_cfg_int_rx_i(1'b0),
|
|
|
+ .ep0_cfg_int_tx_i(1'b0),
|
|
|
+
|
|
|
+ // EP0 Rx SIE Interface
|
|
|
+ .ep0_rx_setup_o(ep0_rx_setup_w),
|
|
|
+ .ep0_rx_valid_o(ep0_rx_valid_w),
|
|
|
+ .ep0_rx_space_i(ep0_rx_space_w),
|
|
|
+
|
|
|
+ // EP0 Tx SIE Interface
|
|
|
+ .ep0_tx_ready_i(ep0_tx_ready_w),
|
|
|
+ .ep0_tx_data_valid_i(ep0_tx_data_valid_w),
|
|
|
+ .ep0_tx_data_strb_i(ep0_tx_data_strb_w),
|
|
|
+ .ep0_tx_data_i(ep0_tx_data_w),
|
|
|
+ .ep0_tx_data_last_i(ep0_tx_data_last_w),
|
|
|
+ .ep0_tx_data_accept_o(ep0_tx_data_accept_w),
|
|
|
+
|
|
|
+ // EP1 Config
|
|
|
+ .ep1_iso_i(1'b0),
|
|
|
+ .ep1_stall_i(ep1_tx_stall_w),
|
|
|
+ .ep1_cfg_int_rx_i(1'b0),
|
|
|
+ .ep1_cfg_int_tx_i(1'b0),
|
|
|
+
|
|
|
+ // EP1 Rx SIE Interface
|
|
|
+ .ep1_rx_setup_o(ep1_rx_setup_w),
|
|
|
+ .ep1_rx_valid_o(ep1_rx_valid_w),
|
|
|
+ .ep1_rx_space_i(ep1_rx_space_w),
|
|
|
+
|
|
|
+ // EP1 Tx SIE Interface
|
|
|
+ .ep1_tx_ready_i(ep1_tx_ready_w),
|
|
|
+ .ep1_tx_data_valid_i(ep1_tx_data_valid_w),
|
|
|
+ .ep1_tx_data_strb_i(ep1_tx_data_strb_w),
|
|
|
+ .ep1_tx_data_i(ep1_tx_data_w),
|
|
|
+ .ep1_tx_data_last_i(ep1_tx_data_last_w),
|
|
|
+ .ep1_tx_data_accept_o(ep1_tx_data_accept_w),
|
|
|
+
|
|
|
+ // EP2 Config
|
|
|
+ .ep2_iso_i(1'b0),
|
|
|
+ .ep2_stall_i(ep2_tx_stall_w),
|
|
|
+ .ep2_cfg_int_rx_i(1'b0),
|
|
|
+ .ep2_cfg_int_tx_i(1'b0),
|
|
|
+
|
|
|
+ // EP2 Rx SIE Interface
|
|
|
+ .ep2_rx_setup_o(ep2_rx_setup_w),
|
|
|
+ .ep2_rx_valid_o(ep2_rx_valid_w),
|
|
|
+ .ep2_rx_space_i(ep2_rx_space_w),
|
|
|
+
|
|
|
+ // EP2 Tx SIE Interface
|
|
|
+ .ep2_tx_ready_i(ep2_tx_ready_w),
|
|
|
+ .ep2_tx_data_valid_i(ep2_tx_data_valid_w),
|
|
|
+ .ep2_tx_data_strb_i(ep2_tx_data_strb_w),
|
|
|
+ .ep2_tx_data_i(ep2_tx_data_w),
|
|
|
+ .ep2_tx_data_last_i(ep2_tx_data_last_w),
|
|
|
+ .ep2_tx_data_accept_o(ep2_tx_data_accept_w),
|
|
|
+
|
|
|
+ // EP3 Config
|
|
|
+ .ep3_iso_i(1'b0),
|
|
|
+ .ep3_stall_i(ep3_tx_stall_w),
|
|
|
+ .ep3_cfg_int_rx_i(1'b0),
|
|
|
+ .ep3_cfg_int_tx_i(1'b0),
|
|
|
+
|
|
|
+ // EP3 Rx SIE Interface
|
|
|
+ .ep3_rx_setup_o(ep3_rx_setup_w),
|
|
|
+ .ep3_rx_valid_o(ep3_rx_valid_w),
|
|
|
+ .ep3_rx_space_i(ep3_rx_space_w),
|
|
|
+
|
|
|
+ // EP3 Tx SIE Interface
|
|
|
+ .ep3_tx_ready_i(ep3_tx_ready_w),
|
|
|
+ .ep3_tx_data_valid_i(ep3_tx_data_valid_w),
|
|
|
+ .ep3_tx_data_strb_i(ep3_tx_data_strb_w),
|
|
|
+ .ep3_tx_data_i(ep3_tx_data_w),
|
|
|
+ .ep3_tx_data_last_i(ep3_tx_data_last_w),
|
|
|
+ .ep3_tx_data_accept_o(ep3_tx_data_accept_w),
|
|
|
+
|
|
|
+ // Status
|
|
|
+ .reg_sts_rst_clr_i(1'b1),
|
|
|
+ .reg_sts_rst_o(usb_reset_w),
|
|
|
+ .reg_sts_frame_num_o()
|
|
|
+);
|
|
|
+
|
|
|
+assign ep0_rx_space_w = 1'b1;
|
|
|
+
|
|
|
+//-----------------------------------------------------------------
|
|
|
+// 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
|
|
|
+
|
|
|
+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 (ep0_rx_setup_w)
|
|
|
+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 (ep0_rx_valid_w && rx_strb_w)
|
|
|
+begin
|
|
|
+ setup_packet_q[setup_wr_idx_q] <= rx_data_w;
|
|
|
+ setup_wr_idx_q <= setup_wr_idx_q + 3'd1;
|
|
|
+ setup_valid_q <= setup_frame_q && rx_last_w;
|
|
|
+ setup_data_q <= !setup_frame_q && rx_last_w;
|
|
|
+ if (rx_last_w)
|
|
|
+ setup_frame_q <= 1'b0;
|
|
|
+end
|
|
|
+// Detect STATUS stage (ACK for SETUP GET requests)
|
|
|
+// TODO: Not quite correct ....
|
|
|
+else if (ep0_rx_valid_w && !rx_strb_w && rx_last_w)
|
|
|
+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 [15:0] ctrl_get_len_r;
|
|
|
+
|
|
|
+reg [7:0] desc_addr_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;
|
|
|
+
|
|
|
+always @ *
|
|
|
+begin
|
|
|
+ ctrl_stall_r = 1'b0;
|
|
|
+ ctrl_get_len_r = 16'b0;
|
|
|
+ ctrl_ack_r = 1'b0;
|
|
|
+ desc_addr_r = 8'b0;
|
|
|
+ device_addr_r = device_addr_q;
|
|
|
+ addressed_r = addressed_q;
|
|
|
+ configured_r = configured_q;
|
|
|
+ set_with_data_r = set_with_data_q;
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ case (bDescriptorType_w)
|
|
|
+ `DESC_DEVICE:
|
|
|
+ begin
|
|
|
+ desc_addr_r = `ROM_DESC_DEVICE_ADDR;
|
|
|
+ ctrl_get_len_r = `ROM_DESC_DEVICE_SIZE;
|
|
|
+ end
|
|
|
+ `DESC_CONFIGURATION:
|
|
|
+ begin
|
|
|
+ desc_addr_r = `ROM_DESC_CONF_ADDR;
|
|
|
+ ctrl_get_len_r = `ROM_DESC_CONF_SIZE;
|
|
|
+ end
|
|
|
+ `DESC_STRING:
|
|
|
+ begin
|
|
|
+ case (bDescriptorIndex_w)
|
|
|
+ `UNICODE_LANGUAGE_STR_ID:
|
|
|
+ begin
|
|
|
+ desc_addr_r = `ROM_DESC_STR_LANG_ADDR;
|
|
|
+ ctrl_get_len_r = `ROM_DESC_STR_LANG_SIZE;
|
|
|
+ end
|
|
|
+ `MANUFACTURER_STR_ID:
|
|
|
+ begin
|
|
|
+ desc_addr_r = `ROM_DESC_STR_MAN_ADDR;
|
|
|
+ ctrl_get_len_r = `ROM_DESC_STR_MAN_SIZE;
|
|
|
+ end
|
|
|
+ `PRODUCT_NAME_STR_ID:
|
|
|
+ begin
|
|
|
+ desc_addr_r = `ROM_DESC_STR_PROD_ADDR;
|
|
|
+ ctrl_get_len_r = `ROM_DESC_STR_PROD_SIZE;
|
|
|
+ end
|
|
|
+ `SERIAL_NUM_STR_ID:
|
|
|
+ begin
|
|
|
+ desc_addr_r = `ROM_DESC_STR_SERIAL_ADDR;
|
|
|
+ ctrl_get_len_r = `ROM_DESC_STR_SERIAL_SIZE;
|
|
|
+ end
|
|
|
+ default:
|
|
|
+ ;
|
|
|
+ endcase
|
|
|
+ end
|
|
|
+ default:
|
|
|
+ ;
|
|
|
+ endcase
|
|
|
+ end
|
|
|
+ `REQ_GET_CONFIGURATION:
|
|
|
+ begin
|
|
|
+ $display("GET_CONF");
|
|
|
+ end
|
|
|
+ `REQ_SET_CONFIGURATION:
|
|
|
+ begin
|
|
|
+ $display("SET_CONF: Configuration %x", wValue_w);
|
|
|
+
|
|
|
+ if (wValue_w == 16'd0)
|
|
|
+ begin
|
|
|
+ configured_r = 1'b0;
|
|
|
+ ctrl_ack_r = setup_set_w && setup_no_data_w;
|
|
|
+ end
|
|
|
+ // Only support one configuration for now
|
|
|
+ else if (wValue_w == 16'd1)
|
|
|
+ begin
|
|
|
+ configured_r = 1'b1;
|
|
|
+ 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
|
|
|
+ `REQ_SET_INTERFACE:
|
|
|
+ begin
|
|
|
+ $display("SET_INTERFACE: %x %x", wValue_w, wIndex_w);
|
|
|
+ if (wValue_w == 16'd0 && wIndex_w == 16'd0)
|
|
|
+ ctrl_ack_r = setup_set_w && setup_no_data_w;
|
|
|
+ else
|
|
|
+ ctrl_stall_r = 1'b1;
|
|
|
+ end
|
|
|
+ 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
|
|
|
+ case (bRequest_w)
|
|
|
+ `CDC_GET_LINE_CODING:
|
|
|
+ begin
|
|
|
+ $display("CDC_GET_LINE_CODING");
|
|
|
+ desc_addr_r = `ROM_CDC_LINE_CODING_ADDR;
|
|
|
+ ctrl_get_len_r = `ROM_CDC_LINE_CODING_SIZE;
|
|
|
+ end
|
|
|
+ default:
|
|
|
+ begin
|
|
|
+ ctrl_ack_r = setup_set_w && setup_no_data_w;
|
|
|
+ set_with_data_r = setup_set_w && !setup_no_data_w;
|
|
|
+ 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 [15:0] ctrl_send_len_q;
|
|
|
+wire ctrl_send_zlp_w = ctrl_sending_q && (ctrl_send_len_q != wLength);
|
|
|
+
|
|
|
+reg ctrl_sending_r;
|
|
|
+reg [15:0] ctrl_send_idx_r;
|
|
|
+reg [15:0] ctrl_send_len_r;
|
|
|
+
|
|
|
+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 = ep0_tx_data_accept_w || !ep0_tx_data_valid_w;
|
|
|
+
|
|
|
+reg [7: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_send_len_r = ctrl_send_len_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;
|
|
|
+
|
|
|
+ // 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
|
|
|
+ begin
|
|
|
+ ctrl_sending_r = setup_get_w && !ctrl_stall_r;
|
|
|
+ ctrl_send_idx_r = 16'b0;
|
|
|
+ ctrl_send_len_r = ctrl_get_len_r;
|
|
|
+ ctrl_txstall_r = 1'b0;
|
|
|
+ end
|
|
|
+ end
|
|
|
+ // Abort control send when STATUS received
|
|
|
+ else if (status_ready_q)
|
|
|
+ begin
|
|
|
+ ctrl_sending_r = 1'b0;
|
|
|
+ ctrl_send_idx_r = 16'b0;
|
|
|
+ ctrl_send_len_r = 16'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
|
|
|
+ // TODO: Send ZLP on exact multiple lengths...
|
|
|
+ ctrl_txvalid_r = 1'b1;
|
|
|
+ ctrl_txdata_r = desc_data_w;
|
|
|
+ ctrl_txstrb_r = 1'b1;
|
|
|
+ ctrl_txlast_r = usb_hs_w ? (ctrl_send_idx_r[5:0] == 6'b111111) : (ctrl_send_idx_r[2:0] == 3'b111);
|
|
|
+
|
|
|
+ // Increment send index
|
|
|
+ ctrl_send_idx_r = ctrl_send_idx_r + 16'd1;
|
|
|
+
|
|
|
+ // TODO: Detect need for ZLP
|
|
|
+ if (ctrl_send_idx_r == wLength)
|
|
|
+ begin
|
|
|
+ ctrl_sending_r = 1'b0;
|
|
|
+ ctrl_txlast_r = 1'b1;
|
|
|
+ end
|
|
|
+ end
|
|
|
+ else if (ctrl_send_accept_w)
|
|
|
+ ctrl_txvalid_r = 1'b0;
|
|
|
+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_send_len_q <= 16'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_send_len_q <= 16'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_send_len_q <= ctrl_send_len_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;
|
|
|
+
|
|
|
+ if (setup_valid_q)
|
|
|
+ desc_addr_q <= desc_addr_r;
|
|
|
+ else if (ctrl_sending_r && ctrl_send_accept_w)
|
|
|
+ desc_addr_q <= desc_addr_q + 8'd1;
|
|
|
+end
|
|
|
+
|
|
|
+assign ep0_tx_ready_w = ctrl_txvalid_q;
|
|
|
+assign ep0_tx_data_valid_w = ctrl_txvalid_q;
|
|
|
+assign ep0_tx_data_strb_w = ctrl_txstrb_q;
|
|
|
+assign ep0_tx_data_w = ctrl_txdata_q;
|
|
|
+assign ep0_tx_data_last_w = ctrl_txlast_q;
|
|
|
+assign ep0_tx_stall_w = ctrl_txstall_q;
|
|
|
+
|
|
|
+//-----------------------------------------------------------------
|
|
|
+// Descriptor ROM
|
|
|
+//-----------------------------------------------------------------
|
|
|
+usb_desc_rom
|
|
|
+u_rom
|
|
|
+(
|
|
|
+ .hs_i(usb_hs_w),
|
|
|
+ .addr_i(desc_addr_q),
|
|
|
+ .data_o(desc_data_w)
|
|
|
+);
|
|
|
+
|
|
|
+//-----------------------------------------------------------------
|
|
|
+// Unused Endpoints
|
|
|
+//-----------------------------------------------------------------
|
|
|
+assign ep1_tx_ready_w = 1'b0;
|
|
|
+assign ep1_tx_data_valid_w = 1'b0;
|
|
|
+assign ep1_tx_data_strb_w = 1'b0;
|
|
|
+assign ep1_tx_data_w = 8'b0;
|
|
|
+assign ep1_tx_data_last_w = 1'b0;
|
|
|
+assign ep1_tx_stall_w = 1'b0;
|
|
|
+assign ep3_tx_ready_w = 1'b0;
|
|
|
+assign ep3_tx_data_valid_w = 1'b0;
|
|
|
+assign ep3_tx_data_strb_w = 1'b0;
|
|
|
+assign ep3_tx_data_w = 8'b0;
|
|
|
+assign ep3_tx_data_last_w = 1'b0;
|
|
|
+assign ep3_tx_stall_w = 1'b0;
|
|
|
+
|
|
|
+assign ep2_rx_space_w = 1'b0;
|
|
|
+assign ep3_rx_space_w = 1'b0;
|
|
|
+
|
|
|
+//-----------------------------------------------------------------
|
|
|
+// Stream I/O
|
|
|
+//-----------------------------------------------------------------
|
|
|
+reg inport_valid_q;
|
|
|
+reg [7:0] inport_data_q;
|
|
|
+reg [10:0] inport_cnt_q;
|
|
|
+
|
|
|
+always @ (posedge clk_i or posedge rst_i)
|
|
|
+if (rst_i)
|
|
|
+begin
|
|
|
+ inport_valid_q <= 1'b0;
|
|
|
+ inport_data_q <= 8'b0;
|
|
|
+end
|
|
|
+else if (inport_accept_o)
|
|
|
+begin
|
|
|
+ inport_valid_q <= inport_valid_i;
|
|
|
+ inport_data_q <= inport_data_i;
|
|
|
+end
|
|
|
+
|
|
|
+wire [10:0] max_packet_w = usb_hs_w ? 11'd511 : 11'd63;
|
|
|
+wire inport_last_w = !inport_valid_i || (inport_cnt_q == max_packet_w);
|
|
|
+
|
|
|
+always @ (posedge clk_i or posedge rst_i)
|
|
|
+if (rst_i)
|
|
|
+ inport_cnt_q <= 11'b0;
|
|
|
+else if (inport_last_w && ep2_tx_data_accept_w)
|
|
|
+ inport_cnt_q <= 11'b0;
|
|
|
+else if (inport_valid_q && ep2_tx_data_accept_w)
|
|
|
+ inport_cnt_q <= inport_cnt_q + 11'd1;
|
|
|
+
|
|
|
+assign ep2_tx_data_valid_w = inport_valid_q;
|
|
|
+assign ep2_tx_data_w = inport_data_q;
|
|
|
+assign ep2_tx_ready_w = ep2_tx_data_valid_w;
|
|
|
+assign ep2_tx_data_strb_w = ep2_tx_data_valid_w;
|
|
|
+assign ep2_tx_data_last_w = inport_last_w;
|
|
|
+assign inport_accept_o = !inport_valid_q | ep2_tx_data_accept_w;
|
|
|
+
|
|
|
+assign outport_valid_o = ep1_rx_valid_w && rx_strb_w;
|
|
|
+assign outport_data_o = rx_data_w;
|
|
|
+assign ep1_rx_space_w = outport_accept_i;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+endmodule
|