module altera_tse_pcs_pma /* synthesis ALTERA_ATTRIBUTE = "SUPPRESS_DA_RULE_INTERNAL=\"R102,R105,D102,D101,D103\"" */(
    // inputs:
    address,
    clk,
    gmii_tx_d,
    gmii_tx_en,
    gmii_tx_err,
    gxb_cal_blk_clk,
    gxb_pwrdn_in,
    mii_tx_d,
    mii_tx_en,
    mii_tx_err,
    read,
    ref_clk,
    reset,
    reset_rx_clk,
    reset_tx_clk,
    rxp,
    write,
    writedata,

    // outputs:
    gmii_rx_d,
    gmii_rx_dv,
    gmii_rx_err,
    hd_ena,
    led_an,
    led_char_err,
    led_col,
    led_crs,
    led_disp_err,
    led_link,
    mii_col,
    mii_crs,
    mii_rx_d,
    mii_rx_dv,
    mii_rx_err,
    pcs_pwrdn_out,
    readdata,
    rx_clk,
	rx_clkena,
	tx_clkena,
    set_10,
    set_100,
    set_1000,
    tx_clk,
    txp,
    waitrequest
);


//  Parameters to configure the core for different variations
//  ---------------------------------------------------------

parameter PHY_IDENTIFIER        = 32'h 00000000; //  PHY Identifier 
parameter DEV_VERSION           = 16'h 0001 ;    //  Customer Phy's Core Version
parameter ENABLE_SGMII          = 1;             //  Enable SGMII logic for synthesis
parameter EXPORT_PWRDN          = 1'b0;          //  Option to export the Alt2gxb powerdown signal
parameter DEVICE_FAMILY         = "ARRIAGX";     //  The device family the the core is targetted for.
parameter TRANSCEIVER_OPTION    = 1'b1;          //  Option to select transceiver block for MAC PCS PMA Instantiation. Valid Values are 0 and 1:  0 - GXB (GIGE Mode) 1 - LVDS I/O
parameter ENABLE_ALT_RECONFIG   = 0;             //  Option to have the Alt_Reconfig ports exposed
parameter SYNCHRONIZER_DEPTH 	= 3;	  	 //  Number of synchronizer

  output  [7:0] gmii_rx_d;
  output  gmii_rx_dv;
  output  gmii_rx_err;
  output  hd_ena;
  output  led_an;
  output  led_char_err;
  output  led_col;
  output  led_crs;
  output  led_disp_err;
  output  led_link;
  output  mii_col;
  output  mii_crs;
  output  [3:0] mii_rx_d;
  output  mii_rx_dv;
  output  mii_rx_err;
  output  pcs_pwrdn_out;
  output  [15:0] readdata;
  output  rx_clk;
  output  set_10;
  output  set_100;
  output  set_1000;
  output  tx_clk;
  output  rx_clkena;
  output  tx_clkena; 
  output  txp;
  output  waitrequest;
  
  input   [4:0] address;
  input   clk;
  input   [7:0] gmii_tx_d;
  input   gmii_tx_en;
  input   gmii_tx_err;
  input   gxb_pwrdn_in;
  input   gxb_cal_blk_clk;
  input   [3:0] mii_tx_d;
  input   mii_tx_en;
  input   mii_tx_err;
  input   read;
  input   ref_clk;
  input   reset;
  input   reset_rx_clk;
  input   reset_tx_clk;
  input   rxp;
  input   write;
  input   [15:0] writedata;


  wire    PCS_rx_reset;
  wire    PCS_tx_reset;
  wire    PCS_reset;
  wire    [7:0] gmii_rx_d;
  wire    gmii_rx_dv;
  wire    gmii_rx_err;
  wire    hd_ena;
  wire    led_an;
  wire    led_char_err;
  wire    led_col;
  wire    led_crs;
  wire    led_disp_err;
  wire    led_link;
  wire    mii_col;
  wire    mii_crs;
  wire    [3:0] mii_rx_d;
  wire    mii_rx_dv;
  wire    mii_rx_err;
  
  wire    [15:0] readdata;
  wire    rx_clk;
  wire    set_10;
  wire    set_100;
  wire    set_1000;
  wire    tbi_rx_clk;
  wire    [9:0] tbi_rx_d;
  wire    [9:0] tbi_tx_d;
  wire    tx_clk;
  wire    rx_clkena;
  wire    tx_clkena;
  wire    txp;
  wire    waitrequest;
  wire    sd_loopback;
  wire    pcs_pwrdn_out_sig;
  wire    gxb_pwrdn_in_sig;
  wire    [9:0] tbi_rx_d_lvds;

  
  reg     pma_digital_rst0;
  reg     pma_digital_rst1;
  reg     pma_digital_rst2;
  reg     [9:0] tbi_rx_d_flip;
  reg     [9:0] tbi_tx_d_flip;
    
  
// Reset logic used to reset the PMA blocks
// ----------------------------------------
always @(posedge clk or posedge reset_rx_clk)
   begin
     if (reset_rx_clk == 1)
        begin
          pma_digital_rst0 <= reset_rx_clk;
          pma_digital_rst1 <= reset_rx_clk;
          pma_digital_rst2 <= reset_rx_clk;
        end
     else 
        begin
          pma_digital_rst0 <= reset_rx_clk;
          pma_digital_rst1 <= pma_digital_rst0;
          pma_digital_rst2 <= pma_digital_rst1;
        end
   end


//  Assign the digital reset of the PMA to the PCS logic
//  --------------------------------------------------------
assign PCS_rx_reset = pma_digital_rst2;
assign PCS_tx_reset = reset_tx_clk | pma_digital_rst2;
assign PCS_reset = reset | pma_digital_rst2;



// Instantiation of the PCS core that connects to a PMA
// --------------------------------------------------------
  altera_tse_top_1000_base_x altera_tse_top_1000_base_x_inst
    (
        .gmii_rx_d (gmii_rx_d),
        .gmii_rx_dv (gmii_rx_dv),
        .gmii_rx_err (gmii_rx_err),
        .gmii_tx_d (gmii_tx_d),
        .gmii_tx_en (gmii_tx_en),
        .gmii_tx_err (gmii_tx_err),
        .hd_ena (hd_ena),
        .led_an (led_an),
        .led_char_err (led_char_err),
        .led_col (led_col),
        .led_crs (led_crs),
        .led_disp_err (led_disp_err),
        .led_link (led_link),
        .mii_col (mii_col),
        .mii_crs (mii_crs),
        .mii_rx_d (mii_rx_d),
        .mii_rx_dv (mii_rx_dv),
        .mii_rx_err (mii_rx_err),
        .mii_tx_d (mii_tx_d),
        .mii_tx_en (mii_tx_en),
        .mii_tx_err (mii_tx_err),
        .reg_addr (address),
        .reg_busy (waitrequest),
        .reg_clk (clk),
        .reg_data_in (writedata),
        .reg_data_out (readdata),
        .reg_rd (read),
        .reg_wr (write),
        .reset_reg_clk (PCS_reset),
        .reset_rx_clk (PCS_rx_reset),
        .reset_tx_clk (PCS_tx_reset),
        .rx_clk (rx_clk),
		.rx_clkena(rx_clkena),
		.tx_clkena(tx_clkena),
		.ref_clk(1'b0),
        .set_10 (set_10),
        .set_100 (set_100),
        .set_1000 (set_1000),
        .sd_loopback(sd_loopback),
        .powerdown(pcs_pwrdn_out_sig),
        .tbi_rx_clk (tbi_rx_clk),
        .tbi_rx_d (tbi_rx_d),
        .tbi_tx_clk (tbi_tx_clk),
        .tbi_tx_d (tbi_tx_d),
        .tx_clk (tx_clk)
    );
    
    defparam
        altera_tse_top_1000_base_x_inst.PHY_IDENTIFIER = PHY_IDENTIFIER,
        altera_tse_top_1000_base_x_inst.DEV_VERSION = DEV_VERSION,
        altera_tse_top_1000_base_x_inst.ENABLE_SGMII = ENABLE_SGMII;



// Export powerdown signal or wire it internally
// ---------------------------------------------
generate if (EXPORT_PWRDN == 1)
    begin          
        assign gxb_pwrdn_in_sig = gxb_pwrdn_in;
        assign pcs_pwrdn_out = pcs_pwrdn_out_sig;
    end
else
    begin
        assign gxb_pwrdn_in_sig = pcs_pwrdn_out_sig;
		assign pcs_pwrdn_out = 1'b0;
    end      
endgenerate



// Either one of these blocks below will be instantiated depending on the parameterization 
// that is chosen.
// ---------------------------------------------------------------------------------------

// Instantiation of the Alt2gxb block as the PMA for Stratix II GX devices
// ----------------------------------------------------------------------- 

generate if (DEVICE_FAMILY != "ARRIAGX" && TRANSCEIVER_OPTION == 0)
    begin          

    altera_tse_alt2gxb_basic the_altera_tse_alt2gxb_basic
      (
        .cal_blk_clk (gxb_cal_blk_clk),
        .gxb_powerdown (gxb_pwrdn_in_sig),
        .pll_inclk (ref_clk),
        .rx_analogreset (reset_rx_clk),
        .rx_clkout (tbi_rx_clk),
        .rx_cruclk (ref_clk),
        .rx_datain (rxp),
        .rx_dataout (tbi_rx_d),
        .rx_digitalreset (pma_digital_rst2),
        .rx_patterndetect (),
        .rx_seriallpbken (sd_loopback),
        .tx_clkout (tbi_tx_clk),
        .tx_datain (tbi_tx_d),
        .tx_dataout (txp),
        .tx_digitalreset (pma_digital_rst2)
      );

    end    
endgenerate



// Instantiation of the Alt2gxb block as the PMA for ArriaGX device
// ---------------------------------------------------------------- 

generate if (DEVICE_FAMILY == "ARRIAGX")
    begin          

    altera_tse_alt2gxb_arriagx the_altera_tse_alt2gxb_arriagx
      (
        .cal_blk_clk (gxb_cal_blk_clk),
        .gxb_powerdown (gxb_pwrdn_in_sig),
        .pll_inclk (ref_clk),
        .rx_analogreset (reset_rx_clk),
        .rx_clkout (tbi_rx_clk),
        .rx_cruclk (ref_clk),
        .rx_datain (rxp),
        .rx_dataout (tbi_rx_d),
        .rx_digitalreset (pma_digital_rst2),
        .rx_patterndetect (),
        .rx_seriallpbken (sd_loopback),
        .tx_clkout (tbi_tx_clk),
        .tx_datain (tbi_tx_d),
        .tx_dataout (txp),
        .tx_digitalreset (pma_digital_rst2)
      );

    end    
endgenerate



// Instantiation of the LVDS SERDES block as the PMA for Stratix III devices
//
// IEEE 802.3 Clause 36 PCS requires that bit 0 of TBI_DATA to be transmitted 
// first.  However, ALTLVDS had bit 9 transmit first.  hence, we need a bit
// reversal algorithm.  
// -------------------------------------------------------------------------

generate if (DEVICE_FAMILY != "ARRIAGX" && TRANSCEIVER_OPTION == 1)
    begin          

    assign tbi_tx_clk = ref_clk;
    assign tbi_rx_d = tbi_rx_d_flip;

    always @(posedge tbi_rx_clk or posedge reset_rx_clk)
        begin
        if (reset_rx_clk == 1)
            tbi_rx_d_flip <= 0;
        else 
            begin
            tbi_rx_d_flip[0] <= tbi_rx_d_lvds[9];
            tbi_rx_d_flip[1] <= tbi_rx_d_lvds[8];
            tbi_rx_d_flip[2] <= tbi_rx_d_lvds[7];
            tbi_rx_d_flip[3] <= tbi_rx_d_lvds[6];
            tbi_rx_d_flip[4] <= tbi_rx_d_lvds[5];
            tbi_rx_d_flip[5] <= tbi_rx_d_lvds[4];
            tbi_rx_d_flip[6] <= tbi_rx_d_lvds[3];
            tbi_rx_d_flip[7] <= tbi_rx_d_lvds[2];
            tbi_rx_d_flip[8] <= tbi_rx_d_lvds[1];
            tbi_rx_d_flip[9] <= tbi_rx_d_lvds[0];
            end
        end

    always @(posedge ref_clk or posedge reset_rx_clk)
        begin
        if (reset_rx_clk == 1)
            tbi_tx_d_flip <= 0;
        else 
            begin
            tbi_tx_d_flip[0] <= tbi_tx_d[9];
            tbi_tx_d_flip[1] <= tbi_tx_d[8];
            tbi_tx_d_flip[2] <= tbi_tx_d[7];
            tbi_tx_d_flip[3] <= tbi_tx_d[6];
            tbi_tx_d_flip[4] <= tbi_tx_d[5];
            tbi_tx_d_flip[5] <= tbi_tx_d[4];
            tbi_tx_d_flip[6] <= tbi_tx_d[3];
            tbi_tx_d_flip[7] <= tbi_tx_d[2];
            tbi_tx_d_flip[8] <= tbi_tx_d[1];
            tbi_tx_d_flip[9] <= tbi_tx_d[0];
            end
        end

     altera_tse_pma_lvds_rx the_altera_tse_pma_lvds_rx
     (
         .rx_divfwdclk (tbi_rx_clk),
         .rx_in (rxp),
         .rx_inclock (ref_clk),
         .rx_out (tbi_rx_d_lvds),
         .rx_outclock (),
         .rx_reset (reset_rx_clk)
     );


    altera_tse_pma_lvds_tx the_altera_tse_pma_lvds_tx
    (
        .tx_in (tbi_tx_d_flip),
        .tx_inclock (ref_clk),
        .tx_out (txp)
    );

    end    
endgenerate

endmodule