FPGA: - Split up MB registers that control daughterboard specific settings so that daughterboards 0 and 1 could have different setings, in preparation for future devices that require different settings. This requires a compat number bump to 8.0. - Add registers for additional RFDC information, including the block/tile mapping of the individual channels, and information about resampling capabilities - Identify sections of code that would be specific to X410/ZBX and move them to their own headers, so it's trivial to add device-specific sections of code instead for other devices in the future. - This includes constraints for clocks and I/O pins. - Remove ability to do timed ctrlport transactions to the MB CPLD, this was unused and possibly broken. - Move daughterboard-specific code into its own code location (dboards/zbx) - Move X410-specific register documentation to its own location (doc/X410) - Refactor Makefiles to split out X410/ZBX specific components and allow switching between device types - Add 512-bit AXI interconnects - Make number of timekeepers configurable (X410 keeps the single timekeeper) MPM: - Required compat is bumped to 8.0 - Now supports new registers for detecting DSP capabilities and multi-rate settings for the daughterboards - Adds MMCM controls (currently unused) Co-authored-by: Wade Fife <wade.fife@ni.com> Co-authored-by: Ryan Marlow <ryan@lmarlow.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Original-commit: c1d268917ea65dd9c5a42366014cb96d3c025223
301 lines
9.6 KiB
VHDL
301 lines
9.6 KiB
VHDL
--
|
|
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
|
--
|
|
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
|
--
|
|
-- Module: x410_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 x410_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 x410_clock_gates;
|
|
|
|
architecture STRUCT of x410_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;
|