---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2008
--
-- Filename: TriggerGenerator.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 DSP FPGA
--
-- Design Description:
-- Trigger Generator / Sequence and Scan counters
---------------------------------------------------------------------------

library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_Arith.all;


entity TriggerGenerator is
  port (nReset          : in  std_ulogic;
        Clk             : in  std_ulogic; -- 100 (200)MHz sample clock
        LClk            : in  std_ulogic; -- 66MHz sample clock
        -- Register interface signals
        AScanRepRate    : in  std_logic_vector(23 downto 0);  -- 10ns resolution
        SequenceRepRate : in  std_logic_vector(31 downto 0);  -- 10ns resolution
        SequenceSteps   : in  std_logic_vector(4 downto 0);
        StartSequence   : in  std_ulogic;
        TriggerDelay    : in  std_logic_vector(23 downto 0);  -- 5ns resolution
        PulseWidth      : in  std_logic_vector(15 downto 0);  -- 5ns resolution
        PulseTrain      : in  std_logic_vector(3 downto 0);
        ExtTriggerEn    : in  std_ulogic;
        FreeRunningEn   : in  std_ulogic;
        EncoderTriggerEn: in  std_ulogic;
        -- Trigger/Pulse i/o
        ForceTrigger    : in  std_ulogic;
        ExtTriggerIn    : in  std_ulogic;
        EncoderTriggerIn: in  std_ulogic;
        TriggerPulse    : out std_ulogic;
        DelayedTrigger  : out std_ulogic;
        AScanStart      : out std_ulogic;
        SequenceStart   : out std_ulogic;
        SequenceActive  : out std_ulogic;
        CaptureDone     : out std_ulogic);
end entity TriggerGenerator;


architecture Rtl of TriggerGenerator is
  signal Enable100M     : std_ulogic;
  signal ForceTriggerD  : std_ulogic;
  signal ForceTrigSamp0 : std_ulogic;
  signal ForceTrigSamp1 : std_ulogic;
  signal ForceTrigPulse : std_ulogic;
  signal ExtTrigSamp0   : std_ulogic;
  signal ExtTrigSamp1   : std_ulogic;
  signal ExtTrigPulse   : std_ulogic;
  signal EncTrigSamp0   : std_ulogic;
  signal EncTrigSamp1   : std_ulogic;
  signal EncTrigPulse   : std_ulogic;
  signal NextSeqTrigger : std_ulogic;
  signal SeqTrigger     : std_ulogic;

  signal SeqTimeCount   : unsigned(31 downto 0);
  signal SequenceCount  : unsigned(4 downto 0);
  signal iSequenceActive: std_ulogic;
  signal SequenceActiveD: std_ulogic;
  signal FreeRunningPulse : std_ulogic;
  signal AScanTimeCount : unsigned(23 downto 0);
  signal IntTrigger     : std_ulogic;
  signal PulseTimeCount : unsigned(15 downto 0);
  signal PulseAlwaysZero  : std_ulogic;
  signal PulseTimeZero  : std_ulogic;
  signal PulseCount     : unsigned(4 downto 0);
  signal PulseCountZero : std_ulogic;

  signal DelayTimeCount : unsigned(23 downto 0);
  signal DelayIsZero    : std_ulogic;
  signal DelayIsOne     : std_ulogic;
  signal DelayTimeZero  : std_ulogic;
  signal DelayRunning   : std_ulogic;

  attribute syn_preserve  : boolean;
  attribute syn_keep : boolean;

  attribute syn_preserve of DelayedTrigger: signal is true;
  attribute syn_preserve of AScanStart    : signal is true;

  attribute syn_keep of NextSeqTrigger : signal is true;

begin

  -- Fix for 100MHz operation
--  En100Gen : process (Clk, nReset) is
--  begin
--    if nReset ='0' then
--      Enable100M <= '0';
--    elsif Clk'event and Clk = '1' then
--      Enable100M <= not Enable100M;
--    end if;
--  end process En100Gen;

  Enable100M <= '1';

  ForceTrigFF: process(nReset, LClk)
  begin
    if nReset = '0' then
      ForceTriggerD <= '0';
    elsif LClk'event and LClk = '0' then -- negative edge to guarantee overlap
      ForceTriggerD <= ForceTrigger;
    end if;
  end process ForceTrigFF;


  -- Sample Force Trigger, External Trigger and Encoder trigger with 200MHz clock
  Sample : process (Clk, nReset) is
  begin
    if nReset ='0' then
      ForceTrigSamp0 <= '0';
      ForceTrigSamp1 <= '0';
      ForceTrigPulse <= '0';

      ExtTrigSamp0 <= '0';
      ExtTrigSamp1 <= '0';
      ExtTrigPulse <= '0';

      EncTrigSamp0 <= '0';
      EncTrigSamp1 <= '0';
      EncTrigPulse <= '0';
    elsif Clk'event and Clk = '1' then
      ForceTrigSamp0 <= ForceTrigger or ForceTriggerD;
      ForceTrigSamp1 <= ForceTrigSamp0;
      ForceTrigPulse <= ForceTrigSamp0 and not ForceTrigSamp1;

      ExtTrigSamp0 <= ExtTriggerIn;
      ExtTrigSamp1 <= ExtTrigSamp0;
      ExtTrigPulse <= ExtTrigSamp0 and not ExtTrigSamp1;

      EncTrigSamp0 <= EncoderTriggerIn;
      EncTrigSamp1 <= EncTrigSamp0;
      EncTrigPulse <= EncTrigSamp0 and not EncTrigSamp1;
    end if;
  end process Sample;
  
  -- Mux trigger sources and produce pulse for 100MHz enable
  Mux: process (SeqTrigger, Enable100M, StartSequence, ForceTrigPulse, ExtTriggerEn, ExtTrigPulse,
                EncoderTriggerEn, EncTrigPulse, FreeRunningPulse) is
  begin
    if (SeqTrigger = '1' and Enable100M = '1') or StartSequence = '0' then
      NextSeqTrigger <= '0';
    elsif ForceTrigPulse = '1' then
      NextSeqTrigger <= '1';
    elsif ExtTriggerEn = '1' and ExtTrigPulse = '1' then
      NextSeqTrigger <= '1';
    elsif EncoderTriggerEn = '1' and EncTrigPulse = '1' then
      NextSeqTrigger <= '1';
    elsif FreeRunningPulse = '1' then
      NextSeqTrigger <= '1';
    else
      NextSeqTrigger <= SeqTrigger;
    end if;
  end process Mux;

  SeqFF : process (Clk, nReset) is
  begin
    if nReset ='0' then
      SeqTrigger <= '0';
    elsif Clk'event and Clk = '1' then
      SeqTrigger <= NextSeqTrigger;
    end if;
  end process SeqFF;

  -- Sequence Reprate counter
  SeqCounter : process (Clk, nReset) is
  begin
    if nReset ='0' then
      SeqTimeCount <= (others => '0');
      FreeRunningPulse <= '0';
    elsif Clk'event and Clk = '1' then
      FreeRunningPulse <= '0';
      if Enable100M = '1' then
        if FreeRunningEn = '1' and StartSequence = '1' and SeqTrigger = '0' and 
            (SeqTimeCount = 0 or iSequenceActive = '0') then
          SeqTimeCount <= unsigned(SequenceRepRate) - 1;
          FreeRunningPulse <= '1';
        elsif SeqTimeCount /= 0 then
          SeqTimeCount <= SeqTimeCount - 1;
        end if;
      end if;
    end if;
  end process SeqCounter;
  
  -- A-Scan Reprate counter
  AScanCounter : process (Clk, nReset) is
  begin
    if nReset ='0' then
      AScanTimeCount <= (others => '0');
      SequenceCount <= (others => '0');
      SequenceStart <= '0';
      iSequenceActive <= '0';
      SequenceActiveD <= '0';
      CaptureDone <= '0';
      IntTrigger <= '0';
    elsif Clk'event and Clk = '1' then
      SequenceStart <= '0';
      IntTrigger <= '0';
      if Enable100M = '1' then
        if SeqTrigger = '1' then
            SequenceCount <= (others => '0');
            SequenceStart <= '1';
            iSequenceActive <= '1';
            AScanTimeCount <= unsigned(AScanRepRate) - 1;
            IntTrigger <= '1';
        elsif AScanTimeCount = 0 and iSequenceActive = '1' then
          if SequenceCount = unsigned(SequenceSteps) then
            iSequenceActive <= '0';
          else
            AScanTimeCount <= unsigned(AScanRepRate) - 1;
            SequenceCount <= SequenceCount + 1;
            IntTrigger <= '1';
          end if;
        else
          AScanTimeCount <= AScanTimeCount - 1;
        end if;
      end if;

      SequenceActiveD <= iSequenceActive;
      CaptureDone <= not iSequenceActive and SequenceActiveD;
    end if;
  end process AScanCounter;

  SequenceActive <= iSequenceActive;

  -- Pulse width generator with pulse train capability
  PWCounter : process (Clk, nReset) is
  begin
    if nReset ='0' then
      PulseTimeCount <= (others => '0');
      PulseAlwaysZero <= '0';
      PulseTimeZero <= '1';
      PulseCount <= (others => '0');
      PulseCountZero <= '1';
      TriggerPulse <= '0';
    elsif Clk'event and Clk = '1' then
      if unsigned(PulseWidth(15 downto 1)) = 0 then
        PulseAlwaysZero <= '1';
      else
        PulseAlwaysZero <= '0';
      end if;

      if IntTrigger = '1' then
        PulseTimeCount <= unsigned(PulseWidth) - 1;
        PulseTimeZero <= PulseAlwaysZero;

        PulseCount <= unsigned(PulseTrain) & '0';
        if unsigned(PulseTrain) = 0 then
          PulseCountZero <= '1';
        else
          PulseCountZero <= '0';
        end if;
        TriggerPulse <= '1';
      elsif PulseTimeZero = '1' then
        if PulseCountZero = '0' then
          PulseTimeCount <= unsigned(PulseWidth) - 1;
          PulseTimeZero <= PulseAlwaysZero;

          PulseCount <= PulseCount - 1;
          if unsigned(PulseCount(4 downto 1)) = 0 then 
            PulseCountZero <= '1';
          else
            PulseCountZero <= '0';
          end if;
        end if;
        if PulseCount(0) = '1' then
          TriggerPulse <= '1';
        else
          TriggerPulse <= '0';
        end if;
      else
        PulseTimeCount <= PulseTimeCount - 1;
        if unsigned(PulseTimeCount(15 downto 1)) = 0 then 
          PulseTimeZero <= '1';
        else
          PulseTimeZero <= '0';
        end if;
      end if;
    end if;
  end process PWCounter;

  -- Delayed pulse output
  DelayCounter : process (Clk, nReset) is
  begin
    if nReset ='0' then
      DelayTimeCount <= (others => '0');
      DelayIsZero <= '0';
      DelayIsOne <= '0';
      DelayTimeZero <= '0';
      DelayRunning <= '0';
      DelayedTrigger <= '0';
      AScanStart <= '0';
    elsif Clk'event and Clk = '1' then
      if unsigned(TriggerDelay) = 0 then
        DelayIsZero <= '1';
      else
        DelayIsZero <= '0';
      end if;
      if unsigned(TriggerDelay) = 1 then
        DelayIsOne <= '1';
      else
        DelayIsOne <= '0';
      end if;

      DelayedTrigger <= '0';
      AScanStart <= '0';
      if DelayIsZero = '1' then
        DelayedTrigger <= IntTrigger;
        AScanStart <= IntTrigger;
      elsif IntTrigger = '1' then
        DelayTimeCount <= unsigned(TriggerDelay) - 1;
        DelayTimeZero <= DelayIsOne;
        DelayRunning <= '1';
      elsif DelayRunning = '1' then
        if DelayTimeZero = '1' then
          DelayedTrigger <= '1';
          AScanStart <= '1';
          DelayRunning <= '0';
        else
          DelayTimeCount <= DelayTimeCount - 1;
          if unsigned(DelayTimeCount(15 downto 1)) = 0 then 
            DelayTimeZero <= '1';
          else
            DelayTimeZero <= '0';
          end if;
        end if;
      end if;
    end if;
  end process DelayCounter;
  
end Rtl;
