--
-- Opcode Block STATBLOCK
--
-- Opcodes serviced: OC_STATREAD
-- 
-- Read bank of 32x16b status words
--
-- 
--

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 hsio;
use hsio.pkg_core_globals.all;

library locallink;


entity ocb_statread 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;
      -- status words in
      stat_i     : in     slv16_array (31 downto 0);
      -- infrastructure
      clk        : in     std_logic;
      rst        : in     std_logic
   );

-- Declarations

end ocb_statread ;


architecture rtl of ocb_statread 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 addr_clr : std_logic;
  signal addr_inc : std_logic;
  signal addr : slv5;
  --signal addr_int : integer range 0 to 31;

  signal stat_blk : slv16_array(31 downto 0);


  signal ack_send    : std_logic;
  signal ack_busy    : std_logic;
  signal ack_payload : slv16;
  signal rx_ocseq    : slv16;
  signal rx_oc_port : slv4;
  signal tx_opcode    : slv16;


  signal ocseq_store_en : std_logic;
signal oc_port_store_en : std_logic;

  type states is (Opcode, OCSeq, Size,
                  BlkRd_WaitEOF, BlkRd_Header, BlkRd_Data,
                  WaitOCReady, Idle, OCDone
                  );

  signal state, nstate : states;

  attribute keep : string;
  attribute keep of addr : signal is "true";
  attribute keep of state : signal is "true";


begin

  stat_blk <= stat_i;

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

      end if;
    end if;
  end process;



  prc_async_machine : process (oc_valid_i, ack_busy, oc_data_i,
                               addr,
                               --addr_int,
                               state
                               )
  begin

    -- defaults
    nstate         <= Idle;
    oc_dack_no     <= '1';
    ack_send       <= '0';
    ocseq_store_en <= '0';
    addr_clr       <= '0';
    addr_inc       <= '0';
    oc_port_store_en <= '0';

    case state is

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

      when WaitOCReady =>                      -- Make sure we get rising edge of oc_valid
        nstate     <= WaitOCReady;
        oc_dack_no <= 'Z';
        addr_clr   <= '1';
        if (oc_valid_i = '0') then
          nstate   <= Idle;
        end if;


      when Idle =>
        nstate     <= Idle;
        oc_dack_no <= 'Z';
        if (oc_valid_i = '1') then
          if (oc_get_opcodepl(oc_data_i) = OC_STATREAD) then
            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';
        nstate     <= BlkRd_WaitEOF;


        -- STATBLOCK_RD
        -------------------------------------------------------------

      when BlkRd_WaitEOF =>
        nstate     <= BlkRd_WaitEOF;
        oc_dack_no <= '0';
        if (oc_valid_i = '0') then
            oc_dack_no <= '1';  -- wait til after ack
          nstate   <= BlkRd_Header;
        end if;


      when BlkRd_Header =>
        nstate     <= BlkRd_Header;
        oc_dack_no <= '1';
        ack_send <= '1';
        addr_clr <= '1';
        if (ack_busy = '0') then  -- if ack_send=1, ack_busy signals start of data transfer (this is advanced mode)
          nstate <= BlkRd_Data;
        end if;


      when BlkRd_Data =>
        nstate     <= BlkRd_Data;
        oc_dack_no <= '1';
        ack_send <= '1';

        --if (addr_int = 31) then
        if (addr = "11111") then
          ack_send <= '0';
        end if;

        if (ack_busy = '0') then
          addr_inc <= '1';
          --if (addr_int = 31) then
          if (addr = "11111") then
            nstate <= OCDone;
          end if;
        end if;


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


    end case;
  end process;


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


  prc_addr_count_store : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') or (addr_clr = '1') then
        addr <= (others => '0');
        --addr_int <= 0;

      else
        if (addr_inc = '1') then
          --if (addr_int < 31) then
          if (addr < "11111") then
            --addr <= '0';
            --addr_int <= 0;
            --else
            addr <= addr + '1';
            --addr_int <= addr_int + 1;
          end if;
        end if;
      end if;
    end if;
  end process;

-- addr_int <= conv_integer(addr(4 downto 0));




-----------------------------------------------------------
-- 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;

  --ack_payload <= stat_blk(addr_int);
  ack_payload <= stat_blk(conv_integer(addr));

  tx_opcode <= oc_insert_port(OC_STATREAD, rx_oc_port);

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


end architecture;