diff options
Diffstat (limited to 'fpga/usrp3/top/x400/rf/common/clock_gates.vhd')
-rw-r--r-- | fpga/usrp3/top/x400/rf/common/clock_gates.vhd | 300 |
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; |