fpga: Add X440/FBX support

Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ni.com>
Co-authored-by: Ryan Marlow <ryan@lmarlow.com>


Original-commit: 596760a12e4834e47589c12f8a4fd083aa2f7c25
This commit is contained in:
Javier Valenzuela
2023-06-12 10:27:29 -05:00
committed by Aki Tomita
co-authored by Martin Braun Wade Fife Ryan Marlow
parent a405111ce7
commit 5cadf901c7
121 changed files with 20670 additions and 8739 deletions
+261
View File
@@ -0,0 +1,261 @@
--
-- Copyright 2022 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: x440_clock_gates
--
-- Description:
--
-- Gate propagation of 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.
--
-- This file heavily leverages the code from '../x410/x410_clock_gates.vhd'.
-- The main changes when comparing against that file are:
-- - One RFDC Clock pair per radio.
-- - No DataClk buffer instantiations (since DataClk is removed from BD).
--
-- 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 x440_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;
-- 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.
aEnableRf0Bufg1x : out std_logic_vector(0 downto 0);
aEnableRf0Bufg2x : out std_logic_vector(0 downto 0);
aEnableRf1Bufg1x : out std_logic_vector(0 downto 0);
aEnableRf1Bufg2x : 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 x440_clock_gates;
architecture STRUCT of x440_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;
-- 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 rEnableRfdc0Clk1x,
rEnableRfdc0Clk2x,
rEnableRfdc1Clk1x,
rEnableRfdc1Clk2x : std_logic;
signal rEnableRfdc0Bufg1xLcl : std_logic := '0';
signal rEnableRfdc0Bufg2xLcl : std_logic := '0';
signal rEnableRfdc1Bufg1xLcl : std_logic := '0';
signal rEnableRfdc1Bufg2xLcl : 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 aEnableRf0Bufg1x : signal is "TRUE";
attribute dont_touch of aEnableRf0Bufg2x : signal is "TRUE";
attribute dont_touch of aEnableRf1Bufg1x : signal is "TRUE";
attribute dont_touch of aEnableRf1Bufg2x : signal is "TRUE";
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
rEnableRfdc0Bufg1xLcl <= '0';
rEnableRfdc0Bufg2xLcl <= '0';
rEnableRfdc1Bufg1xLcl <= '0';
rEnableRfdc1Bufg2xLcl <= '0';
else
rEnableRfdc0Bufg1xLcl <=
rSafeToEnableGatedClks and
rEnableRfdc0Clk1x and
(not rPllUnlockedSticky);
rEnableRfdc0Bufg2xLcl <=
rSafeToEnableGatedClks and
rEnableRfdc0Clk2x and
(not rPllUnlockedSticky);
rEnableRfdc1Bufg1xLcl <=
rSafeToEnableGatedClks and
rEnableRfdc1Clk1x and
(not rPllUnlockedSticky);
rEnableRfdc1Bufg2xLcl <=
rSafeToEnableGatedClks and
rEnableRfdc1Clk2x and
(not rPllUnlockedSticky);
end if;
end if;
end process DataClkEnables;
aEnableRf0Bufg1x(0) <= rEnableRfdc0Bufg1xLcl;
aEnableRf0Bufg2x(0) <= rEnableRfdc0Bufg2xLcl;
aEnableRf1Bufg1x(0) <= rEnableRfdc1Bufg1xLcl;
aEnableRf1Bufg2x(0) <= rEnableRfdc1Bufg2xLcl;
-----------------------------------------------------------------------------
-- 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);
rEnableRfdc0Clk1x <= rSoftwareControl(kENABLE_RF0_CLK);
rEnableRfdc0Clk2x <= rSoftwareControl(kENABLE_RF0_CLK_2X);
rEnableRfdc1Clk1x <= rSoftwareControl(kENABLE_RF1_CLK);
rEnableRfdc1Clk2x <= rSoftwareControl(kENABLE_RF1_CLK_2X);
rSoftwareStatus(kDATA_CLK_PLL_LOCKED) <= rPllLockedLcl;
rSoftwareStatus(kDATA_CLK_PLL_UNLOCKED_STICKY) <= rPllUnlockedSticky;
end STRUCT;
@@ -0,0 +1,189 @@
--
-- Copyright 2022 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: x440_rf_reset_controller
--
-- Description:
--
-- Control RFDC, ADC, and DAC resets.
-- This file contains a similar structure to '../x410/x410_rf_reset_controller',
-- with the main difference of not having to support a complex reset chain.
-- This means that the instantiations of 'rf_reset' can be taken out in favor
-- having a simple combination of the incoming pulses and the software triggers
-- to generate the different outputs.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library WORK;
use WORK.PkgRFDC_REGS_REGMAP.all;
entity x440_rf_reset_controller is
port(
-- Clocks
-- Config clock is async to all the others.
ConfigClk : in std_logic;
PllRefClk : in std_logic;
RfClk : in std_logic;
RfClk2x : in std_logic;
-- Master resets from the Radio
rAdcResetPulse : in std_logic;
rDacResetPulse : in std_logic;
-- ADC Resets
r2AdcReset_n : out std_logic;
rAdcEnableData : out std_logic;
rAdcReset_n : out std_logic;
-- DAC Resets
r2DacReset_n : out std_logic;
rDacReset_n : out std_logic;
-- SW Control and Status
-- Control to initiate resets to RFDC.
-- The reset status is a sticky status of both ADC and DAC.
cSoftwareControl : in std_logic_vector(31 downto 0);
cSoftwareStatus : out std_logic_vector(31 downto 0)
);
end x440_rf_reset_controller;
architecture RTL of x440_rf_reset_controller is
-- POR value for all resets are high.
signal cTriggerAdcReset : std_logic := '1';
signal cTriggerAdcResetDlyd : std_logic := '1';
signal cTriggerDacReset : std_logic := '1';
signal cTriggerDacResetDlyd : std_logic := '1';
signal rTriggerAdcReset_ms : std_logic := '1';
signal rTriggerAdcReset : std_logic := '1';
signal rTriggerDacReset_ms : std_logic := '1';
signal rTriggerDacReset : std_logic := '1';
-- POR value of all reset done signals are set to low.
signal cTriggerAdcResetDone_ms : std_logic := '0';
signal cTriggerAdcResetDone : std_logic := '0';
signal cAdcResetDoneSticky : std_logic := '0';
signal cTriggerDacResetDone_ms : std_logic := '0';
signal cTriggerDacResetDone : std_logic := '0';
signal cDacResetDoneSticky : std_logic := '0';
attribute ASYNC_REG : string;
attribute ASYNC_REG of rTriggerAdcReset : signal is "TRUE";
attribute ASYNC_REG of rTriggerDacReset : signal is "TRUE";
attribute ASYNC_REG of cTriggerAdcResetDone : signal is "TRUE";
attribute ASYNC_REG of cTriggerDacResetDone : signal is "TRUE";
attribute ASYNC_REG of rTriggerAdcReset_ms : signal is "TRUE";
attribute ASYNC_REG of rTriggerDacReset_ms : signal is "TRUE";
attribute ASYNC_REG of cTriggerAdcResetDone_ms : signal is "TRUE";
attribute ASYNC_REG of cTriggerDacResetDone_ms : signal is "TRUE";
begin
-- rAdcEnableData is set to '1' as we don't control the flow of RX data.
rAdcEnableData <= '1';
cTriggerAdcReset <= cSoftwareControl(kADC_RESET);
cTriggerDacReset <= cSoftwareControl(kDAC_RESET);
cSoftwareStatus <= (
kADC_SEQ_DONE => cAdcResetDoneSticky,
kDAC_SEQ_DONE => cDacResetDoneSticky,
others => '0'
);
-----------------------------------------------------------------------------
-- High-Level Resets Using ConfigClk
-----------------------------------------------------------------------------
-- Pass the master FSM reset around to the other clock domains and then
-- return them back to the ConfigClk domain. This is also a handy way to
-- prove all your clocks are toggling to some extent.
-----------------------------------------------------------------------------
SeqResetRfClk : process(RfClk)
begin
if rising_edge(RfClk) then
-- double-syncs have no sync reset!
rTriggerAdcReset_ms <= cTriggerAdcReset;
rTriggerAdcReset <= rTriggerAdcReset_ms;
rTriggerDacReset_ms <= cTriggerDacReset;
rTriggerDacReset <= rTriggerDacReset_ms;
end if;
end process;
-----------------------------------------------------------------------------
-- Reset Sequence Done Status
-----------------------------------------------------------------------------
-- Now back to ConfigClk! We provide the status for all software controlled
-- resets. We move the signal from ConfigClk to RfClk domain and move it
-- back to ConfigClk domain. This just proves that RfClk is toggling and
-- the reset requested by software is sampled in the RfClk.
-----------------------------------------------------------------------------
SeqResetDone : process(ConfigClk)
begin
if rising_edge(ConfigClk) then
-- double-syncs have no sync reset!
cTriggerAdcResetDone_ms <= rTriggerAdcReset;
cTriggerAdcResetDone <= cTriggerAdcResetDone_ms;
cTriggerDacResetDone_ms <= rTriggerDacReset;
cTriggerDacResetDone <= cTriggerDacResetDone_ms;
end if;
end process;
-- ADC reset done
SwAdcResetDone: process(ConfigClk)
begin
if rising_edge(ConfigClk) then
cTriggerAdcResetDlyd <= cTriggerAdcReset;
-- De-assert reset status on the rising edge of SW ADC reset.
if cTriggerAdcReset = '1' and cTriggerAdcResetDlyd = '0' then
cAdcResetDoneSticky <= '0';
-- Assert and hold the ADC reset status on ADC reset strobe.
elsif cTriggerAdcResetDone = '1' then
cAdcResetDoneSticky <= '1';
end if;
end if;
end process SwAdcResetDone;
-- DAC reset done
SwDacResetDone: process(ConfigClk)
begin
if rising_edge(ConfigClk) then
cTriggerDacResetDlyd <= cTriggerDacReset;
-- De-assert reset status on the rising edge of SW DAC reset.
if cTriggerDacReset = '1' and cTriggerDacResetDlyd = '0' then
cDacResetDoneSticky <= '0';
-- Assert and hold the DAC reset status on DAC reset strobe.
elsif cTriggerDacResetDone = '1' then
cDacResetDoneSticky <= '1';
end if;
end if;
end process SwDacResetDone;
-----------------------------------------------------------------------------
-- rf_reset Instances
-----------------------------------------------------------------------------
RfClkResets: process(RfClk)
begin
if rising_edge(RfClk) then
rAdcReset_n <= not (rAdcResetPulse or rTriggerAdcReset);
rDacReset_n <= not (rDacResetPulse or rTriggerDacReset);
end if;
end process RfClkResets;
RfClk2xResets: process(RfClk2x)
begin
if rising_edge(RfClk2x) then
r2AdcReset_n <= not (rAdcResetPulse or rTriggerAdcReset);
r2DacReset_n <= not (rDacResetPulse or rTriggerDacReset);
end if;
end process RfClk2xResets;
end RTL;
@@ -0,0 +1,35 @@
//
// Copyright 2022 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: x440_rfdc_tx_control_remap.v
// Description:
// Allows for remapping control signals to the correct tile on X440.
// This file targets the DAC tiles specifically
//
`default_nettype none
module x440_rfdc_tx_control_remap (
// Input control (Front-Panel ordering)
input wire [7:0] input_controls,
// output_control (DAC Tile ordering)
output wire [7:0] output_controls
);
`include "../../regmap/x440/rfdc_mapping_regmap_utils.vh"
// Decode TX Channel mapping
assign output_controls[CH0_TX_MAPPING] = input_controls[0];
assign output_controls[CH1_TX_MAPPING] = input_controls[1];
assign output_controls[CH2_TX_MAPPING] = input_controls[2];
assign output_controls[CH3_TX_MAPPING] = input_controls[3];
assign output_controls[CH4_TX_MAPPING] = input_controls[4];
assign output_controls[CH5_TX_MAPPING] = input_controls[5];
assign output_controls[CH6_TX_MAPPING] = input_controls[6];
assign output_controls[CH7_TX_MAPPING] = input_controls[7];
endmodule
`default_nettype wire