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:
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
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user