--==============================================================================
-- CERN (BE-CO-HT)
-- General-purpose pulse generator
--==============================================================================
--
-- author: Theodor Stana (t.stana@cern.ch)
--
-- date of creation: 2013-02-28
--
-- version: 2.0
--
-- description:
--
--   This module generates pulses with configurable frequency, width and delay.
--
--   In order to generate pulses, the module must be enabled via the en_i port.
--   Once en_i is high, pulses are generated at the frequency specified via
--   freq_i, with the width specified via pwidth_i.
--
--   An optional delay can be added before the start of the pulse, via the delay_i
--   port.
--
--   Note that this delay can be set only before the module is enabled.
--
--   freq_i, pwidth_i and delay_i are given in clk_i cycles.
--
-- dependencies:
--
-- references:
-- 
--==============================================================================
-- 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:
--    2013-02-28   Theodor Stana     t.stana@cern.ch     File created
--    2013 08-15   Theodor Stana     t.stana@cern.ch     v2.0, delay, pwidth, freq
--                                                       now controllable via
--                                                       inputs (regs, etc.)
--==============================================================================
-- TODO: - 
--==============================================================================

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


entity pulse_gen_gp is
  port
  (
    -- Input clock and active-low reset
    clk_i    : in  std_logic;
    rst_n_i  : in  std_logic;

    -- Active high enable signal
    en_i     : in  std_logic;
    
    -- Delay, pulse width and frequency inputs, in number of clk_i cycles
    delay_i  : in  std_logic_vector(31 downto 0);
    pwidth_i : in  std_logic_vector(31 downto 0);
    freq_i   : in  std_logic_vector(31 downto 0);

    -- Output pulse signal
    pulse_o  : out std_logic
  );
end entity pulse_gen_gp;


architecture behav of pulse_gen_gp is

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

  --============================================================================
  -- Signal declarations
  --============================================================================
  signal delay_int  : unsigned(31 downto 0);
  signal pwidth_int : unsigned(31 downto 0);
  signal freq_int   : unsigned(31 downto 0);

  signal pulse_cnt  : unsigned(31 downto 0);
  signal delay_cnt  : unsigned(31 downto 0);

  signal delay_en   : std_logic;

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

  --============================================================================
  -- Convert std_logic_vector inputs to unsigned
  --============================================================================
  delay_int  <= unsigned(delay_i);
  pwidth_int <= unsigned(pwidth_i);
  freq_int   <= unsigned(freq_i);

  --============================================================================
  -- Delay logic
  --============================================================================
  p_delay: process (clk_i)
  begin
    if rising_edge(clk_i) then
      if (rst_n_i = '0') or (en_i = '0') then
        delay_en  <= '1';
        delay_cnt <= (others => '0');
      else
        if (delay_int = (delay_int'range => '0')) then
          delay_en <= '0';
        elsif (delay_en = '1') then
          delay_cnt <= delay_cnt + 1;
          if (delay_cnt = delay_int) then
            delay_en  <= '0';
            delay_cnt <= (others => '0');
          end if;
        end if;
      end if;
    end if;
  end process p_delay;

  --============================================================================
  -- Pulse generation logic
  --============================================================================
  p_gen_pulse: process(clk_i)
  begin
    if rising_edge(clk_i) then
      if (rst_n_i = '0') or (en_i = '0') then
        pulse_cnt <= (others => '0');
        pulse_o   <= '0';
      elsif (delay_en = '0') then
        pulse_cnt <= pulse_cnt + 1;
        pulse_o   <= '0';
        if (pulse_cnt < pwidth_int) then
          pulse_o <= '1';
        elsif (pulse_cnt = freq_int-1) then
          pulse_cnt <= (others => '0');
        end if;
      end if;
    end if;
  end process p_gen_pulse;

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