aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/rf/common/clock_gates.vhd
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/x400/rf/common/clock_gates.vhd')
-rw-r--r--fpga/usrp3/top/x400/rf/common/clock_gates.vhd300
1 files changed, 300 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/rf/common/clock_gates.vhd b/fpga/usrp3/top/x400/rf/common/clock_gates.vhd
new file mode 100644
index 000000000..c4df28d8f
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/common/clock_gates.vhd
@@ -0,0 +1,300 @@
+--
+-- Copyright 2021 Ettus Research, a National Instruments Brand
+--
+-- SPDX-License-Identifier: LGPL-3.0-or-later
+--
+-- Module: clock_gates
+--
+-- Description:
+--
+-- Gate propagation of DataClk and RfdcClk instances until the PLL lock
+-- status signal is stable and software has acknowledged it by asserting the
+-- pertinent controls.
+--
+-- RfdcClks are used on other Xilinx IP components in the Board Design, and
+-- Vivado fails to detect their frequency correctly their buffer is
+-- explicitly instantiated in the Block Design. Therefore, we only generate
+-- the buffer enable signals for these clocks within this component.
+--
+-- Since DataClk are only used in other Custom IP blocks within the Block
+-- design, it is possible to instantiate the clock buffers within this block
+-- for without running into IP generation failures.
+--
+-- Parameters:
+--
+-- kReliableClkPeriodNs: Clock period (ns) for ReliableClk.
+--
+
+library IEEE;
+ use IEEE.std_logic_1164.ALL;
+ use IEEE.numeric_std.ALL;
+
+library UNISIM;
+ use UNISIM.Vcomponents.ALL;
+
+library WORK;
+use WORK.PkgRFDC_REGS_REGMAP.all;
+
+
+entity clock_gates is
+ generic (
+ kReliableClkPeriodNs : integer := 25
+ );
+ port (
+ -- MMCM reset
+ -- This clock will be asserted via AXI access before any clocking
+ -- configuration done, signals coming into this component will not change
+ -- immediately after this reset is de-asserted.
+ rPllReset_n : in std_logic;
+
+ aPllLocked : in std_logic;
+
+ -- Input Clocks (from MMCM)
+ ReliableClk : in std_logic;
+ DataClk1xPll : in std_logic;
+ DataClk2xPll : in std_logic;
+
+ -- Buffered Clock Outputs (to design)
+ DataClk1x : out std_logic;
+ DataClk2x : out std_logic;
+
+ -- Buffers for these signals must be instantiated on Block design for clock
+ -- rates to be identified. The Utility Buffers instantiated on the Block
+ -- Design require signals to be of type std_logic_vector.
+ aEnableRfBufg1x : out std_logic_vector(0 downto 0);
+ aEnableRfBufg2x : out std_logic_vector(0 downto 0);
+
+ -- PLL Status Signals
+ rPllLocked : out std_logic;
+
+ -- Window Interface
+ rSafeToEnableGatedClks : in std_logic;
+ rGatedBaseClksValid : out std_logic;
+
+ -- AXI GPIO interface
+ rSoftwareControl : in std_logic_vector(31 downto 0);
+ rSoftwareStatus : out std_logic_vector(31 downto 0)
+ );
+end clock_gates;
+
+architecture STRUCT of clock_gates is
+
+ component sync_wrapper
+ generic (
+ WIDTH : integer := 1;
+ STAGES : integer := 2;
+ INITIAL_VAL : integer := 0;
+ FALSE_PATH_TO_IN : integer := 1);
+ port (
+ clk : in std_logic;
+ rst : in std_logic;
+ signal_in : in std_logic_vector((WIDTH-1) downto 0);
+ signal_out : out std_logic_vector((WIDTH-1) downto 0));
+ end component;
+
+ component BUFGCE
+ generic(
+ CE_TYPE : string);
+ port (
+ O : out std_ulogic;
+ CE : in std_ulogic;
+ I : in std_ulogic);
+ end component;
+
+ -- UltraScale MMCM max lock time = 100 us / 25 ns = 4,000 clk cycles. If the
+ -- division kPllLockTimeNs / kReliableClkPeriodNs does not evaluate to an
+ -- integer, Vivado could either round up or down. In case they round down, we
+ -- add '1' to the result to ensure we have the full lock time accounted for.
+ -- In this case, it is better to count 1 more than necessary than kill the
+ -- process prematurely.
+ constant kPllLockTimeNs : integer := 100000;
+ constant kMaxPllLockCount : integer := kPllLockTimeNs / kReliableClkPeriodNs + 1;
+ signal rLockedFilterCount : integer range 0 to kMaxPllLockCount-1 := kMaxPllLockCount-1;
+
+ signal rClearDataClkUnlockedSticky : std_logic;
+
+ -----------------------------------------------------------------------------
+ -- PLL locked signals
+ -----------------------------------------------------------------------------
+
+ -- Synchronizer signals
+ signal aPllLockedLcl : std_logic_vector(0 downto 0);
+ signal rPllLockedDs : std_logic_vector(0 downto 0) := (others => '0');
+
+ -- Lock status indicators
+ signal rPllLockedLcl : std_logic := '0';
+ signal rPllUnlockedSticky : std_logic := '0';
+
+ -- Safe BUFG enable signals
+ signal rEnableDataClk1x,
+ rEnableDataClk2x,
+ rEnableRfdcClk1x,
+ rEnableRfdcClk2x : std_logic;
+
+ signal rEnableDataBufg1x : std_logic := '0';
+ signal rEnableDataBufg2x : std_logic := '0';
+ signal rEnableRfdcBufg1xLcl : std_logic := '0';
+ signal rEnableRfdcBufg2xLcl : std_logic := '0';
+
+ -- Active high version of reset required for synchronizer blocks.
+ signal rPllReset : std_logic;
+
+ -- Since these signals control sensitive components (clock enables), we apply
+ -- a dont_touch attribute to preserve the signals through both synthesis and
+ -- P&R. Implementation of "dont_touch" has been confirmed after P&R.
+ attribute dont_touch : string;
+ attribute dont_touch of rEnableDataBufg1x : signal is "TRUE";
+ attribute dont_touch of rEnableDataBufg2x : signal is "TRUE";
+ attribute dont_touch of aEnableRfBufg1x : signal is "TRUE";
+ attribute dont_touch of aEnableRfBufg2x : signal is "TRUE";
+
+ attribute X_INTERFACE_INFO : string;
+ attribute X_INTERFACE_PARAMETER : string;
+
+ attribute X_INTERFACE_INFO of DataClk1xPll : signal is
+ "xilinx.com:signal:clock:1.0 DataClk1xPll CLK";
+ attribute X_INTERFACE_INFO of DataClk2xPll : signal is
+ "xilinx.com:signal:clock:1.0 DataClk2xPll CLK";
+
+begin
+
+ rPllReset <= not rPllReset_n;
+
+ -- Assert rGatedBaseClksValid once the PLL has been locked for the specified
+ -- time.
+ rGatedBaseClksValid <= rPllLockedLcl;
+
+ DataClkEnables : process(ReliableClk)
+ begin
+ if rising_edge(ReliableClk) then
+ if rPllReset_n = '0' then
+ rEnableDataBufg1x <= '0';
+ rEnableDataBufg2x <= '0';
+ rEnableRfdcBufg1xLcl <= '0';
+ rEnableRfdcBufg2xLcl <= '0';
+ else
+ rEnableDataBufg1x <=
+ rSafeToEnableGatedClks and
+ rEnableDataClk1x and
+ (not rPllUnlockedSticky);
+
+ rEnableDataBufg2x <=
+ rSafeToEnableGatedClks and
+ rEnableDataClk2x and
+ (not rPllUnlockedSticky);
+
+ rEnableRfdcBufg1xLcl <=
+ rSafeToEnableGatedClks and
+ rEnableRfdcClk1x and
+ (not rPllUnlockedSticky);
+
+ rEnableRfdcBufg2xLcl <=
+ rSafeToEnableGatedClks and
+ rEnableRfdcClk2x and
+ (not rPllUnlockedSticky);
+ end if;
+ end if;
+ end process DataClkEnables;
+
+ aEnableRfBufg1x(0) <= rEnableRfdcBufg1xLcl;
+ aEnableRfBufg2x(0) <= rEnableRfdcBufg2xLcl;
+
+ DataClk1xSafeBufg: BUFGCE
+ generic map(
+ CE_TYPE => "ASYNC"
+ )
+ port map (
+ I => DataClk1xPll,
+ CE => rEnableDataBufg1x,
+ O => DataClk1x
+ );
+
+ DataClk2xSafeBufg: BUFGCE
+ generic map(
+ CE_TYPE => "ASYNC"
+ )
+ port map (
+ I => DataClk2xPll,
+ CE => rEnableDataBufg2x,
+ O => DataClk2x
+ );
+
+
+ -----------------------------------------------------------------------------
+ -- Create PLL Lock Signal
+ -----------------------------------------------------------------------------
+ -- Double-sync the incoming aPllLocked signal from the PLL.
+
+ aPllLockedLcl(0) <= aPllLocked;
+
+ DataClkPllLockedDS: sync_wrapper
+ generic map (
+ WIDTH => 1,
+ STAGES => open,
+ INITIAL_VAL => open,
+ FALSE_PATH_TO_IN => open)
+ port map (
+ clk => ReliableClk,
+ rst => rPllReset,
+ signal_in => aPllLockedLcl,
+ signal_out => rPllLockedDs
+ );
+
+ -- Filter the Lock signal. Assert a lock when the PLL lock signal has been
+ -- asserted for kPllLockTimeNs
+ --
+ -- !!! SAFE COUNTER STARTUP !!!
+ -- rLockedFilterCount cannot start incrementing until rPllReset_n is
+ -- de-asserted. Once rPllReset_n is de-asserted through a AXI access, input
+ -- values for the registers in this state machine will not change until the
+ -- MMCM locks and the double synchronizer reflects a locked status, making
+ -- this start-up safe.
+ PllLockFilter: process (ReliableClk)
+ begin
+ if rising_edge(ReliableClk) then
+ if rPllReset_n = '0' then
+ rLockedFilterCount <= kMaxPllLockCount-1;
+ rPllLockedLcl <= '0';
+ else
+ if rPllLockedDs(0) = '1' then
+ if rLockedFilterCount = 0 then
+ rPllLockedLcl <= '1';
+ else
+ rPllLockedLcl <= '0';
+ rLockedFilterCount <= rLockedFilterCount - 1;
+ end if;
+ else
+ rLockedFilterCount <= kMaxPllLockCount-1;
+ rPllLockedLcl <= '0';
+ end if;
+ end if;
+ end if;
+ end process PllLockFilter;
+
+ -- Sticky bit to hold '1' if PLL ever comes unlocked
+ PllStickyBit: process (ReliableClk)
+ begin
+ if rising_edge(ReliableClk) then
+ if (not rPllReset_n or rClearDataClkUnlockedSticky) = '1' then
+ rPllUnlockedSticky <= '0';
+ else
+ if rPllLockedLcl = '1' and rPllLockedDs(0) = '0' then
+ rPllUnlockedSticky <= '1';
+ end if;
+ end if;
+ end if;
+ end process;
+
+ rPllLocked <= rPllLockedLcl;
+
+ -- AXI transaction decoding
+ rClearDataClkUnlockedSticky <= rSoftwareControl(kCLEAR_DATA_CLK_UNLOCKED);
+ rEnableDataClk1x <= rSoftwareControl(kENABLE_DATA_CLK);
+ rEnableDataClk2x <= rSoftwareControl(kENABLE_DATA_CLK_2X);
+ rEnableRfdcClk1x <= rSoftwareControl(kENABLE_RF_CLK);
+ rEnableRfdcClk2x <= rSoftwareControl(kENABLE_RF_CLK_2X);
+
+ rSoftwareStatus(kDATA_CLK_PLL_LOCKED) <= rPllLockedLcl;
+ rSoftwareStatus(kDATA_CLK_PLL_UNLOCKED_STICKY) <= rPllUnlockedSticky;
+
+end STRUCT;