--
-- Opcode Block STREAMS
--
-- Opcodes serviced: OC_STRM_CONF_WR, OC_STRM_REQ_STAT [, OC_STRM_CONF2_WR]
-- 
-- Write to bank(s) of 16b registers, send stat reqs
--
-- 
-- 
-- 
--

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_streams is
   generic(
      NSTREAMS : integer := 16
   );
   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;
      -- streams interface
      strm_req_stat_o : out    std_logic_vector ((NSTREAMS-1) downto 0);
      strm_reg_o      : out    slv16_array ((NSTREAMS-1) downto 0);
      strm_cmd_o      : out    slv16_array ((NSTREAMS-1) downto 0);
      -- infrastructure
      clk             : in     std_logic;
      rst             : in     std_logic
   );

-- Declarations

end ocb_streams ;


architecture rtl of ocb_streams 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 a_ok : std_logic;

  signal ack_send    : std_logic;
  signal ack_busy    : std_logic;
  signal ack_opcode  : slv16;
  signal ack_opcode0 : slv16;
  signal ack_size    : slv16;
  signal ack_payload : slv16;

  signal ocseq_store      : std_logic;
  signal ocsize_store     : std_logic;
  signal rx_ocseq         : slv16;
  signal rx_port          : slv4;
  signal oc_data_opcodepl : slv16;

  signal rx_ocsize     : slv16;
  signal rx_ocsize_int : integer range 0 to 2047;


  constant BCOUNT_MAX : integer := 2047;
  signal   bcount     : integer range 0 to BCOUNT_MAX;
  signal   bcount_clr : std_logic;

  signal strm_mask       : slv16_array(8 downto 0);
  signal strm_mask_store : std_logic;
  signal strm_mask_all   : std_logic_vector(NSTREAMS-1 downto 0);



  signal send_req_stat : std_logic;


  signal stream       : slv8;
  signal stream_int   : integer range 0 to (NSTREAMS-1);
  signal stream_store : std_logic;

  signal reg_mask : slv16 := x"0000";
  signal reg_data : slv16_array(NSTREAMS-1 downto 0) := (others => x"0000");

  signal mask_store  : std_logic;
  signal reg_store   : std_logic;
  signal cmd_strobe  : std_logic;
  signal breg_store  : std_logic;
  signal bcmd_strobe : std_logic;

-- signal stream_id_overflow : std_logic;

  type oc_decoded_type is (CONF_WR, B_CONF_WR,
                           COMMAND, B_COMMAND,
                           REQ_STAT
                           );

  signal oc_decoded       : oc_decoded_type;
  signal oc_decoded_d     : oc_decoded_type;
  signal oc_decoded_store : std_logic;
  signal oc_decode_en     : std_logic;


  type states is (Opcode, OCSeq, Size,
                  CW_DataMask, CW_StreamAddr, CW_Data,
                  BCW_DataMask, BCW_Data,
                  CMD_StreamAddr, CMD_Data,
                  BCMD_Data,
                  RX_Strm_Mask, GS_Send_Req_Stat,
                  WaitEOF,
                  SendAck, WaitAckBusy,
                  WaitOCReady, Idle, OCDone
                  );

  signal state, nstate : states;


begin


  --stream_id_overflow <= '0' when (conv_integer(oc_data_i(6 downto 0)) < NSTREAMS) else '1';

  oc_data_opcodepl <= oc_get_opcodepl(oc_data_i);

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

      end if;
    end if;
  end process;



  prc_async_machine : process (oc_valid_i, oc_data_i, oc_decoded, oc_data_opcodepl,
                               bcount, ack_busy,  --stream_id_overflow,
                               rx_ocsize_int, strm_mask_all,
                               state
                               )
  begin

    -- defaults
    nstate           <= Idle;
    oc_dack_no       <= '1';
    ack_send         <= '0';
    ocseq_store      <= '0';
    ocsize_store     <= '0';
    oc_decoded_d     <= CONF_WR;
    oc_decoded_store <= '0';
    stream_store     <= '0';
    mask_store       <= '0';
    cmd_strobe       <= '0';
    reg_store        <= '0';
    bcmd_strobe      <= '0';
    breg_store       <= '0';
    bcount_clr       <= '0';
    strm_mask_store  <= '0';
    send_req_stat    <= '0';


    case state is

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

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


      when Idle =>
        nstate             <= Idle;
        oc_dack_no         <= 'Z';
        if (oc_valid_i = '1') then
          oc_decoded_store <= '1';

          case oc_data_opcodepl is
            when OC_STRM_CONF_WR  => oc_decoded_d <= CONF_WR; nstate <= Opcode;
            when OC_BSTRM_CONF_WR => oc_decoded_d <= B_CONF_WR; nstate <= Opcode;
            when OC_STRM_REQ_STAT => oc_decoded_d <= REQ_STAT; nstate <= Opcode;
            when OC_STRM_COMMAND  => oc_decoded_d <= COMMAND; nstate <= Opcode;
            when OC_BSTRM_COMMAND => oc_decoded_d <= B_COMMAND; nstate <= Opcode;
            when others           => nstate       <= WaitOCReady;
          end case;
        end if;


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


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


      when Size          =>
        oc_dack_no                 <= '0';
        ocsize_store               <= '1';
        bcount_clr                 <= '1';
        case oc_decoded is
          when CONF_WR   => nstate <= CW_DataMask;
          when B_CONF_WR => nstate <= RX_Strm_Mask;
          when COMMAND   => nstate <= CMD_StreamAddr;
          when B_COMMAND => nstate <= RX_Strm_Mask;
          when REQ_STAT  => nstate <= RX_Strm_Mask;
        end case;


        --== Conf Write ================================================

      when CW_DataMask =>

        oc_dack_no <= '0';
        mask_store <= '1';
        nstate     <= CW_StreamAddr;


      when CW_StreamAddr =>
        if (bcount < (rx_ocsize_int/2)) then  -- do this here to get bcount+1, and catch bad packets
          oc_dack_no   <= '0';
          --if (stream_id_overflow = '0') then
          stream_store <= '1';
          --end if;
          nstate       <= CW_Data;
        else
          nstate       <= WaitEOF;
        end if;


      when CW_Data =>

        --if (stream_id_overflow = '0') then
        reg_store  <= '1';
        --end if;
        oc_dack_no <= '0';
        if (oc_valid_i = '1') then
          nstate   <= CW_StreamAddr;
        else
          oc_dack_no <= '1';  -- wait til after ack
          nstate   <= SendAck;
        end if;


        --== Send Command =================================================

      when CMD_StreamAddr =>
        if (bcount < (rx_ocsize_int/2)) then  -- need bcount+1
          oc_dack_no   <= '0';
          --if (stream_id_overflow = '0') then
          stream_store <= '1';
          --end if;
          nstate       <= CMD_Data;
        else
          nstate       <= WaitEOF;
        end if;


      when CMD_Data =>
        --if (stream_id_overflow = '0') then
        cmd_strobe <= '1';
        --end if;
        oc_dack_no <= '0';
        if (oc_valid_i = '1') then      -- *** there must be a better way
          nstate   <= CMD_StreamAddr;
        else
          oc_dack_no <= '1';  -- wait til after ack
          nstate   <= SendAck;
        end if;



        --== Generic RX Mask Store =========================================

      when RX_Strm_Mask    =>
        nstate                       <= RX_Strm_Mask;
        oc_dack_no                   <= '0';
        strm_mask_store              <= '1';
        if (bcount = 8) then
          bcount_clr                 <= '1';
          case oc_decoded is
            when B_CONF_WR => nstate <= BCW_DataMask;
            when B_COMMAND => nstate <= BCMD_Data;
            when REQ_STAT  => nstate <= GS_Send_Req_Stat;
            when others    => null;     -- should NEVER get here.
          end case;
        end if;


        --== Get Stats ======================================================

      when GS_Send_Req_Stat =>
        send_req_stat <= '1';
        nstate        <= WaitEOF;


        --== Broadcast Config Write  ========================================

      when BCW_DataMask =>
        oc_dack_no <= '0';
        mask_store <= '1';
        bcount_clr <= '1';
        nstate     <= BCW_Data;


      when BCW_Data =>
        nstate     <= BCW_Data;
        breg_store <= strm_mask_all(bcount);
        if (bcount = (NSTREAMS-1)) then
          nstate   <= WaitEOF;
        end if;


        --== Broadcast Send Command  ========================================


      when BCMD_Data =>
        nstate      <= BCMD_Data;
        bcmd_strobe <= strm_mask_all(bcount);
        if (bcount = (NSTREAMS-1)) then
          nstate    <= WaitEOF;
        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_byte_counter : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') or (bcount_clr = '1') then
        bcount <= 0;

      else
        if (bcount /= BCOUNT_MAX) then
          bcount <= bcount + 1;
        end if;
      end if;
    end if;
  end process;

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

  prc_gen_stores : process (clk)
  begin
    if rising_edge(clk) then
      if (stream_store = '1') then
        stream <= oc_data_i(7 downto 0);
      end if;

      if (oc_decoded_store = '1') then
        rx_port    <= oc_get_port(oc_data_i);
        oc_decoded <= oc_decoded_d;
      end if;

      if (ocseq_store = '1') then
        rx_ocseq <= oc_data_i;
      end if;

      if (ocsize_store = '1') then
        rx_ocsize <= oc_data_i;
      end if;


    end if;
  end process;

  stream_int    <= conv_integer(stream);
  rx_ocsize_int <= conv_integer(rx_ocsize);



--------------------------------------------------------------------
  prc_reg_stores : process (clk)
  begin
    if rising_edge(clk) then

      if (rst = '1') then
        --reg_mask <= x"0000";
        reg_data  <= (others => x"0000");  -- *** default set to enabled,
                                           -- capture for testing
        strm_mask <= (others => x"0000");
        --cmd_data <= (others => x"0000")

      else

        -- Mask                         ----------------------
        if (mask_store = '1') then
          reg_mask <= oc_data_i;
        end if;

        -- Register                     ----------------------
        if (reg_store = '1') then
          reg_data(stream_int) <= (reg_data(stream_int) and not(reg_mask)) or (oc_data_i and reg_mask);
        elsif (breg_store = '1') then
          reg_data(bcount)     <= (reg_data(bcount) and not(reg_mask)) or (oc_data_i and reg_mask);
        end if;


        -- Command                      ----------------------
        -- default
        strm_cmd_o               <= (others => x"0000");
        if (cmd_strobe = '1') then
          strm_cmd_o(stream_int) <= oc_data_i;
        elsif (bcmd_strobe = '1') then
          strm_cmd_o(bcount)     <= oc_data_i;
        end if;


        -- Stream Mask (multi use)      ----------------------
        if (strm_mask_store = '1') then
          strm_mask(bcount) <= oc_data_i;
        end if;

      end if;
    end if;
  end process;


  strm_mask_all <= strm_mask(8) &
                   strm_mask(7) &
                   strm_mask(6) &
                   strm_mask(5) &
                   strm_mask(4) &
                   strm_mask(3) &
                   strm_mask(2) &
                   strm_mask(1) &
                   strm_mask(0);


  strm_reg_o(NSTREAMS-1 downto 0) <= reg_data;
  strm_req_stat_o                 <= (others => '0') when (send_req_stat = '0') else
                                     strm_mask_all;


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

  ack_opcode0 <= OC_STRM_CONF_WR  when (oc_decoded = CONF_WR)   else
                 OC_BSTRM_CONF_WR when (oc_decoded = B_CONF_WR) else
                 OC_STRM_COMMAND  when (oc_decoded = COMMAND)   else
                 OC_BSTRM_COMMAND when (oc_decoded = B_COMMAND) else
                 OC_STRM_REQ_STAT;

  ack_opcode <= oc_insert_port(ack_opcode0, rx_port);

  ack_size    <= x"0002";
  ack_payload <= x"ACAC";


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