Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / design / sys / iop / niu / rtl / pcs_link_config.v
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: pcs_link_config.v
// Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved
// 4150 Network Circle, Santa Clara, California 95054, U.S.A.
//
// * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// For the avoidance of doubt, and except that if any non-GPL license
// choice is available it will apply instead, Sun elects to use only
// the General Public License version 2 (GPLv2) at this time for any
// software where a choice of GPL license versions is made
// available with the language indicating that GPLv2 or any later version
// may be used, or where a choice of which version of the GPL is applied is
// otherwise unspecified.
//
// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
// CA 95054 USA or visit www.sun.com if you need additional information or
// have any questions.
//
// ========== Copyright Header End ============================================
// @(#)pcs_link_config.v 1.6G
/**********************************************************************/
/* Project Name : CASSINI */
/* Module Name : PCS Link Configuration */
/* Description : This block performs the link configuration */
/* procedure. Based on inputs from the Sequence */
/* detection module, it determines whether the link */
/* is up or not. This block also provides control */
/* to the Tx out mux to mux out the proper control */
/* symbols during link configuration. It allows */
/* data from the encoder to pass through when the */
/* link is up. */
/* */
/* If word synchronization is lost, the autonegot- */
/* iation is restarted and the link config state */
/* machine will only send C ordered sets until */
/* synchronization is achieved again. Word sync */
/* is lost when loss of signal is detected. Link */
/* up local signal only transitions on even */
/* boundaries. */
/* */
/* Assumptions : none. */
/* */
/* Parent module : pcs.v */
/* Child modules : none. */
/* Author Name : Linda Chen */
/* Date Created : 10/15/96 */
/* */
/* Copyright (c) 1994, Sun Microsystems, Inc. */
/* Sun Proprietary and Confidential */
/* */
/* Modifications : */
/* 8/6/98 - change the autonegotiation so that */
/* breaklink is exited despite no C codes received */
/* 1/17/02 : added LT_TM related logic */
/* from the link partner */
/* Synthesis Notes : none yet */
/**********************************************************************/
`include "pcs_define.h"
module pcs_link_config (// inputs
txclk,reset_pci,res_auto_pci,
loss_sync_rx,pos_disp_tx_p,
good3_Cnack_rx,good3_CorData_rx,
got_C_rx,CorData_err_rx,
autoneg_ena_pci,serdes_rdy,
got3_config0_rx,timer_override_pci,
lost_link_pci,set_lol_from_los_pci,
set_lol_from_c_pci,
link_up_filter_en,
// outputs
link_up_loc,link_up_tx,
tx_enc_conf_sel,txd_sel,reset_tx,
ack,odd_tx,res_auto_tx,
link_state_tx,an_loss_sync,
lost_link_tx,set_lol_from_los_tx,
set_lol_from_c_tx,link_not_up_diag_tx);
input txclk; // Tx Clk 125 MHz
input reset_pci; // hw and sw reset OR'ed, needs sync from PCI
input res_auto_pci; // from MII Config, needs sync from PCI
input loss_sync_rx; // indication of word sync status
input pos_disp_tx_p; // running disparity from encoder
input good3_Cnack_rx; // received 3 sequential and identical Config
// regs, disregard ack bit, needs sync from Rx
input good3_CorData_rx; // received 3 sequential and identical Config
// regs or data, needs sync from Rx
input got_C_rx; // last received or receiving Link Config set
input CorData_err_rx; // not receiving Config regs or good data
input autoneg_ena_pci; // enable for autonegotiation from MII config
input serdes_rdy; // from slink control block
input got3_config0_rx; // flag used to trigger restart of config
input timer_override_pci; // make timer short for speedy bringup
input lost_link_pci; // used to clear lost_link_tx register
input set_lol_from_los_pci; // clears diag bit for why link was lost
input set_lol_from_c_pci; // clears diag bit for why link was lost
input link_up_filter_en;
output link_up_loc; // a signal to the MII status register
output link_up_tx; // a signal to the MII status register
output [3:0] tx_enc_conf_sel; // encoder control lines
output [1:0] txd_sel; // select for tx data mux
output reset_tx; // synchronous reset to txclk
output ack; // used in configuration register sent
output odd_tx; // odd polarity for tx
output res_auto_tx; // synchronized version of restart_autoneg
output [3:0] link_state_tx; // link configuration state bits to slave
output an_loss_sync; // lost sync for over 10 ms, use to reinit
// serdes part
output lost_link_tx; // used to set link status bit
output set_lol_from_los_tx; // sets diag bit for why link was lost
output set_lol_from_c_tx; // sets diag bit for why link was lost
output [5:0] link_not_up_diag_tx;// gives reasons for why link not up
wire reset_tx; // synchronized version of reset to rxclk
wire [3:0] link_state_tx, // link config state machine
nxt_link_state_tx;
wire error, nxt_error; // flag for incorrect character reception
wire res_auto_1shot; // 1 shot for restart auto config
wire res_auto_tx_d; // synchronized version of restart_autoneg del
wire got_C_tx; // synchronized version of got_C
wire CorData_err_tx; // synchronized version of CorData_err_rx
wire good3_Cnack_tx; // synchronized version of good3_Cnack_rx
wire good3_CorData_tx; // synchronized version of good3_CorData_rx
wire loss_sync_oneshot; // pulse for loss of sync, to trigger autneg
wire loss_sync_tx; // loss_sync synchronized
wire loss_sync_tx_d; // loss_sync synchronized and delayed
wire set_err, clr_err; // control for temp error register
wire ack; // used in configuration register sent
wire conf_polarity; // which polarity of config reg last sent
wire nxt_conf_polarity; // which polarity of config reg last sent
wire flip_conf_polarity; // signal used to flip config polarity flag
wire autoneg_ena_tx; // synchronized autonegotiation enable
wire [2:0] nxt_an_timer, // timer value
an_timer; // timer value
wire an_timer_done; // timer equals 12 ms - 16 ms
wire timer_tick; // one shot version of slow clk
wire start_timer; // initialize timer pulse
wire breaklink, // marks progress of autonegotiation
nxt_breaklink; // marks progress of autonegotiation
wire got3_config0_tx; // flag for restart an, synchronized
wire [2:0] nxt_los_timer, // count for 10 ms loss of sync timeout
los_timer; // count for 10 ms loss of sync timeout
wire los_timer_done; // timer equals 12 ms - 16 ms
wire nxt_restart_an, // flag to restart aneg
restart_an; // flag to restart aneg
wire slow_clk, // 4.2 ms clk
slow_clk_d; // 4.2 ms clk one txclk delayed
wire begin_an; // flag to clear restart an flag
wire timer_override_tx; // synchronized version of timer_override_pci
wire nxt_lost_link; // used to generate lost_link_tx signal
wire clr_lost_link; // used to generate lost_link_tx signal
wire set_lost_link; // used to generate lost_link_tx signal
wire nxt_set_lol_from_los; // for diagnostic flag why link was lost
wire nxt_set_lol_from_c; // for diagnostic flag why link was lost
wire clr_lol_from_los; // for diagnostic flag why link was lost
wire clr_lol_from_c; // for diagnostic flag why link was lost
wire set_restart_onC; // for lol_from_c diag flag
wire an_loss_sync_d; // used for diag bit creation to prevent
// bit high after read from reg
wire an_los_1shot; // one shot used for diag bit creation
wire link_up_loc_ori; // used for detecting link down
wire link_up_loc_ori_temp; // used for detecting link down before filtering
wire serdes_rdy_tx; // synchronized version of serdes_rdy in tx
// clock domain
wire link_up_tx;
parameter LINK_NA = 4'h0,
LINK_CNACK2 = 4'h1,
LINK_CNACK3 = 4'h2,
LINK_CNACK4 = 4'h3,
LINK_CORDATA1 = 4'h4,
LINK_CORDATA2 = 4'h5,
LINK_CORDATA3 = 4'h6,
LINK_CORDATA4 = 4'h7,
CHECK_DISP = 4'h8,
FLIP_DISP = 4'h9,
LINK_UP_US = 4'hA,
LINK_UP_BOTH = 4'hB;
/*
** An LFSR is used to create a 50% duty cycle clock which has a
** 4.2 ms cycle time. This is used by the loss of sync and the
** autonegotiation timers which count to a value between 12.6 and
** 16.8 ms. This is within the 10 - 20 ms window required.
*/
pcs_lfsr pcs_lfsr(txclk,timer_override_tx,reset_tx,slow_clk);
assign
nxt_error = (reset_tx|clr_err)? 1'h0 : (set_err)? 1'h1 : error,
nxt_conf_polarity = (reset_tx) ? 1'h0 : (flip_conf_polarity) ?
~conf_polarity : conf_polarity,
loss_sync_oneshot = loss_sync_tx & !loss_sync_tx_d,
res_auto_1shot = !res_auto_tx_d & res_auto_tx,
timer_tick = slow_clk & ~slow_clk_d, // every 4.2 ms
nxt_an_timer = (start_timer | reset_tx) ? 3'h0 :
an_timer_done ? an_timer : (timer_tick)?
an_timer + 3'h1: an_timer,
nxt_los_timer = (loss_sync_oneshot | reset_tx) ? 3'h0 :
los_timer_done ? los_timer : (timer_tick)?
los_timer + 3'h1 : los_timer,
nxt_restart_an = (reset_tx | begin_an)? 1'h0:
(restart_an | res_auto_1shot | an_loss_sync),
an_loss_sync = (los_timer_done & loss_sync_tx & ~loss_sync_oneshot),
an_los_1shot = an_loss_sync & ~an_loss_sync_d,
los_timer_done = (los_timer == 3'h4), // 12 -16 ms
an_timer_done = (an_timer == 3'h4),
set_lost_link = ~link_up_loc_ori & link_up_tx,// link_up_tx = link_up_loc_d
nxt_lost_link = (reset_tx) ? 1'b0: (set_lost_link)? 1'b1 :
(clr_lost_link)? 1'b0 : lost_link_tx,
nxt_set_lol_from_los = (reset_tx) ? 1'b0: (an_los_1shot)? (1'b1) :
(clr_lol_from_los)? 1'b0 : set_lol_from_los_tx,
nxt_set_lol_from_c = (reset_tx) ? 1'b0: (set_restart_onC)? (1'b1) :
(clr_lol_from_c)? 1'b0 : set_lol_from_c_tx,
link_up_loc = link_up_loc_ori | ~autoneg_ena_tx, // in manual mode allows tx ctrl to
// to send idles
link_not_up_diag_tx[0] = got3_config0_tx,
link_not_up_diag_tx[1] = !serdes_rdy_tx,
link_not_up_diag_tx[2] = !good3_Cnack_tx & ~link_up_loc,
link_not_up_diag_tx[3] = loss_sync_tx,
link_not_up_diag_tx[4] = !good3_CorData_tx &((link_state_tx == LINK_CORDATA1)
| (link_state_tx == LINK_CORDATA2) | (link_state_tx == LINK_CORDATA3)
| (link_state_tx == LINK_CORDATA4)),
link_not_up_diag_tx[5] = got_C_tx & (link_state_tx == LINK_UP_US),
/*
** Call of function link_config_fn
*/
{set_err,clr_err,ack,link_up_loc_ori_temp,
tx_enc_conf_sel,txd_sel,flip_conf_polarity,
start_timer,nxt_breaklink,begin_an,
set_restart_onC,nxt_link_state_tx}
= link_config_fn(reset_tx,restart_an,error,
good3_Cnack_tx,good3_CorData_tx,odd_tx,
CorData_err_tx,pos_disp_tx_p,loss_sync_tx,
conf_polarity,got_C_tx,autoneg_ena_tx,
serdes_rdy_tx,an_timer_done,
got3_config0_tx,breaklink,link_state_tx);
/*
** Link Configuration state machine
*/
function [18:0] link_config_fn;
input reset; // reset
input restart_an; // trigger for autonegotiation restart
input error; // output of temporary error register
input good3_Cnack; // flag for 3 good C's no ack
input good3_CorData; // flag for 3 good C's or data
input odd; // this byte is odd flag
input CorData_err; // not receiving C sets or data
input pos_disp; // this data is positive disparity flag
input loss_sync; // prevents link up when loss_sync
input conf_polarity; // which polarity of config reg last sent
input got_C; // received Config sequence
input autoneg_ena; // enable autonegotiation from MII config
input serdes_rdy_tx; // serdes is initialized, rx clock recovered
input timer_done; // short timer reached terminal count
input got3_config0; // received Config Reg of zeros, break link
input breaklink; // stores what stage autoneg is in
input [3:0] state;
reg set_err; // set temp error register
reg clr_err; // clear temp error register
reg ack; // ack bit of config register
reg link_up; // link is up
reg [3:0] tx_enc_conf_sel;// tx encoder control
reg [1:0] txd_sel; // tx data mux control
reg flip_config_polarity; // flips config reg polarity flag
reg start_timer;
reg nxt_breaklink;
reg begin_an;
reg set_restart_onC;
reg [3:0] n_state; // next state
begin
set_err = 1'h0; // to catch all invalid codes - restart an
clr_err = 1'h0;
ack = 1'h0;
link_up = 1'h0;
tx_enc_conf_sel = `PCS_ENC_K285;
txd_sel = `PCS_TX_GMII_DATA;
flip_config_polarity = 1'h0;
start_timer = 1'h0;
nxt_breaklink = breaklink;
begin_an = 1'h0;
set_restart_onC = 1'h0;
n_state = 4'h0;
case (state)
LINK_NA : // 0
begin
if (~reset & odd & ~autoneg_ena & ~loss_sync & ~got_C)
begin
n_state = LINK_UP_BOTH; // enter on even
begin_an = 1'h1;
end
else if (~reset & ~odd & autoneg_ena)
n_state = LINK_CNACK2; // enter on odd
else // reset or disabled or odd
begin
n_state = LINK_NA;
tx_enc_conf_sel = `PCS_ENC_H_CHAR;
// to prevent rx sync on odd
start_timer = 1'h1;
set_err = loss_sync;
nxt_breaklink = 1'h1;
end
end
LINK_CNACK2 : // 1
begin
set_err = loss_sync;
if (conf_polarity)
tx_enc_conf_sel = `PCS_ENC_LINK_CONFA;
else
tx_enc_conf_sel = `PCS_ENC_LINK_CONFB;
flip_config_polarity = 1'h1;
n_state = LINK_CNACK3;
end
LINK_CNACK3 : // 2
begin
tx_enc_conf_sel = `PCS_ENC_DATA;
if (breaklink)
txd_sel = `PCS_TX_CONF_ZEROS;
else
txd_sel = `PCS_TX_CONF_REG1;
set_err = loss_sync;
n_state = LINK_CNACK4;
end
LINK_CNACK4 : // 3
begin
tx_enc_conf_sel = `PCS_ENC_DATA;
if (breaklink) // break link
txd_sel = `PCS_TX_CONF_ZEROS;
else
txd_sel = `PCS_TX_CONF_REG2;
if (error)
begin
clr_err = 1'h1;
n_state = LINK_NA;
nxt_breaklink = 1'h1;
start_timer = 1'h1;
end
else if (breaklink & timer_done & serdes_rdy_tx)
begin // break link, promote
n_state = LINK_NA;
nxt_breaklink = 1'h0;
end
else if (~breaklink & good3_Cnack & ~got3_config0)
// base page, promote
begin
n_state = LINK_CORDATA1;
clr_err = 1'h1;
start_timer = 1'h1;
begin_an = 1'h1;
end
else
n_state = LINK_NA;
end
LINK_CORDATA1 : // 4
begin
set_err = CorData_err | loss_sync;
n_state = LINK_CORDATA2;
end
LINK_CORDATA2 : // 5
begin
set_err = CorData_err | loss_sync;
n_state = LINK_CORDATA3;
if (conf_polarity)
tx_enc_conf_sel = `PCS_ENC_LINK_CONFA;
else
tx_enc_conf_sel = `PCS_ENC_LINK_CONFB;
flip_config_polarity = 1'h1;
end
LINK_CORDATA3 : // 6
begin
set_err = CorData_err | loss_sync;
tx_enc_conf_sel = `PCS_ENC_DATA;
txd_sel = `PCS_TX_CONF_REG1;
n_state = LINK_CORDATA4;
end
LINK_CORDATA4 : // 7
begin
tx_enc_conf_sel = `PCS_ENC_DATA;
txd_sel = `PCS_TX_CONF_REG2;
ack = 1'h1;
if (error | got3_config0)
begin
clr_err = 1'h1;
n_state = LINK_NA;
nxt_breaklink = 1'h1;
start_timer = 1'h1;
end
else if (good3_CorData & timer_done)
begin
n_state = CHECK_DISP;
start_timer = 1'h1;
end
else
n_state = LINK_CORDATA1; // waiting for 3 good C's or Data
end
CHECK_DISP : // 8
begin
nxt_breaklink = 1'h1; // reset for future use
if (pos_disp) // disp at output of encoder
n_state = FLIP_DISP; // K28.5
else
begin
n_state = LINK_UP_US;
end
end
FLIP_DISP : // 9
begin
tx_enc_conf_sel = `PCS_ENC_IDLE1;
n_state = LINK_UP_US;
end
LINK_UP_US : // A
begin
if ((restart_an | got3_config0) & odd)
n_state = LINK_NA;
else if (got_C | ~timer_done | ~odd) // flush incoming C's
n_state = LINK_UP_US;
else
begin
n_state = LINK_UP_BOTH;
end
if (odd)
tx_enc_conf_sel = `PCS_ENC_IDLE2;
end
LINK_UP_BOTH : // B
begin // always enter on even
tx_enc_conf_sel = `PCS_ENC_DATA;
if ((restart_an | good3_Cnack) & odd) // always exit on odd
begin
n_state = LINK_NA;
set_restart_onC = good3_Cnack;
end
else
begin
link_up = 1'h1;
n_state = LINK_UP_BOTH;
end
end
endcase
link_config_fn = {set_err,clr_err,ack,
link_up,tx_enc_conf_sel,txd_sel,
flip_config_polarity,start_timer,
nxt_breaklink,begin_an,set_restart_onC,n_state};
end
endfunction
RREG #(4) r_state (link_state_tx, txclk, reset_tx,
nxt_link_state_tx);
RREG #(1) r_odd_tx (odd_tx,txclk,reset_tx,~odd_tx);
REG #(1) r_error (error, txclk, nxt_error);
REG #(1) r_loss_sync_d (loss_sync_tx_d, txclk, loss_sync_tx);
REG #(1) r_conf_polarity(conf_polarity,txclk,nxt_conf_polarity);
REG #(1) r_res_auto_d(res_auto_tx_d,txclk,res_auto_tx);
REG #(1) r_link_up_tx (link_up_tx, txclk, link_up_loc_ori); // unglitch
REG #(3) r_an_timer(an_timer,txclk,nxt_an_timer);
REG #(3) r_los_timer(los_timer,txclk,nxt_los_timer);
REG #(1) r_slowclk(slow_clk_d,txclk,slow_clk);
REG #(1) r_lost_link_tx(lost_link_tx,txclk,nxt_lost_link);
RREG #(1) r_breaklink(breaklink,txclk,reset_tx,nxt_breaklink);
RREG #(1) r_restart_an(restart_an,txclk,reset_tx,nxt_restart_an);
REG #(1) r_set_lol_los (set_lol_from_los_tx,txclk,nxt_set_lol_from_los);
REG #(1) r_set_lol_c (set_lol_from_c_tx,txclk,nxt_set_lol_from_c);
REG #(1) r_an_los_1shot (an_loss_sync_d,txclk,an_loss_sync);
// SYNCREG (qout, clk, din);
SYNCREG r_res_auto(res_auto_tx,txclk,res_auto_pci);
SYNCREG r_loss_sync_tx (loss_sync_tx, txclk, loss_sync_rx);
SYNCREG r_reset_tx (.qout(reset_tx),.clk(txclk),.din(reset_pci));
SYNCREG r_got_C (got_C_tx, txclk, got_C_rx);
SYNCREG r_autoneg_ena_tx (autoneg_ena_tx, txclk, autoneg_ena_pci);
SYNCREG r_CorData_err (CorData_err_tx, txclk, CorData_err_rx);
SYNCREG r_good3_Cnack (good3_Cnack_tx, txclk, good3_Cnack_rx);
SYNCREG r_good3_CorData (good3_CorData_tx, txclk, good3_CorData_rx);
SYNCREG r_got3_config0 (got3_config0_tx, txclk, got3_config0_rx);
SYNCREG r_timer_override (timer_override_tx, txclk, timer_override_pci);
SYNCREG r_clr_lost_link (clr_lost_link, txclk, lost_link_pci);
SYNCREG r_clr_lol_los (clr_lol_from_los, txclk,set_lol_from_los_pci);
SYNCREG r_clr_lol_c (clr_lol_from_c, txclk,set_lol_from_c_pci);
SYNCREG r_serdes_rdy_tx (serdes_rdy_tx,txclk,serdes_rdy);
/**************************************************
* Added link_up filter logic -loj @Nov. 1st 2005
**************************************************/
wire link_up_pls;
wire link_down_pls;
wire link_up_filter_en;
wire timer_on;
reg [10:0] timer_cnt;
PlsGen2 PlsGen2(.sig_in(link_up_loc_ori_temp),.clk(txclk),.lead(link_up_pls),.trail(link_down_pls));
SRFF timer_on_SRFF(.reset(reset_tx),.clk(txclk),.iSet(link_up_pls),.iRst(link_down_pls),.oQ(timer_on));
always @ (posedge txclk)
if (reset_tx | (~timer_on))
timer_cnt <= 0;
else if (timer_cnt[10])
timer_cnt <= timer_cnt;
else timer_cnt <= timer_cnt + 1;
assign link_up_loc_ori = link_up_filter_en ? (timer_cnt[10] & link_up_loc_ori_temp) : link_up_loc_ori_temp;
/* --------------- spare gates --------------- */
`ifdef NEPTUNE
wire [3:0] do_nad;
wire [3:0] do_nor;
wire [3:0] do_inv;
wire [3:0] do_mux;
wire [3:0] do_q;
wire so;
mac_spare_gates pcs_link_config_spare_gates (
.di_nd3 ({1'h1, 1'h1, do_q[3]}),
.di_nd2 ({1'h1, 1'h1, do_q[2]}),
.di_nd1 ({1'h1, 1'h1, do_q[1]}),
.di_nd0 ({1'h1, 1'h1, do_q[0]}),
.di_nr3 ({1'h0, 1'h0}),
.di_nr2 ({1'h0, 1'h0}),
.di_nr1 ({1'h0, 1'h0}),
.di_nr0 ({1'h0, 1'h0}),
.di_inv (do_nad[3:0]),
.di_mx3 ({1'h0, 1'h0}),
.di_mx2 ({1'h0, 1'h0}),
.di_mx1 ({1'h0, 1'h0}),
.di_mx0 ({1'h0, 1'h0}),
.mx_sel (do_nor[3:0]),
.di_reg (do_inv[3:0]),
.wt_ena (do_mux[3:0]),
.rst ({4{reset_tx}}),
.si (1'h0),
.se (1'h0),
.clk (txclk),
.do_nad (do_nad[3:0]),
.do_nor (do_nor[3:0]),
.do_inv (do_inv[3:0]),
.do_mux (do_mux[3:0]),
.do_q (do_q[3:0]),
.so (so)
);
`else
`endif
endmodule