--==============================================================================
-- CERN (BE-CO-HT)
-- Pulse generator with trigger
--==============================================================================
--
-- author: Theodor Stana (t.stana@cern.ch)
--
-- date of creation: 2013-03-01
--
-- version: 1.0
--
-- description:
--    This module generates a variable-width pulse. The width is set using the
--    g_pwidth generic, given in number of clk_i cycles. With a clk_i
--    period of 8ns, the output pulse width is by default 8*16=128ns.
--
--    The module contains a variable length filter, with the length adjustable
--    in clock cycle units via the g_gf_len generic. The glitch filter can be used
--    to avoid a signal being generated as a result of a short glitch on the input.
--
--    Enabling the glitch filter will result in jitter on the leading edge of the
--    output pulse signal. This jitter can be avoided by bypassing the glitch
--    filter; this is done via the gf_en_n_i input.
--
--    Regardless of whether the glitch filter is enabled, the input trigger signal
--    is extended or cut to g_pwidth, if it is shorter or respectively longer than
--    g_pwidth. At the end of the pulse, a rejection phase is implemented in order
--    to avoid too many pulses arriving on the input. This is to safeguard the
--    isolation transformers on the CONV-TTL-BLO boards. The isolation phase
--    limits the input pulse at a 1/5 duty cycle.
--
-- dependencies:
--    none
--
--==============================================================================
-- 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:
--    01-03-2013   Theodor Stana     t.stana@cern.ch     File created
--    02-08-2013   Theodor Stana     t.stana@cern.ch     Implemented rejection
--                                                       phase
--==============================================================================
-- TODO: - 
--==============================================================================

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

entity ctb_pulse_gen is
  generic 
  (
    -- Pulse width, in number of clk_i cycles
    g_pwidth : natural := 15;

    -- Glitch filter length:
    -- g_gf_len=1 => trigger width should be > 1 clk_i cycle
    -- g_gf_len=2 => trigger width should be > 2 clk_i cycles
    -- etc.
    g_gf_len : natural := 4
  );
  port
  (
    clk_i     : in  std_logic;
    rst_n_i   : in  std_logic;
    
    -- Glitch filter enable input
    -- '1' - Glitch filter disabled (glitch-sensitive, no output jitter)
    -- '0' - Glitch filter enabled (glitch-insensitive, with output jitter)
    gf_en_n_i : in  std_logic;

    -- Enable input, pulse generation is enabled when '1'
    en_i      : in  std_logic;

    -- Trigger input, has to be '1' to assure pulse output with delay no greater
    -- than internal gate delays.
    trig_i    : in  std_logic;
    
    -- Pulse output, active-high
    -- latency:
    --    glitch filter disabled: none
    --    glitch filter enabled: g_gf_len+3 clk_i cycles
    pulse_o   : out std_logic
  );
end entity ctb_pulse_gen;


architecture behav of ctb_pulse_gen is

  --============================================================================
  -- Type declarations
  --============================================================================
  type t_state is ( ST_IDLE, ST_GEN_GF_OFF, ST_REJ_GF_OFF,
                             ST_GEN_GF_ON, ST_REJ_GF_ON );

  --============================================================================
  -- Constant declarations
  --============================================================================
  -- Max value of pulse counter for pulse width and pulse rejection width; see
  -- below for explanation for their values
  constant c_pulse_width_gf_off : natural := g_pwidth-5;
  constant c_pulse_rej_gf_off   : natural := 5*g_pwidth-5;
  constant c_pulse_width_gf_on  : natural := g_pwidth-1;
  constant c_pulse_rej_gf_on    : natural := 5*g_pwidth-1;
  
  --============================================================================
  -- Function and procedure declarations
  --============================================================================
  function f_log2_size (A : natural) return natural is
  begin
    for I in 1 to 64 loop               -- Works for up to 64 bits
      if (2**I >= A) then
        return(I);
      end if;
    end loop;
    return(63);
  end function f_log2_size;

  --============================================================================
  -- Component declarations
  --============================================================================
  component glitch_filt is
    generic
    (
    -- Length of glitch filter:
    -- g_len = 1 => data width should be > 1 clk_i cycle
    -- g_len = 2 => data width should be > 2 clk_i cycle
    -- etc.
      g_len : natural := 4
    );
    port
    (
      clk_i   : in  std_logic;
      rst_n_i : in  std_logic;

    -- Data input
      dat_i   : in  std_logic;

    -- Data output
    -- latency: g_len+1 clk_i cycles
      dat_o   : out std_logic
    );
  end component glitch_filt;

  --============================================================================
  -- Signal declarations
  --============================================================================
  -- Deglitched trigger
  signal trig_degl      : std_logic;
  signal trig_degl_d0   : std_logic;

  -- Pulse length counter
  signal pulse_cnt      : unsigned(f_log2_size(6*g_pwidth)-1 downto 0);
  
  -- Pulse-specific signals
  signal pulse_gf_off    : std_logic;
  signal pulse_gf_off_d0 : std_logic;
  signal pulse_gf_off_d1 : std_logic;
  signal pulse_gf_off_d2 : std_logic;
  signal pulse_rst      : std_logic;
  
  signal pulse_gf_on    : std_logic;

  -- FSM signal
  signal state          : t_state;
  
--==============================================================================
--  architecture begin
--==============================================================================
begin

  --============================================================================
  -- Output logic
  --============================================================================
  pulse_o <= pulse_gf_off when (gf_en_n_i = '1') else
             pulse_gf_on;

  --============================================================================
  -- Pulse generation logic
  --============================================================================
  -- Generate the pulse on rising edge of trig_i
  p_pulse_gf_off: process(pulse_rst, trig_i)
  begin
    if (pulse_rst = '1') then
      pulse_gf_off <= '0';
    elsif rising_edge(trig_i) then
      if (en_i = '1') then
        pulse_gf_off <= '1';
      end if;
    end if;
  end process p_pulse_gf_off;

  -- and synchronize it in clk_i domain
  p_sync_pulse_gf_off: process (clk_i) is
  begin
    if rising_edge(clk_i) then
      if (rst_n_i = '0') then
        pulse_gf_off_d0 <= '0';
        pulse_gf_off_d1 <= '0';
        pulse_gf_off_d2 <= '0';
      elsif (en_i = '1') then
        pulse_gf_off_d0 <= pulse_gf_off;
        pulse_gf_off_d1 <= pulse_gf_off_d0;
        pulse_gf_off_d2 <= pulse_gf_off_d1;
      end if;
    end if;
  end process p_sync_pulse_gf_off;
  
  --============================================================================
  -- Glitch filtration logic
  --============================================================================
  cmp_glitch_filt: glitch_filt
    generic map
    (
      g_len => g_gf_len
    )
    port map
    (
      clk_i   => clk_i,
      rst_n_i => rst_n_i,
      dat_i   => trig_i,
      dat_o   => trig_degl
    );

  --============================================================================
  -- Pulse width adjustment logic
  --============================================================================
  p_pulse_width: process(clk_i)
  begin
    if rising_edge(clk_i) then
      if (rst_n_i = '0') then
        state        <= ST_IDLE;
        pulse_rst    <= '1';
        pulse_gf_on  <= '0';
        pulse_cnt    <= (others => '0');
        trig_degl_d0 <= '0';
      elsif (en_i = '1') then
        -- Deglitched trigger delay
        trig_degl_d0 <= trig_degl;

        -- State machine
        case state is
          ---------------------------------------------------------------------
          -- ST_IDLE
          ---------------------------------------------------------------------
          -- Clear all values and go to pulse generation state when the 
          -- appropriate input arrives
          ---------------------------------------------------------------------
          when ST_IDLE =>
            pulse_cnt <= (others => '0');
            pulse_rst <= '0';
            if (gf_en_n_i = '1') then
              if (pulse_gf_off_d1 = '1') and (pulse_gf_off_d2 = '0') then
                state <= ST_GEN_GF_OFF;
              end if;
            else
              if (trig_degl = '1') and (trig_degl_d0 = '0') then
                state <= ST_GEN_GF_ON;
              end if;
            end if;

          ---------------------------------------------------------------------
          -- ST_GEN_GF_OFF
          ---------------------------------------------------------------------
          -- Increment pulse counter to pulse width value.
          --
          -- Max value: g_pwidth-5 due to:
          -- 1. pulse_cnt starts from 0 => g_pwidth-1
          -- 2. three cycle delay in clock sync FFs
          -- 3. one cycle delay due to reset in next state
          --
          -- No clock cycle delay for switching from ST_IDLE to ST_GEN_GF_ON,
          -- since pulse is already generated on rising edge of trig_i
          ---------------------------------------------------------------------
          when ST_GEN_GF_OFF =>
            pulse_cnt <= pulse_cnt + 1;
            if (pulse_cnt = c_pulse_width_gf_off) then
              state     <= ST_REJ_GF_OFF;
            end if;
            
          ---------------------------------------------------------------------
          -- ST_REJ_GF_OFF
          ---------------------------------------------------------------------
          -- Increment pulse counter to pulse rejection value, while keeping
          -- the pulse_rst high. Max pulse rejection value is 5x that of
          -- pulse width value, to enable 1/5 duty cycle.
          ---------------------------------------------------------------------
          when ST_REJ_GF_OFF =>
            pulse_rst <= '1';
            pulse_cnt <= pulse_cnt + 1;
            if (pulse_cnt = c_pulse_rej_gf_off) then
              state <= ST_IDLE;
            end if;
          
          ---------------------------------------------------------------------
          -- ST_GEN_GF_ON
          ---------------------------------------------------------------------
          -- Increment counter to pulse width value and generate glitch-filtered
          -- pulse while incrementing.
          --
          -- Max value: g_pwidth-1, since pulse_cnt starts from 0
          ---------------------------------------------------------------------
          when ST_GEN_GF_ON =>
            pulse_cnt   <= pulse_cnt + 1;
            pulse_gf_on <= '1';
            if (pulse_cnt = c_pulse_width_gf_on) then
              state     <= ST_REJ_GF_ON;
            end if;

          ---------------------------------------------------------------------
          -- ST_REJ_GF_ON
          ---------------------------------------------------------------------
          -- Increment pulse counter to pulse rejection value, while keeping
          -- the pulse_rst high. Max pulse rejection value is 5x that of
          -- pulse width value, to enable 1/5 duty cycle.
          ---------------------------------------------------------------------
          when ST_REJ_GF_ON =>
            pulse_gf_on <= '0';
            pulse_rst   <= '1';
            pulse_cnt   <= pulse_cnt + 1;
            if (pulse_cnt = c_pulse_rej_gf_on) then
              state <= ST_IDLE;
            end if;

          when others =>
            state <= ST_IDLE;

        end case;
      end if;
    end if;
  end process p_pulse_width;

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