www.pudn.com > USB2.0_rtl_ipcore_verilog.rar > usbf_utmi_ls.v
/////////////////////////////////////////////////////////////////////
//// ////
//// UTMI Line Status & Speed Negotiation block ////
//// ////
//// ////
//// Author: Rudolf Usselmann ////
//// rudi@asics.ws ////
//// ////
//// ////
//// Downloaded from: http://www.opencores.org/cores/usb/ ////
//// ////
/////////////////////////////////////////////////////////////////////
//// ////
//// Copyright (C) 2000-2003 Rudolf Usselmann ////
//// www.asics.ws ////
//// rudi@asics.ws ////
//// ////
//// 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 SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY ////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR ////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ////
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ////
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE ////
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ////
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ////
//// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ////
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ////
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ////
//// POSSIBILITY OF SUCH DAMAGE. ////
//// ////
/////////////////////////////////////////////////////////////////////
// CVS Log
//
// $Id: usbf_utmi_ls.v,v 1.6 2003/11/11 07:15:16 rudi Exp $
//
// $Date: 2003/11/11 07:15:16 $
// $Revision: 1.6 $
// $Author: rudi $
// $Locker: $
// $State: Exp $
//
// Change History:
// $Log: usbf_utmi_ls.v,v $
// Revision 1.6 2003/11/11 07:15:16 rudi
// Fixed Resume signaling and initial attachment
//
// Revision 1.5 2003/10/17 02:36:57 rudi
// - Disabling bit stuffing and NRZI encoding during speed negotiation
// - Now the core can send zero size packets
// - Fixed register addresses for some of the higher endpoints
// (conversion between decimal/hex was wrong)
// - The core now does properly evaluate the function address to
// determine if the packet was intended for it.
// - Various other minor bugs and typos
//
// Revision 1.4 2001/11/04 12:22:45 rudi
//
// - Fixed previous fix (brocke something else ...)
// - Majore Synthesis cleanup
//
// Revision 1.3 2001/09/24 01:15:28 rudi
//
// Changed reset to be active high async.
//
// Revision 1.2 2001/08/10 08:48:33 rudi
//
// - Changed IO names to be more clear.
// - Uniquifyed define names to be core specific.
//
// Revision 1.1 2001/08/03 05:30:09 rudi
//
//
// 1) Reorganized directory structure
//
// Revision 1.2 2001/03/31 13:00:52 rudi
//
// - Added Core configuration
// - Added handling of OUT packets less than MAX_PL_SZ in DMA mode
// - Modified WISHBONE interface and sync logic
// - Moved SSRAM outside the core (added interface)
// - Many small bug fixes ...
//
// Revision 1.1 2001/03/07 09:08:13 rudi
//
// Added USB control signaling (Line Status) block. Fixed some minor
// typos, added resume bit and signal.
//
//
//
`include "usbf_defines.v"
module usbf_utmi_ls( clk, rst,
resume_req,
// UTMI Interface
rx_active, tx_ready, drive_k,
XcvSelect, TermSel, SuspendM, LineState, OpMode,
usb_vbus,
// Misc Interfaces
mode_hs, usb_reset, usb_suspend, usb_attached,
suspend_clr
);
input clk;
//input wclk;
input rst;
input resume_req;
input rx_active, tx_ready;
output drive_k;
output XcvSelect;
output TermSel;
output SuspendM;
input [1:0] LineState;
output [1:0] OpMode;
input usb_vbus;
output mode_hs; // High Speed Mode
output usb_reset; // USB Reset
output usb_suspend; // USB Suspend
output usb_attached; // Attached to USB
output suspend_clr;
///////////////////////////////////////////////////////////////////
//
// Parameters
//
parameter [14:0] // synopsys enum state
POR = 15'b000_0000_0000_0001,
NORMAL = 15'b000_0000_0000_0010,
RES_SUSP = 15'b000_0000_0000_0100,
SUSPEND = 15'b000_0000_0000_1000,
RESUME = 15'b000_0000_0001_0000,
RESUME_REQUEST = 15'b000_0000_0010_0000,
RESUME_WAIT = 15'b000_0000_0100_0000,
RESUME_SIG = 15'b000_0000_1000_0000,
ATTACH = 15'b000_0001_0000_0000,
RESET = 15'b000_0010_0000_0000,
SPEED_NEG = 15'b000_0100_0000_0000,
SPEED_NEG_K = 15'b000_1000_0000_0000,
SPEED_NEG_J = 15'b001_0000_0000_0000,
SPEED_NEG_HS = 15'b010_0000_0000_0000,
SPEED_NEG_FS = 15'b100_0000_0000_0000;
///////////////////////////////////////////////////////////////////
//
// Local Wires and Registers
//
reg [14:0] /* synopsys enum state */ state, next_state;
// synopsys state_vector state
reg [1:0] line_state_r;
reg mode_hs, mode_set_hs, mode_set_fs;
reg usb_suspend, suspend_set, suspend_clr;
reg usb_attached, attached_set, attached_clr;
reg TermSel, fs_term_on, fs_term_off;
reg XcvSelect, xcv_set_hs, xcv_set_fs;
reg [1:0] OpMode;
reg bit_stuff_on, bit_stuff_off;
reg usb_reset, usb_reset_d;
wire ls_se0, ls_j, ls_k, ls_se1;
reg ls_k_r, ls_j_r, ls_se0_r;
reg ls_idle_r;
wire ls_idle;
reg idle_long;
wire idle_long_set, idle_long_clr;
wire k_long, j_long, se0_long;
reg drive_k, drive_k_d;
reg [3:0] ps_cnt;
reg ps_cnt_clr;
reg idle_cnt_clr;
reg idle_cnt1_clr,idle_cnt0_clr;
reg [7:0] idle_cnt1, idle_cnt1_next,idle_cnt0,idle_cnt0_next;
reg T1_gt_2_5_uS, T1_st_3_0_mS, T1_gt_3_0_mS;
reg T1_gt_3_125_mS, T1_gt_5_0_mS;
reg [7:0] me_ps;
reg me_cnt_clr;
reg me_ps_2_5_us;
reg [7:0] me_ps2;
reg me_ps2_0_5_ms;
reg [7:0] me_cnt;
reg me_cnt_100_ms;
reg T2_gt_100_uS, T2_wakeup, T2_gt_1_0_mS, T2_gt_1_2_mS;
reg [2:0] chirp_cnt;
reg chirp_cnt_clr, chirp_cnt_inc;
reg chirp_cnt_is_6;
reg resume_req_s1;
reg resume_req_s;
///////////////////////////////////////////////////////////////////
//
// Misc Logic
//
always @(posedge clk)
drive_k <= drive_k_d;
assign SuspendM = (usb_suspend & !resume_req_s) | (LineState == 2'b10);
always @(posedge clk)
resume_req_s1 <= resume_req;
always @(posedge clk)
resume_req_s <= resume_req_s1;
// ---------------------------------------------------------
// USB State/Operation Mode JK Flops
always @(posedge clk)
if(mode_set_fs) mode_hs <= 1'b0;
else
if(mode_set_hs) mode_hs <= 1'b1;
always @(posedge clk)
if(suspend_clr) usb_suspend <= 1'b0;
else
if(suspend_set) usb_suspend <= 1'b1;
always @(posedge clk)
if(attached_clr) usb_attached <= 1'b0;
else
if(attached_set) usb_attached <= 1'b1;
always @(posedge clk)
if(fs_term_off) TermSel <= 1'b0;
else
if(fs_term_on) TermSel <= 1'b1;
always @(posedge clk)
if(xcv_set_fs) XcvSelect <= 1'b1;
else
if(xcv_set_hs) XcvSelect <= 1'b0;
always @(posedge clk)
if(bit_stuff_off) OpMode <= 2'b10;
else
if(bit_stuff_on) OpMode <= 2'b00;
always @(posedge clk)
usb_reset <= usb_reset_d;
// ---------------------------------------------------------
// Line State Detector
always @(posedge clk)
line_state_r <= LineState;
assign ls_se0 = (line_state_r == 2'b00);
assign ls_j = (line_state_r == 2'b01);
assign ls_k = (line_state_r == 2'b10);
assign ls_se1 = (line_state_r == 2'b11);
assign ls_idle = mode_hs ? ls_se0 : ls_j;
// Idle Detection
// Idle Has to persist for at least two cycles in a roe in the
// same state to recognized
always @(posedge clk)
ls_idle_r <= ls_idle;
assign idle_long_set = ls_idle & ls_idle_r;
assign idle_long_clr = !ls_idle & !ls_idle_r;
`ifdef USBF_ASYNC_RESET
always @(posedge clk or negedge rst)
`else
always @(posedge clk)
`endif
if(!rst) idle_long <= 1'b0;
else
if(idle_long_clr) idle_long <= 1'b0;
else
if(idle_long_set) idle_long <= 1'b1;
// Detect Signals for two cycles ina row before making a transaction ...
always @(posedge clk)
ls_k_r <= ls_k;
always @(posedge clk)
ls_j_r <= ls_j;
always @(posedge clk)
ls_se0_r <= ls_se0;
assign k_long = ls_k & ls_k_r;
assign j_long = ls_j & ls_j_r;
assign se0_long = ls_se0 & ls_se0_r;
///////////////////////////////////////////////////////////////////
//
// Counters
//
// ---------------------------------------------------------
// idle Counter
// Pre-Scaler
// Generates a 0.25 uS Count Enable (ps_cnt_clr)
always @(posedge clk)
if(!idle_long || idle_cnt_clr || ps_cnt_clr) ps_cnt <= 4'd0;
else ps_cnt <= ps_cnt + 4'd1;
always @(posedge clk) // Clear the pre-scaler in 250 nS intervals
ps_cnt_clr <= (ps_cnt == `USBF_T1_PS_250_NS);
// ---------------------------------------------------------
// Count 62.5uS every 0.25us
always @(posedge clk)
if(!idle_long || idle_cnt0_clr || idle_cnt_clr) idle_cnt0 <= 8'h0;
else
if( ps_cnt_clr) idle_cnt0 <= idle_cnt0_next;
always @(posedge clk)
idle_cnt0_next <= idle_cnt0 + 8'h1;
always @(posedge clk) // Clear the uS counter every 62.5 uS
idle_cnt0_clr <= idle_cnt0 == `USBF_T1_C_62_5_US;
//always @(posedge clk) // Greater Than 2.5uS (Actual Time will be T0+2.75uS)
// if(idle_cnt_clr) T1_gt_2_5_uS <= 1'h0;
// else if (idle_cnt0 > `USBF_T1_C_2_5_US)
// T1_gt_2_5_uS <= 1'h1;
always @(posedge clk) // Greater Than 2.5uS (Actual Time will be T0+2.75uS)
T1_gt_2_5_uS <= !idle_cnt_clr&(idle_cnt0 > `USBF_T1_C_2_5_US);
// ---------------------------------------------------------
// Count mS every 62.5us
always @(posedge clk)
if(!idle_long || idle_cnt1_clr || idle_cnt_clr) idle_cnt1 <= 8'h0;
else
if(!T1_gt_5_0_mS && idle_cnt0_clr) idle_cnt1 <= idle_cnt1_next;
always @(posedge clk)
idle_cnt1_next <= idle_cnt1 + 8'h1;
always @(posedge clk) // Clear the uS counter every 62.5 uS
idle_cnt1_clr <= idle_cnt1 == `USBF_T1_C_5_MS;
always @(posedge clk) // Smaller Than 3 mS (Actual Time will be 0-2.9375mS)
T1_st_3_0_mS <= !idle_cnt_clr & (idle_cnt1 < `USBF_T1_C_3_0_MS);
always @(posedge clk) // Greater Than 3 mS (Actual Time will be T0+3.0625mS)
T1_gt_3_0_mS <= !idle_cnt_clr & (idle_cnt1 > `USBF_T1_C_3_0_MS);
always @(posedge clk) // Greater Than 3.125 mS (Actual Time will be T0+3.1875mS)
T1_gt_3_125_mS <= !idle_cnt_clr & (idle_cnt1 > `USBF_T1_C_3_125_MS);
always @(posedge clk) // Greater Than 5 mS (Actual Time will be T0+5.0625mS)
T1_gt_5_0_mS <= !idle_cnt_clr & (idle_cnt1 > `USBF_T1_C_5_MS);
// ---------------------------------------------------------
// Misc Events Counter
// Pre-scaler - 2.5uS
always @(posedge clk)
if(me_cnt_clr || me_ps_2_5_us) me_ps <= 8'h0;
else me_ps <= me_ps + 8'h1;
always @(posedge clk) // Generate a pulse every 2.5 uS
me_ps_2_5_us <= (me_ps == `USBF_T2_C_2_5_US);
// Second Pre-scaler - 0.5mS
always @(posedge clk)
if(me_cnt_clr || me_ps2_0_5_ms ) me_ps2 <= 8'h0;
else
if(me_ps_2_5_us) me_ps2 <= me_ps2 + 8'h1;
always @(posedge clk) // Generate a pulse every 0.5 mS
me_ps2_0_5_ms <= (me_ps2 == `USBF_T2_C_0_5_MS) & !me_ps2_0_5_ms;
// final misc Counter
always @(posedge clk)
if(me_cnt_clr) me_cnt <= 8'h0;
else
if(!me_cnt_100_ms && me_ps2_0_5_ms) me_cnt <= me_cnt + 8'h1;
always @(posedge clk) // Indicate when 100uS have passed
T2_gt_100_uS <= !me_cnt_clr & (me_ps2 > `USBF_T2_C_100_US); // Actual Time: 102.5 uS
always @(posedge clk) // Indicate when wakeup period has passed
T2_wakeup <= !me_cnt_clr & (me_cnt > `USBF_T2_C_WAKEUP);
always @(posedge clk) // Indicate when 1 mS has passed
T2_gt_1_0_mS <= !me_cnt_clr & (me_cnt > `USBF_T2_C_1_0_MS); // Actual Time: 1.5 mS
always @(posedge clk) // Indicate when 1.2 mS has passed
T2_gt_1_2_mS <= !me_cnt_clr & (me_cnt > `USBF_T2_C_1_2_MS); // Actual Time: 1.5 mS
always @(posedge clk) // Generate a pulse after 100 mS
me_cnt_100_ms <= !me_cnt_clr & (me_cnt == `USBF_T2_C_100_MS); // Actual Time: 100 mS
// ---------------------------------------------------------
// Chirp Counter
always @(posedge clk)
if(chirp_cnt_clr) chirp_cnt <= 3'h0;
else
if(chirp_cnt_inc) chirp_cnt <= chirp_cnt + 3'h1;
always @(posedge clk)
chirp_cnt_is_6 <= (chirp_cnt == 3'h6);
///////////////////////////////////////////////////////////////////
//
// Main State Machine
//
`ifdef USBF_ASYNC_RESET
always @(posedge clk or negedge rst)
`else
always @(posedge clk)
`endif
if(!rst) state <= POR;
else
if(usb_vbus) state <= POR;
else state <= next_state;
always @(state or mode_hs or idle_long or resume_req_s or me_cnt_100_ms or
j_long or k_long or se0_long or ls_se0 or
T1_gt_2_5_uS or T1_st_3_0_mS or T1_gt_3_0_mS or
T1_gt_5_0_mS or T2_gt_100_uS or T2_wakeup or T2_gt_1_0_mS or
T2_gt_1_2_mS or chirp_cnt_is_6)
begin
next_state = state; // Default don't change state
mode_set_hs = 1'b0;
mode_set_fs = 1'b0;
suspend_set = 1'b0;
suspend_clr = 1'b0;
attached_set = 1'b0;
attached_clr = 1'b0;
usb_reset_d = 1'b0;
fs_term_on = 1'b0;
fs_term_off = 1'b0;
xcv_set_hs = 1'b0;
xcv_set_fs = 1'b0;
bit_stuff_on = 1'b0;
bit_stuff_off = 1'b0;
idle_cnt_clr = 1'b0;
me_cnt_clr = 1'b0;
drive_k_d = 1'b0;
chirp_cnt_clr = 1'b0;
chirp_cnt_inc = 1'b0;
case(state) // synopsys full_case parallel_case
POR: // Power On/Reset
begin
me_cnt_clr = 1'b1;
xcv_set_fs = 1'b1;
fs_term_on = 1'b1;
mode_set_fs = 1'b1;
attached_clr = 1'b1;
bit_stuff_on = 1'b0;
suspend_clr = 1'b1;
next_state = ATTACH;
end
NORMAL: // Normal Operation
begin
if(!mode_hs && T1_gt_2_5_uS && T1_st_3_0_mS && !idle_long)
begin
me_cnt_clr = 1'b1;
next_state = RESET;
end
else
if(!mode_hs && T1_gt_3_0_mS)
begin
idle_cnt_clr = 1'b1;
suspend_set = 1'b1;
next_state = SUSPEND;
end
else
if(mode_hs && T1_gt_3_0_mS)
begin // Switch to FS mode, and decide
// if it's a RESET or SUSPEND
me_cnt_clr = 1'b1;
xcv_set_fs = 1'b1;
fs_term_on = 1'b1;
next_state = RES_SUSP;
end
end
RES_SUSP: // Decide if it's a Reset or Suspend Signaling
begin // We are now in FS mode, wait 100uS first
if(T2_gt_100_uS && se0_long)
begin
me_cnt_clr = 1'b1;
next_state = RESET;
end
else
if(T2_gt_100_uS && j_long)
begin
idle_cnt_clr = 1'b1;
suspend_set = 1'b1;
next_state = SUSPEND;
end
end
SUSPEND: // In Suspend
begin
if(T1_gt_2_5_uS && se0_long)
begin
suspend_clr = 1'b1;
me_cnt_clr = 1'b1;
next_state = RESET;
end
else
if(k_long) // Start Resuming
next_state = RESUME;
else
if(T1_gt_5_0_mS && resume_req_s)
next_state = RESUME_REQUEST;
end
RESUME:
begin
suspend_clr = 1'b1;
if(ls_se0)
begin
if(mode_hs)
begin // Switch Back to HS mode
xcv_set_hs = 1'b1;
fs_term_off = 1'b1;
end
bit_stuff_on = 1'b1; // Enable Bit Stuffing and NRZI encoding
me_cnt_clr = 1'b1;
next_state = RESUME_WAIT;
end
end
RESUME_WAIT:
begin
if(T2_gt_100_uS) next_state = NORMAL;
end
RESUME_REQUEST: // Function Resume Request
begin
suspend_clr = 1'b1;
// Wait for internal wake up
if(T2_wakeup)
begin
fs_term_on = 1'b1; // Switch Termination to Full Speed
bit_stuff_off = 1'b1; // disable Bit Stuffing and NRZI encoding
me_cnt_clr = 1'b1;
next_state = RESUME_SIG;
end
end
RESUME_SIG: // Signal resume
begin
// Drive Resume ('K') for 1-15 mS
drive_k_d = 1'b1;
// Stop driving after 1.5 mS
if(T2_gt_1_0_mS) next_state = RESUME;
end
ATTACH: // Attach To USB Detected
begin
idle_cnt_clr = 1'b1;
if(me_cnt_100_ms)
//if(me_cnt_100_ms && j_long)
begin
attached_set = 1'b1;
next_state = NORMAL;
end
/*
if(me_cnt_100_ms && se0_long)
begin
attached_set = 1'b1;
me_cnt_clr = 1'b1;
next_state = RESET;
end
*/
end
RESET: // In Reset
begin
usb_reset_d = 1'b1; // Assert Internal USB Reset
xcv_set_hs = 1'b1; // Switch xcvr to HS mode
fs_term_on = 1'b1; // Turn FS termination On
mode_set_fs = 1'b1; // Change mode to FS
bit_stuff_off = 1'b1; // disable Bit Stuffing and NRZI encoding
// Get out of reset after 1.5 mS
if(T2_gt_1_0_mS)
begin
me_cnt_clr = 1'b1;
next_state = SPEED_NEG_FS; // comment this line if support HS
//next_state = SPEED_NEG;
end
end
SPEED_NEG: // Speed Negotiation
begin
drive_k_d = 1'b1;
chirp_cnt_clr = 1'b1;
// Start looking for 'K' after 1.5 mS
if(T2_gt_1_2_mS) next_state = SPEED_NEG_K;
end
SPEED_NEG_K:
begin
if(chirp_cnt_is_6) next_state = SPEED_NEG_HS;
else
begin
if(k_long)
begin
chirp_cnt_inc = 1'b1;
next_state = SPEED_NEG_J;
end
if(se0_long)
next_state = SPEED_NEG_FS;
end
end
SPEED_NEG_J:
begin
if(chirp_cnt_is_6) next_state = SPEED_NEG_HS;
else
begin
if(j_long)
begin
chirp_cnt_inc = 1'b1;
next_state = SPEED_NEG_K;
end
if(se0_long)
next_state = SPEED_NEG_FS;
end
end
SPEED_NEG_HS:
begin
bit_stuff_on = 1'b1; // Enable Bit Stuffing and NRZI encoding
xcv_set_hs = 1'b1; // Switch xcvr to HS mode
fs_term_off = 1'b1; // Turn FS termination Off
mode_set_hs = 1'b1; // Change mode to HS
if(se0_long) next_state = NORMAL;
end
SPEED_NEG_FS:
begin
bit_stuff_on = 1'b1; // Enable Bit Stuffing and NRZI encoding
xcv_set_fs = 1'b1; // Switch xcvr to FS mode
fs_term_on = 1'b1; // Turn FS termination On
mode_set_fs = 1'b1; // Change mode to FS
if (!se0_long)
next_state = NORMAL;
end
endcase
end
endmodule