blob: f83656dd9a1aa3711d84adb1af96b37ac8372dd5 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// SPDX-FileCopyrightText: 2021 , Dinesh Annayya
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileContributor: Created by Dinesh Annayya <dinesha@opencores.org>
//
//-----------------------------------------------------------------
// USB Full Speed (12mbps) Phy
// V0.2
// Ultra-Embedded.com
// Copyright 2015
//
// 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_fs_phy
//-----------------------------------------------------------------
// Params
//-----------------------------------------------------------------
#(
parameter USB_CLK_FREQ = 60000000
)
(
// Inputs
input clk_i
,input rstn_i
,input [ 7:0] utmi_data_out_i
,input utmi_txvalid_i
,input [ 1:0] utmi_op_mode_i
,input [ 1:0] utmi_xcvrselect_i
,input utmi_termselect_i
,input utmi_dppulldown_i
,input utmi_dmpulldown_i
,input usb_rx_rcv_i
,input usb_rx_dp_i
,input usb_rx_dn_i
,input usb_reset_assert_i
// Outputs
,output [ 7:0] utmi_data_in_o
,output utmi_txready_o
,output utmi_rxvalid_o
,output utmi_rxactive_o
,output utmi_rxerror_o
,output [ 1:0] utmi_linestate_o
,output usb_tx_dp_o
,output usb_tx_dn_o
,output usb_tx_oen_o
,output usb_reset_detect_o
,output usb_en_o
);
//-------------------------------------------------------------------------
// For 60Mhz usb clock, data need to sample at once in 4 cycle (60/4 = 12Mhz)
// For 48Mhz usb clock, data need to sample at once in 3 cycle (48/3 = 12Mhz)
// ------------------------------------------------------------------------
localparam SAMPLE_RATE = (USB_CLK_FREQ == 60000000) ? 3'd4 : 3'd3;
//-----------------------------------------------------------------
// Wires / Registers
//-----------------------------------------------------------------
reg rx_en_q;
// Xilinx placement pragmas:
//synthesis attribute IOB of out_dp_q is "TRUE"
//synthesis attribute IOB of out_dn_q is "TRUE"
reg out_dp_q;
reg out_dn_q;
wire in_dp_w;
wire in_dn_w;
wire in_rx_w;
wire in_j_w;
wire in_k_w;
wire in_se0_w;
wire in_invalid_w;
wire sample_w;
wire bit_edge_w;
wire bit_transition_w;
reg [2:0] bit_count_q;
reg [2:0] ones_count_q;
reg [7:0] data_q;
reg send_eop_q;
reg sync_j_detected_q;
wire bit_stuff_bit_w;
wire next_is_bit_stuff_w;
wire usb_reset_assert_w = usb_reset_assert_i |
(utmi_xcvrselect_i == 2'b00 &&
utmi_termselect_i == 1'b0 &&
utmi_op_mode_i == 2'b10 &&
utmi_dppulldown_i &&
utmi_dmpulldown_i);
//-----------------------------------------------------------------
// Resample async signals
//-----------------------------------------------------------------
reg rx_dp_ms;
reg rx_dn_ms;
reg rxd_ms;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
begin
rx_dp_ms <= 1'b0;
rx_dn_ms <= 1'b0;
rxd_ms <= 1'b0;
end
else
begin
rx_dp_ms <= in_dp_w;
rx_dn_ms <= in_dn_w;
rxd_ms <= in_rx_w;
end
//-----------------------------------------------------------------
// Edge Detection
//-----------------------------------------------------------------
reg rx_dp0_q;
reg rx_dn0_q;
reg rx_dp1_q;
reg rx_dn1_q;
reg rx_dp_q;
reg rx_dn_q;
reg rxd0_q;
reg rxd1_q;
reg rxd_q;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
begin
rx_dp0_q <= 1'b0;
rx_dn0_q <= 1'b0;
rx_dp1_q <= 1'b0;
rx_dn1_q <= 1'b0;
rx_dp_q <= 1'b0;
rx_dn_q <= 1'b0;
rxd0_q <= 1'b0;
rxd1_q <= 1'b0;
rxd_q <= 1'b0;
end
else
begin
// Glitch free versions
if (rx_dp0_q & rx_dp1_q)
rx_dp_q <= 1'b1;
else if (!rx_dp0_q & !rx_dp1_q)
rx_dp_q <= 1'b0;
if (rx_dn0_q & rx_dn1_q)
rx_dn_q <= 1'b1;
else if (!rx_dn0_q & !rx_dn1_q)
rx_dn_q <= 1'b0;
if (rxd0_q & rxd1_q)
rxd_q <= 1'b1;
else if (!rxd0_q & !rxd1_q)
rxd_q <= 1'b0;
// Resyncs
rx_dp1_q <= rx_dp0_q;
rx_dp0_q <= rx_dp_ms;
rx_dn1_q <= rx_dn0_q;
rx_dn0_q <= rx_dn_ms;
rxd1_q <= rxd0_q;
rxd0_q <= rxd_ms;
end
// For Full Speed USB:
// SE0 = D+ = 0 && D- = 0
// J = D+ = 1 && D- = 0
// K = D+ = 0 && D- = 1
assign in_j_w = in_se0_w ? 1'b0 : rxd_q;
assign in_k_w = in_se0_w ? 1'b0 : ~rxd_q;
assign in_se0_w = (!rx_dp_q & !rx_dn_q);
assign in_invalid_w = (rx_dp_q & rx_dn_q);
// Line state matches tx outputs if drivers enabled
assign utmi_linestate_o = usb_tx_oen_o ? {rx_dn_q, rx_dp_q} : {usb_tx_dn_o, usb_tx_dp_o};
//-----------------------------------------------------------------
// State Machine
//-----------------------------------------------------------------
localparam STATE_W = 4;
localparam STATE_IDLE = 4'd0;
localparam STATE_RX_DETECT = 4'd1;
localparam STATE_RX_SYNC_J = 4'd2;
localparam STATE_RX_SYNC_K = 4'd3;
localparam STATE_RX_ACTIVE = 4'd4;
localparam STATE_RX_EOP0 = 4'd5;
localparam STATE_RX_EOP1 = 4'd6;
localparam STATE_TX_SYNC = 4'd7;
localparam STATE_TX_ACTIVE = 4'd8;
localparam STATE_TX_EOP_STUFF = 4'd9;
localparam STATE_TX_EOP0 = 4'd10;
localparam STATE_TX_EOP1 = 4'd11;
localparam STATE_TX_EOP2 = 4'd12;
localparam STATE_TX_RST = 4'd13;
// Current state
reg [STATE_W-1:0] state_q;
reg [STATE_W-1:0] next_state_r;
always @ *
begin
next_state_r = state_q;
case (state_q)
//-----------------------------------------
// STATE_IDLE
//-----------------------------------------
STATE_IDLE :
begin
if (in_k_w)
next_state_r = STATE_RX_DETECT;
else if (utmi_txvalid_i)
next_state_r = STATE_TX_SYNC;
else if (usb_reset_assert_w)
next_state_r = STATE_TX_RST;
end
//-----------------------------------------
// STATE_RX_DETECT
//-----------------------------------------
STATE_RX_DETECT :
begin
if (in_k_w && sample_w)
next_state_r = STATE_RX_SYNC_K;
else if (sample_w)
next_state_r = STATE_IDLE;
end
//-----------------------------------------
// STATE_RX_SYNC_J
//-----------------------------------------
STATE_RX_SYNC_J :
begin
if (in_k_w && sample_w)
next_state_r = STATE_RX_SYNC_K;
// K glitch followed by multiple J's - return to idle
else if ((bit_count_q == 3'd1) && sample_w)
next_state_r = STATE_IDLE;
end
//-----------------------------------------
// STATE_RX_SYNC_K
//-----------------------------------------
STATE_RX_SYNC_K :
begin
// End of SYNC field ends with 2 K's
// Must have seen at least 1 J state first!
if (sync_j_detected_q && in_k_w && sample_w)
next_state_r = STATE_RX_ACTIVE;
// No J detected since IDLE, must be an error!
else if (!sync_j_detected_q && in_k_w && sample_w)
next_state_r = STATE_IDLE;
else if (in_j_w && sample_w)
next_state_r = STATE_RX_SYNC_J;
end
//-----------------------------------------
// STATE_RX_ACTIVE
//-----------------------------------------
STATE_RX_ACTIVE :
begin
if (in_se0_w && sample_w)
next_state_r = STATE_RX_EOP0;
// Error!
else if (in_invalid_w && sample_w)
next_state_r = STATE_IDLE;
end
//-----------------------------------------
// STATE_RX_EOP0
//-----------------------------------------
STATE_RX_EOP0 :
begin
if (in_se0_w && sample_w)
next_state_r = STATE_RX_EOP1;
// Error!
else if (sample_w)
next_state_r = STATE_IDLE;
end
//-----------------------------------------
// STATE_RX_EOP1
//-----------------------------------------
STATE_RX_EOP1 :
begin
// Return to idle
if (in_j_w && sample_w)
next_state_r = STATE_IDLE;
// Error!
else if (sample_w)
next_state_r = STATE_IDLE;
end
//-----------------------------------------
// STATE_TX_SYNC
//-----------------------------------------
STATE_TX_SYNC :
begin
if (bit_count_q == 3'd7 && sample_w)
next_state_r = STATE_TX_ACTIVE;
end
//-----------------------------------------
// STATE_TX_ACTIVE
//-----------------------------------------
STATE_TX_ACTIVE :
begin
if (bit_count_q == 3'd7 && sample_w && (!utmi_txvalid_i || send_eop_q) && !bit_stuff_bit_w)
begin
// Bit stuff required at end of packet?
if (next_is_bit_stuff_w)
next_state_r = STATE_TX_EOP_STUFF;
else
next_state_r = STATE_TX_EOP0;
end
end
//-----------------------------------------
// STATE_TX_EOP_STUFF
//-----------------------------------------
STATE_TX_EOP_STUFF :
begin
if (sample_w)
next_state_r = STATE_TX_EOP0;
end
//-----------------------------------------
// STATE_TX_EOP0
//-----------------------------------------
STATE_TX_EOP0 :
begin
if (sample_w)
next_state_r = STATE_TX_EOP1;
end
//-----------------------------------------
// STATE_TX_EOP1
//-----------------------------------------
STATE_TX_EOP1 :
begin
if (sample_w)
next_state_r = STATE_TX_EOP2;
end
//-----------------------------------------
// STATE_TX_EOP2
//-----------------------------------------
STATE_TX_EOP2 :
begin
if (sample_w)
next_state_r = STATE_IDLE;
end
//-----------------------------------------
// STATE_TX_RST
//-----------------------------------------
STATE_TX_RST :
begin
if (!usb_reset_assert_w)
next_state_r = STATE_IDLE;
end
default:
;
endcase
end
// Update state
always @ (negedge rstn_i or posedge clk_i)
if (!rstn_i)
state_q <= STATE_IDLE;
else
state_q <= next_state_r;
//-----------------------------------------------------------------
// SYNC detect
//-----------------------------------------------------------------
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
sync_j_detected_q <= 1'b0;
// Reset sync detect state in IDLE
else if (state_q == STATE_IDLE)
sync_j_detected_q <= 1'b0;
// At least one J detected
else if (state_q == STATE_RX_SYNC_J)
sync_j_detected_q <= 1'b1;
//-----------------------------------------------------------------
// Rx Error Detection
//-----------------------------------------------------------------
reg rx_error_q;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
rx_error_q <= 1'b0;
// Rx bit stuffing error
else if (ones_count_q == 3'd7)
rx_error_q <= 1'b1;
// Invalid line state detection
else if (in_invalid_w && sample_w)
rx_error_q <= 1'b1;
// Detect invalid SYNC sequence
else if ((state_q == STATE_RX_SYNC_K) && !sync_j_detected_q && in_k_w && sample_w)
rx_error_q <= 1'b1;
else
rx_error_q <= 1'b0;
assign utmi_rxerror_o = rx_error_q;
//-----------------------------------------------------------------
// Edge Detector
//-----------------------------------------------------------------
reg rxd_last_q;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
rxd_last_q <= 1'b0;
else
rxd_last_q <= in_j_w;
assign bit_edge_w = rxd_last_q ^ in_j_w;
//-----------------------------------------------------------------
// Sample Timer
//-----------------------------------------------------------------
reg [2:0] sample_cnt_q;
reg adjust_delayed_q;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i) begin
sample_cnt_q <= 3'd0;
adjust_delayed_q <= 1'b0;
end else begin
// Delayed adjustment
if (adjust_delayed_q)
adjust_delayed_q <= 1'b0;
else if (bit_edge_w && (sample_cnt_q != 3'd0) && (state_q < STATE_TX_SYNC))
sample_cnt_q <= 3'd0;
// Can't adjust sampling point now?
else if (bit_edge_w && (sample_cnt_q == 3'd0) && (state_q < STATE_TX_SYNC)) begin
// Want to reset sampling point but need to delay adjustment by 1 cycle!
adjust_delayed_q <= 1'b1;
if(sample_cnt_q == SAMPLE_RATE)
sample_cnt_q <= 'b0;
else
sample_cnt_q <= sample_cnt_q + 'd1;
end else begin
if(sample_cnt_q == SAMPLE_RATE)
sample_cnt_q <= 'b0;
else
sample_cnt_q <= sample_cnt_q + 'd1;
end
end
assign sample_w = (sample_cnt_q == 'd0);
//-----------------------------------------------------------------
// NRZI Receiver
//-----------------------------------------------------------------
reg rxd_last_j_q;
// NRZI:
// 0 = transition between J & K
// 1 = same state
// After 6 consequitive 1's, a 0 is inserted to maintain the transitions
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
rxd_last_j_q <= 1'b0;
else if ((state_q == STATE_IDLE) || sample_w)
rxd_last_j_q <= in_j_w;
assign bit_transition_w = sample_w ? rxd_last_j_q ^ in_j_w : 1'b0;
//-----------------------------------------------------------------
// Bit Counters
//-----------------------------------------------------------------
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
ones_count_q <= 3'd1;
// The packet starts with a double K (no transition)
else if (state_q == STATE_IDLE)
ones_count_q <= 3'd1;
// Rx
else if ((state_q == STATE_RX_ACTIVE) && sample_w)
begin
if (bit_transition_w)
ones_count_q <= 3'b0;
else
ones_count_q <= ones_count_q + 3'd1;
end
// Tx
else if ((state_q == STATE_TX_ACTIVE) && sample_w)
begin
// Toggle output data
if (!data_q[0] || bit_stuff_bit_w)
ones_count_q <= 3'b0;
else
ones_count_q <= ones_count_q + 3'd1;
end
assign bit_stuff_bit_w = (ones_count_q == 3'd6);
assign next_is_bit_stuff_w = (ones_count_q == 3'd5) && !bit_transition_w;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
bit_count_q <= 3'b0;
else if ((state_q == STATE_IDLE) || (state_q == STATE_RX_SYNC_K))
bit_count_q <= 3'b0;
else if ((state_q == STATE_RX_ACTIVE || state_q == STATE_TX_ACTIVE) && sample_w && !bit_stuff_bit_w)
bit_count_q <= bit_count_q + 3'd1;
else if (((state_q == STATE_TX_SYNC) || (state_q == STATE_RX_SYNC_J)) && sample_w)
bit_count_q <= bit_count_q + 3'd1;
//-----------------------------------------------------------------
// Shift register
//-----------------------------------------------------------------
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
data_q <= 8'b0;
// Pre-load shift register with SYNC word
else if (state_q == STATE_IDLE)
data_q <= 8'b00101010;
else if ((state_q == STATE_RX_ACTIVE) && sample_w && !bit_stuff_bit_w)
data_q <= {~bit_transition_w, data_q[7:1]};
else if ((state_q == STATE_TX_SYNC) && sample_w)
begin
if (bit_count_q == 3'd7)
data_q <= utmi_data_out_i;
else
data_q <= {~bit_transition_w, data_q[7:1]};
end
else if ((state_q == STATE_TX_ACTIVE) && sample_w && !bit_stuff_bit_w)
begin
if (bit_count_q == 3'd7)
data_q <= utmi_data_out_i;
else
data_q <= {~bit_transition_w, data_q[7:1]};
end
// Receive active (SYNC recieved)
assign utmi_rxactive_o = (state_q == STATE_RX_ACTIVE);
assign utmi_data_in_o = data_q;
//-----------------------------------------------------------------
// Rx Ready
//-----------------------------------------------------------------
reg rx_ready_q;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
rx_ready_q <= 1'b0;
else if ((state_q == STATE_RX_ACTIVE) && sample_w && (bit_count_q == 3'd7) && !bit_stuff_bit_w)
rx_ready_q <= 1'b1;
else
rx_ready_q <= 1'b0;
assign utmi_rxvalid_o = rx_ready_q;
//-----------------------------------------------------------------
// Tx Ready
//-----------------------------------------------------------------
reg tx_ready_q;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
tx_ready_q <= 1'b0;
else if ((state_q == STATE_TX_SYNC) && sample_w && (bit_count_q == 3'd7))
tx_ready_q <= 1'b1;
else if ((state_q == STATE_TX_ACTIVE) && sample_w && !bit_stuff_bit_w && (bit_count_q == 3'd7) && !send_eop_q)
tx_ready_q <= 1'b1;
else
tx_ready_q <= 1'b0;
assign utmi_txready_o = tx_ready_q;
//-----------------------------------------------------------------
// EOP pending
//-----------------------------------------------------------------
always @ (negedge rstn_i or negedge clk_i)
if (!rstn_i)
send_eop_q <= 1'b0;
else if ((state_q == STATE_TX_ACTIVE) && !utmi_txvalid_i)
send_eop_q <= 1'b1;
else if (state_q == STATE_TX_EOP0)
send_eop_q <= 1'b0;
//-----------------------------------------------------------------
// Tx
//-----------------------------------------------------------------
wire out_bit_w = sample_w ? data_q[0] : 1'bz;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
begin
out_dp_q <= 1'b0;
out_dn_q <= 1'b0;
rx_en_q <= 1'b1;
end
else if (state_q == STATE_IDLE)
begin
// IDLE
out_dp_q <= 1'b1;
out_dn_q <= 1'b0;
if (utmi_txvalid_i || usb_reset_assert_w)
rx_en_q <= 1'b0;
else
rx_en_q <= 1'b1;
end
else if ((state_q == STATE_TX_SYNC) && sample_w)
begin
out_dp_q <= data_q[0];
out_dn_q <= ~data_q[0];
end
else if ((state_q == STATE_TX_ACTIVE || state_q == STATE_TX_EOP_STUFF) && sample_w)
begin
// 0 = toggle, 1 = hold
if (!data_q[0] || bit_stuff_bit_w)
begin
out_dp_q <= ~out_dp_q;
out_dn_q <= ~out_dn_q;
end
end
else if ((state_q == STATE_TX_EOP0 || state_q == STATE_TX_EOP1) && sample_w)
begin
// SE0
out_dp_q <= 1'b0;
out_dn_q <= 1'b0;
end
else if ((state_q == STATE_TX_EOP2) && sample_w)
begin
// IDLE
out_dp_q <= 1'b1;
out_dn_q <= 1'b0;
// Set bus to input
rx_en_q <= 1'b1;
end
else if (state_q == STATE_TX_RST)
begin
// SE0
out_dp_q <= 1'b0;
out_dn_q <= 1'b0;
end
//-----------------------------------------------------------------
// Reset detection
//-----------------------------------------------------------------
reg [6:0] se0_cnt_q;
always @ (posedge clk_i or negedge rstn_i)
if (!rstn_i)
se0_cnt_q <= 7'b0;
else if (in_se0_w)
begin
if (se0_cnt_q != 7'd127)
se0_cnt_q <= se0_cnt_q + 7'd1;
end
else
se0_cnt_q <= 7'b0;
assign usb_reset_detect_o = (se0_cnt_q == 7'd127);
//-----------------------------------------------------------------
// Transceiver Interface
//-----------------------------------------------------------------
// Tx output enable (active low)
assign usb_tx_oen_o = rx_en_q;
// Tx +/-
assign usb_tx_dp_o = out_dp_q;
assign usb_tx_dn_o = out_dn_q;
// Receive D+/D-
assign in_dp_w = usb_rx_dp_i;
assign in_dn_w = usb_rx_dn_i;
// Receive data
assign in_rx_w = usb_rx_rcv_i;
// USB device pull-up enable
assign usb_en_o = utmi_termselect_i;
endmodule