--
-- Opcode COMMAND
--  
-- For pulsed signals
--
-- 
--


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

library locallink;

library hsio;
use hsio.pkg_core_globals.all;

entity ocb_command is
   port(
      -- oc rx interface
      oc_valid_i        : in     std_logic;
      oc_data_i         : in     slv16;
      oc_dack_no        : out    std_logic;
      -- locallink tx interface
      lls_o             : out    t_llsrc;
      lld_i             : in     std_logic;
      -- payload output
      command_o         : out    slv16;
      rst_ro_o          : out    std_logic;
      rst_trig_o        : out    std_logic;
      --rst_ocb_o   : out std_logic;
      rst_feo_o         : out    std_logic;
      rst_disp_o        : out    std_logic;
      rst_nettx_o       : out    std_logic;
      rst_netrx_o       : out    std_logic;
      slow_reset_tick_i : in     std_logic;
      -- infrastructure
      clk               : in     std_logic;
      rst               : in     std_logic
   );

-- Declarations

end ocb_command ;


architecture rtl of ocb_command is


  component ll_ack_gen
    port (
      -- input interface
      opcode_i  : in  slv16;
      ocseq_i   : in  slv16;
      ocsize_i  : in  slv16;
      payload_i : in  slv16;
      send_i    : in  std_logic;
      busy_o    : out std_logic;
      -- locallink tx interface
      lls_o     : out t_llsrc;
      lld_i     : in  std_logic;
      -- infrastucture
      clk       : in  std_logic;
      rst       : in  std_logic
      );
  end component;

  signal cmd        : slv16;
  signal cmd_store_en : std_logic;
  signal cmd_out_en : std_logic;

  signal reset_cmd     : slv16;
  signal reset_cmd_clr : std_logic;
  signal reset_out     : slv16;

  signal ocseq_store_en     : std_logic;
  signal reset_cmd_store_en : std_logic;
  signal rx_ocseq           : slv16;
  signal oc_port_store_en   : std_logic;
  signal rx_oc_port         : slv4;
  signal tx_opcode          : slv16;

  signal ack_busy : std_logic;
  signal ack_send : std_logic;



  type states is (Reset,
                  Opcode, OCSeq, Size,
                  SendCommand,
                  LongCommand0, LongCommand1,
                  SendAck, WaitAckBusy,
                  Idle, WaitOCReady, OCDone
                  );

  signal state, nstate : states;

begin

  -- State Machine
  --------------------------------------------------------
  prc_sync_part : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        state <= Reset;
      else
        state <= nstate;

      end if;
    end if;
  end process;


  prc_async_machine : process (oc_valid_i, oc_data_i, ack_busy,
                               slow_reset_tick_i,
                               state
                               )
  begin

    -- defaults
    nstate             <= Reset;
    oc_dack_no         <= '1';
    ack_send           <= '0';
    ocseq_store_en     <= '0';
    reset_cmd_clr     <= '0';
    reset_cmd_store_en <= '0';
    cmd_store_en       <= '0';
    cmd_out_en         <= '0';
    oc_port_store_en   <= '0';


    case state is

      -------------------------------------------------------------
      when Reset =>
        oc_dack_no   <= 'Z';
        cmd_out_en <= '1'; -- sends all resets
        nstate       <= WaitOCReady;


      when WaitOCReady =>
        nstate     <= WaitOCReady;
        oc_dack_no <= 'Z';
        if (oc_valid_i = '0') then      -- wait for OC to be done
          nstate   <= Idle;
        end if;


      when Idle =>
        nstate               <= Idle;
        oc_dack_no           <= 'Z';
        reset_cmd_clr       <= '1';
        if (oc_valid_i = '1') then
          if (oc_get_opcodepl(oc_data_i) = OC_COMMAND) then  -- check for opcode here
            oc_port_store_en <= '1';
            nstate           <= Opcode;
          else
            nstate           <= WaitOCReady;
          end if;
        end if;


      when Opcode =>
        oc_dack_no <= '0';
        nstate     <= OCSeq;


      when OCSeq =>
        oc_dack_no     <= '0';
        ocseq_store_en <= '1';
        nstate         <= Size;


      when Size =>
        oc_dack_no <= '0';
        if (oc_data_i = 16#4#) then
          nstate   <= LongCommand0;
        else
          nstate   <= SendCommand;
        end if;


        --------------------------------------------------------------
      when SendCommand =>
        --oc_dack_no <= '0';            -- skip dtack in case this is the last word
        cmd_store_en <= '1';
        nstate       <= SendAck;


        --------------------------------------------------------------
      when LongCommand0 =>
        oc_dack_no   <= '0';
        cmd_store_en <= '1';
        nstate       <= LongCommand1;


      when LongCommand1 =>
        --oc_dack_no <= '0';            -- skip dtack in case this is the last word
        reset_cmd_store_en <= '1';
        nstate             <= SendAck;

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



      when SendAck =>
        nstate       <= SendAck;
        oc_dack_no   <= '0';
        if (oc_valid_i = '0') then
          oc_dack_no <= '1';            -- wait until after ack
          cmd_out_en <= '1';
          ack_send   <= '1';
          nstate     <= WaitAckBusy;
        end if;


      when WaitAckBusy =>
        nstate     <= WaitAckBusy;
        oc_dack_no <= '1';
        if (ack_busy = '0') then
          nstate   <= OCDone;
        end if;


        --=========================================================================
      when OCDone =>
        oc_dack_no <= '0';              -- final oc_dack to release ocbus
        nstate     <= Idle;


    end case;
  end process;


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

  prc_clockout : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        cmd       <= (others => '0');
        command_o <= (others => '0');

        reset_cmd <= (others => '0');
        reset_out <= (others => '1');

      else

        if (cmd_store_en = '1') then
          cmd <= oc_data_i;
        end if;

        if (reset_cmd_clr = '1') then
          reset_cmd <= x"0000";

        elsif (reset_cmd_store_en = '1') then
          reset_cmd <= oc_data_i;

        end if;


        -- defaults
        command_o <= (others => '0');
        reset_out <= (others => '0');

        if (cmd_out_en = '1') then
          command_o <= cmd;
          reset_out <= reset_cmd;
        end if;

      end if;

    end if;
  end process;

  rst_ro_o    <= reset_out(CMD_RST_RO);
  rst_trig_o  <= reset_out(CMD_RST_TRIG);
  --rst_ocb_o   <= reset_out(CMD_RST_OCB);
  --rst_feo_o   <= reset_out(CMD_RST_FEO);
  rst_disp_o  <= reset_out(CMD_RST_DISP);
  rst_nettx_o <= reset_out(CMD_RST_NETTX);
  rst_netrx_o <= reset_out(CMD_RST_NETRX);
  --rst_drv_o   <= reset_out(CMD_RST_DRV);



-----------------------------------------------------------
-- Ack Interface

  prc_ocseq_store : process (clk)
  begin
    if rising_edge(clk) then
      if (oc_port_store_en = '1') then
        rx_oc_port <= oc_get_port(oc_data_i);
      end if;
      if (ocseq_store_en = '1') then
        rx_ocseq   <= oc_data_i;
      end if;
    end if;
  end process;


  tx_opcode <= oc_insert_port(OC_COMMAND, rx_oc_port);

  ocbcommand_ack : ll_ack_gen
    port map (
      opcode_i  => tx_opcode,
      ocseq_i   => rx_ocseq,
      ocsize_i  => x"0002",
      payload_i => x"acac",
      send_i    => ack_send,
      busy_o    => ack_busy,
      lls_o     => lls_o,
      lld_i     => lld_i,
      clk       => clk,
      rst       => rst
      );



end architecture;