library ieee;
use ieee.std_logic_1164.all;

entity sata_controller is
  port (
    clk_62m5_i    :     std_logic;
    rst_62m5_n_i  :     std_logic;
    tx_clk_i      :     std_logic;
    rx_clk_i      :     std_logic;

    --  Got a trigger from SATA (clocked by rx_clk_i)
    trig_out_o    : out std_logic;

    --  Trigger to send over SATA (clocked by tx_clk_i)
    trig_in_i     : in  std_logic;

    --  When SATA is ready.
    rdy_i         : in  std_logic;

    --  TX
    reg_tx_data_i : in  std_logic_vector(7 downto 0);
    reg_tx_wr_i   : in  std_logic;
    tx_data_o     : out std_logic_vector(7 downto 0);
    tx_k_o        : out std_logic;

    --  RX
    reg_rx_data_o : out std_logic_vector(7 downto 0);
    reg_rx_rd_i   : in  std_logic;
    reg_rx_o      : out std_logic;
    rx_data_i     : in  std_logic_vector(7 downto 0);
    rx_k_i        : in  std_logic
  );
end sata_controller;

architecture arch of sata_controller is
  signal tx_data_reg : std_logic_vector(7 downto 0);
  signal tx_data_wr : std_logic;
  signal tx_wr_r : std_logic;

  signal rx_wr, rx_valid : std_logic;

  signal trig_out : std_logic_vector(2 downto 0);
  signal trig_in_sync, trig_in : std_logic;
begin
  inst_sync_tx: entity work.gc_sync_word_wr
    generic map (
      g_auto_wr => False,
      g_width => 8
    )
    port map (
      clk_in_i => clk_62m5_i,
      rst_in_n_i => rst_62m5_n_i,
      clk_out_i => tx_clk_i,
      rst_out_n_i => '1',
      data_i => reg_tx_data_i,
      wr_i => reg_tx_wr_i,
      busy_o => open,
      ack_o => open,
      data_o => tx_data_reg,
      wr_o => tx_data_wr
    );
  
  --  Resync trig_in
  cmp_conn_trig_sync : entity work.gc_sync
    port map (
      clk_i     => rx_clk_i,
      rst_n_a_i => '1',
      d_i       => trig_in_i,
      q_o       => trig_in_sync);

  cmp_conn_trig_posedge : entity work.gc_posedge
    port map (
      clk_i   => rx_clk_i,
      rst_n_i => '1',
      data_i  => trig_in_sync,
      pulse_o => trig_in);

  --  TX arbiter:
  --   First trig_in
  --   Second tx_data (and save tx_wr pulse in case of trig_in)
  --   Last, idle
  process (tx_clk_i)
  begin
    if rising_edge(tx_clk_i) then
      tx_wr_r <= (tx_wr_r or tx_data_wr) and trig_in;
    end if;
  end process;

  process (tx_data_reg, tx_data_wr, trig_in_i)
  begin
    if trig_in = '1' then
      tx_data_o <= x"f7";
      tx_k_o <= '1';
    elsif tx_data_wr = '1' or tx_wr_r = '1' then
      tx_data_o <= tx_data_reg;
      tx_k_o <= '0';
    else
      tx_data_o <= x"bc";
      tx_k_o <= '1';
    end if;
  end process;

  rx_valid <= (not rx_k_i) and rdy_i;

  --  Generate trig_out (extended)
  process (rx_clk_i)
  begin
    if rising_edge(rx_clk_i) then
      if rx_k_i = '1' and rdy_i = '1' and rx_data_i = x"f7" then
        trig_out <= "111";
        trig_out_o <= '1';
      else
        trig_out_o <= trig_out (0);
        trig_out <= '0' & trig_out(2 downto 1);
      end if;
    end if;
  end process;

  inst_rx_data: entity work.gc_sync_word_wr
    generic map (
      g_auto_wr => False,
      g_width => 8
    )
    port map (
      clk_in_i => rx_clk_i,
      rst_in_n_i => '1',
      clk_out_i => clk_62m5_i,
      rst_out_n_i => rst_62m5_n_i,
      data_i => rx_data_i,
      wr_i => rx_valid,
      busy_o => open,
      ack_o => open,
      data_o => reg_rx_data_o,
      wr_o => rx_wr
    );

  process(clk_62m5_i) is
  begin
    if rising_edge(clk_62m5_i) then
      if rst_62m5_n_i = '0' then
        reg_rx_o <= '0';
      else
        if rx_wr = '1' then
          reg_rx_o <= '1';
        elsif reg_rx_rd_i = '1' then
          reg_rx_o <= '0';
        end if;
      end if;
    end if;
  end process;
end arch;