-------------------------------------------------------------------------------
--
-- Copyright 2018 Ettus Research, a National Instruments Company
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
--
-- Purpose:
--
-- Wrapper for the TDC and register control modules.
--
-- vreview_group Tdc
-- vreview_reviewers dabaker sgupta jmarsar
-------------------------------------------------------------------------------

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

library work;
  use work.PkgRegs.all;

entity TdcWrapper is
  port (
    -- Clocks and Resets : --------------------------------------------------------------
    -- Bus Clock and synchronous bus reset.
    BusClk          : in  std_logic;
    bBusReset       : in  std_logic;
    -- Reference Clock
    RefClk          : in  std_logic;
    -- Sample Clock
    SampleClk       : in  std_logic;
    -- Measurement Clock must run at a very specific frequency, determined by the
    -- SampleClk, RefClk, and Sync Pulse rates... oh and a lot of math.
    MeasClk         : in  std_logic;


    -- Register Port: -------------------------------------------------------------------
    bSyncRegPortOut : out RegPortOut_t;
    bSyncRegPortIn  : in  RegPortIn_t;


    -- PPS In and Out : -----------------------------------------------------------------
    -- Only required to pulse 1 RefClk cycle.
    rPpsPulse       : in  std_logic;
    -- PPS pulse output on the SampleClk domain.
    sPpsPulse       : out std_logic;


    -- Sync Pulse Outputs : -------------------------------------------------------------
    -- The repeating pulses can be useful for many things, including passing triggers.
    rRpTransfer : out std_logic;
    sSpTransfer : out std_logic;

    -- Pin bouncers out and in. Must go to unused and unconnected pins on the FPGA!
    rGatedPulseToPin : inout std_logic;
    sGatedPulseToPin : inout std_logic
  );
end TdcWrapper;


architecture struct of TdcWrapper is

  component SyncRegsIfc
    port (
      aBusReset               : in  std_logic;
      bBusReset               : in  std_logic;
      BusClk                  : in  std_logic;
      aTdcReset               : out std_logic;
      bRegPortInFlat          : in  std_logic_vector(49 downto 0);
      bRegPortOutFlat         : out std_logic_vector(33 downto 0);
      RefClk                  : in  std_logic;
      rResetTdc               : out std_logic;
      rResetTdcDone           : in  std_logic;
      rEnableTdc              : out std_logic;
      rReRunEnable            : out std_logic;
      rEnablePpsCrossing      : out std_logic;
      rPpsPulseCaptured       : in  std_logic;
      rPulserEnableDelayVal   : out std_logic_vector(3 downto 0);
      SampleClk               : in  std_logic;
      sPpsClkCrossDelayVal    : out std_logic_vector(3 downto 0);
      MeasClk                 : in  std_logic;
      mRpOffset               : in  std_logic_vector(39 downto 0);
      mSpOffset               : in  std_logic_vector(39 downto 0);
      mOffsetsDone            : in  std_logic;
      mOffsetsValid           : in  std_logic;
      rLoadRePulseCounts      : out std_logic;
      rRePulsePeriodInRClks   : out std_logic_vector(23 downto 0);
      rRePulseHighTimeInRClks : out std_logic_vector(23 downto 0);
      rLoadRpCounts           : out std_logic;
      rRpPeriodInRClks        : out std_logic_vector(15 downto 0);
      rRpHighTimeInRClks      : out std_logic_vector(15 downto 0);
      rLoadRptCounts          : out std_logic;
      rRptPeriodInRClks       : out std_logic_vector(15 downto 0);
      rRptHighTimeInRClks     : out std_logic_vector(15 downto 0);
      sLoadSpCounts           : out std_logic;
      sSpPeriodInSClks        : out std_logic_vector(15 downto 0);
      sSpHighTimeInSClks      : out std_logic_vector(15 downto 0);
      sLoadSptCounts          : out std_logic;
      sSptPeriodInSClks       : out std_logic_vector(15 downto 0);
      sSptHighTimeInSClks     : out std_logic_vector(15 downto 0));
  end component;

  -- Generic values for the TdcTop instantiation below. These generics are the maximum
  -- of possible values for all combinations of Sample and Reference clocks for the N3xx
  -- family of devices.
  constant kRClksPerRePulsePeriodBitsMax : integer := 24;
  constant kRClksPerRpPeriodBitsMax      : integer := 16;
  constant kSClksPerSpPeriodBitsMax      : integer := 16;
  constant kPulsePeriodCntSize           : integer := 13;
  -- The following are ideal values for balancing measurement time and accuracy, based
  -- on calcs given in the spec doc.
  constant kFreqRefPeriodsToCheckSize : integer := 17;
  constant kSyncPeriodsToStampSize    : integer := 10;

  --vhook_sigstart
  signal aTdcReset: std_logic;
  signal bSyncRegPortInFlat: std_logic_vector(49 downto 0);
  signal bSyncRegPortOutFlat: std_logic_vector(33 downto 0);
  signal mOffsetsDone: boolean;
  signal mOffsetsValid: boolean;
  signal mRpOffset: unsigned(kPulsePeriodCntSize+kSyncPeriodsToStampSize+kFreqRefPeriodsToCheckSize-1 downto 0);
  signal mSpOffset: unsigned(kPulsePeriodCntSize+kSyncPeriodsToStampSize+kFreqRefPeriodsToCheckSize-1 downto 0);
  signal rEnablePpsCrossing: std_logic;
  signal rEnableTdc: std_logic;
  signal rLoadRePulseCounts: std_logic;
  signal rLoadRpCounts: std_logic;
  signal rLoadRptCounts: std_logic;
  signal rPpsPulseCaptured: boolean;
  signal rPulserEnableDelayVal: std_logic_vector(3 downto 0);
  signal rRePulseHighTimeInRClks: std_logic_vector(kRClksPerRePulsePeriodBitsMax-1 downto 0);
  signal rRePulsePeriodInRClks: std_logic_vector(kRClksPerRePulsePeriodBitsMax-1 downto 0);
  signal rReRunEnable: std_logic;
  signal rResetTdc: std_logic;
  signal rResetTdcDone: boolean;
  signal rRpHighTimeInRClks: std_logic_vector(kRClksPerRpPeriodBitsMax-1 downto 0);
  signal rRpPeriodInRClks: std_logic_vector(kRClksPerRpPeriodBitsMax-1 downto 0);
  signal rRptHighTimeInRClks: std_logic_vector(kRClksPerRpPeriodBitsMax-1 downto 0);
  signal rRptPeriodInRClks: std_logic_vector(kRClksPerRpPeriodBitsMax-1 downto 0);
  signal rRpTransferBool: boolean;
  signal sLoadSpCounts: std_logic;
  signal sLoadSptCounts: std_logic;
  signal sPpsClkCrossDelayVal: std_logic_vector(3 downto 0);
  signal sPpsPulseAsyncReset: boolean;
  signal sSpHighTimeInSClks: std_logic_vector(kSClksPerSpPeriodBitsMax-1 downto 0);
  signal sSpPeriodInSClks: std_logic_vector(kSClksPerSpPeriodBitsMax-1 downto 0);
  signal sSptHighTimeInSClks: std_logic_vector(kSClksPerSpPeriodBitsMax-1 downto 0);
  signal sSptPeriodInSClks: std_logic_vector(kSClksPerSpPeriodBitsMax-1 downto 0);
  signal sSpTransferBool: boolean;
  --vhook_sigend

  signal rPpsPulseAsyncReset_ms, rPpsPulseAsyncReset,
         sPpsPulseOut_ms,        sPpsPulseOut : std_logic := '0';

  function to_StdLogic(b : boolean) return std_ulogic is
  begin
    if b then
      return '1';
    else
      return '0';
    end if;
  end to_StdLogic;

  function to_Boolean (s : std_ulogic) return boolean is
  begin
    return (To_X01(s)='1');
  end to_Boolean;

  attribute ASYNC_REG : string;
  attribute ASYNC_REG of rPpsPulseAsyncReset_ms : signal is "true";
  attribute ASYNC_REG of rPpsPulseAsyncReset    : signal is "true";
  attribute ASYNC_REG of sPpsPulseOut_ms : signal is "true";
  attribute ASYNC_REG of sPpsPulseOut    : signal is "true";

begin

  -- Cross the PPS from the no-reset domain into the aTdcReset domain since there is a
  -- reset crossing going into the TdcWrapper (reset by aTdcReset)! No clock domain
  -- crossing here, so crossing a single-cycle pulse is safe.
  DoubleSyncToAsyncReset : process (aTdcReset, RefClk)
  begin
    if to_boolean(aTdcReset) then
      rPpsPulseAsyncReset_ms <= '0';
      rPpsPulseAsyncReset    <= '0';
    elsif rising_edge(RefClk) then
      rPpsPulseAsyncReset_ms <= rPpsPulse;
      rPpsPulseAsyncReset    <= rPpsPulseAsyncReset_ms;
    end if;
  end process;

  -- In a similar fashion, cross the output PPS trigger from the async aTdcReset domain
  -- to the no-reset of the rest of the design. The odds of this signal triggering a
  -- failure are astronomically low (since it only pulses one clock cycle per second),
  -- but two flops is worth the assurance it won't mess something else up downstream.
  -- Note this double-sync mainly protects against the reset assertion case, since in the
  -- de-assertion case sPpsPulseAsyncReset should be zero and not transition for a long
  -- time afterwards. Again no clock crossing here, so crossing a single-cycle pulse
  -- is safe.
  DoubleSyncToNoReset : process (SampleClk)
  begin
    if rising_edge(SampleClk) then
      sPpsPulseOut_ms <= to_stdlogic(sPpsPulseAsyncReset);
      sPpsPulseOut    <= sPpsPulseOut_ms;
    end if;
  end process;

  sPpsPulse  <= sPpsPulseOut;


  rRpTransfer <= to_stdlogic(rRpTransferBool);
  sSpTransfer <= to_stdlogic(sSpTransferBool);

  --vhook_e TdcTop
  --vhook_a aReset               to_boolean(aTdcReset)
  --vhook_a rResetTdc            to_boolean(rResetTdc)
  --vhook_a rEnableTdc           to_boolean(rEnableTdc)
  --vhook_a rReRunEnable         to_boolean(rReRunEnable)
  --vhook_a rPpsPulse            to_boolean(rPpsPulseAsyncReset)
  --vhook_a rLoadRePulseCounts   to_boolean(rLoadRePulseCounts)
  --vhook_a rLoadRpCounts        to_boolean(rLoadRpCounts)
  --vhook_a rLoadRptCounts       to_boolean(rLoadRptCounts)
  --vhook_a sLoadSpCounts        to_boolean(sLoadSpCounts)
  --vhook_a sLoadSptCounts       to_boolean(sLoadSptCounts)
  --vhook_a rEnablePpsCrossing   to_boolean(rEnablePpsCrossing)
  --vhook_a rPulserEnableDelayVal unsigned(rPulserEnableDelayVal)
  --vhook_a sPpsClkCrossDelayVal  unsigned(sPpsClkCrossDelayVal)
  --vhook_a rRpTransfer          rRpTransferBool
  --vhook_a sSpTransfer          sSpTransferBool
  --vhook_a sPpsPulse            sPpsPulseAsyncReset
  --vhook_p {^rR(.*)In(.*)Clks}  unsigned(rR$1In$2Clks)
  --vhook_p {^sS(.*)In(.*)Clks}  unsigned(sS$1In$2Clks)
  TdcTopx: entity work.TdcTop (struct)
    generic map (
      kRClksPerRePulsePeriodBitsMax => kRClksPerRePulsePeriodBitsMax,  --integer range 3:32 :=24
      kRClksPerRpPeriodBitsMax      => kRClksPerRpPeriodBitsMax,       --integer range 3:16 :=16
      kSClksPerSpPeriodBitsMax      => kSClksPerSpPeriodBitsMax,       --integer range 3:16 :=16
      kPulsePeriodCntSize           => kPulsePeriodCntSize,            --integer:=13
      kFreqRefPeriodsToCheckSize    => kFreqRefPeriodsToCheckSize,     --integer:=17
      kSyncPeriodsToStampSize       => kSyncPeriodsToStampSize)        --integer:=10
    port map (
      aReset                  => to_boolean(aTdcReset),              --in  boolean
      RefClk                  => RefClk,                             --in  std_logic
      SampleClk               => SampleClk,                          --in  std_logic
      MeasClk                 => MeasClk,                            --in  std_logic
      rResetTdc               => to_boolean(rResetTdc),              --in  boolean
      rResetTdcDone           => rResetTdcDone,                      --out boolean
      rEnableTdc              => to_boolean(rEnableTdc),             --in  boolean
      rReRunEnable            => to_boolean(rReRunEnable),           --in  boolean
      rPpsPulse               => to_boolean(rPpsPulseAsyncReset),    --in  boolean
      rPpsPulseCaptured       => rPpsPulseCaptured,                  --out boolean
      rPulserEnableDelayVal   => unsigned(rPulserEnableDelayVal),    --in  unsigned(3:0)
      rEnablePpsCrossing      => to_boolean(rEnablePpsCrossing),     --in  boolean
      sPpsClkCrossDelayVal    => unsigned(sPpsClkCrossDelayVal),     --in  unsigned(3:0)
      sPpsPulse               => sPpsPulseAsyncReset,                --out boolean
      mRpOffset               => mRpOffset,                          --out unsigned(kPulsePeriodCntSize+ kSyncPeriodsToStampSize+ kFreqRefPeriodsToCheckSize-1:0)
      mSpOffset               => mSpOffset,                          --out unsigned(kPulsePeriodCntSize+ kSyncPeriodsToStampSize+ kFreqRefPeriodsToCheckSize-1:0)
      mOffsetsDone            => mOffsetsDone,                       --out boolean
      mOffsetsValid           => mOffsetsValid,                      --out boolean
      rLoadRePulseCounts      => to_boolean(rLoadRePulseCounts),     --in  boolean
      rRePulsePeriodInRClks   => unsigned(rRePulsePeriodInRClks),    --in  unsigned(kRClksPerRePulsePeriodBitsMax-1:0)
      rRePulseHighTimeInRClks => unsigned(rRePulseHighTimeInRClks),  --in  unsigned(kRClksPerRePulsePeriodBitsMax-1:0)
      rLoadRpCounts           => to_boolean(rLoadRpCounts),          --in  boolean
      rRpPeriodInRClks        => unsigned(rRpPeriodInRClks),         --in  unsigned(kRClksPerRpPeriodBitsMax-1:0)
      rRpHighTimeInRClks      => unsigned(rRpHighTimeInRClks),       --in  unsigned(kRClksPerRpPeriodBitsMax-1:0)
      rLoadRptCounts          => to_boolean(rLoadRptCounts),         --in  boolean
      rRptPeriodInRClks       => unsigned(rRptPeriodInRClks),        --in  unsigned(kRClksPerRpPeriodBitsMax-1:0)
      rRptHighTimeInRClks     => unsigned(rRptHighTimeInRClks),      --in  unsigned(kRClksPerRpPeriodBitsMax-1:0)
      sLoadSpCounts           => to_boolean(sLoadSpCounts),          --in  boolean
      sSpPeriodInSClks        => unsigned(sSpPeriodInSClks),         --in  unsigned(kSClksPerSpPeriodBitsMax-1:0)
      sSpHighTimeInSClks      => unsigned(sSpHighTimeInSClks),       --in  unsigned(kSClksPerSpPeriodBitsMax-1:0)
      sLoadSptCounts          => to_boolean(sLoadSptCounts),         --in  boolean
      sSptPeriodInSClks       => unsigned(sSptPeriodInSClks),        --in  unsigned(kSClksPerSpPeriodBitsMax-1:0)
      sSptHighTimeInSClks     => unsigned(sSptHighTimeInSClks),      --in  unsigned(kSClksPerSpPeriodBitsMax-1:0)
      rRpTransfer             => rRpTransferBool,                    --out boolean
      sSpTransfer             => sSpTransferBool,                    --out boolean
      rGatedPulseToPin        => rGatedPulseToPin,                   --inout std_logic
      sGatedPulseToPin        => sGatedPulseToPin);                  --inout std_logic

  -- Expand/compress the RegPort for moving through the netlist boundary.
  bSyncRegPortOut <= Unflatten(bSyncRegPortOutFlat);
  bSyncRegPortInFlat <= Flatten(bSyncRegPortIn);

  --vhook   SyncRegsIfc
  --vhook_# Tying this low is safe because the sync reset is used inside SyncRegsIfc.
  --vhook_a aBusReset '0'
  --vhook_a bRegPortInFlat  bSyncRegPortInFlat
  --vhook_a bRegPortOutFlat bSyncRegPortOutFlat
  --vhook_a rResetTdcDone     to_stdlogic(rResetTdcDone)
  --vhook_a rPpsPulseCaptured to_stdlogic(rPpsPulseCaptured)
  --vhook_a mOffsetsDone  to_stdlogic(mOffsetsDone)
  --vhook_a mOffsetsValid to_stdlogic(mOffsetsValid)
  --vhook_a mRpOffset std_logic_vector(mRpOffset)
  --vhook_a mSpOffset std_logic_vector(mSpOffset)
  SyncRegsIfcx: SyncRegsIfc
    port map (
      aBusReset               => '0',                             --in  std_logic
      bBusReset               => bBusReset,                       --in  std_logic
      BusClk                  => BusClk,                          --in  std_logic
      aTdcReset               => aTdcReset,                       --out std_logic
      bRegPortInFlat          => bSyncRegPortInFlat,              --in  std_logic_vector(49:0)
      bRegPortOutFlat         => bSyncRegPortOutFlat,             --out std_logic_vector(33:0)
      RefClk                  => RefClk,                          --in  std_logic
      rResetTdc               => rResetTdc,                       --out std_logic
      rResetTdcDone           => to_stdlogic(rResetTdcDone),      --in  std_logic
      rEnableTdc              => rEnableTdc,                      --out std_logic
      rReRunEnable            => rReRunEnable,                    --out std_logic
      rEnablePpsCrossing      => rEnablePpsCrossing,              --out std_logic
      rPpsPulseCaptured       => to_stdlogic(rPpsPulseCaptured),  --in  std_logic
      rPulserEnableDelayVal   => rPulserEnableDelayVal,           --out std_logic_vector(3:0)
      SampleClk               => SampleClk,                       --in  std_logic
      sPpsClkCrossDelayVal    => sPpsClkCrossDelayVal,            --out std_logic_vector(3:0)
      MeasClk                 => MeasClk,                         --in  std_logic
      mRpOffset               => std_logic_vector(mRpOffset),     --in  std_logic_vector(39:0)
      mSpOffset               => std_logic_vector(mSpOffset),     --in  std_logic_vector(39:0)
      mOffsetsDone            => to_stdlogic(mOffsetsDone),       --in  std_logic
      mOffsetsValid           => to_stdlogic(mOffsetsValid),      --in  std_logic
      rLoadRePulseCounts      => rLoadRePulseCounts,              --out std_logic
      rRePulsePeriodInRClks   => rRePulsePeriodInRClks,           --out std_logic_vector(23:0)
      rRePulseHighTimeInRClks => rRePulseHighTimeInRClks,         --out std_logic_vector(23:0)
      rLoadRpCounts           => rLoadRpCounts,                   --out std_logic
      rRpPeriodInRClks        => rRpPeriodInRClks,                --out std_logic_vector(15:0)
      rRpHighTimeInRClks      => rRpHighTimeInRClks,              --out std_logic_vector(15:0)
      rLoadRptCounts          => rLoadRptCounts,                  --out std_logic
      rRptPeriodInRClks       => rRptPeriodInRClks,               --out std_logic_vector(15:0)
      rRptHighTimeInRClks     => rRptHighTimeInRClks,             --out std_logic_vector(15:0)
      sLoadSpCounts           => sLoadSpCounts,                   --out std_logic
      sSpPeriodInSClks        => sSpPeriodInSClks,                --out std_logic_vector(15:0)
      sSpHighTimeInSClks      => sSpHighTimeInSClks,              --out std_logic_vector(15:0)
      sLoadSptCounts          => sLoadSptCounts,                  --out std_logic
      sSptPeriodInSClks       => sSptPeriodInSClks,               --out std_logic_vector(15:0)
      sSptHighTimeInSClks     => sSptHighTimeInSClks);            --out std_logic_vector(15:0)


end struct;


--------------------------------------------------------------------------------
-- Testbench for TdcWrapper
--------------------------------------------------------------------------------

--synopsys translate_off
library ieee;
  use ieee.std_logic_1164.all;
  use ieee.numeric_std.all;

library work;
  use work.PkgRegs.all;

entity tb_TdcWrapper is end tb_TdcWrapper;

architecture test of tb_TdcWrapper is

  --vhook_sigstart
  signal bBusReset: std_logic;
  signal bSyncRegPortIn: RegPortIn_t;
  signal bSyncRegPortOut: RegPortOut_t;
  signal BusClk: std_logic := '0';
  signal MeasClk: std_logic := '0';
  signal RefClk: std_logic := '0';
  signal rGatedPulseToPin: std_logic;
  signal rPpsPulse: std_logic;
  signal rRpTransfer: std_logic;
  signal SampleClk: std_logic := '0';
  signal sGatedPulseToPin: std_logic;
  signal sPpsPulse: std_logic;
  signal sSpTransfer: std_logic;
  --vhook_sigend

begin

  --vhook_e TdcWrapper dutx
  dutx: entity work.TdcWrapper (struct)
    port map (
      BusClk           => BusClk,            --in  std_logic
      bBusReset        => bBusReset,         --in  std_logic
      RefClk           => RefClk,            --in  std_logic
      SampleClk        => SampleClk,         --in  std_logic
      MeasClk          => MeasClk,           --in  std_logic
      bSyncRegPortOut  => bSyncRegPortOut,   --out RegPortOut_t
      bSyncRegPortIn   => bSyncRegPortIn,    --in  RegPortIn_t
      rPpsPulse        => rPpsPulse,         --in  std_logic
      sPpsPulse        => sPpsPulse,         --out std_logic
      rRpTransfer      => rRpTransfer,       --out std_logic
      sSpTransfer      => sSpTransfer,       --out std_logic
      rGatedPulseToPin => rGatedPulseToPin,  --inout std_logic
      sGatedPulseToPin => sGatedPulseToPin); --inout std_logic

  main: process

  begin
    report "TdcWrapper Test is EMPTY! (but that's ok in this case)" severity note;
    --vhook_nowarn tb_TdcWrapper.test.*
    wait;
  end process;

end test;
--synopsys translate_on