---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2005
--
-- Filename: PulseGen.vhd
-- Language: VHDL '93
-- Project:  Pulse Receive Card Control FPGA
--
-- Design Description:
-- Reprate counter and pulse width generation
--
---------------------------------------------------------------------------

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;

use work.ControlFPGA_pkg.all;

entity PulseGen is
  port(
    pClock        : in  std_ulogic;   -- 100MHz Clock
    pClockx2      : in  std_ulogic;   -- 200MHz Clock
    pnReset       : in  std_ulogic;
    pTrigger      : in  std_ulogic;
    pTriggerOut   : out std_ulogic;
    pTriggerDel   : in  std_ulogic;
    pPulse        : out std_logic_vector(8 downto 1); -- must be combinatorial
    pPulseDisable : in  std_logic_vector(8 downto 1);
    pIntTrigDis   : in  std_ulogic;
    pExtTrigEn    : in  std_ulogic;
    pTxEn         : in  std_logic_vector(7 downto 0);
    pPulseWidth   : in  std_logic_vector(15 downto 0);
    pPulseCount   : in  std_logic_vector(3 downto 0);
    pRepRate      : in  std_logic_vector(31 downto 0);
    pNewSeqTable  : in  std_ulogic;
    pUpdatePulse  : out std_ulogic;  -- start updating hardware from Sequence Table
    pAckStart     : in  std_ulogic;
    pAckEn        : in  std_ulogic;
    pAck          : out std_ulogic);
end PulseGen;

architecture RTL of PulseGen is

  attribute uselowskewlines: string;
  attribute KEEP: string;

  signal TriggerInt0: std_ulogic;
  signal TriggerInt1: std_ulogic;
  signal TriggerInt2: std_ulogic;
  signal TriggerInt3: std_ulogic;
  signal TriggerInt: std_ulogic;
  signal TriggerMux: std_ulogic;
  signal TriggerSample0: std_ulogic;
  signal TriggerSample1: std_ulogic;
  signal TriggerPulse: std_logic;
  signal TriggerSample0x2: std_ulogic;
  signal TriggerSample1x2: std_ulogic;
  signal TriggerPulsex2: std_logic;
  signal LoadCount: std_logic;

  signal AckInt: std_ulogic;

  signal ClkEn: std_ulogic;
  attribute uselowskewlines of ClkEn : signal is "yes";
  attribute KEEP of ClkEn : signal is "true";

  signal RepRateDec: unsigned(31 downto 0);
  attribute KEEP of RepRateDec : signal is "true";

  signal RepCount: unsigned(31 downto 0);
  signal PulseCount: unsigned(3 downto 0);
  signal AckCount: unsigned(3 downto 0);

  signal CycleWidthDec: unsigned(15 downto 0);
  signal PulseWidthDec: unsigned(14 downto 0);
  attribute KEEP of CycleWidthDec : signal is "true";
  attribute KEEP of PulseWidthDec : signal is "true";

  signal CWCount: unsigned(15 downto 0);
  signal PWCount: unsigned(14 downto 0);

begin
  
  TriggerMux <= pTrigger when pExtTrigEn = '1' else TriggerInt;
  pTriggerOut <= TriggerMux;

  Pulse : process (pTxEn, pTriggerDel) is
  begin
    for i in 0 to 7 loop
      if (pTxEn(i) ='1' and pPulseDisable(i + 1) = '0') then
        pPulse(i + 1) <= pTriggerDel;
      else
        pPulse(i + 1) <= '0';
      end if;
    end loop;
  end process Pulse;

  -- Sample External trigger with 100MHz clock
  Sample : process (pClock, pnReset) is
  begin
    if (pnReset ='0') then
      TriggerSample0 <= '0';
      TriggerSample1 <= '0';
    elsif (pClock'event and pClock = '1') then
--      TriggerSample0 <= pTriggerDel; -- Mk1 board
      TriggerSample0 <= pTrigger; -- Mk2 board
      TriggerSample1 <= TriggerSample0;
    end if;
  end process Sample;

  Mux : process (pClock, pnReset) is
  begin
    if (pnReset ='0') then
      TriggerPulse <= '0';
    elsif (pClock'event and pClock = '1') then
      if (pExtTrigEn = '1') then
         TriggerPulse <= TriggerSample0 and not TriggerSample1; -- rising edge of external trigger pulse
      else
         if (RepCount = 1) then
           TriggerPulse <= '1';
         else
           TriggerPulse <= '0';
         end if;
      end if;
    end if;
  end process Mux;

  RepRateDec <= unsigned(pRepRate) - 1;

  -- Reprate counter
  RepCounter : process (pClock, pnReset, pExtTrigEn) is
    variable vUpdate: unsigned(31 downto 0);
  begin
    if (pExtTrigEn = '1') then
      vUpdate := x"00000001";
    else
      vUpdate := conv_unsigned(cUpdateHardwareTime, 32);
    end if;

    if (pnReset ='0') then
      RepCount <= (others => '0');
      pUpdatePulse <= '0';
    elsif (pClock'event and pClock = '1') then
      if (pIntTrigDis = '1') then
        RepCount <= (others => '0');
      elsif (pNewSeqTable =  '1') then
        RepCount <= vUpdate;      -- if a new table has been loaded allow enough time for update
      elsif (TriggerPulse = '1') then
        RepCount <= RepRateDec;     -- if trigger load reprate value
      elsif (RepCount /= 0) then
        RepCount <= RepCount - 1;
      end if;
      if (RepCount = vUpdate) then
        pUpdatePulse <= '1';
      else
        pUpdatePulse <= '0';
      end if;
    end if;
  end process RepCounter;

  -- Sample trigger with 200MHz clock
  Samplex2 : process (pClockx2, pnReset) is
  begin
    if (pnReset ='0') then
      TriggerSample0x2 <= '0';
      TriggerSample1x2 <= '0';
      TriggerPulsex2 <= '0';
    elsif (pClockx2'event and pClockx2 = '1') then
      TriggerSample0x2 <= TriggerPulse;
      TriggerSample1x2 <= TriggerSample0x2;
      TriggerPulsex2 <= TriggerSample0x2 and not TriggerSample1x2;
    end if;
  end process Samplex2;

  GenCe : process (pClockx2, pnReset) is
  begin
    if (pnReset ='0') then
      ClkEn <= '0';
      LoadCount <= '0';
    elsif (pClockx2'event and pClockx2 = '1') then
      if (TriggerPulsex2 = '1') then
        ClkEn <= '0';
      else
        ClkEn <= not ClkEn;
      end if;
      if (TriggerPulsex2 = '1') then
        LoadCount <= '1';
      elsif (ClkEn = '1') then
        LoadCount <= '0';
      end if;
    end if;
  end process GenCe;

  CycleWidthDec <= unsigned(pPulseWidth) - 1;
  PulseWidthDec <= unsigned(pPulseWidth(15 downto 1)) - 1;

  -- Internal pulse width generator with pulse train capability
  PWCounter : process (pClockx2, pnReset) is
  begin
    if (pnReset ='0') then
      CWCount <= (others => '0');
      PulseCount <= (others => '0');
      TriggerInt0 <= '0';
    elsif (pClockx2'event and pClockx2 = '1') then
      if (ClkEn = '1') then
          CWCount <= (others => '0');
        if (LoadCount = '1') then
          PulseCount <= unsigned(pPulseCount); -- count of cycles
          CWCount <= CycleWidthDec;
          TriggerInt0 <= '1';
        elsif (CWCount /= 0) then
          CWCount <= CWCount - 1;
          TriggerInt0 <= '0';
        else
          if (PulseCount /= 0) then
            PulseCount <= PulseCount - 1;
            CWCount <= CycleWidthDec;
            TriggerInt0 <= '1';
          else
            TriggerInt0 <= '0';
          end if;
        end if;
      end if;
    end if;
  end process PWCounter;

  GenTrigger : process (pClockx2, pnReset) is
  begin
    if (pnReset ='0') then
      PWCount <= (others => '0');
      TriggerInt1 <= '0';
      TriggerInt2 <= '0';
      TriggerInt3 <= '0';
      TriggerInt <= '0';
    elsif (pClockx2'event and pClockx2 = '1') then
      if (ClkEn = '1') then
        PWCount <= (others => '0');
        TriggerInt2 <= '0';
        if (TriggerInt0 = '1') then
          if unsigned(pPulseWidth) /= 1 then
            PWCount <= PulseWidthDec;
            TriggerInt2 <= '1';
          end if;
        elsif (PWCount /= 0) then
          PWCount <= PWCount - 1;
          TriggerInt2 <= '1';
        end if;
      end if;
      TriggerInt1 <= TriggerInt0 and ClkEn;
      TriggerInt3 <= TriggerInt2 and pPulseWidth(0);
      TriggerInt <= TriggerInt1 or TriggerInt2 or TriggerInt3;
    end if;
  end process GenTrigger;

  AckCounter : process (pClock, pnReset) is
  begin
    if (pnReset ='0') then
      AckCount <= (others => '0');
      AckInt <= '0';
    elsif (pClock'event and pClock = '1') then
      if (pAckStart = '1') then
        AckCount <= conv_unsigned(cAckPulseWidth, 4);
        AckInt <= '1';
      elsif (AckCount /= 0) then
        AckCount <= AckCount - 1;
        AckInt <= '1';
      else
        AckInt <= '0';
      end if;
    end if;
  end process AckCounter;

  pAck <= AckInt or (not pAckEn and pTrigger);

end RTL;
