Files
b210-k7-fpga/top/x400/rf/common/clock_gates.vhd
T
61782b02d7 fpga: x400: Add support for X410 motherboard FPGA
Co-authored-by: Andrew Moch <Andrew.Moch@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com>
Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com>
Co-authored-by: Max Köhler <max.koehler@ni.com>
Co-authored-by: Michael Auchter <michael.auchter@ni.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>
Co-authored-by: Hector Rubio <hrubio@ni.com>


Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
2021-06-10 11:56:58 -05:00

301 lines
9.6 KiB
VHDL

--
-- 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;