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
This commit is contained in:
Wade Fife
2021-06-10 11:56:58 -05:00
committed by Aaron Rossetto
co-authored by Andrew Moch Daniel Jepson Javier Valenzuela Joerg Hofrichter Kumaran Subramoniam Max Köhler Michael Auchter Paul Butler Hector Rubio
parent bfef20ea45
commit 61782b02d7
205 changed files with 299634 additions and 0 deletions
+220
View File
@@ -0,0 +1,220 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: PkgRf
--
-- Description:
--
-- This package has some type definition and functions used in the RF data
-- chain.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
package PkgRf is
-- DDC sample data out width.
constant kDdcDataOutWidth : natural := 17;
-- Each sample is padded in MSB with 7 extra bits of zero to byte align.
constant kDdcDataWordWidth : natural := kDdcDataOutWidth+7;
-- DUC sample data out width.
constant kDucDataOutWidth : natural := 18;
-- Each sample is padded in MSB with 6 extra bits of zero to byte align.
constant kDucDataWordWidth : natural := kDucDataOutWidth+6;
-- Saturated data output width.
constant kSatDataWidth : natural := 16;
-- ADC sample resolution.
constant kAdcSampleRes : natural := 16;
subtype Sample18_t is signed(17 downto 0);
subtype Sample17_t is signed(16 downto 0);
subtype Sample16_t is signed(15 downto 0);
subtype Sample16slv_t is std_logic_vector(15 downto 0);
type Samples16_t is array(natural range<>) of Sample16_t;
type Samples17_t is array(natural range<>) of Sample17_t;
type Samples18_t is array(natural range<>) of Sample18_t;
-- These constants have the largest and smallest 18-bit, 17-bit, and 16-bit
-- signed values.
constant kLargest18 : Sample18_t := to_signed(2**17 - 1, 18);
constant kSmallest18 : Sample18_t := to_signed(-2**17, 18);
constant kLargest17 : Sample17_t := to_signed(2**16 - 1, 17);
constant kSmallest17 : Sample17_t := to_signed(-2**16, 17);
constant kLargest16 : Sample16_t := to_signed(2**15 - 1, 16);
constant kSmallest16 : Sample16_t := to_signed(-2**15, 16);
function Saturate(s : signed ) return Sample16_t;
function to_stdlogicvector(d : Samples16_t) return std_logic_vector;
function to_Samples16(d : std_logic_vector) return Samples16_t;
function to_Samples17(d : std_logic_vector) return Samples17_t;
function to_Samples18(d : std_logic_vector) return Samples18_t;
-- Shift the ADC sample to the left by 1 bit.
function Gain2x(d : std_logic_vector) return std_logic_vector;
function Gain2x(s : Samples16_t) return Samples16_t;
--synopsys translate_off
function tb_saturate(s: std_logic_vector) return Sample16slv_t;
--synopsys translate_on
end package PkgRf;
package body PkgRf is
-- Function to saturate any signed number greater then 16 bits.
-- A saturated 16-bit data is returned.
function Saturate ( s : signed) return Sample16_t is
begin
if s > kLargest16 then
return kLargest16;
elsif s < kSmallest16 then
return kSmallest16;
else
return resize(s, 16);
end if;
end function Saturate;
-- This function will convert 16 bit signed array into a single
-- std_logic_vector.
function to_stdlogicvector(d : Samples16_t) return std_logic_vector is
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
alias normalD : Samples16_t(d'length-1 downto 0) is d;
variable rval : std_logic_vector(d'length * 16 - 1 downto 0);
constant dataWidth : natural := Sample16_t'length;
begin
for i in normalD'range loop
rval(i*dataWidth + dataWidth-1 downto i*dataWidth)
:= std_logic_vector(normalD(i));
end loop;
return rval;
end function to_stdlogicvector;
-- This function will convert a std_logic_vector into an array of 18 bit
-- signed array. The input std_logic_vector has data packed in 24 bits. But
-- only 18 bits has valid data and remaining 6 MSB bits are padded with
-- zeros.
function to_Samples18(d : std_logic_vector) return Samples18_t is
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
alias normalD : std_logic_vector(d'length-1 downto 0) is d;
variable rval : Samples18_t(d'length / kDucDataWordWidth - 1 downto 0);
begin
--synopsys translate_off
assert (((d'length) mod kDucDataWordWidth) = 0)
report "Input to the function to_Samples18 must be a multiple of kDucDataWordWidth"
severity error;
--synopsys translate_on
for i in rval'range loop
rval(i) := Sample18_t(normalD(i*kDucDataWordWidth + Sample18_t'length-1
downto i*kDucDataWordWidth));
end loop;
return rval;
end function to_Samples18;
-- This function will convert a std_logic_vector into an array of 16 bit
-- signed array. The input std_logic_vector has data packed in 16 bits. But
-- only 15 bits has valid data and the uper two bits only have the signed
-- bit.
function to_Samples16(d : std_logic_vector) return Samples16_t is
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
alias normalD : std_logic_vector(d'length-1 downto 0) is d;
variable rval : Samples16_t(d'length / kAdcSampleRes - 1 downto 0);
begin
--synopsys translate_off
assert (((d'length) mod kAdcSampleRes) = 0)
report "Input to the function to_Samples16 must be a multiple of kAdcSampleRes"
severity error;
--synopsys translate_on
for i in rval'range loop
rval(i) := Sample16_t(normalD(i*kAdcSampleRes + Sample16_t'length-1
downto i*kAdcSampleRes));
end loop;
return rval;
end function to_Samples16;
-- This function will convert a std_logic_vector into an array of 19 bit
-- signed array. The input std_logic_vector has data packed in 24 bits. But
-- only 17 bits has valid data and remaining 7 MSB bits are padded with
-- zeros.
function to_Samples17(d : std_logic_vector) return Samples17_t is
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
alias normalD : std_logic_vector(d'length-1 downto 0) is d;
variable rval : Samples17_t(d'length / kDdcDataWordWidth - 1 downto 0);
begin
--synopsys translate_off
assert (((d'length) mod kDdcDataWordWidth) = 0)
report "Input to the function to_Samples17 must be a multiple of kDdcDataWordWidth"
severity error;
--synopsys translate_on
for i in rval'range loop
rval(i) := Sample17_t(normalD(i*kDdcDataWordWidth + Sample17_t'length-1
downto i*kDdcDataWordWidth));
end loop;
return rval;
end function to_Samples17;
-- Function to shift the sample to the left by one bit and effectively
-- multiply by 2.
function Gain2x(s : Samples16_t) return Samples16_t is
variable rval : Samples16_t(s'range);
begin
for i in rval'range loop
rval(i) := s(i)(kAdcSampleRes-2 downto 0) & '0';
end loop;
return rval;
end function Gain2x;
function Gain2x (d : std_logic_vector) return std_logic_vector is
begin
return to_stdlogicvector(Gain2x(to_Samples16(d)));
end function;
--synopsys translate_off
---------------------------------------------------------------
-- Function below this comment is used only for testbench.
---------------------------------------------------------------
-- This function does saturation of a signed number in std_logic_vector data
-- type. The current implementation supports only 17 or 18 bit signed
-- number.
function tb_saturate(s: std_logic_vector) return Sample16slv_t is
-- This alias is used to normalize the input vector to [s'length-1 downto 0]
alias normalS : std_logic_vector(s'length-1 downto 0) is s;
variable rval : Sample16slv_t;
constant len : integer := s'length;
begin
-- If 2 MSBs = 00, output <= input without MSB, e.g. positive number < 1
-- If 2 MSBs = 01, output <= 0.111111111111111, e.g. positive number >= 1
-- If 2 MSBs = 10, output <= 1.000000000000000, e.g. negative number < -1
-- If 2 MSBs = 11, output <= input without MSB, e.g. negative number >= -1
if len = kDdcDataOutWidth then
if normalS(len-1 downto len-2) = "01" then
rval := "0111111111111111";
elsif normalS(len-1 downto len-2) = "10" then
rval := "1000000000000000";
else
rval := normalS(len-2 downto 0);
end if;
-- If 3 MSBs = 000, output <= input without MSB, e.g. positive number < 1
-- If 3 MSBs = 0x1/01x, output <= 0.111111111111111, e.g. positive number >= 1
-- If 3 MSBs = 1x0/10x, output <= 1.000000000000000, e.g. negative number < -1
-- If 3 MSBs = 111, output <= input without MSB, e.g. negative number >= -1
else -- len = kDucDataOutWidth
if normalS(len-1) = '0' and normalS(len-2 downto len-3) /= "00" then
rval := "0111111111111111";
elsif (normalS(len-1 downto len-3) = "000") or
(normalS(len-1 downto len-3) = "111") then
rval := normalS(len-3 downto 0);
else
rval := "1000000000000000";
end if;
end if;
return rval;
end function tb_saturate;
--synopsys translate_on
end package body;
+98
View File
@@ -0,0 +1,98 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: axis_mux
--
-- Description:
--
-- This module implements a data mux for a single AXIS bus. When
-- mux_select='0' m_axis_tdata comes from s_axis_tdata. mux_select='1'
-- chooses GPIO as the output data.
--
-- This module IS NOT useful for crossing clock domain boundaries s_axis_aclk
-- and m_axis_mclk must be connected to the same clock.
--
-- This mux is intended for muxing in constant calibration data from gpio.
-- gpio and mux_select are expected to be asynchronous to s_axis_aclk, but
-- this module includes no synchronization logic. When mux_select or gpio
-- change, m_axis_tvalid and m_axis_tdata are undefined in the first few
-- clock cycles. You must wait for bad axis cycles to flush through the
-- remainder of the pipeline before performing calibration and again after
-- exiting calibration mode.
--
-- kAxiWidth must be an integer multiple of kGpioWidth. A concurrent assert
-- statement checks this assumption and should produce a synthesis warning if
-- that requirement is not met.
--
-- Parameters:
--
-- kGpioWidth : GPIO width.
-- kAxiWidth : AXI bus width. Must be an integer multiple of kGpioWidth
--
library IEEE;
use IEEE.std_logic_1164.all;
entity axis_mux is
generic (
kGpioWidth : natural := 32;
kAxiWidth : natural := 256
);
port(
gpio : in std_logic_vector(kGpioWidth-1 downto 0);
mux_select : in std_logic;
-- s_axis_aclk MUST be the same as m_axis_aclk.
-- Declaring an unused clock allows the BD tool to identify the
-- synchronicity of the slave AXIS port signals.
s_axis_aclk : in std_logic;
s_axis_tdata : in std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_tvalid : in std_logic;
s_axis_tready : out std_logic;
m_axis_aclk : in std_logic;
m_axis_tvalid : out std_logic;
m_axis_tdata : out std_logic_vector(kAxiWidth - 1 downto 0)
);
end entity axis_mux;
architecture RTL of axis_mux is
constant kWordSize : natural := gpio'length;
constant kWordCount : natural := kAxiWidth / kWordSize;
subtype AxiData_t is std_logic_vector(kAxiWidth - 1 downto 0);
impure function ConcatenatedData return AxiData_t is
variable rval : AxiData_t;
begin
for i in 0 to kWordCount - 1 loop
rval(i*kWordSize + kWordSize - 1 downto i*kWordSize) := gpio;
end loop;
return rval;
end function ConcatenatedData;
begin
assert kWordSize * kWordCount = kAxiWidth
report "m_axis_tdata'length is not an integer multiple of gpio'length"
severity failure;
MuxOutputRegister:
process (m_axis_aclk) is
begin
if rising_edge(m_axis_aclk) then
if mux_select='1' then
m_axis_tdata <= ConcatenatedData;
m_axis_tvalid <= '1';
else
m_axis_tdata <= s_axis_tdata;
m_axis_tvalid <= s_axis_tvalid;
end if;
end if;
end process MuxOutputRegister;
s_axis_tready <= '1';
end RTL;
+50
View File
@@ -0,0 +1,50 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: capture_sysref
//
// Description:
//
// Capture SYSREF and transfer it to the higher clock domain. Module incurs
// in 2 pll_ref_clk cycles + 1 rfdc_clk cycle of delay.
//
module capture_sysref (
// Clocks
input wire pll_ref_clk,
input wire rfdc_clk,
// SYSREF input and control
input wire sysref_in, // Single-ended SYSREF (previously buffered)
input wire enable_rclk, // Enables SYSREF output in the rfdc_clk domain.
// Captured SYSREF outputs
output wire sysref_out_pclk, // Debug output (Domain: pll_ref_clk).
output wire sysref_out_rclk // RFDC output (Domain: rfdc_clk).
);
reg sysref_pclk_ms = 1'b0, sysref_pclk = 1'b0, sysref_rclk = 1'b0;
// Capture SYSREF synchronously with the pll_ref_clk, but double-sync it just
// in case static timing isn't met so as not to destroy downstream logic.
always @ (posedge pll_ref_clk) begin
sysref_pclk_ms <= sysref_in;
sysref_pclk <= sysref_pclk_ms;
end
assign sysref_out_pclk = sysref_pclk;
// Transfer to faster clock which is edge-aligned with the pll_ref_clk.
always @ (posedge rfdc_clk) begin
if (enable_rclk) begin
sysref_rclk <= sysref_pclk;
end else begin
sysref_rclk <= 1'b0;
end
end
assign sysref_out_rclk = sysref_rclk;
endmodule
+300
View File
@@ -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;
+147
View File
@@ -0,0 +1,147 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: gpio_to_axis_mux
--
-- Description:
--
-- This module either drives the AXIS outputs with the corresponding AXIS
-- slave inputs, or it drives the output AXIS with data provided by the GPIO
-- lines. This allows the calibration process to drive a constant value to
-- the DAC's. Although every AXIS interface has its own clock, all the clocks
-- must be connected to the same source. Independent clock inputs allows the
-- block design editor to automatically detect the clock domain of the
-- corresponding interface.
--
-- kAxiWidth must be an integer multiple of kGpioWidth. A concurrent assert
-- statement in axis_mux checks this assumption and should produce a
-- synthesis warning if that requirement is not met.
--
-- Parameters:
--
-- kGpioWidth : GPIO width.
-- kAxiWidth : AXI bus width. Must be an integer multiple of kGpioWidth
--
library IEEE;
use IEEE.std_logic_1164.all;
entity gpio_to_axis_mux is
generic (
kGpioWidth : natural := 32;
kAxiWidth : natural := 256
);
port(
gpio : in std_logic_vector(kGpioWidth-1 downto 0);
-- mux_select(n) chooses the data source for AXIS interface n.
-- '0' chooses s_axis_tdata_n. '1' chooses gpio as the data source.
-- The only used bits are 0, 1, 4, 5. The remaining bits are reserved for
-- future expansion.
mux_select : in std_logic_vector(7 downto 0);
s_axis_0_aclk : in std_logic;
s_axis_tdata_0 : in std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_tvalid_0 : in std_logic;
s_axis_tready_0 : out std_logic;
m_axis_0_aclk : in std_logic;
m_axis_tvalid_0 : out std_logic;
m_axis_tdata_0 : out std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_1_aclk : in std_logic;
s_axis_tdata_1 : in std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_tvalid_1 : in std_logic;
s_axis_tready_1 : out std_logic;
m_axis_1_aclk : in std_logic;
m_axis_tvalid_1 : out std_logic;
m_axis_tdata_1 : out std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_2_aclk : in std_logic;
s_axis_tdata_2 : in std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_tvalid_2 : in std_logic;
s_axis_tready_2 : out std_logic;
m_axis_2_aclk : in std_logic;
m_axis_tvalid_2 : out std_logic;
m_axis_tdata_2 : out std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_3_aclk : in std_logic;
s_axis_tdata_3 : in std_logic_vector(kAxiWidth - 1 downto 0);
s_axis_tvalid_3 : in std_logic;
s_axis_tready_3 : out std_logic;
m_axis_3_aclk : in std_logic;
m_axis_tvalid_3 : out std_logic;
m_axis_tdata_3 : out std_logic_vector(kAxiWidth - 1 downto 0)
);
end entity;
architecture RTL of gpio_to_axis_mux is
begin
axis_mux0: entity work.axis_mux (RTL)
generic map (
kGpioWidth => kGpioWidth,
kAxiWidth => kAxiWidth)
port map (
gpio => gpio,
mux_select => mux_select(0),
s_axis_aclk => s_axis_0_aclk,
s_axis_tdata => s_axis_tdata_0,
s_axis_tvalid => s_axis_tvalid_0,
s_axis_tready => s_axis_tready_0,
m_axis_aclk => m_axis_0_aclk,
m_axis_tvalid => m_axis_tvalid_0,
m_axis_tdata => m_axis_tdata_0
);
axis_mux1: entity work.axis_mux (RTL)
generic map (
kGpioWidth => kGpioWidth,
kAxiWidth => kAxiWidth)
port map (
gpio => gpio,
mux_select => mux_select(1),
s_axis_aclk => s_axis_1_aclk,
s_axis_tdata => s_axis_tdata_1,
s_axis_tvalid => s_axis_tvalid_1,
s_axis_tready => s_axis_tready_1,
m_axis_aclk => m_axis_1_aclk,
m_axis_tvalid => m_axis_tvalid_1,
m_axis_tdata => m_axis_tdata_1
);
axis_mux2: entity work.axis_mux (RTL)
generic map (
kGpioWidth => kGpioWidth,
kAxiWidth => kAxiWidth)
port map (
gpio => gpio,
mux_select => mux_select(4),
s_axis_aclk => s_axis_2_aclk,
s_axis_tdata => s_axis_tdata_2,
s_axis_tvalid => s_axis_tvalid_2,
s_axis_tready => s_axis_tready_2,
m_axis_aclk => m_axis_2_aclk,
m_axis_tvalid => m_axis_tvalid_2,
m_axis_tdata => m_axis_tdata_2
);
axis_mux3: entity work.axis_mux (RTL)
generic map (
kGpioWidth => kGpioWidth,
kAxiWidth => kAxiWidth)
port map (
gpio => gpio,
mux_select => mux_select(5),
s_axis_aclk => s_axis_3_aclk,
s_axis_tdata => s_axis_tdata_3,
s_axis_tvalid => s_axis_tvalid_3,
s_axis_tready => s_axis_tready_3,
m_axis_aclk => m_axis_3_aclk,
m_axis_tvalid => m_axis_tvalid_3,
m_axis_tdata => m_axis_tdata_3
);
end RTL;
+228
View File
@@ -0,0 +1,228 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: rf_nco_reset
--
-- Description:
--
-- This entity has the logic needed to synchronously reset the NCO inside the
-- RF section.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity rf_nco_reset is
port(
-- AXI-lite clock used for RFDC configuration.
ConfigClk : in std_logic;
-- Radio clock used in the converter data path.
DataClk : in std_logic;
-- PL SYSREF
dSysref : in std_logic;
--Strobe dNcoResetEn for one DataClk cycle to initiate NCO reset.
dStartNcoReset : in std_logic;
---------------------------------------------------------------------------
-- NCO reset controls and status
---------------------------------------------------------------------------
-- Port naming convention:
-- cDac<Tile Number><Converter Number><signal name>
-- cAdc<Tile Number><Converter Number><signal name>
-----------------------------------
-- DAC Tile 228
-----------------------------------
-- DAC common NCO update controls and status.
cDac0xNcoUpdateBusy : in std_logic_vector(1 downto 0);
cDac0xNcoUpdateReq : out std_logic := '0';
cDac0xSysrefIntGating : out std_logic := '0';
cDac0xSysrefIntReenable : out std_logic := '0';
-----------------------------------
-- DAC Tile 229
-----------------------------------
-- DAC common NCO update controls and status.
cDac1xNcoUpdateBusy : in std_logic;
cDac1xNcoUpdateReq : out std_logic := '0';
-----------------------------------
--ADC Tile 224
-----------------------------------
-- ADC common NCO update controls and status.
cAdc0xNcoUpdateBusy : in std_logic;
cAdc0xNcoUpdateReq : out std_logic := '0';
-----------------------------------
--ADC Tile 226
-----------------------------------
-- ADC common NCO update controls and status.
cAdc2xNcoUpdateBusy : in std_logic;
cAdc2xNcoUpdateReq : out std_logic := '0';
-- NCO reset can be initiated only when cNcoPhaseRst is set to '1' and
-- cNcoUpdateEn = 0x20. The FSM in this entity will set these values when
-- an NCO reset is initiated during synchronization. These ports are common
-- for all the converters. So, we will fan these signals out to each
-- converter outside this entity.
cNcoPhaseRst : out std_logic := '1';
cNcoUpdateEn : out std_logic_vector(5 downto 0) := "100000";
-- NCO reset status back to the user.
dNcoResetDone : out std_logic := '0'
);
end rf_nco_reset;
architecture RTL of rf_nco_reset is
-- State machine to sequence NCO reset across different RFDC tiles.
type ResetState_t is (Idle, ReqGating, CheckGating, CheckUpdateDone,
CheckResetDone, ResetDone);
signal cResetState : ResetState_t := Idle;
signal dNcoResetDone_ms, cNcoResetDone : std_logic := '0';
signal dStartNcoResetReg, cStartNcoReset_ms, cStartNcoReset : std_logic := '0';
signal cSysref_ms, cSysref, cSysrefDlyd : std_logic := '0';
signal cSysrefIntGating, dSysrefIntGating_ms,
dSysrefIntGating : std_logic := '0';
begin
-- NCO start signal from the user is a one DataClk cycle strobe. In this
-- process, we register the NCO start request from the user. This NCO start
-- request register is cleared after the NCO reset sequence is initiated. We
-- used the signal used to gate SYSREF to clear this register.
RegNcoStart: process(DataClk)
begin
if rising_edge(DataClk) then
dSysrefIntGating_ms <= cSysrefIntGating;
dSysrefIntGating <= dSysrefIntGating_ms;
if dSysrefIntGating = '1' then
dStartNcoResetReg <= '0';
elsif dStartNcoReset = '1' then
dStartNcoResetReg <= '1';
end if;
end if;
end process RegNcoStart;
-- Irrespective of when NCO reset strobe is issued by the user, we need to
-- initiate NCO reset only on the rising edge of SYSREF. This is because, we
-- have to complete the reset within a SYSREF period.
ConfigClkCross: process(ConfigClk)
begin
if rising_edge(ConfigClk) then
cSysref_ms <= dSysref;
cSysref <= cSysref_ms;
cSysrefDlyd <= cSysref;
cStartNcoReset_ms <= dStartNcoResetReg;
cStartNcoReset <= cStartNcoReset_ms;
end if;
end process ConfigClkCross;
-- These signals can be set to a constant value as NCO phase reset is only
-- initiated by *NcoUpdateReq signal.
cNcoPhaseRst <= '1';
cNcoUpdateEn <= "100000";
-- ! STATE MACHINE STARTUP !
-- The state machine starts in Idle state and does not change state until
-- cStartNcoReset is set to '1'. cStartNcoReset signal and cSysref are based
-- of ConfigClock so changing state from Idle cannot go metastable. State
-- machine to initiate NCO reset on all enabled RFDC tiles. This state
-- machine was written based of the information provided in "NCO frequency
-- hopping" section in PG269 (v2.2). We use multi-mode for NCO reset.
ResetFsm: process(ConfigClk)
begin
if rising_edge(ConfigClk) then
cResetState <= Idle;
cNcoResetDone <= '0';
cDac0xNcoUpdateReq <= '0';
cSysrefIntGating <= '0';
cDac0xSysrefIntReenable <= '0';
cDac1xNcoUpdateReq <= '0';
cAdc0xNcoUpdateReq <= '0';
cAdc2xNcoUpdateReq <= '0';
case cResetState is
-- Stay in this state until NCO reset sequence is initiated. NCO reset
-- is initiated only on the rising edge of SYSREF.
when Idle =>
if cSysref = '1' and cSysrefDlyd = '0' and cStartNcoReset = '1' then
cResetState <= ReqGating;
cSysrefIntGating <= '1';
end if;
-- When NCO reset is initiated, gate the RFDC internal SYSREF. To gate
-- internal SYSREF set cSysrefIntGating to '1'. To request NCO reset
-- strobe cDac0xNcoUpdateReq for one ConfigClk period. At this point,
-- we can only request NCO reset for RF-DAC tile 228.
when ReqGating =>
cResetState <= CheckGating;
cDac0xNcoUpdateReq <= '1';
cSysrefIntGating <= '1';
-- Since we are gating SYSREF inside RFDC, we need to wait until SYSREF
-- is gated internally. RFDC sets cDac0xNcoUpdateBusy[0] to '1' when
-- SYSREF is gated. cDac0xNcoUpdateBusy[1] is also set to '1' to
-- indicate that NCO reset is still in progress. After the SYSREF is
-- gated request NCO reset on all other converter tiles.
when CheckGating =>
cSysrefIntGating <= '1';
cResetState <= CheckGating;
if cDac0xNcoUpdateBusy = "11" then
cResetState <= CheckUpdateDone;
cDac1xNcoUpdateReq <= '1';
cAdc0xNcoUpdateReq <= '1';
cAdc2xNcoUpdateReq <= '1';
end if;
-- In this state, we check if the RFDC block is ready for NCO reset.
-- This check is done using the *Busy signal from RFDC. Once RFDC is
-- ready for NCO reset, disable internal SYSREF gating.
when CheckUpdateDone =>
cSysrefIntGating <= '1';
cResetState <= CheckUpdateDone;
if cDac0xNcoUpdateBusy = "10" and cAdc0xNcoUpdateBusy = '0' and
cAdc2xNcoUpdateBusy = '0' and cDac1xNcoUpdateBusy = '0' and
cSysref = '1' and cSysrefDlyd = '0' then
cDac0xSysrefIntReenable <= '1';
cResetState <= CheckResetDone;
end if;
-- NCO reset is done when cDac0xNcoUpdateBusy[1] is set to '0'. RFDC is
-- programmed from software to reset the NCO on a SYSREF rising edge.
when CheckResetDone =>
cSysrefIntGating <= '1';
cResetState <= CheckResetDone;
if cDac0xNcoUpdateBusy = "00" then
cResetState <= ResetDone;
end if;
-- Wait in this state until another NCO reset request is issued.
when ResetDone =>
cNcoResetDone <= '1';
cResetState <= ResetDone;
if cSysref = '1' and cSysrefDlyd = '0' and cStartNcoReset = '1' then
cResetState <= ReqGating;
cSysrefIntGating <= '1';
end if;
end case;
end if;
end process ResetFsm;
cDac0xSysrefIntGating <= cSysrefIntGating;
-- Move the NCO reset done status to DataClk domain.
DataClkCrossing: process(DataClk)
begin
if rising_edge(DataClk) then
dNcoResetDone_ms <= cNcoResetDone;
dNcoResetDone <= dNcoResetDone_ms;
end if;
end process DataClkCrossing;
end RTL;
+216
View File
@@ -0,0 +1,216 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: rf_reset
--
-- Description:
--
-- Control RFDC, ADC, and DAC resets.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity rf_reset is
port(
-- Clocks used in the data path.
DataClk : in std_logic;
PllRefClk : in std_logic;
RfClk : in std_logic;
RfClk2x : in std_logic;
DataClk2x : in std_logic;
-- Master resets from the Radio.
dTimedReset : in std_logic;
dSwReset : in std_logic;
-- Resets outputs.
dReset_n : out std_logic := '0';
d2Reset_n : out std_logic := '0';
r2Reset_n : out std_logic := '0';
rAxiReset_n : out std_logic := '0';
rReset_n : out std_logic := '0'
);
end rf_reset;
architecture RTL of rf_reset is
-- POR value for all resets are active high or low.
signal dResetPulseDly : std_logic_vector(2 downto 0) := "111";
signal dResetPulseStretch : std_logic := '1';
signal pResetPulseStretch : std_logic_vector(1 downto 0) := "11";
signal pResetPulse_n : std_logic := '0';
signal pAxiReset_n : std_logic := '0';
begin
-----------------------------------------------------------------------------
-- Clock Phase Diagram
-----------------------------------------------------------------------------
-- Before we look into the details of the clock alignment, here is the clock
-- frequencies of all the synchronous clocks that is used in the design.
-- PllRefClk is the reference clock for the FPGA PLL and all other clocks are
-- derived from PllRefClk. PllRefClk for X410 is ~62.5 MHz
-- PllRefClk = ~62.5 MHz (Sample clock/48. This is the X410 configuration and
-- could be different for other x4xx variants.)
-- DataClk = PllRefClk*2
-- DataClkx2 = PllRefClk*4
-- RfClk = PllRefClk*3
-- RfClkx2 = PllRefClk*6
-- DataClk = PllRefClk*4 for legacy mode. In legacy mode, we will not use
-- DataClkx2 as the clock frequency will be too high to close timing.
-- Five clocks with five different frequencies, all related and occasionally
-- aligned. Rising edge of all clocks are aligned to the rising edge of
-- PllRefClk. We will use the rising edge of PllRefClk as the reference to
-- assert synchronous reset for all clock domains. The synchronous reset
-- pulse is in the DataClk domain. As we can see from the timing diagram, the
-- DataClk rising edge is not always aligned to the rising edge of all the
-- other clocks. But, it is guaranteed that the DataClk will be aligned to
-- all the other clock on the rising edge of PLL reference clock. In case 1,
-- the synchronous reset pulse is on the DataClk edge where the data clock is
-- not aligned to RfClk. We stretch the pulse from DataClk domain and send
-- the reset out on the rising edge of PllRefClk where all the clocks rising
-- edge is aligned. In case 2, the synchronous reset is received on the
-- DataClk cycle where all the clocks are aligned. This is because, in
-- case 2, the synchronous reset is received on the rising edge of PllRefClk.
-- For case 1 and case 2, all the output resets are asserted only on the
-- PllRefClk rising edge to guarantee a known relationship between the resets
-- in different clock domains.
--
-- Alignment * * *
-- ___________ ___________ ___________ ___________ ___________
-- PllRefClk __| |___________| |___________| |___________| |___________| |
-- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-- RfClk2x __| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_
-- ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
-- RfClk __| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___|
-- __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
-- DataClk2x __| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__|
-- _____ _____ _____ _____ _____ _____ _____ _____ _____
-- DataClk __| |_____| |_____| |_____| |_____| |_____| |_____| |_____| |_____| |_____|
-- . : : : :
-- --------- Case 1 ---------.-- : : : :
-- ^ : : ^ :
-- Reset Strobe --> | : Aligned reset strobe -->| :
-- ____________ : : : :
-- dResetPulse________| |______________________________________ : :
-- : _____________________________________________________________________________
-- dResetPulseStretch ______________________| :
-- : ________________________________________________
-- pResetPulseStretch ____________________________________________| : : |___
-- _________________________________________________________________________ :
-- pResetPulse_n : |________________________________
-- : : : :
-- --------- Case 2 ----------- : : : :
-- ^ : ^ :
-- Reset Strobe --> | : | <-- Aligned reset strobe
-- ____________ : : :
-- dResetPulse(0) ________| |______________________________________________________________________________
-- _______________________________________________________________________________
-- dResetPulseStretch ______________________| :
-- ________________________________________________________
-- pResetPulseStretch ____________________________________________| :
-- _________________________________________________________________________
-- pResetPulse_n |________________________________
-- --------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Implementation
-----------------------------------------------------------------------------
-- Since the dTimedReset is asserted only for one DataClk cycle, we need to
-- stretch the strobe to four DataClk cycles, so the strobe is wide enough to
-- be sampled by PllRefClk which is four times the DataClk period. Pulse
-- stretch is done for 4 DataClk periods to support the legacy mode. We also
-- do a logical OR on resets from software. Software resets are from the
-- ConfigClock domain which is a slower clock than the PllRefClk. So, we
-- don't have to stretch the software reset.
PulseStretch: process(DataClk)
begin
if rising_edge(DataClk) then
dResetPulseDly <= dResetPulseDly(1 downto 0) & (dTimedReset or dSwReset);
dResetPulseStretch <= '0';
if (dResetPulseDly /= "000") or dTimedReset = '1' or dSwReset = '1' then
dResetPulseStretch <= '1';
end if;
end if;
end process PulseStretch;
-- Strobe reset pulse for 2 PllRefClk period to make sure we have the reset
-- asserted for longer period. The FIR filter is the only design that
-- requires reset to be asserted for 2 clock cycles. This requirement is
-- satisfied with one PllRefClk period. RFDC does not have any AXI stream
-- reset time requirement. We will reset all designs for two PllRefClk period
-- just to be on the safer side. The same strategy is used for DAC resets as
-- well.
ResetOut: process(PllRefClk)
begin
if rising_edge(PllRefClk) then
pResetPulseStretch <= pResetPulseStretch(0) & dResetPulseStretch;
pResetPulse_n <= not (pResetPulseStretch(1) or pResetPulseStretch(0));
end if;
end process ResetOut;
-- We are using PllRefClk as the reference and issuing resets to all the
-- other clock domains. We are not trying to align all the resets in
-- different clock domains. We are making sure that all resets will be
-- asserted with respect to each other at the same time from run to run.
DataClkReset: process(DataClk)
begin
if rising_edge(DataClk) then
dReset_n <= pResetPulse_n;
end if;
end process DataClkReset;
DataClk2xReset: process(DataClk2x)
begin
if rising_edge(DataClk2x) then
d2Reset_n <= pResetPulse_n;
end if;
end process DataClk2xReset;
Rfclk2xReset: process(RfClk2x)
begin
if rising_edge(RfClk2x) then
r2Reset_n <= pResetPulse_n;
end if;
end process Rfclk2xReset;
RfclkReset: process(RfClk)
begin
if rising_edge(RfClk) then
rReset_n <= pResetPulse_n;
end if;
end process RfclkReset;
-------------------------------------
-- RF Resets
-------------------------------------
-- RFDC resets are asserted only once and it should be done using the reset
-- from software. This is because we want the RFDC AXI-S interface in reset
-- until the RfClk is stable. The only way to know if the RfClk is stable is
-- by reading the lock status of sample clock PLL and MMCM used to generate
-- all clocks in the signal path. dSwReset is a software reset while is
-- asserted for a longer period of time and it does not require any pulse
-- stretch.
RfdcReset: process(PllRefClk)
begin
if rising_edge(PllRefClk) then
pAxiReset_n <= not dSwReset;
end if;
end process RfdcReset;
RfclkAxiReset: process(RfClk)
begin
if rising_edge(RfClk) then
rAxiReset_n <= pAxiReset_n;
end if;
end process RfclkAxiReset;
end RTL;
+208
View File
@@ -0,0 +1,208 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: rf_reset_controller
--
-- Description:
--
-- Control RFDC, ADC, and DAC resets.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library WORK;
use WORK.PkgRFDC_REGS_REGMAP.all;
entity rf_reset_controller is
port(
-- Clocks
-- Config clock is async to all the others.
ConfigClk : in std_logic;
DataClk : in std_logic;
PllRefClk : in std_logic;
RfClk : in std_logic;
RfClk2x : in std_logic;
DataClk2x : in std_logic;
-- Master resets from the Radio
dAdcResetPulse : in std_logic;
dDacResetPulse : in std_logic;
-- ADC Resets
dAdcDataOutReset_n : out std_logic;
r2AdcFirReset_n : out std_logic;
rAdcRfdcAxiReset_n : out std_logic;
rAdcEnableData : out std_logic;
rAdcGearboxReset_n : out std_logic;
-- DAC Resets
dDacDataInReset_n : out std_logic;
r2DacFirReset_n : out std_logic;
d2DacFirReset_n : out std_logic;
rDacRfdcAxiReset_n : out std_logic;
rDacGearboxReset_n : out std_logic;
-- SW Control and Status
-- Control to initiate resets to RFDC and decimation block including the
-- gearboxes. 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 rf_reset_controller;
architecture RTL of 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 dTriggerAdcReset_ms : std_logic := '1';
signal dTriggerAdcReset : std_logic := '1';
signal dTriggerDacReset_ms : std_logic := '1';
signal dTriggerDacReset : 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 dTriggerAdcReset : signal is "TRUE";
attribute ASYNC_REG of dTriggerDacReset : signal is "TRUE";
attribute ASYNC_REG of cTriggerAdcResetDone : signal is "TRUE";
attribute ASYNC_REG of cTriggerDacResetDone : signal is "TRUE";
attribute ASYNC_REG of dTriggerAdcReset_ms : signal is "TRUE";
attribute ASYNC_REG of dTriggerDacReset_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.
-----------------------------------------------------------------------------
SeqResetDataClk : process(DataClk)
begin
if rising_edge(DataClk) then
-- double-syncs have no sync reset!
dTriggerAdcReset_ms <= cTriggerAdcReset;
dTriggerAdcReset <= dTriggerAdcReset_ms;
dTriggerDacReset_ms <= cTriggerDacReset;
dTriggerDacReset <= dTriggerDacReset_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 DataClk domain and move it
-- back to ConfigClk domain. This just proves that DataClk is toggling and
-- the reset requested by software is sampled in the DataClk.
-----------------------------------------------------------------------------
SeqResetDone : process(ConfigClk)
begin
if rising_edge(ConfigClk) then
-- double-syncs have no sync reset!
cTriggerAdcResetDone_ms <= dTriggerAdcReset;
cTriggerAdcResetDone <= cTriggerAdcResetDone_ms;
cTriggerDacResetDone_ms <= dTriggerDacReset;
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
-----------------------------------------------------------------------------
AdcResets: entity work.rf_reset (RTL)
port map (
DataClk => DataClk,
PllRefClk => PllRefClk,
RfClk => RfClk,
RfClk2x => RfClk2x,
DataClk2x => DataClk2x,
dTimedReset => dAdcResetPulse,
dSwReset => dTriggerAdcReset,
dReset_n => dAdcDataOutReset_n,
d2Reset_n => open,
r2Reset_n => r2AdcFirReset_n,
rAxiReset_n => rAdcRfdcAxiReset_n,
rReset_n => rAdcGearboxReset_n
);
DacResets: entity work.rf_reset (RTL)
port map (
DataClk => DataClk,
PllRefClk => PllRefClk,
RfClk => RfClk,
RfClk2x => RfClk2x,
DataClk2x => DataClk2x,
dTimedReset => dDacResetPulse,
dSwReset => dTriggerDacReset,
dReset_n => dDacDataInReset_n,
d2Reset_n => d2DacFirReset_n,
r2Reset_n => r2DacFirReset_n,
rAxiReset_n => rDacRfdcAxiReset_n,
rReset_n => rDacGearboxReset_n
);
end RTL;
+51
View File
@@ -0,0 +1,51 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: scale_2x
--
-- Description:
--
-- This block does the scaling of IQ data by 2. The data from the mixer is
-- 1/2 the full scale and the upper two bits will only have the signed bits,
-- so it is okay to multiply the data by 2 and resize it back to 16 bits.
--
-- Parameters:
--
-- kDataWidth: Data width, should be a multiple of 16 bits.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library WORK;
use WORK.PkgRf.all;
entity scale_2x is
generic(
kDataWidth : integer range 1 to 256 := 32
);
port(
-- [..Q1,I1,Q0,I0] (I in LSBs). Each I and Q data is 16 bits wide, but
-- since the data is only 1/2 full scale. Useful information is only
-- in the lower 15 bits, with upper two bits used as a signed bit.
cDataIn : in std_logic_vector(kDataWidth-1 downto 0);
cDataValidIn : in std_logic;
-- [..Q1,I1,Q0,I0] (I in LSBs). 16 bit output with a gain of 2x.
cDataOut : out std_logic_vector(kDataWidth-1 downto 0);
cDataValidOut : out std_logic
);
end scale_2x;
architecture RTL of scale_2x is
begin
-- Scale the date by 2 by shifting the data to the left by 1 bit.
cDataOut <= Gain2x(cDataIn);
cDataValidOut <= cDataValidIn;
end RTL;
+43
View File
@@ -0,0 +1,43 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: sync_wrapper
//
// Description:
//
// As the original synchronizer component has port signal names that are
// incompatible with VHDL (in, out), this modules provides an an interface to
// instantiate the synchronizer block in VHDL.
//
`default_nettype none
module sync_wrapper #(
parameter WIDTH = 1,
parameter STAGES = 2,
parameter INITIAL_VAL = 0,
parameter FALSE_PATH_TO_IN = 1
)(
input wire clk,
input wire rst,
input wire [WIDTH-1:0] signal_in,
output wire [WIDTH-1:0] signal_out
);
synchronizer #(
.WIDTH (WIDTH),
.STAGES (STAGES),
.INITIAL_VAL (INITIAL_VAL),
.FALSE_PATH_TO_IN (FALSE_PATH_TO_IN)
) synchronizer_i (
.clk (clk),
.rst (rst),
.in (signal_in),
.out (signal_out)
);
endmodule //sync_wrapper
`default_nettype wire