| //----------------------------------------------------------------- |
| // USB CDC Device |
| // V0.1 |
| // Ultra-Embedded.com |
| // Copyright 2014-2019 |
| // |
| // 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 usbf_sie_rx |
| ( |
| // Inputs |
| input clk_i |
| ,input rst_i |
| ,input enable_i |
| ,input [ 7:0] utmi_data_i |
| ,input utmi_rxvalid_i |
| ,input utmi_rxactive_i |
| ,input [ 6:0] current_addr_i |
| |
| // Outputs |
| ,output [ 7:0] pid_o |
| ,output frame_valid_o |
| ,output [ 10:0] frame_number_o |
| ,output token_valid_o |
| ,output [ 6:0] token_addr_o |
| ,output [ 3:0] token_ep_o |
| ,output token_crc_err_o |
| ,output handshake_valid_o |
| ,output data_valid_o |
| ,output data_strb_o |
| ,output [ 7:0] data_o |
| ,output data_last_o |
| ,output data_crc_err_o |
| ,output data_complete_o |
| ); |
| |
| |
| |
| //----------------------------------------------------------------- |
| // Defines: |
| //----------------------------------------------------------------- |
| `include "usbf_defs.v" |
| |
| localparam STATE_W = 4; |
| localparam STATE_RX_IDLE = 4'd0; |
| localparam STATE_RX_TOKEN2 = 4'd1; |
| localparam STATE_RX_TOKEN3 = 4'd2; |
| localparam STATE_RX_TOKEN_COMPLETE = 4'd3; |
| localparam STATE_RX_SOF2 = 4'd4; |
| localparam STATE_RX_SOF3 = 4'd5; |
| localparam STATE_RX_DATA = 4'd6; |
| localparam STATE_RX_DATA_COMPLETE = 4'd7; |
| localparam STATE_RX_IGNORED = 4'd8; |
| reg [STATE_W-1:0] state_q; |
| |
| //----------------------------------------------------------------- |
| // Wire / Regs |
| //----------------------------------------------------------------- |
| `define USB_FRAME_W 11 |
| reg [`USB_FRAME_W-1:0] frame_num_q; |
| |
| `define USB_DEV_W 7 |
| reg [`USB_DEV_W-1:0] token_dev_q; |
| |
| `define USB_EP_W 4 |
| reg [`USB_EP_W-1:0] token_ep_q; |
| |
| `define USB_PID_W 8 |
| reg [`USB_PID_W-1:0] token_pid_q; |
| |
| //----------------------------------------------------------------- |
| // Data delay (to strip the CRC16 trailing bytes) |
| //----------------------------------------------------------------- |
| reg [31:0] data_buffer_q; |
| reg [3:0] data_valid_q; |
| reg [3:0] rx_active_q; |
| |
| wire shift_en_w = (utmi_rxvalid_i & utmi_rxactive_i) || !utmi_rxactive_i; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| data_buffer_q <= 32'b0; |
| else if (shift_en_w) |
| data_buffer_q <= {utmi_data_i, data_buffer_q[31:8]}; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| data_valid_q <= 4'b0; |
| else if (shift_en_w) |
| data_valid_q <= {(utmi_rxvalid_i & utmi_rxactive_i), data_valid_q[3:1]}; |
| else |
| data_valid_q <= {data_valid_q[3:1], 1'b0}; |
| |
| reg [1:0] data_crc_q; |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| data_crc_q <= 2'b0; |
| else if (shift_en_w) |
| data_crc_q <= {!utmi_rxactive_i, data_crc_q[1]}; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| rx_active_q <= 4'b0; |
| else |
| rx_active_q <= {utmi_rxactive_i, rx_active_q[3:1]}; |
| |
| wire [7:0] data_w = data_buffer_q[7:0]; |
| wire data_ready_w = data_valid_q[0]; |
| wire crc_byte_w = data_crc_q[0]; |
| wire rx_active_w = rx_active_q[0]; |
| |
| wire address_match_w = (token_dev_q == current_addr_i); |
| |
| //----------------------------------------------------------------- |
| // Next state |
| //----------------------------------------------------------------- |
| reg [STATE_W-1:0] next_state_r; |
| |
| always @ * |
| begin |
| next_state_r = state_q; |
| |
| case (state_q) |
| |
| //----------------------------------------- |
| // IDLE |
| //----------------------------------------- |
| STATE_RX_IDLE : |
| begin |
| if (data_ready_w) |
| begin |
| // Decode PID |
| case (data_w) |
| |
| `PID_OUT, `PID_IN, `PID_SETUP, `PID_PING: |
| next_state_r = STATE_RX_TOKEN2; |
| |
| `PID_SOF: |
| next_state_r = STATE_RX_SOF2; |
| |
| `PID_DATA0, `PID_DATA1, `PID_DATA2, `PID_MDATA: |
| begin |
| next_state_r = STATE_RX_DATA; |
| end |
| |
| `PID_ACK, `PID_NAK, `PID_STALL, `PID_NYET: |
| next_state_r = STATE_RX_IDLE; |
| |
| default : // SPLIT / ERR |
| next_state_r = STATE_RX_IGNORED; |
| endcase |
| end |
| end |
| |
| //----------------------------------------- |
| // RX_IGNORED: Unknown / unsupported |
| //----------------------------------------- |
| STATE_RX_IGNORED : |
| begin |
| // Wait until the end of the packet |
| if (!rx_active_w) |
| next_state_r = STATE_RX_IDLE; |
| end |
| |
| //----------------------------------------- |
| // SOF (BYTE 2) |
| //----------------------------------------- |
| STATE_RX_SOF2 : |
| begin |
| if (data_ready_w) |
| next_state_r = STATE_RX_SOF3; |
| else if (!rx_active_w) |
| next_state_r = STATE_RX_IDLE; |
| end |
| |
| //----------------------------------------- |
| // SOF (BYTE 3) |
| //----------------------------------------- |
| STATE_RX_SOF3 : |
| begin |
| if (data_ready_w || !rx_active_w) |
| next_state_r = STATE_RX_IDLE; |
| end |
| |
| //----------------------------------------- |
| // TOKEN (IN/OUT/SETUP) (Address/Endpoint) |
| //----------------------------------------- |
| STATE_RX_TOKEN2 : |
| begin |
| if (data_ready_w) |
| next_state_r = STATE_RX_TOKEN3; |
| else if (!rx_active_w) |
| next_state_r = STATE_RX_IDLE; |
| end |
| |
| //----------------------------------------- |
| // TOKEN (IN/OUT/SETUP) (Endpoint/CRC) |
| //----------------------------------------- |
| STATE_RX_TOKEN3 : |
| begin |
| if (data_ready_w) |
| next_state_r = STATE_RX_TOKEN_COMPLETE; |
| else if (!rx_active_w) |
| next_state_r = STATE_RX_IDLE; |
| end |
| |
| //----------------------------------------- |
| // RX_TOKEN_COMPLETE |
| //----------------------------------------- |
| STATE_RX_TOKEN_COMPLETE : |
| begin |
| next_state_r = STATE_RX_IDLE; |
| end |
| |
| //----------------------------------------- |
| // RX_DATA |
| //----------------------------------------- |
| STATE_RX_DATA : |
| begin |
| // Receive complete |
| if (crc_byte_w) |
| next_state_r = STATE_RX_DATA_COMPLETE; |
| end |
| |
| //----------------------------------------- |
| // RX_DATA_COMPLETE |
| //----------------------------------------- |
| STATE_RX_DATA_COMPLETE : |
| begin |
| if (!rx_active_w) |
| next_state_r = STATE_RX_IDLE; |
| end |
| |
| default : |
| ; |
| |
| endcase |
| end |
| |
| // Update state |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| state_q <= STATE_RX_IDLE; |
| else if (!enable_i) |
| state_q <= STATE_RX_IDLE; |
| else |
| state_q <= next_state_r; |
| |
| //----------------------------------------------------------------- |
| // Handshake: |
| //----------------------------------------------------------------- |
| reg handshake_valid_q; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| handshake_valid_q <= 1'b0; |
| else if (state_q == STATE_RX_IDLE && data_ready_w) |
| begin |
| case (data_w) |
| `PID_ACK, `PID_NAK, `PID_STALL, `PID_NYET: |
| handshake_valid_q <= address_match_w; |
| default : |
| handshake_valid_q <= 1'b0; |
| endcase |
| end |
| else |
| handshake_valid_q <= 1'b0; |
| |
| assign handshake_valid_o = handshake_valid_q; |
| |
| //----------------------------------------------------------------- |
| // SOF: Frame number |
| //----------------------------------------------------------------- |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| frame_num_q <= `USB_FRAME_W'b0; |
| else if (state_q == STATE_RX_SOF2 && data_ready_w) |
| frame_num_q <= {3'b0, data_w}; |
| else if (state_q == STATE_RX_SOF3 && data_ready_w) |
| frame_num_q <= {data_w[2:0], frame_num_q[7:0]}; |
| else if (!enable_i) |
| frame_num_q <= `USB_FRAME_W'b0; |
| |
| assign frame_number_o = frame_num_q; |
| |
| reg frame_valid_q; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| frame_valid_q <= 1'b0; |
| else |
| frame_valid_q <= (state_q == STATE_RX_SOF3 && data_ready_w); |
| |
| assign frame_valid_o = frame_valid_q; |
| |
| //----------------------------------------------------------------- |
| // Token: PID |
| //----------------------------------------------------------------- |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| token_pid_q <= `USB_PID_W'b0; |
| else if (state_q == STATE_RX_IDLE && data_ready_w) |
| token_pid_q <= data_w; |
| else if (!enable_i) |
| token_pid_q <= `USB_PID_W'b0; |
| |
| assign pid_o = token_pid_q; |
| |
| reg token_valid_q; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| token_valid_q <= 1'b0; |
| else |
| token_valid_q <= (state_q == STATE_RX_TOKEN_COMPLETE) && address_match_w; |
| |
| assign token_valid_o = token_valid_q; |
| |
| //----------------------------------------------------------------- |
| // Token: Device Address |
| //----------------------------------------------------------------- |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| token_dev_q <= `USB_DEV_W'b0; |
| else if (state_q == STATE_RX_TOKEN2 && data_ready_w) |
| token_dev_q <= data_w[6:0]; |
| else if (!enable_i) |
| token_dev_q <= `USB_DEV_W'b0; |
| |
| assign token_addr_o = token_dev_q; |
| |
| //----------------------------------------------------------------- |
| // Token: Endpoint |
| //----------------------------------------------------------------- |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| token_ep_q <= `USB_EP_W'b0; |
| else if (state_q == STATE_RX_TOKEN2 && data_ready_w) |
| token_ep_q[0] <= data_w[7]; |
| else if (state_q == STATE_RX_TOKEN3 && data_ready_w) |
| token_ep_q[3:1] <= data_w[2:0]; |
| else if (!enable_i) |
| token_ep_q <= `USB_EP_W'b0; |
| |
| assign token_ep_o = token_ep_q; |
| assign token_crc_err_o = 1'b0; |
| |
| wire [7:0] input_data_w = data_w; |
| wire input_ready_w = state_q == STATE_RX_DATA && data_ready_w && !crc_byte_w; |
| |
| //----------------------------------------------------------------- |
| // CRC16: Generate CRC16 on incoming data bytes |
| //----------------------------------------------------------------- |
| reg [15:0] crc_sum_q; |
| wire [15:0] crc_out_w; |
| reg crc_err_q; |
| |
| usbf_crc16 |
| u_crc16 |
| ( |
| .crc_in_i(crc_sum_q), |
| .din_i(data_w), |
| .crc_out_o(crc_out_w) |
| ); |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| crc_sum_q <= 16'hFFFF; |
| else if (state_q == STATE_RX_IDLE) |
| crc_sum_q <= 16'hFFFF; |
| else if (data_ready_w) |
| crc_sum_q <= crc_out_w; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| crc_err_q <= 1'b0; |
| else if (state_q == STATE_RX_IDLE) |
| crc_err_q <= 1'b0; |
| else if (state_q == STATE_RX_DATA_COMPLETE && next_state_r == STATE_RX_IDLE) |
| crc_err_q <= (crc_sum_q != 16'hB001); |
| |
| assign data_crc_err_o = crc_err_q; |
| |
| reg data_complete_q; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| data_complete_q <= 1'b0; |
| else if (state_q == STATE_RX_DATA_COMPLETE && next_state_r == STATE_RX_IDLE) |
| data_complete_q <= 1'b1; |
| else |
| data_complete_q <= 1'b0; |
| |
| assign data_complete_o = data_complete_q; |
| |
| reg data_zlp_q; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| data_zlp_q <= 1'b0; |
| else if (state_q == STATE_RX_IDLE && next_state_r == STATE_RX_DATA) |
| data_zlp_q <= 1'b1; |
| else if (input_ready_w) |
| data_zlp_q <= 1'b0; |
| |
| //----------------------------------------------------------------- |
| // Data Output |
| //----------------------------------------------------------------- |
| reg valid_q; |
| reg last_q; |
| reg [7:0] data_q; |
| reg mask_q; |
| |
| always @ (posedge clk_i or posedge rst_i) |
| if (rst_i) |
| begin |
| valid_q <= 1'b0; |
| data_q <= 8'b0; |
| mask_q <= 1'b0; |
| last_q <= 1'b0; |
| end |
| else |
| begin |
| valid_q <= input_ready_w || ((state_q == STATE_RX_DATA) && crc_byte_w && data_zlp_q); |
| data_q <= input_data_w; |
| mask_q <= input_ready_w; |
| last_q <= (state_q == STATE_RX_DATA) && crc_byte_w; |
| end |
| |
| // Data |
| assign data_valid_o = valid_q; |
| assign data_strb_o = mask_q; |
| assign data_o = data_q; |
| assign data_last_o = last_q | crc_byte_w; |
| |
| |
| endmodule |