---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2008
--
-- Filename: Gate.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 DSP FPGA
--
-- Design Description:
-- Module to to implement standard Gate - optimised for 200MHz operation
---------------------------------------------------------------------------

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

entity Gate is
  port(nReset         : in  std_ulogic;
       Clk            : in  std_ulogic;
       SampleEn       : in  std_ulogic;
       TriggersIn     : in  std_logic_vector(15 downto 0);
       NoTriggersIn   : in  std_logic_vector(15 downto 0);
       Enable         : in  std_ulogic;
       StartCount     : in  std_logic_vector(23 downto 0);
       StopCount      : in  std_logic_vector(23 downto 0);
       ForceStop      : in  std_ulogic;
       Threshold      : in  std_logic_vector(10 downto 0);
       TriggerNum     : in  std_logic_vector(3 downto 0);
       TriggerMode    : in  std_ulogic;
       ZeroCrossMode  : in  std_ulogic;
       PolarityMode   : in  std_logic_vector(1 downto 0);
       DistMetric     : in  std_logic_vector(1 downto 0);
       PeakSelAmp     : in  std_logic_vector(3 downto 0);
       PeakSelDist    : in  std_logic_vector(3 downto 0);
       Holdoff        : in  std_logic_vector(7 downto 0);
       SampleData     : in  std_logic_vector(11 downto 0);
       RectifiedData  : in  std_logic_vector(10 downto 0);
       StoreNoTrig    : in  std_ulogic;
       GateDistFire   : out std_ulogic;
       GateDistClear  : out std_ulogic;
       GateFiring     : out std_ulogic;
       GateUnTrigger  : out std_ulogic;
       GateFired      : out std_ulogic;
       GateDidNotFire : out std_ulogic;
       GateReFire     : out std_ulogic;
       GateOutputData : out std_logic_vector(13 downto 0));
end entity Gate;


architecture Rtl of Gate is

  attribute register_duplication: string;
  attribute keep: string;

  signal Reset:          std_ulogic;
  signal iPeakSelAmp:    std_logic_vector(3 downto 0);
  signal LastSelAmp:     std_ulogic;
  signal CheckAllPeaks:  std_ulogic;
  signal MaxSelAmp:      std_ulogic;
  signal iPeakSelDist:   std_logic_vector(3 downto 0);
  signal LastSelDist:    std_ulogic;
  signal MaxSelDist:     std_ulogic;
  signal DistPeakNext:   std_ulogic;
  signal AmpPeakNext:    std_ulogic;

  signal HoldoffCount:   unsigned(7 downto 0);

  signal RawSampleEarly: std_logic_vector(11 downto 0);
  signal RawSampleData:  std_logic_vector(11 downto 0);
  signal NewSampleEarly: std_logic_vector(10 downto 0);
  signal NewSampleData:  std_logic_vector(10 downto 0);

  signal Trigger:        std_ulogic;
  signal NoTrigger:      std_ulogic;
  signal TriggerRst:     std_ulogic;
  signal iGateUnTrigger: std_ulogic;

  signal TimeCount:      std_logic_vector(23 downto 0);
  signal TimeCountStop:  std_ulogic;
  signal TimeDone:       std_ulogic;
  signal TimeDoneNext:   std_ulogic;
  signal GateOpen:       std_ulogic;
  signal GateClose:      std_ulogic;
  signal GateCloseD1:    std_ulogic;
  signal GateCloseD2:    std_ulogic;
  signal GateActive:     std_ulogic;
  signal GateActiveNext: std_ulogic;
  signal iGateFiring:    std_ulogic;
  signal iGateFired:     std_ulogic;
  signal iGateDidNotFire:std_ulogic;
  signal AmpFired:       std_ulogic;
  signal iGateDistFire:  std_ulogic;
  signal GateDistDel:    std_ulogic;
  signal GateDistFiring: std_ulogic;

  signal GateZeroFiring:     std_ulogic;
  signal GateZeroFired:      std_ulogic;
  signal GateZeroUnTrigger:  std_ulogic;
  signal GateZeroReFire:     std_ulogic;
  signal FirstZeroAfterPeak: std_ulogic;

  signal DataLessThreshold:     std_ulogic;
  signal LastDataLessThreshold: std_ulogic;

  signal LeadFlank:       std_ulogic;  -- Signal asserted when a lead flank is seen
  signal TrailFlank:      std_ulogic;  -- Signal asserted when a trailing flank is seen
  signal PeakActive:      std_ulogic;
  signal PeakActiveNext:  std_ulogic;
  signal TrigPeakActive:  std_ulogic;
  signal TrigPeakActiveNext: std_ulogic;
  signal TrigPeakClose:   std_ulogic;
  signal AmpPeakActive:   std_ulogic;
  signal AmpPeakDone:     std_ulogic;
  signal AmpPeakClose:    std_ulogic;
  signal PeakCount:       integer range 0 to 15;
  signal PeakCountValid:  std_ulogic;

  signal MaxTrigPeak:  std_logic_vector(10 downto 0);
  signal NewMaxPeak:   std_ulogic;

  signal LeadEdgeDetectDone:  std_ulogic;
  signal TrailEdgeDetectDone: std_ulogic;

  signal MaxDataValue:    std_logic_vector(10 downto 0);
  signal MaxDataValueRaw: std_logic_vector(11 downto 0);
  signal StoredDataValue: std_logic_vector(11 downto 0);

  signal StatusNibble:    std_logic_vector(3 downto 0);

  signal MaxCurrentPeak: std_logic_vector(10 downto 0);
  signal MaxPeakUpdate:  std_ulogic;
  signal PeakMaxima:     std_ulogic;

  attribute register_duplication of TimeCount: signal is "no";
  attribute keep of TimeCount: signal is "true";
                           
begin

  -- Decode PeakSelAmp input from registers
  DecodePeakSelAmp: process(PeakSelAmp)
    variable vSelect: integer range 0 to 15;
  begin
     vSelect := conv_integer(unsigned(PeakSelAmp));
    case vSelect is
      when 0 to 16#D# =>
        -- Specified peak select
        iPeakSelAmp   <= PeakSelAmp;
        LastSelAmp    <= '0';
        CheckAllPeaks <= '0';
        MaxSelAmp     <= '0';
      when 16#E# =>
        -- Last peak select
        iPeakSelAmp   <= X"0";
        LastSelAmp    <= '1';
        CheckAllPeaks <= '1';
        MaxSelAmp     <= '0';
      when 16#F# =>
        -- Max peak select
        iPeakSelAmp   <= X"0";
        LastSelAmp    <= '0';
        CheckAllPeaks <= '1';
        MaxSelAmp     <= '1';
    end case;
  end process DecodePeakSelAmp;


  -- Decode PeakSelDist input from registers
  DecodePeakSelDist: process(PeakSelDist)
    variable vSelect: integer range 0 to 15;
  begin
     vSelect := conv_integer(unsigned(PeakSelDist));
    case vSelect is
      when 0 to 16#D# =>
        -- Specified peak select
        iPeakSelDist <= PeakSelDist;
        LastSelDist  <= '0';
        MaxSelDist   <= '0';
      when 16#E# =>
        -- Last peak select
        iPeakSelDist <= PeakSelDist;
        LastSelDist  <= '1';
        MaxSelDist   <= '0';
      when 16#F# =>
        -- Max peak select
        iPeakSelDist <= PeakSelDist;
        LastSelDist  <= '1';
        MaxSelDist   <= '1';
    end case;
  end process DecodePeakSelDist;


  GenTrigger: process(nReset, Clk)
  begin
    if nReset = '0' then
      Trigger   <= '0';
      NoTrigger <= '0';
    elsif rising_edge(Clk) then
      if SampleEn = '1' then
        case TriggerNum is
          when "0000" =>  Trigger   <= TriggersIn(0);
                          NoTrigger <= NoTriggersIn(0);
          when "0001" =>  Trigger   <= TriggersIn(1);
                          NoTrigger <= NoTriggersIn(1);
          when "0010" =>  Trigger   <= TriggersIn(2);
                          NoTrigger <= NoTriggersIn(2);
          when "0011" =>  Trigger   <= TriggersIn(3);
                          NoTrigger <= NoTriggersIn(3);
          when "0100" =>  Trigger   <= TriggersIn(4);
                          NoTrigger <= NoTriggersIn(4);
          when "0101" =>  Trigger   <= TriggersIn(5);
                          NoTrigger <= NoTriggersIn(5);
          when "0110" =>  Trigger   <= TriggersIn(6);
                          NoTrigger <= NoTriggersIn(6);
          when "0111" =>  Trigger   <= TriggersIn(7);
                          NoTrigger <= NoTriggersIn(7);
          when "1111" =>  Trigger   <= TriggersIn(15);
                          Trigger   <= TriggersIn(15);
                          NoTrigger <= NoTriggersIn(15);
          when others =>  Trigger   <= TriggersIn(15);
                          NoTrigger <= NoTriggersIn(15);
        end case;
      end if;
    end if;
  end process GenTrigger;

  TriggerRst <= TriggersIn(15) or Trigger;

  -- Create a rectified datastream depending on the PolarityMode control input
  ProcessSampleData: process(nReset, Clk)
  begin
    if nReset = '0' then
      RawSampleEarly <= X"000";
      RawSampleData  <= X"000";
      NewSampleEarly <= (others => '0');
      NewSampleData  <= (others => '0');

    elsif rising_edge(Clk) then
      if SampleEn = '1' then
        RawSampleEarly <= SampleData;
        RawSampleData  <= RawSampleEarly;

        -- Create a rectified version of the data depending on the Polarity Mode
        case PolarityMode is
          when "00" =>  -- Bipolar mode
            NewSampleEarly <= RectifiedData;

          when "01" =>  -- +Ve threshold
            if SampleData(11) = '0' then  -- +Ve Sample
              NewSampleEarly <= RectifiedData;
            else
              NewSampleEarly <= (others => '0');
            end if;

          when others =>  -- "10" -Ve threshold
            if SampleData(11) = '1' then
              NewSampleEarly <= RectifiedData;
            else
              NewSampleEarly <= (others => '0');
            end if;
        end case;

        NewSampleData <= NewSampleEarly;
      end if;
    end if;
  end process ProcessSampleData;

  TimeCountStop <= '1' when TimeCount = StopCount else '0';

  GenGateActive: process(nReset, Clk)
  begin
    if nReset = '0' then
      TimeCount   <= (others => '1');
      GateOpen    <= '0';
      GateClose   <= '0';
      GateActive  <= '0';
      TimeDone    <= '1';
    elsif rising_edge(Clk) then
      GateOpen    <= '0';
      GateClose   <= '0';

      if GateActive = '1' and (TimeDone = '1' or ForceStop = '1') then
        GateClose  <= '1';
        GateActive <= '0';
       
      elsif TimeCount = StopCount and TimeDone = '0' then
        -- Gate has closed
        GateClose   <= '1';
        GateActive  <= '0';
        TimeDone    <= '1';

      elsif TimeCount = StartCount and TimeDone = '0' then
        -- Gate has become active
        GateOpen    <= '1';
        GateActive  <= '1';
      end if;

      if Trigger = '1' then
        GateActive  <= '0';
      end if;

      if SampleEn = '1' then
        if Trigger = '1' then
          TimeCount <= (others => '0');
        elsif TimeDone = '0' then
          TimeCount <= unsigned(TimeCount) + 1;
        end if;

        if Trigger = '1' then
          TimeDone  <= '0';
        elsif TimeCount = x"FFFFFF" then
          TimeDone  <= '1';
        end if;
      end if;

      if Enable = '0' or ForceStop = '1' then
        TimeDone <= '1';
      end if;
    end if;
  end process GenGateActive;


  -- Process to analyse data stream and detect edges and so on
  AnalyseDatastream:  process(nReset, Clk)
  begin
    if nReset = '0' then
      LeadFlank           <= '0';
      TrailFlank          <= '0';
      LeadEdgeDetectDone  <= '0';
      TrailEdgeDetectDone <= '0';
      PeakActive          <= '0';
      TrigPeakActive      <= '0';
      AmpPeakActive       <= '0';
      AmpPeakClose        <= '0';
      AmpPeakDone         <= '0';
      TrigPeakClose       <= '0';
      AmpFired            <= '0';

    elsif rising_edge(Clk) then
      if SampleEn = '1' then
        AmpPeakClose  <= '0';
        TrigPeakClose <= '0';
      end if;

      if GateActive = '1' then
        if TriggerMode = '1' then
          --
          -- Edge trigger detection
          --
          if LastDataLessThreshold = '1' and DataLessThreshold = '0' then
            -- Leading edge crossing of a +ve threshold
            LeadFlank  <= '1';
            PeakActive <= '1';

            if DistPeakNext = '1' or LastSelDist = '1' then
              TrigPeakActive <= '1';
            end if;

            if AmpPeakNext = '1' or CheckAllPeaks = '1' then
              AmpPeakActive <= '1';
              AmpFired      <= '1';
            end if;

          else
            LeadFlank <= '0';
          end if;

        else  -- TriggerMode = '0'
          --
          -- Level trigger detection
          --
          if GateActive = '1' and DataLessThreshold = '0' and PeakActive = '0' then
            -- Leading edge reaches a +ve threshold
            LeadFlank  <= '1';
            PeakActive <= '1';

            if DistPeakNext = '1' or LastSelDist = '1' then
              TrigPeakActive <= '1';
            end if;

            if AmpPeakNext = '1' or CheckAllPeaks = '1' then
              AmpPeakActive <= '1';
              AmpFired      <= '1';
            end if;

          else
            LeadFlank <= '0';
          end if;
        end if;

        if DataLessThreshold = '1' and PeakActive = '1' then
          -- Trailing edge crossing of a +ve threshold
          TrailFlank <= '1';
          PeakActive <= '0';
          if TrigPeakActive = '1' then
            TrigPeakClose <= '1';
          end if;
          TrigPeakActive <= '0';
          if AmpPeakActive = '1' then
            AmpPeakDone  <= '1';
            AmpPeakClose <= '1';
          end if;
          if MaxSelAmp /= '1' then
            AmpPeakActive  <= '0';
          end if;

        elsif SampleEn = '1' then
          TrailFlank <= '0';
        end if;

      else  -- GateActive = '0'
        -- Force a Trailing Flank if we are in active peak when the gate closes
        if PeakActive = '1' then
          PeakActive     <= '0';
          if TrigPeakActive = '1' then
            TrigPeakClose <= '1';
          end if;
          TrigPeakActive <= '0';
          if AmpPeakActive = '1' then
            AmpPeakDone  <= '1';
            AmpPeakClose <= '1';
          end if;
          if MaxSelAmp /= '1' then
            AmpPeakActive  <= '0';
          end if;
          TrailFlank <= '1';

        elsif SampleEn = '1' then  -- PeakActive = '0'
          TrailFlank <= '0';
        end if;
      end if;

      -- Reset for signals at the beginning of each A-Scan
      if TriggerRst = '1' then
        LeadEdgeDetectDone  <= '0';
        TrailEdgeDetectDone <= '0';
        PeakActive          <= '0';
        TrigPeakActive      <= '0';
        AmpPeakDone         <= '0';
        AmpPeakActive       <= '0';
        AmpFired            <= '0';
      end if;
    end if;
  end process AnalyseDatastream;


  GenDataLessThreshold: process(nReset, Clk)
  begin
    if nReset = '0' then
      DataLessThreshold     <= '0';
      LastDataLessThreshold <= '0';
      HoldoffCount          <= (others => '0');

    elsif rising_edge(Clk) then
      if SampleEn = '1' then

        if NewSampleEarly >= Threshold and PeakCount = 0 then
          HoldoffCount <= unsigned(Holdoff);
        else
          if HoldoffCount /= 0 then
            HoldoffCount <= HoldoffCount - 1;
          end if;
        end if;

        if NewSampleEarly < Threshold and HoldoffCount = 0 then
          DataLessThreshold <= '1';
        else
          DataLessThreshold <= '0';
        end if;

        -- Latch the last value of signal
        LastDataLessThreshold <= DataLessThreshold;
      end if;
    end if;
  end process;


  GenPeakCount: process(nReset, Clk)
  begin
    if nReset = '0' then
      PeakCount <= 0;
      PeakCountValid <= '1';
      DistPeakNext <= '0';
      AmpPeakNext  <= '0';

    elsif rising_edge(Clk) then
      if SampleEn = '1' then
        -- Count how many peaks we have seen
        -- if in leading edge zero crossing mode wait until first zero crossing before counting peaks
        if TriggerRst = '1' or (ZeroCrossMode = '1' and DistMetric = "00" and GateZeroFired = '0') then
          PeakCount <= 0;
          PeakCountValid <= '1';
        elsif LeadFlank = '1' then
          if (PeakCount = 15) then 
            PeakCountValid <= '0'; -- disable when max peak count reached
          else
            PeakCount <= (PeakCount + 1) mod 16;
          end if;
        end if;

        if (PeakCountValid ='1') then
          -- Check if the next peak is the selected distance metric peak
          -- Only update at the end of a peak unless first peak is selected
          if TrailFlank = '1' or PeakCount = 0 then
            if PeakCount = conv_integer(unsigned(iPeakSelDist)) then
              DistPeakNext <= '1';
            else
              DistPeakNext <= '0';
            end if;
          end if;

          -- Check if the next peak is the selected amplitude metric peak
          if PeakCount = conv_integer(unsigned(iPeakSelAmp)) then
            AmpPeakNext <= '1';
          else
            AmpPeakNext <= '0';
          end if;
        end if;
      end if;
    end if;
  end process GenPeakCount;


  GenGateOutputs: process(nReset, Clk)
  begin
    if nReset = '0' then
      GateCloseD1    <= '0';
      GateCloseD2    <= '0';
      iGateFired     <= '0';
      iGateFiring    <= '0';
      iGateDidNotFire<= '0';
    elsif rising_edge(Clk) then
      if SampleEn = '1' then
        iGateFiring <= '0';

        if NoTrigger = '1' then
          -- Our reference gate did not trigger, therefore we cannot fire either
          iGateFired      <= '0';
          iGateDidNotFire <= '1';
          
        elsif Trigger = '1' then
          -- Reset flags on trigger
          iGateFired      <= '0';
          iGateDidNotFire <= '0';

        elsif GateActive = '1' then
          case DistMetric is
            when "00" =>  -- Leading Edge
              if LeadFlank = '1' then
                if ((iGateFired = '0' and DistPeakNext = '1') or LastSelDist = '1') and
                    not (ZeroCrossMode = '1' and GateZeroFired = '0') then
                  iGateFired  <= '1';
                  iGateFiring <= '1';
                end if;
              end if;

            when "01" =>  -- Trailing Edge
              if TrailFlank = '1' then
                if (iGateFired = '0' and DistPeakNext = '1') or LastSelDist = '1' then
                  if MaxSelDist /= '1' or NewMaxPeak /= '0' then
                    -- Only trigger on Max peak when Max peak selected
                    iGateFired  <= '1';
                    iGateFiring <= '1';
                  end if;
                end if;
              end if;

            when others =>  -- (10) Peak
              if PeakMaxima = '1' then
                if DistPeakNext = '1' or LastSelDist = '1' then
                  if MaxSelDist /= '1' or NewMaxPeak /= '0' then
                    iGateFired  <= '1';
                    iGateFiring <= '1';
                  end if;
                end if;
              end if;
          end case;

        elsif TrigPeakClose = '1' and (MaxSelDist /= '1' or iGateFired = '0') then
          -- Gate closed before trailing edge trigger
          iGateFired  <= '1';
          iGateFiring <= '1';

        elsif GateCloseD1 = '1' and iGateFired = '0' then
          -- Gate closed with no active trigger
          iGateDidNotFire <= '1';
        end if;

        GateCloseD1 <= GateClose;
        GateCloseD2 <= GateCloseD1;
      end if;
    end if;
  end process GenGateOutputs;

  GenZeroCrossingOutputs: process(nReset, Clk)
  begin
    if nReset = '0' then
      GateZeroFired      <= '0';
      GateZeroFiring     <= '0';
      GateZeroReFire     <= '0';
      GateZeroUnTrigger  <= '0';
      FirstZeroAfterPeak <= '0';

    elsif rising_edge(Clk) then
      if SampleEn = '1' then
        GateZeroFiring <= '0';
        GateZeroUnTrigger <= '0';

        if NoTrigger = '1' then
          -- Our reference gate did not trigger, therefore we cannot fire either
          GateZeroFired      <= '0';
          GateZeroReFire     <= '0';
          FirstZeroAfterPeak <= '0';

        elsif Trigger = '1' then
          -- Reset flags on trigger
          GateZeroFired      <= '0';
          GateZeroReFire     <= '0';
          FirstZeroAfterPeak <= '0';

        elsif GateActive = '1' then
          case DistMetric is
            when "00" =>  -- Leading Edge
              if RawSampleData(11) /= RawSampleEarly(11) then -- zero crossing
                if (iGateFired = '0' and DistPeakNext = '1') or LastSelDist = '1' then
                  GateZeroFired  <= '1';
                  GateZeroFiring <= '1';
                end if;
              end if;
              if iGateFiring = '1' then
                GateZeroReFire <= '0';
              elsif GateZeroFiring = '1' and iGateFired = '1' then
                GateZeroReFire <= '1';
              end if;
            when "01" =>  -- Trailing Edge
              if RawSampleData(11) /= RawSampleEarly(11) then -- zero crossing
                if iGateFired = '1' and (GateZeroFired = '0' or (LastSelDist = '1' and FirstZeroAfterPeak = '0')) then
                  -- Only trigger after gate trigger
                  GateZeroFired  <= '1';
                  GateZeroFiring <= '1';
                  FirstZeroAfterPeak <= '1';
                end if;
              end if;
              if iGateFiring = '1' then
                FirstZeroAfterPeak <= '0';
              end if;
            when others =>  -- (10) Peak
              null; -- not valid with zero crossing
          end case;
        elsif GateCloseD2 = '1' then
        if GateZeroReFire = '1' then
          GateZeroUnTrigger <= '1';
        end if;
        if iGateFired = '0' then
          GateZeroFired <= '0';
        end if;
        end if;
      end if;
    end if;
  end process GenZeroCrossingOutputs;


  -- Find Max point of peak
  FindMaxPoint: process(nReset, Clk)
  begin
    if nReset = '0' then
      MaxCurrentPeak <= (others => '0');
      MaxPeakUpdate  <= '0';
      PeakMaxima     <= '0';

    elsif rising_edge(Clk) then
      if SampleEn = '1' then
        PeakMaxima <= '0';
      end if;

      if PeakActive = '0' then
        -- Reset values between peaks
        MaxCurrentPeak <= NewSampleData;
        MaxPeakUpdate  <= '1';
        PeakMaxima     <= '0';
      else
        if MaxCurrentPeak < NewSampleData then
          MaxCurrentPeak <= NewSampleData;
          MaxPeakUpdate <= '1';
        else
          if MaxPeakUpdate = '1' then
            PeakMaxima <= '1';
          end if;

          MaxPeakUpdate <= '0';
        end if;
      end if;
    end if;
  end process FindMaxPoint;



  AnalysePeaksForTrigger: process(nReset, Clk)
    variable vSampleLessPeakValue: std_ulogic;
  begin
    if nReset = '0' then
      MaxTrigPeak <= (others => '0');
      NewMaxPeak  <= '0';
      iGateUnTrigger <= '0';

    elsif rising_edge(Clk) then

      if unsigned(NewSampleData) < unsigned(MaxTrigPeak) then
        vSampleLessPeakValue := '1';
      else
        vSampleLessPeakValue := '0';
      end if;

      if SampleEn = '1' then
        iGateUnTrigger <= '0';
      end if;

      if TriggerRst = '1' then
        -- Reset MaxTrigPeak
        MaxTrigPeak <= (others => '0');

      elsif TrigPeakClose = '1' then
        -- Trigger Peak closing
        if DistMetric = "00" then
        --Distance metric is leading edge
          if NewMaxPeak = '0' and MaxSelDist = '1' then
            -- This peak is not a new maximum, untrigger it
            iGateUnTrigger <= '1';
          end if;
        end if;
        if SampleEn = '1' then
          NewMaxPeak  <= '0';
        end if;

      elsif TrigPeakActive = '1' then
        -- Update max data if sample data is greater (than current max data)
        -- Positive threshold value
        if vSampleLessPeakValue = '0' then
          MaxTrigPeak <= NewSampleData;
          NewMaxPeak  <= '1';
        end if;
      end if;
    end if;
  end process AnalysePeaksForTrigger;

  GateUnTrigger <= iGateUnTrigger or (GateZeroUnTrigger and ZeroCrossMode);


  GenPeakDataValue: process(nReset, Clk)
    variable vSampleLessPeakValue: std_ulogic;
  begin
    if nReset = '0' then
      MaxDataValue    <= (others => '0');
      MaxDataValueRaw <= X"000";
      StoredDataValue <= X"000";
      iGateDistFire   <= '0';
      GateDistDel     <= '0';
      GateDistFiring  <= '0';

    elsif rising_edge(Clk) then
      if unsigned(NewSampleData) < unsigned(MaxDataValue) then
        vSampleLessPeakValue := '1';
      else
        vSampleLessPeakValue := '0';
      end if;

      if SampleEn = '1' then
        iGateDistFire <= '0';
        GateDistDel <= '0';

        if (StoreNoTrig = '0' and AmpPeakActive = '1') or (StoreNoTrig = '1' and GateActive = '1') then
          -- Update max data if sample data is greater (than current max data)
          -- Positive threshold value
          if vSampleLessPeakValue = '0' then
            MaxDataValue    <= NewSampleData;
            MaxDataValueRaw <= RawSampleData;
            iGateDistFire <= '1';
          end if;
        else  -- AmpPeakActive = '0'
          if AmpPeakDone = '0' or LastSelAmp = '1' then
            -- Capture first data sample of peak
            -- Actually captures all data up to this point!
            MaxDataValue    <= NewSampleData;
            MaxDataValueRaw <= RawSampleData;
          end if;
        end if;

        if AmpPeakClose = '1' or (StoreNoTrig = '1' and GateClose = '1' and MaxSelAmp = '1') then
          StoredDataValue <= MaxDataValueRaw;
        elsif GateOpen = '1' then
         StoredDataValue <= X"000";
        end if;

        if StoreNoTrig = '1' and MaxSelDist = '1' and DistMetric = "10" then
          GateDistDel <= iGateDistFire;
        end if;
        GateDistFiring <= GateDistDel;
      end if;
    end if;
  end process GenPeakDataValue;


  GateDistFire   <= '0' when Enable = '0' else
                     GateZeroFiring when ZeroCrossMode = '1' else (iGateFiring or GateDistFiring);
  GateFiring     <= '0' when Enable = '0' else 
                     GateZeroFiring when ZeroCrossMode = '1' else iGateFiring;
  GateFired      <= '0' when Enable = '0' else
                     GateZeroFired when ZeroCrossMode = '1' else iGateFired;
  GateDidNotFire <= '1' when Enable = '0' else iGateDidNotFire;
  GateDistClear  <= '0' when Enable = '0' or StoreNoTrig = '1' else iGateDidNotFire;

  GateReFire     <= '0' when Enable = '0' or ZeroCrossMode = '0' else GateZeroReFire;

  -- Generate the status nibble
  StatusNibble(0) <= GateZeroFired when ZeroCrossMode = '1' else iGateFired;  -- The distance metric was measured
  StatusNibble(1) <= AmpFired;   -- The amplitude metric was measured
  StatusNibble(3 downto 2) <= "10";  -- A value that cannot occur in sample data

  GateOutputData <= StatusNibble(1 downto 0) & StoredDataValue;


end architecture Rtl;