--
-- Opcode Block RAWSEQ
-- 
-- Modified RAWSIGS to allow any/all to be sent at once - a sequencer.
-- Operates at 80Mb though
--
-- 
-- Matt Warren 2013
--
-- change log
-- 2013-06-12 copied ocb_rawsigs to this file
-- 2013-10-08 converted to 80Mb with large RAM
-- 2014-03-12 fixed no cyclic bug
--

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_rawseq is
  port(
    running_o : out std_logic;

    reg_sigs_idle_i     : in  slv16;
    -- 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 functions
    sigs_o        : out std_logic_vector (15 downto 0);
    pattern_go_i  : in  std_logic;
    reg_sq_ctl_i      : in  std_logic_vector (15 downto 0);
    sq_stat_o     : out std_logic_vector (7 downto 0);
    sq_addr_o     : out std_logic_vector (15 downto 0);

    -- infrastructure
    s40_i       : in  std_logic;
    tog20_i       : in  std_logic;
    clk              : in  std_logic;
    rst              : in  std_logic;
    ocrawseq_start_o : out std_logic
    );

-- Declarations

end ocb_rawseq;

architecture rtl of ocb_rawseq is

  component cg_bram_8kx16
    port (
      clka  : in  std_logic;
      wea   : in  std_logic_vector(0 downto 0);
      addra : in  std_logic_vector(12 downto 0);
      dina  : in  std_logic_vector(15 downto 0);
      douta : out std_logic_vector(15 downto 0)
      );
  end component;

  component cg_bram_16kx16
    port (
      clka  : in  std_logic;
      wea   : in  std_logic_vector(0 downto 0);
      addra : in  std_logic_vector(13 downto 0);
      dina  : in  std_logic_vector(15 downto 0);
      douta : out std_logic_vector(15 downto 0)
      );
  end component;


  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 soq : std_logic_vector(15 downto 0);
  signal sigs_out : std_logic_vector(15 downto 0);

  signal ocseq_store_en : std_logic;
  signal rx_ocseq       : slv16;

  signal ack_busy     : std_logic;
  signal ack_send     : std_logic;
  signal sigs_start   : std_logic;
  signal sigs_start_q : std_logic;


  constant RAM_AD_WIDTH : integer := 14;

  signal pattram_dout : slv16;
  signal pattram_din  : slv16;
  signal pattram_we   : std_logic;
  signal pattram_we00   : std_logic_vector(0 downto 0);
  signal pattram_ad   : std_logic_vector(RAM_AD_WIDTH-1 downto 0);

  constant WCOUNT_MAX : integer := (2**RAM_AD_WIDTH-1);

  signal wcount     : integer range 0 to WCOUNT_MAX;
  signal wcount_en  : std_logic;
  signal wcount_clr : std_logic;
  signal wcount_load : std_logic;

  type oc_codes is (RAWSEQ, SEQ_PATTERN, INVALID);

  signal oc_coded          : oc_codes;
  signal rx_oc_coded       : oc_codes;
  signal rx_opcode         : slv16;
  signal rx_opcode0        : slv16;
  signal oc_coded_store_en : std_logic;
  signal oc_data_opcodepl  : slv16;
  signal rx_oc_port        : slv4;
  signal tx_payload        : slv16;

  signal sq_running  : std_logic;
  signal sq_ready    : std_logic;


  signal end_addr  : std_logic_vector(RAM_AD_WIDTH-1 downto 0);
  signal cyclic_en : std_logic;

  type states is (
    Opcode, OCSeq, Size,
    RawSeqStart, RawSeqRun,
    PatternAddr, PatternWords,
    PatternStart0, PatternStart1,  PatternRun,
    SendAck, WaitAckBusy,
    Idle, WaitOCReady, OCDone
    );

  signal state, nstate : states;

begin

  sq_stat_o <= "000000" & sq_running & sq_ready;
  sq_addr_o <= conv_std_logic_vector(wcount, 16);

  end_addr  <= reg_sq_ctl_i((RAM_AD_WIDTH-1) downto 0);
  cyclic_en <= reg_sq_ctl_i(B_SQ_CYCLIC_EN);


  oc_data_opcodepl <= oc_get_opcodepl(oc_data_i);

  oc_coded <= RAWSEQ      when (oc_data_opcodepl = OC_RAWSEQ)      else
              SEQ_PATTERN when (oc_data_opcodepl = OC_SEQ_PATTERN) else
              INVALID;


  -- 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_coded,
                               pattern_go_i, rx_oc_coded, pattram_dout,
                               wcount, s40_i, tog20_i, ack_busy, end_addr,
                               reg_sigs_idle_i,
                               state
                               )
  begin

    -- defaults
    nstate            <= WaitOCReady;
    oc_dack_no        <= '1';
    --sigs_out          <= x"0000";
    sigs_out          <= reg_sigs_idle_i; --***
    ack_send          <= '0';
    ocseq_store_en    <= '0';
    sigs_start        <= '0';
    oc_coded_store_en <= '0';
    pattram_we        <= '0';
    wcount_clr        <= '0';
    wcount_load       <= '0';
    wcount_en         <= '0';
    sq_running        <= '0';
    sq_ready          <= '0';

    case state is

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

      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;
        sq_running            <= '0';
        oc_dack_no            <= 'Z';
        if (pattern_go_i = '1') then
          nstate              <= PatternStart0;
        elsif (oc_valid_i = '1') then
          if (oc_coded /= INVALID) then  -- check for opcode here
            oc_coded_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 (rx_oc_coded = RAWSEQ) then
          nstate   <= RawSeqStart;
        else
          nstate   <= PatternAddr;
        end if;


        ------------------------------------------------------------------
        -- OC_RAWSEQ
        ------------------------------------------------------------------

      when RawSeqStart =>               -- align with BCO          
        nstate       <= RawSeqStart;
        if (s40_i = '0') and (tog20_i = '0') then -- *** cludge!
          sigs_start <= '1';
          --oc_dack_no <= '0';
          nstate     <= RawSeqRun;
        end if;

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


        ------------------------------------------------------------------
        -- OC_SEQ_PATTERN
        ------------------------------------------------------------------
        -- Load
        -------

      when PatternAddr =>               -- word 0 = address
        if (oc_data_i(15) = '0') then -- check is don't change address is enabled
          wcount_load <= '1';
        end if;
        oc_dack_no  <= '0';
        nstate      <= PatternWords;


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


        ----------------------------------------------------------------
        ----------------------------------------------------------------
        -- Run 

      when PatternStart0 =>
        oc_dack_no   <= 'Z';
        wcount_clr   <= '1';
        nstate     <= PatternStart1;

      when PatternStart1 =>  -- Wait for ram to produce data
        nstate       <= PatternStart1;
        oc_dack_no   <= 'Z';
        -- sync with BCO
        if (s40_i = '0') and (tog20_i = '0') then -- *** cludge!
          wcount_en  <= '1';
          sigs_start <= '1';
          nstate     <= PatternRun;
        end if;


      when PatternRun =>
        nstate     <= PatternRun;
        oc_dack_no <= 'Z';
        sigs_out   <= pattram_dout;
        sq_running        <= '1';
        wcount_en  <= '1';
        if (wcount >= conv_integer(end_addr)) then -- *** don't really like >=
          wcount_en  <= '0';
          if (cyclic_en = '1') then
            nstate   <= PatternStart0;
          else
            nstate   <= Idle; -- OCDone; -- started by a command, ot opcode
          end if;
        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
          nstate   <= OCDone;
        end if;


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


    end case;
  end process;



--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==  --==
--------------------------------------------------------------------
  prc_wcount : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        wcount   <= 0;
      else
        if (wcount_clr = '1') then
          wcount <= 0;

        elsif (wcount_load = '1') then
          wcount <= conv_integer(oc_data_i);

        elsif (wcount_en = '1') and (wcount < WCOUNT_MAX) then
          wcount <= wcount + 1;

        end if;
      end if;
    end if;

  end process;


  pattram_ad <= conv_std_logic_vector(wcount, RAM_AD_WIDTH);
  pattram_we00 <= "1" when pattram_we = '1' else "0";
  pattram_din <= oc_data_i;

  --inst_pattram : cg_bram_8kx16
  inst_pattram : cg_bram_16kx16
    port map (
      clka  => clk,
      wea   => pattram_we00,
      addra => pattram_ad,
      dina  => pattram_din,
      douta => pattram_dout
      );

-----------------------------------------------------------
-- OC/Ack Interface

  prc_stores : process (clk)
  begin
    if rising_edge(clk) then
      if (oc_coded_store_en = '1') then
        rx_oc_coded <= oc_coded;
        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;



  rx_opcode0 <= OC_RAWSEQ when (rx_oc_coded = RAWSEQ) else
                OC_SEQ_PATTERN;

  rx_opcode <= oc_insert_port(rx_opcode0, rx_oc_port);

  tx_payload <= conv_std_logic_vector(wcount, 16); -- x"acac";


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


  ----------------------------------------------------
  prc_sig_start : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') then
        sigs_start_q       <= '0';
        ocrawseq_start_o <= '0';

      else
        sigs_start_q   <= sigs_start;  -- sync to align with bco and make 25ns pulse
        ocrawseq_start_o <= sigs_start_q or sigs_start;

      end if;
    end if;
  end process;




  -----------------------------------------------------
  prc_sigs_out : process (clk)
  begin
    if rising_edge(clk) then
      --if (rst = '1') then
      --  soq <= (others => '0');
      --else
        soq <= sigs_out;
        running_o <= sq_running;
      --end if;
    end if;
  end process;


  sigs_o <= soq;




-------------------------------------------------------------------------------------------------
end architecture;