--
-- Opcode Block REGBLOCK
--
-- Opcodes serviced: OC_REGWRITE, OC_REGBLOCK_RD, OC_REGBLOCK_WR
-- 
-- Read/Write to bank of 16b registers
--
-- An example of a multi-opcode OCB
-- 
-- 
--

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_regblock is
   port(
      -- locallink tx interface
      lls_o      : out    t_llsrc;
      lld_i      : in     std_logic;
      -- oc rx interface
      oc_valid_i : in     std_logic;
      oc_data_i  : in     slv16;
      oc_dack_no : out    std_logic;
      -- registers
      reg_o      : out    t_reg_bus;
      db_wr_o    : out    slv32;
      db_data_o  : out    slv16;
      -- infrastructure
      clk        : in     std_logic;
      rst        : in     std_logic
   );

-- Declarations

end ocb_regblock ;


architecture rtl of ocb_regblock 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_str : std_logic;
  signal addr_clr : std_logic;
  signal addr_inc : std_logic;
  --signal addr     : slv16;
  signal addr_int : integer range 0 to 31;
  signal data_str : std_logic;
-- signal reg_data : slv16_array (0 to 31) := (others => x"0000");
  signal reg_data : t_reg_bus := (others => x"0000");

  signal opcode_ok : std_logic;

  signal ack_send    : std_logic;
  signal ack_busy    : std_logic;
  signal ack_opcode  : slv16;
  signal ack_size    : slv16;
  signal ack_payload : slv16;
  signal rx_oc_port    : slv4;
  signal rx_ocseq    : slv16;

  signal ocseq_store_en : std_logic;
  signal oc_decode_en   : std_logic;
  signal oc_data_opcodepl : slv16;


  type oc_decoded_type is (REGWRITE, REGBLOCK_RD, REGBLOCK_WR, UNRECOG_OPCODE);
  signal oc_decoded : oc_decoded_type;


  type states is (Opcode, OCSeq, Size,
                  RegWr_Start, RegWr_Data,
                  BlkWr_Start, BlkWr_Data,
                  BlkRd_WaitEOF, BlkRd_Header, BlkRd_Data,
                  WaitEOF,
                  SendAck, WaitAckBusy,
                  WaitOCReady, Idle, OCDone
                  );

  signal state, nstate : states;


begin

  oc_data_opcodepl <= oc_get_opcodepl(oc_data_i);

  opcode_ok <= '1' when (oc_data_opcodepl = OC_REGWRITE)     else
               '1' when (oc_data_opcodepl = OC_REGBLOCK_RD ) else
               '1' when (oc_data_opcodepl = OC_REGBLOCK_WR ) else
               '0';


  -- 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,
                                opcode_ok, oc_decoded,
                                --addr,
                                addr_int,
                                state
                                )
  begin

    -- defaults
    nstate         <= Idle;
    oc_dack_no     <= '1';
    ack_send       <= '0';
    ocseq_store_en <= '0';
    oc_decode_en   <= '0';
    addr_str       <= '0';
    data_str       <= '0';
    addr_clr       <= '0';
    addr_inc       <= '0';

    case state is

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

      when WaitOCReady =>                      -- Make sure we get rising edge of oc_valid
        nstate     <= WaitOCReady;
        addr_clr   <= '1';
        oc_dack_no <= 'Z';
        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 (opcode_ok = '1') then
            oc_decode_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';

        case oc_decoded is
          when REGWRITE    => nstate <= RegWr_Start;
          when REGBLOCK_WR => nstate <= BlkWr_Start;
          when REGBLOCK_RD => nstate <= BlkRd_WaitEOF;
          when others      => nstate <= BlkRd_WaitEOF;
        end case;



        -- REGWRITE
        -------------------------------------------------------------

      when RegWr_Start =>               -- Address
        oc_dack_no <= '0';
        addr_str   <= '1';
        nstate     <= RegWr_Data;


      when RegWr_Data =>
        --oc_dack_no <= '0';
        data_str <= '1';
        nstate   <= WaitEOF;


        -- REGBLOCK_WR
        -------------------------------------------------------------
      when BlkWr_Start =>
        addr_clr <= '1';
        nstate   <= BlkWr_Data;


      when BlkWr_Data =>
        nstate     <= BlkWr_Data;
        addr_inc   <= '1';
        oc_dack_no <= '0';
        if (addr_int = 31) then
          if (oc_valid_i = '0') then
            oc_dack_no <= '1'; -- wait til after ack
            nstate <= SendAck;
          else
            nstate <= WaitEOF;
          end if;
        end if;



        -- REGBLOCK_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
          ack_send <= '0';
        end if;

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


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

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


      when SendAck =>
        oc_dack_no <= '1';

        ack_send <= '1';
        nstate   <= WaitAckBusy;


      when WaitAckBusy =>
        oc_dack_no <= '1';

        nstate   <= WaitAckBusy;
        if (ack_busy = '0') then      -- wait for Ack to be done
          nstate <= OCDone;
        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_str = '1') then
          --addr <= oc_data_i;
          addr_int <= conv_integer(oc_data_i(4 downto 0));

        elsif (addr_inc = '1') then
          if (addr_int < 31) then
            --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));




  prc_reg_block : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then

        reg_data <= REG_INITVAL;

        db_data_o <= (others => '0');
        db_wr_o   <= (others => '0');

      else

        -- default
        db_wr_o <= (others => '0');

        -- store data to register and output
        if (data_str = '1') then
          reg_data(addr_int) <= oc_data_i;
          db_data_o          <= oc_data_i;
          db_wr_o(addr_int)  <= '1';

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


  reg_o <= reg_data;



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

  prc_oc_decode_store : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        oc_decoded <= UNRECOG_OPCODE;

      elsif (oc_decode_en = '1') then
        rx_oc_port <= oc_get_port(oc_data_i);
        -- default
        oc_decoded                          <= UNRECOG_OPCODE;
        case oc_data_opcodepl is
          when OC_REGWRITE    => oc_decoded <= REGWRITE;
          when OC_REGBLOCK_RD => oc_decoded <= REGBLOCK_RD;
          when OC_REGBLOCK_WR => oc_decoded <= REGBLOCK_WR;
          when others         => oc_decoded <= UNRECOG_OPCODE;
        end case;
      end if;
    end if;
  end process;



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

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



  prc_ack_sigs : process (oc_decoded, reg_data, addr_int)
  begin
    case oc_decoded is
      when REGWRITE =>
        ack_opcode  <= oc_insert_port(OC_REGWRITE, rx_oc_port);
        ack_size    <= x"0002";
        ack_payload <= x"ACAC";

      when REGBLOCK_RD =>
        ack_opcode  <= oc_insert_port(OC_REGBLOCK_RD, rx_oc_port);
        ack_size    <= x"0040";
        ack_payload <= reg_data(addr_int);

      when REGBLOCK_WR =>
        ack_opcode  <= oc_insert_port(OC_REGBLOCK_WR, rx_oc_port);
        ack_size    <= x"0002";
        ack_payload <= x"ACAC";

      when others =>
        ack_opcode  <= oc_insert_port(OC_INVALID, rx_oc_port);
        ack_size    <= x"0002";
        ack_payload <= x"ACAC";

    end case;
  end process;



  ocbregblk_ack : ll_ack_gen
    port map (
      opcode_i  => ack_opcode,
      ocseq_i   => rx_ocseq,
      ocsize_i  => ack_size,
      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;