--==============================================================================
-- CERN (BE-CO-HT)
-- Pulse trigger for pulse converter boards
--==============================================================================
--
-- author: Theodor Stana (t.stana@cern.ch)
--
-- date of creation: 2014-01-28
--
-- version: 1.0
--
-- description:
--    This module generates a pulse for the conv_pulse_gen module for manually
--    triggering a debug pulse on a channel output. It works in conjunction
--    with the converter board registers component (conv_regs), from where it
--    obtains the value of the MPT (manual pulse trigger) field in the control
--    register.
--
--    To manually trigger a pulse, a magic sequence of numbers (0xde, 0xad, 0xbe,
--    0xef) should first be sent to the MPT field, followed by the channel number
--    to send the pulse on. When the channel number is sent, a single pulse is
--    generated by the conv_pulse_gen component at the output.
--
--    The conv_man_trig module checks to see whether the proper magic sequence
--    is written the the MPT field using a simple FSM. The FSM advances when
--    the MPT field is written, if the MPT field corresponds to the proper byte
--    in the magic sequence. If at any time during the magic sequence the value
--    of the MPT field does not correspond to the expected value, the FSM returns
--    to IDLE.
--
--    After the magic sequence is received, the FSM waits for the channel number
--    to be written to the MPT. If a valid channel number is input, a pulse is
--    generated on this channel. The check of whether a valid number is input is
--    based on the g_nr_ttl_chan generic. Should an invalid channel number be
--    input, no error is reported and no pulse is generated.
--
--    The output trigger pulse is extended within the last state of the FSM, to
--    account for when the glitch filter of the conv_pulse_gen component is on.
--    To extend the pulse by an appropriate number of clock cycles, the length
--    of the conv_pulse_gen glitch filter should be input via the g_gf_len.
--
-- dependencies:
--      genram_pkg : git://ohwr.org/hdl-core-lib/general-cores.git
--
--==============================================================================
-- GNU LESSER GENERAL PUBLIC LICENSE
--==============================================================================
-- This source file is free software; you can redistribute it and/or modify it
-- under the terms of the GNU Lesser General Public License as published by the
-- Free Software Foundation; either version 2.1 of the License, or (at your
-- option) any later version. This source is distributed in the hope that it
-- will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU Lesser General Public License for more details. You should have
-- received a copy of the GNU Lesser General Public License along with this
-- source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
--==============================================================================
-- last changes:
--    2014-01-28   Theodor Stana     File created
--==============================================================================
-- TODO: -
--==============================================================================

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.genram_pkg.all;

entity conv_man_trig is
  generic
  (
    -- Number of conversion channels
    g_nr_chan : positive := 6;

    -- Length of pulse generator glitch filter, needed to generate a long
    -- enough pulse
    g_gf_len  : positive := 1
  );
  port
  (
    -- Clock, active-low inputs
    clk_i     : in  std_logic;
    rst_n_i   : in  std_logic;

    -- Control inputs from conv_regs
    reg_ld_i : in  std_logic;
    reg_i    : in  std_logic_vector(7 downto 0);

    -- One-clock pulse output
    trig_o   : out std_logic_vector(g_nr_chan downto 1)
  );
end entity conv_man_trig;


architecture behav of conv_man_trig is

  --============================================================================
  -- Type declarations
  --============================================================================
  -- Type for the "password" array
  type t_pass_arr is array(integer range <>) of std_logic_vector(7 downto 0);

  -- FSM type
  type t_state is
    (
      IDLE,
      PASS1,
      PASS2,
      PASS3,
      GET_CHAN,
      GEN
    );

  --============================================================================
  -- Constant declarations
  --============================================================================
  constant c_pass_arr : t_pass_arr(0 to 3) := (x"de", x"ad", x"be", x"ef");

  --============================================================================
  -- Function and procedures declaration
  --============================================================================
  procedure f_change_state (
    signal   ld     : in  std_logic;
    signal   pass   : in  std_logic_vector(7 downto 0);
    constant idx    : in  integer;
    signal   state  : out t_state;
    constant nstate : in  t_state
  ) is
  begin
    if (ld = '1') then
      if (pass = c_pass_arr(idx)) then
        state <= nstate;
      else
        state <= IDLE;
      end if;
    end if;
  end procedure f_change_state;

  --============================================================================
  -- Signal declarations
  --============================================================================
  -- Signal for the current state of the FSM
  signal state : t_state;

  -- Counter to extend the pulse to the needed number of channels
  signal cnt   : unsigned(f_log2_size(g_gf_len)-1 downto 0);

--==============================================================================
--  architecture begin
--==============================================================================
begin

  --============================================================================
  -- FSM logic
  --============================================================================
  p_fsm : process (clk_i)
  begin
    if rising_edge(clk_i) then
      if (rst_n_i = '0') then
        state  <= IDLE;
        cnt    <= (others => '0');
        trig_o <= (others => '0');
      else
        case state is
          when IDLE =>
            trig_o <= (others => '0');
            f_change_state(reg_ld_i, reg_i, 0, state, PASS1);
          when PASS1 =>
            f_change_state(reg_ld_i, reg_i, 1, state, PASS2);
          when PASS2 =>
            f_change_state(reg_ld_i, reg_i, 2, state, PASS3);
          when PASS3 =>
            f_change_state(reg_ld_i, reg_i, 3, state, GET_CHAN);
          when GET_CHAN =>
            if (reg_ld_i = '1') then
              for i in 1 to g_nr_chan loop
                if (i = to_integer(unsigned(reg_i))) then
                  trig_o(i) <= '1';
                end if;
              end loop;
              cnt   <= (others => '0');
              state <= GEN;
            end if;
          when GEN =>
            cnt <= cnt + 1;
            if (cnt = g_gf_len-1) then
              state <= IDLE;
            end if;
          when others =>
            state <= IDLE;
        end case;
      end if;
    end if;
  end process p_fsm;

end architecture behav;
--==============================================================================
--  architecture end
--==============================================================================
