--
-- Opcode Block ECHO
--
-- Echoes packet received
-- Now with added functions to watch for timeouts on an opcode and send back 
--

-- changelog
-- 2012-01-01 around when this was born
-- 2012-07-16 started to add opcode watchdog


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_echo_watchdog is
   port(
      -- oc rx interface
      oc_valid_i  : in     std_logic;
      oc_data_i   : in     slv16;
      oc_dack_nio : inout  std_logic;
      -- locallink tx interface
      lls_o       : out    t_llsrc;
      lld_i       : in     std_logic;
      -- infrastructure
      tick_4hz_i  : in     std_logic;
      clk         : in     std_logic;
      rst         : in     std_logic
   );

-- Declarations

end ocb_echo_watchdog ;


architecture rtl of ocb_echo_watchdog is

  signal   watchdog_timer : slv2;
  constant WDTIMER_MAX    : slv2 := "11";
  signal   watchdog_clr   : std_logic;

  type states is (SrcRdy, SOF, WaitEOF,
                  BarkSrcRdy, BarkSOF,
                  WaitOCReady, Idle
                  );

  signal state, nstate : states;


begin


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



  prc_async_machine : process ( oc_valid_i, oc_dack_nio,
                                lld_i, oc_data_i,
                                state
                                )
  begin

    -- defaults
    nstate        <= Idle;
    oc_dack_nio    <= '1';
    lls_o.src_rdy <= '0';
    lls_o.sof     <= '0';
    lls_o.eof     <= '0';
    watchdog_clr  <= '1';
    lls_o.data    <= oc_data_i;



    case state is

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


      when WaitOCReady =>                      -- Make sure we get rising edge of oc_valid
        nstate       <= WaitOCReady;           -- If we wait too long for oc_valid to
        oc_dack_nio   <= 'Z';
        watchdog_clr <= not oc_dack_nio; -- drop, watchdog will handle OC
        if (oc_valid_i = '0') then
          nstate     <= Idle;
        end if;


      when Idle =>
        nstate     <= Idle;
        oc_dack_nio <= 'Z';
        if (oc_valid_i = '1') then
          if (oc_get_opcodepl(oc_data_i) = OC_ECHO) then
            nstate <= SrcRdy;
          else
            nstate <= WaitOCReady;
          end if;
        end if;


      when SrcRdy =>
        nstate        <= SrcRdy;
        lls_o.src_rdy <= '1';
        if (lld_i = '1') then
          nstate      <= SOF;
        end if;


      when SOF =>
        nstate        <= SOF;
        lls_o.src_rdy <= '1';
        lls_o.sof     <= '1';
        if (lld_i = '1') then
          oc_dack_nio  <= '0';
          nstate      <= WaitEOF;
        end if;


      when WaitEOF =>
        nstate        <= WaitEOF;
        lls_o.src_rdy <= '1';
        if (oc_valid_i = '0') then
          lls_o.eof   <= '1';
        end if;
        if (lld_i = '1') then
          oc_dack_nio  <= '0';
          if (oc_valid_i = '0') then
            nstate    <= Idle;
          end if;
        end if;

        ----------------------------------------------------------------
      when BarkSrcRdy =>
        nstate                   <= BarkSrcRdy;
        lls_o.src_rdy            <= '1';
        lls_o.data(15 downto 12) <= x"b";  -- just for neatness 
        if (lld_i = '1') then
          nstate                 <= BarkSOF;
        end if;


      when BarkSOF =>
        nstate                   <= BarkSOF;
        lls_o.src_rdy            <= '1';
        lls_o.sof                <= '1';
        lls_o.data(15 downto 12) <= x"b";
        if (lld_i = '1') then
          oc_dack_nio             <= '0';
          nstate                 <= WaitEOF;
        end if;


    end case;


  end process;



  -------------------------------------------------------------------------
  -- Watchdog Timer
  -------------------------------------------------------------------------

  prc_timeout_counter : process (clk)
  begin
    if rising_edge(clk) then
      if (rst = '1') or (watchdog_clr = '1') then
        watchdog_timer <= (others => '0');

      else
        if (tick_4Hz_i = '1') then
          watchdog_timer <= watchdog_timer + '1';

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


end architecture;