library ieee;
use ieee.std_logic_1164.all;

library unisim;
use unisim.vcomponents.all;

use work.wishbone_pkg.all;

entity spec_sata is
  generic (
    g_SIMULATION : integer := 0
  );
  port (
    clk_sys_62m5_i        : in   std_logic;
    rst_sys_62m5_n_i      : in   std_logic;

    clk_ref_125m_i        : in   std_logic;

    clk_125m_clk0_123_n_i : in   std_logic;            -- 125 MHz GTP reference (clk0 123)
    clk_125m_clk0_123_p_i : in   std_logic;

    clk_125m_clk1_101_n_i : in   std_logic;            -- 125 MHz GTP ref (clk1 101)
    clk_125m_clk1_101_p_i : in   std_logic;

    conn_trig_in_i        : in   std_logic;
    conn_trig_out_o       : out  std_logic;

    -- SATA
    sata0_txp_o           : out  std_logic;
    sata0_txn_o           : out  std_logic;
    sata0_rxp_i           : in   std_logic;
    sata0_rxn_i           : in   std_logic;

    sata1_txp_o           : out  std_logic;
    sata1_txn_o           : out  std_logic;
    sata1_rxp_i           : in   std_logic;
    sata1_rxn_i           : in   std_logic;

    wb_i                  : in   t_wishbone_slave_in;
    wb_o                  : out  t_wishbone_slave_out
  );
end spec_sata;

architecture arch of spec_sata is
  signal sata_rdy, sata_rx, sata_rst, sata_rst1   : std_logic_vector(1 downto 0);
  signal sata_rx_data_rd, sata_tx_data_wr         : std_logic_vector(1 downto 0);
  signal sata_rx_data0, sata_rx_data1             : std_logic_vector(31 downto 0);
  signal sata_tx_data0, sata_tx_data1             : std_logic_vector(31 downto 0);
  signal gtp_rx_data0, gtp_tx_data0               : std_logic_vector(7 downto 0);
  signal gtp_rx_k0, gtp_tx_k0, gtp_rx_clk0        : std_logic;
  signal gtp_rx_data1, gtp_tx_data1               : std_logic_vector(7 downto 0);
  signal gtp_rx_k1, gtp_tx_k1, gtp_rx_clk1        : std_logic;
  signal clk_125m_gtp123_buf, clk_125m_gtp101_buf : std_logic;
  signal trig_in, trig_out : std_logic_vector(1 downto 0);
begin
  inst_sata_regs: entity work.sata_regs
    port map (
      rst_n_i => rst_sys_62m5_n_i,
      clk_i => clk_sys_62m5_i,
      wb_i => wb_i,
      wb_o => wb_o,
      rdy0_i => sata_rdy(0),
      rdy1_i => sata_rdy(1),
      rx0_i => sata_rx(0),
      rx1_i => sata_rx(1),
      rst0_o => sata_rst(0),
      rst1_o => sata_rst(1),
      rx_data_0_i => sata_rx_data0,
      rx_data_0_rd_o => sata_rx_data_rd(0),
      rx_data_1_i => sata_rx_data1,
      rx_data_1_rd_o => sata_rx_data_rd(1),
      tx_data_0_o => sata_tx_data0,
      tx_data_0_wr_o => sata_tx_data_wr(0),
      tx_data_1_o => sata_tx_data1,
      tx_data_1_wr_o => sata_tx_data_wr(1)
      );

  sata_rst1 <= sata_rst or not (1 downto 0 => rst_sys_62m5_n_i);

  cmp_ibufgds_gtp_123 : IBUFGDS
    generic map (
      DIFF_TERM    => TRUE,
      IBUF_LOW_PWR => TRUE,
      IOSTANDARD   => "DEFAULT")
    port map (
      O  => clk_125m_gtp123_buf,
      I  => clk_125m_clk0_123_p_i,
      IB => clk_125m_clk0_123_n_i);

  cmp_ibufgds_gtp_101 : IBUFGDS
    generic map (
      DIFF_TERM    => TRUE,
      IBUF_LOW_PWR => TRUE,
      IOSTANDARD   => "DEFAULT")
    port map (
      O  => clk_125m_gtp101_buf,
      I  => clk_125m_clk1_101_p_i,
      IB => clk_125m_clk1_101_n_i);

  --  SATA0: gt 101, ch1
  inst_gt_101: entity work.wr_gtp_phy_spartan6
    generic map (
      g_simulation => g_simulation,
      g_force_disparity => 0,
      g_enable_ch0 => 0, --  For FMC
      g_enable_ch1 => 1  --  For SATA
      )
    port map (
      --  Channel 0 is not used.
      gtp0_clk_i => '0',
      ch0_ref_clk_i => '0',
      ch0_tx_data_i => (others => '0'),
      ch0_tx_k_i => '0',
      ch0_tx_disparity_o => open,
      ch0_tx_enc_err_o => open,
      ch0_rx_rbclk_o => open,
      ch0_rx_data_o => open,
      ch0_rx_k_o => open,
      ch0_rx_enc_err_o => open,
      ch0_rx_bitslide_o => open,
      ch0_rst_i => '1',
      ch0_loopen_i => '0',
      ch0_loopen_vec_i => (others => '0'),
      ch0_tx_prbs_sel_i => "000",
      ch0_rdy_o => open,
      --  Channel 1 is SATA!
      gtp1_clk_i => clk_125m_gtp101_buf,
      ch1_ref_clk_i => clk_ref_125m_i,
      ch1_tx_data_i => gtp_tx_data0,
      ch1_tx_k_i => gtp_tx_k0,
      ch1_tx_disparity_o => open,
      ch1_tx_enc_err_o => open,
      ch1_rx_data_o => gtp_rx_data0,
      ch1_rx_rbclk_o => gtp_rx_clk0,
      ch1_rx_k_o => gtp_rx_k0,
      ch1_rx_enc_err_o => open,
      ch1_rx_bitslide_o => open,
      ch1_rst_i => sata_rst1(0),
      ch1_loopen_i => '0',
      ch1_loopen_vec_i => (others => '0'),
      ch1_tx_prbs_sel_i => (others => '0'),
      ch1_rdy_o => sata_rdy(0),
      ch0_ref_sel_pll => "100",
      ch1_ref_sel_pll => "100",
      pad_txn0_o => open,
      pad_txp0_o => open,
      pad_rxn0_i => '0',
      pad_rxp0_i => '0',
      pad_txn1_o => sata0_txn_o,
      pad_txp1_o => sata0_txp_o,
      pad_rxn1_i => sata0_rxn_i,
      pad_rxp1_i => sata0_rxp_i
      );

  inst_sata_ctrl0: entity work.sata_controller
    port map (
      clk_62m5_i => clk_sys_62m5_i,
      rst_62m5_n_i => rst_sys_62m5_n_i,
      tx_clk_i => clk_ref_125m_i,
      rx_clk_i => gtp_rx_clk0,
      trig_in_i => trig_in(0),
      trig_out_o => trig_out(0),
      rdy_i => sata_rdy(0),
      reg_tx_data_i => sata_tx_data0(7 downto 0),
      reg_tx_wr_i => sata_tx_data_wr(0),
      tx_data_o => gtp_tx_data0,
      tx_k_o => gtp_tx_k0,
      reg_rx_data_o => sata_rx_data0(7 downto 0),
      reg_rx_rd_i => sata_rx_data_rd(0),
      reg_rx_o => sata_rx(0),
      rx_data_i => gtp_rx_data0,
      rx_k_i => gtp_rx_k0
    );
  sata_rx_data0 (31 downto 8) <= (others => '0');

  --  SATA1: gt 123, ch 0
  inst_gt_123: entity work.wr_gtp_phy_spartan6
    generic map (
      g_simulation => g_simulation,
      g_force_disparity => 0,
      g_enable_ch0 => 1, --  For SATA1
      g_enable_ch1 => 0  --  For SFP
    )
    port map (
      --  Channel 0 is SATA!
      gtp0_clk_i => clk_125m_gtp123_buf,
      ch0_ref_clk_i => clk_ref_125m_i,
      ch0_tx_data_i => gtp_tx_data1,
      ch0_tx_k_i => gtp_tx_k1,
      ch0_tx_disparity_o => open,
      ch0_tx_enc_err_o => open,
      ch0_rx_data_o => gtp_rx_data1,
      ch0_rx_rbclk_o => gtp_rx_clk1,
      ch0_rx_k_o => gtp_rx_k1,
      ch0_rx_enc_err_o => open,
      ch0_rx_bitslide_o => open,
      ch0_rst_i => sata_rst1(1),
      ch0_loopen_i => '0',
      ch0_loopen_vec_i => (others => '0'),
      ch0_tx_prbs_sel_i => (others => '0'),
      ch0_rdy_o => sata_rdy(1),
      --  Channel 0 is not used.
      gtp1_clk_i => '0',
      ch1_ref_clk_i => '0',
      ch1_tx_data_i => (others => '0'),
      ch1_tx_k_i => '0',
      ch1_tx_disparity_o => open,
      ch1_tx_enc_err_o => open,
      ch1_rx_rbclk_o => open,
      ch1_rx_data_o => open,
      ch1_rx_k_o => open,
      ch1_rx_enc_err_o => open,
      ch1_rx_bitslide_o => open,
      ch1_rst_i => '1',
      ch1_loopen_i => '0',
      ch1_loopen_vec_i => (others => '0'),
      ch1_tx_prbs_sel_i => "000",
      ch1_rdy_o => open,
      ch0_ref_sel_pll => "000",
      ch1_ref_sel_pll => "000",
      pad_txn1_o => open,
      pad_txp1_o => open,
      pad_rxn1_i => '0',
      pad_rxp1_i => '0',
      pad_txn0_o => sata1_txn_o,
      pad_txp0_o => sata1_txp_o,
      pad_rxn0_i => sata1_rxn_i,
      pad_rxp0_i => sata1_rxp_i
    );
  
  inst_sata_ctrl1: entity work.sata_controller
    port map (
      clk_62m5_i => clk_sys_62m5_i,
      rst_62m5_n_i => rst_sys_62m5_n_i,
      tx_clk_i => clk_ref_125m_i,
      rx_clk_i => gtp_rx_clk0,
      trig_in_i => trig_in(1),
      trig_out_o => trig_out(1),
      rdy_i => sata_rdy(1),
      reg_tx_data_i => sata_tx_data1(7 downto 0),
      reg_tx_wr_i => sata_tx_data_wr(1),
      tx_data_o => gtp_tx_data1,
      tx_k_o => gtp_tx_k1,
      reg_rx_data_o => sata_rx_data1(7 downto 0),
      reg_rx_rd_i => sata_rx_data_rd(1),
      reg_rx_o => sata_rx(1),
      rx_data_i => gtp_rx_data1,
      rx_k_i => gtp_rx_k1
    );
  sata_rx_data1 (31 downto 8) <= (others => '0');

  --  Trigger connections.
  conn_trig_out_o <= trig_out(0) or trig_out(1);
  trig_in(0) <= trig_out(1) or conn_trig_in_i;
  trig_in(1) <= trig_out(0) or conn_trig_in_i;

  gen_chipscope: if False and g_simulation = 0 generate
    component chipscope_icon_sata
      port (
        CONTROL0 : inout STD_LOGIC_VECTOR(35 downto 0);
        CONTROL1 : inout STD_LOGIC_VECTOR(35 downto 0));
    end component;

    component chipscope_ila_sata
      port (
        CONTROL : inout STD_LOGIC_VECTOR(35 downto 0);
        CLK     : in    STD_LOGIC;
        TRIG0   : in    STD_LOGIC_VECTOR(9 downto 0));
    end component;

    signal control0, control1: std_logic_vector(35 downto 0);
  begin
      inst_ila_icon : chipscope_icon_sata
        port map (
          CONTROL0 => CONTROL0,
          CONTROL1 => CONTROL1);

      inst_ila_gtp0_rx : chipscope_ila_sata
        port map (
          CONTROL => CONTROL0,
          CLK => gtp_rx_clk0,
          TRIG0 (7 downto 0)=> gtp_rx_data0,
          TRIG0 (8) => gtp_rx_k0,
          TRIG0 (9) => sata_rdy(0));

      inst_ila_gtp0_tx : chipscope_ila_sata
        port map (
          CONTROL => CONTROL1,
          CLK => clk_ref_125m_i,
          TRIG0 (7 downto 0)=> gtp_tx_data0,
          TRIG0 (8) => gtp_tx_k0,
          TRIG0 (9) => sata_rst(0));
  end generate;
end arch;