--
-- Downlink Encoder
--
-- A bit more of a non-optimal hack, as this one isn't for an ASIC
--
-- Matt Warren Aug 2016
--
--


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library downlink;
use downlink.lcb_pkg_globals.all;


entity lcbtst_encoder is
  port(
    hccid_i      : in  std_logic_vector(4 downto 0);
    l0a_i        : in  std_logic;
    bcr_i        : in  std_logic;
    tag_i        : in  std_logic_vector(6 downto 0);
    cmd7_i       : in  std_logic_vector(6 downto 0);
    cmd7_ready_i : in  std_logic;
    cmd7_start_i : in  std_logic;
    cmd7_end_i   : in  std_logic;
    cmd7_ack_o   : out std_logic;

    ------------------------------
    frame_sync_o : out std_logic_vector(1 downto 0);

    par4_o : out std_logic_vector(3 downto 0);

    -- Infra
    --clk160 : in std_logic;
    clk40 : in std_logic;               --40MHz BCO
    rst   : in std_logic
    );

-- Declarations

end lcbtst_encoder;

---------------------------------------------------------------------------
architecture rtl of lcbtst_encoder is


  signal frame_sync : std_logic_vector(1 downto 0);

  signal bcr     : std_logic;
  signal l0a_sr  : std_logic_vector(3 downto 0);
  signal l0a_map : std_logic_vector(3 downto 0);
  signal tag     : std_logic_vector(6 downto 0);
  signal tag0    : std_logic_vector(6 downto 0);

  signal data6b0_l0a : std_logic_vector(5 downto 0);
  signal data6b1_l0a : std_logic_vector(5 downto 0);

  signal ready_l0a : std_logic;
  signal lone_bcr  : std_logic;


  signal dbg_bcr     : std_logic;
  signal dbg_l0a_map : std_logic_vector(3 downto 0);
  signal dbg_tag     : std_logic_vector(6 downto 0);

  signal data6b0_cmd : std_logic_vector(5 downto 0);
  signal data6b1_cmd : std_logic_vector(5 downto 0);
  signal ready_cmd   : std_logic;
  signal cmd7_ack0   : std_logic;


-- IDLE frame
  signal symb0_idle : std_logic_vector(7 downto 0);
  signal symb1_idle : std_logic_vector(7 downto 0);


  signal enc_in       : std_logic_vector(5 downto 0);
  signal enc_out      : std_logic_vector(7 downto 0);
  signal symbol_out   : std_logic_vector(7 downto 0);
  signal symbol_out_q : std_logic_vector(7 downto 0);


  type states is (Symbol00,
                  L0A0, CMDStart0, CMDReady0, CMDEnd0, Idle0,
                  L0A1, CMDStart1, CMDReady1, CMDEnd1, Idle1
                  );
  signal state, nstate : states;

begin

  -- Generate frame sync
  prc_frame_sync_gen : process (clk40)
  begin
    if rising_edge(clk40) then
      if (rst = '1') then
        frame_sync <= FSYNC_NIB0;

      else
        if (bcr_i = '1') then  -- Using BCR to set the frame phase ... gets
          -- messy if BCR is sent at the wrong time
          frame_sync <= FSYNC_NIB0;  -- BCR arrives in NIB3, so next NIB is 0

        elsif (frame_sync = "11") then
          frame_sync <= "00";

        else
          frame_sync <= frame_sync + '1';

        end if;

      end if;
    end if;
  end process;

  frame_sync_o <= frame_sync;

  -----------------------------------------------

  -- Shift L0A in
  l0a_sr(0)          <= l0a_i;
  l0a_sr(3 downto 1) <= l0a_sr(2 downto 0) when rising_edge(clk40);


  -- next frame gen and store
  prc_frame_store : process (clk40)
  begin
    if rising_edge(clk40) then
      if (rst = '1') then
        bcr     <= '0';
        l0a_map <= "0000";
        tag     <= "0000000";

      else

        if (frame_sync = FSYNC_NIB0) then
          tag0 <= tag_i;                -- sample tag at the begining
          -- to allow for auto incementing

        elsif (frame_sync = FSYNC_FRAME) then
          tag     <= tag0;  -- still need taq in phase with everything else
          bcr     <= bcr_i;
          l0a_map <= l0a_sr;

        end if;
      end if;
    end if;
  end process;




  -- L0A frame
  lone_bcr <= '1' when (bcr = '1') and (l0a_map = "0000") else '0';

  data6b0_l0a <= bcr & l0a_map & tag(6) when (lone_bcr = '0') else "100000";
  data6b1_l0a <= tag(5 downto 0)        when (lone_bcr = '0') else "000000";


  ready_l0a <= '1' when (l0a_map /= "0000") or (bcr = '1') else '0';




  -- Easy now, but may need to revert to more complicated latching
  dbg_bcr             <= bcr;              -- when rising_edge(symbol0_sync);
  dbg_l0a_map         <= l0a_map;          -- when rising_edge(symbol0_sync);
  dbg_tag(6)          <= tag(6);           -- when rising_edge(symbol0_sync);
  dbg_tag(5 downto 0) <= tag(5 downto 0);  -- when rising_edge(symbol1_sync);

-- CMD frame
  data6b0_cmd <= "00000" & cmd7_i(6);
  data6b1_cmd <= cmd7_i(5 downto 0);
  ready_cmd   <= cmd7_ready_i;


-- IDLE frame
  symb0_idle <= MAP_K_8B(0);
  symb1_idle <= MAP_K_8B(1);



  -- Encoder
  --------------------------------------------- 
  enc_out <= MAP_6B_8B(conv_integer(enc_in));


  prc_sm_sync_part : process (clk40)
  begin
    if rising_edge(clk40) then
      if (rst = '1') then
        state <= Symbol00;
      else

        if (frame_sync = FSYNC_FRAME) then  -- Next state symbol 0
          state <= Symbol00;

        else
          state <= nstate;

        end if;
      end if;
    end if;
  end process;



  prc_frame_out : process (cmd7_end_i, cmd7_ready_i, cmd7_start_i, data6b0_cmd,
                           data6b0_l0a, data6b1_cmd, data6b1_l0a, enc_out,
                           frame_sync, hccid_i, ready_l0a, state, symb0_idle,
                           symb1_idle)
  begin

    -- defaults
    cmd7_ack0  <= '0';
    enc_in     <= data6b0_l0a;
    symbol_out <= enc_out;

    case state is

      when Symbol00 =>

        if (ready_l0a = '1') then
          enc_in     <= data6b0_l0a;
          symbol_out <= enc_out;
          nstate     <= L0A0;

        elsif (cmd7_start_i = '1') then
          symbol_out <= S6B8B_K2;
          nstate     <= CMDStart0;

        elsif (cmd7_end_i = '1') then
          symbol_out <= S6B8B_K3;
          nstate     <= CMDEnd0;

        elsif (cmd7_ready_i = '1') then
          enc_in     <= data6b0_cmd;
          symbol_out <= enc_out;
          nstate     <= CMDReady0;

        else
          symbol_out <= symb0_idle;
          nstate     <= Idle0;
        end if;



      -- Symbol 0 nib 1 states
------------------------------------------------------
      when L0A0 =>
        enc_in     <= data6b0_l0a;
        symbol_out <= enc_out;
        nstate     <= L0A1;

      when CMDStart0 =>
        symbol_out <= S6B8B_K2;
        nstate     <= CMDStart1;

      when CMDEnd0 =>
        symbol_out <= S6B8B_K3;
        nstate     <= CMDEnd1;

      when CMDReady0 =>
        enc_in     <= data6b0_cmd;
        symbol_out <= enc_out;
        nstate     <= CMDReady1;

      when Idle0 =>
        symbol_out <= symb0_idle;
        nstate     <= Idle1;


        -- symbol 1 states (stay here until sync part changes state)
        -----------------------------------------------------------

      when L0A1 =>
        enc_in     <= data6b1_l0a;
        symbol_out <= enc_out;
        nstate     <= L0A1;


      when CMDStart1 =>
        cmd7_ack0  <= '1';
        enc_in     <= '0' & hccid_i;
        symbol_out <= enc_out;
        nstate     <= CMDStart1;


      when CMDEnd1 =>
        cmd7_ack0  <= '1';
        symbol_out <= SYMB_CMD_END;
        nstate     <= CMDEnd1;

      when CMDReady1 =>
        cmd7_ack0  <= '1';
        enc_in     <= data6b1_cmd;
        symbol_out <= enc_out;
        nstate     <= CMDReady1;


      when Idle1 =>
        symbol_out <= symb1_idle;
        nstate     <= Idle1;

    end case;


  end process;

  cmd7_ack_o <= cmd7_ack0 when (frame_sync = FSYNC_FRAME) else '0';



  -- par4b symbol mux
  -- -----------------------------------------------------------------------

  -- Not worried about latency here at the mo, so sync

  prc_par4_out : process (clk40)
  begin

    if rising_edge(clk40) then
      --    if (rst = '1') then
      --      par4_o <= "0000";
      --    else

      symbol_out_q <= symbol_out;       -- store symbol for later tx (well it's
      --                                      -- just the lower nibble really)

      --      if (frame_sync(0) = '1') then   -- symbol sync
      --        par4_o <= symbol_out(7 downto 4);
      --      else
      --        par4_o <= symbol_out_q(3 downto 0);
      --      end if;

    --    end if;
    end if;
  end process;


  -- OR we just do it the easy way - to hell with sync in sim
  par4_o <= symbol_out(7 downto 4) when (frame_sync(0) = '1') else  -- symbol sync
            symbol_out_q(3 downto 0);



end rtl;