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
+114
View File
@@ -0,0 +1,114 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: adc_3_1_clk_converter
--
-- Description:
--
-- This module transfers data from s_axis_aclk to m_axis_aclk. s_axis_aclk
-- must be three times the frequency of m_axis_aclk, and the two clocks must
-- be related (this module requires timing closure across the clock domain
-- boundary).
--
library IEEE;
use IEEE.std_logic_1164.all;
entity adc_3_1_clk_converter is
port(
s_axis_clk : in std_logic;
s_axis_resetn : in std_logic;
s_axis_tdata : in std_logic_vector(47 downto 0);
s_axis_tvalid : in std_logic;
m_axis_clk : in std_logic;
m_axis_resetn : in std_logic;
m_axis_tvalid : out std_logic;
m_axis_tdata : out std_logic_vector(47 downto 0)
);
end entity;
architecture RTL of adc_3_1_clk_converter is
-- To keep the implementation simple, this module does not implement a
-- correct AXIS handshake - it ignores m_axis_tready. adc_100m_bd already had
-- an assumption that the AXIS handshake is unneeded: ddc_saturate does not
-- accept _tready from the following component.
subtype Word_t is std_logic_vector(s_axis_tdata'range);
signal s_axis_tvalid_pipe : std_logic_vector(2 downto 0);
signal s_axis_tdata_reg : Word_t;
-- These _CDC signals will be sampled in the destination clock domain, but
-- will not produce any metastability because the input clocks must be
-- synchronous.
--
-- These signals must be driven by registers not to prevent glitches (as in
-- an asynchronous CDC), but to improve timing closure.
signal s_axis_tvalid_CDC : std_logic;
signal s_axis_tdata_CDC : Word_t;
-- m_axis_clk and s_axis_clk are nominally aligned by their rising edges.
-- Because m_axis_clk is more heavily loaded than s_axis_clk, m_axis_clk has
-- a larger distribution delay, which causes a large hold violation using
-- post-place timing estimates. The Ultrafast method (UG 949) recommends
-- addressing such hold violations when WHS < -0.5 ns. By resampling on the
-- falling edge of the destination clock, we get nominally half a period of
-- setup and half a period of hold. The destination clock delay reduces the
-- hold margin, and increases the setup margin.
signal m_axis_tvalid_fall : std_logic;
signal m_axis_tdata_fall : Word_t;
begin
-- In the source clock domain, we capture incoming valid data and keep a
-- history of _tvalid over the last three clock cycles. If s_axis_tvalid has
-- been asserted once in the last three clock cycles, assert
-- s_axis_tvalid_CDC to be sampled in the output clock domain. The length of
-- s_axis_tvalid_pipe must match the ratio of the clock frequencies (3:1).
InputSampling:
process (s_axis_clk) is
begin
if rising_edge(s_axis_clk) then
if s_axis_tvalid='1' then
s_axis_tdata_reg <= s_axis_tdata;
end if;
s_axis_tdata_CDC <= s_axis_tdata_reg;
if s_axis_resetn='0' then
s_axis_tvalid_pipe <= (others => '0');
s_axis_tvalid_CDC <= '0';
else
s_axis_tvalid_pipe <= s_axis_tvalid_pipe(1 downto 0) & s_axis_tvalid;
if (s_axis_tvalid_pipe /= "000") then
s_axis_tvalid_CDC <= '1';
else
s_axis_tvalid_CDC <= '0';
end if;
end if;
end if;
end process InputSampling;
FallingEdgeSampling:
process (m_axis_clk) is
begin
if falling_edge(m_axis_clk) then
m_axis_tvalid_fall <= s_axis_tvalid_CDC;
m_axis_tdata_fall <= s_axis_tdata_CDC;
end if;
end process FallingEdgeSampling;
OutputRegisters:
process (m_axis_clk) is
begin
if rising_edge(m_axis_clk) then
m_axis_tdata <= m_axis_tdata_fall;
if m_axis_resetn='0' then
m_axis_tvalid <= '0';
else
m_axis_tvalid <= m_axis_tvalid_fall;
end if;
end if;
end process OutputRegisters;
end RTL;
+120
View File
@@ -0,0 +1,120 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: adc_gearbox_2x1
//
// Description:
//
// Gearbox ADC data from 2 SPC to 1 SPC and corresponding 2x clock to 1x
// clock. Also implement data swapping to format packets to fit the FIR
// filter input requirements.
//
// This modules incurs one clk1x cycle of delay on the data and valid signals
// from input on the 1x domain to output on the 2x domain.
//
`default_nettype none
module adc_gearbox_2x1 (
input wire clk1x,
input wire reset_n_1x,
// Data is _presumed_ to be packed [Sample1, Sample0] (Sample0 in LSBs).
input wire [31:0] adc_q_in_1x,
input wire [31:0] adc_i_in_1x,
input wire valid_in_1x,
// De-assert enable_1x to clear the data synchronously from this module.
input wire enable_1x,
input wire clk2x,
// Data is packed [Q,I] (I in LSBs) when swap_iq_1x is '0'.
input wire swap_iq_2x,
output wire [31:0] adc_out_2x,
output wire valid_out_2x
);
// Re-create the 1x clock in the 2x domain to produce a deterministic
// crossing.
reg toggle_1x, toggle_2x = 1'b0, toggle_2x_dly = 1'b0, valid_2x = 1'b0, valid_dly_2x = 1'b0;
reg [31:0] data_out_2x = 32'b0, adc_q_data_in_2x = 32'b0, adc_i_data_in_2x = 32'b0;
// Create a toggle in the 1x clock domain (clock divider /2).
always @(posedge clk1x or negedge reset_n_1x) begin
if ( ! reset_n_1x) begin
toggle_1x <= 1'b0;
end else begin
toggle_1x <= ! toggle_1x;
end
end
// clk1x and clk2x are nominally aligned on their rising edges, but clk2x is
// more heavily loaded, which results in a later arrival time. That late
// arrival causes large estimated hold violations after place. The Ultrafast
// method (UG 949) suggests fixing post-place hold violations that are worse
// than -0.5 ns.
// Resampling 1x signals on the falling edge of clk2x provides nominally half
// a period of setup and half a period of hold. The late arrival of clk2x
// shifts some of that margin away from hold slack and into setup slack.
reg toggle_2x_fall = 1'b0;
reg [31:0] adc_q_in_2x_fall = 32'b0;
reg [31:0] adc_i_in_2x_fall = 32'b0;
reg valid_in_2x_fall = 1'b0;
reg enable_2x_fall = 1'b0;
always @(negedge clk2x) begin
toggle_2x_fall <= toggle_1x;
adc_q_in_2x_fall <= adc_q_in_1x;
adc_i_in_2x_fall <= adc_i_in_1x;
valid_in_2x_fall <= valid_in_1x;
enable_2x_fall <= enable_1x;
end
// Transfer the toggle from the 1x to the 2x domain. Delay the toggle in the
// 2x domain by one cycle and compare it to the non-delayed version. When
// they differ, push data_in[15:0] onto the output. When the match, push
// [31:16] onto the output. The datasheet is unclear on the exact
// implementation.
//
// It is safe to not reset this domain because all of the input signals will
// be cleared by the 1x reset. Safe default values are assigned to all these
// registers.
always @(posedge clk2x) begin
toggle_2x <= toggle_2x_fall;
toggle_2x_dly <= toggle_2x;
adc_q_data_in_2x <= adc_q_in_2x_fall;
adc_i_data_in_2x <= adc_i_in_2x_fall;
// Place Q in the MSBs, I in the LSBs by default, unless swapped = 1.
if (valid_2x) begin
if (swap_iq_2x) begin
if (toggle_2x != toggle_2x_dly) begin
data_out_2x[31:16] <= adc_i_data_in_2x[15:0];
data_out_2x[15: 0] <= adc_q_data_in_2x[15:0];
end else begin
data_out_2x[31:16] <= adc_i_data_in_2x[31:16];
data_out_2x[15: 0] <= adc_q_data_in_2x[31:16];
end
end else begin
if (toggle_2x != toggle_2x_dly) begin
data_out_2x[31:16] <= adc_q_data_in_2x[15:0];
data_out_2x[15: 0] <= adc_i_data_in_2x[15:0];
end else begin
data_out_2x[31:16] <= adc_q_data_in_2x[31:16];
data_out_2x[15: 0] <= adc_i_data_in_2x[31:16];
end
end
end else begin
data_out_2x <= 32'b0;
end
// Valid is simply a transferred version of the 1x clock's valid. Delay it
// one more cycle to align outputs.
valid_2x <= valid_in_2x_fall && enable_2x_fall;
valid_dly_2x <= valid_2x;
end
assign adc_out_2x = data_out_2x;
assign valid_out_2x = valid_dly_2x;
endmodule
`default_nettype wire
+143
View File
@@ -0,0 +1,143 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_1_3_clk_converter
--
-- Description:
--
-- This module transfers data from s_axis_aclk to m_axis_aclk. m_axis_aclk
-- must be three times the frequency of s_axis_aclk, and the two clocks must
-- be related (this module requires timing closure across the clock domain
-- boundary).
--
library IEEE;
use IEEE.std_logic_1164.all;
entity dac_1_3_clk_converter is
port(
s_axis_aclk : in std_logic;
s_axis_aresetn : in std_logic;
s_axis_tvalid : in std_logic;
s_axis_tdata : in std_logic_vector(31 downto 0);
s_axis_tready : out std_logic := '1';
m_axis_aclk : in std_logic;
m_axis_aresetn : in std_logic;
m_axis_tready : in std_logic;
m_axis_tdata : out std_logic_vector(31 downto 0);
m_axis_tvalid : out std_logic
);
end entity dac_1_3_clk_converter;
architecture RTL of dac_1_3_clk_converter is
-- I was unable to think of a simple implementation that implements a correct
-- AXIS handshake on both ports. All my ideas became equivalent to a two
-- clock FIFO (although the clocks are synchronous, so the write-to-read
-- latency would have been certain).
--
-- We don't expect the DAC to ever hold off incoming data, and dac_100m_bd
-- already has the AXIS handshake disconnected: the FIR is configured to
-- disallow back pressure - it has no m_axis_data_tready pin.
--
-- I'm going with the simple, but not strictly correct, implementation.
-- s_axis_tready will be constantly true, even when it shouldn't be. The
-- bottom line is this component is likely useless for any application but
-- dac_100m_bd.
type output_fsm is (
idle,
got_data,
-- The recovery state of delay ensures that we don't re-use an old input
-- valid signal (remember the output clock is 3x the frequency of the input
-- clock)
recovery
);
subtype word is std_logic_vector(s_axis_tdata'range);
signal output_state_mclk : output_fsm;
signal axis_tdata_sclk : word;
signal axis_tvalid_sclk : std_logic;
signal axis_tdata_mclk : word;
signal axis_tvalid_mclk : std_logic;
begin
s_axis_tready <= '1';
input_valid_register:
process(s_axis_aclk, s_axis_aresetn) is
begin
if s_axis_aresetn='0' then
axis_tvalid_sclk <= '0';
elsif rising_edge(s_axis_aclk) then
axis_tvalid_sclk <= s_axis_tvalid;
end if;
end process;
input_data_register:
process (s_axis_aclk) is
begin
if rising_edge(s_axis_aclk) then
axis_tdata_sclk <= s_axis_tdata;
end if;
end process input_data_register;
-- These CDC registers will not become metastable because the two clock
-- domains are related.
cdc_input_valid_register:
process (m_axis_aclk, m_axis_aresetn) is
begin
if m_axis_aresetn='0' then
axis_tvalid_mclk <= '0';
elsif rising_edge(m_axis_aclk) then
axis_tvalid_mclk <= axis_tvalid_sclk;
end if;
end process cdc_input_valid_register;
cdc_input_data_register:
process (m_axis_aclk) is
begin
if rising_edge(m_axis_aclk) then
axis_tdata_mclk <= axis_tdata_sclk;
end if;
end process cdc_input_data_register;
output_data_register:
process (m_axis_aclk) is
begin
if rising_edge(m_axis_aclk) then
if output_state_mclk=idle then
m_axis_tdata <= axis_tdata_mclk;
end if;
end if;
end process output_data_register;
fsm: process(m_axis_aresetn, m_axis_aclk) is
begin
if m_axis_aresetn='0' then
output_state_mclk <= idle;
m_axis_tvalid <= '0';
elsif rising_edge(m_axis_aclk) then
m_axis_tvalid <= '0';
case output_state_mclk is
when idle =>
if axis_tvalid_mclk='1' then
output_state_mclk <= got_data;
end if;
when got_data =>
m_axis_tvalid <= '1';
output_state_mclk <= recovery;
when recovery =>
output_state_mclk <= idle;
end case;
end if;
end process fsm;
end RTL;
+118
View File
@@ -0,0 +1,118 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_2_1_clk_converter
--
-- Description:
--
-- This module transfers data from s_axis_aclk to m_axis_aclk. s_axis_aclk
-- must be two times the frequency of m_axis_aclk, and the two clocks must be
-- related (this module requires timing closure across the clock domain
-- boundary).
--
library IEEE;
use IEEE.std_logic_1164.all;
entity dac_2_1_clk_converter is
port (
s_axis_aclk : in std_logic;
s_axis_aresetn : in std_logic;
s_axis_tvalid : in std_logic;
s_axis_tdata : in std_logic_vector(63 downto 0);
m_axis_aclk : in std_logic;
m_axis_aresetn : in std_logic;
m_axis_tready : in std_logic;
m_axis_tvalid : out std_logic;
m_axis_tdata : out std_logic_vector(63 downto 0)
);
end entity dac_2_1_clk_converter;
architecture RTL of dac_2_1_clk_converter is
-- To keep the implementation simple, this module does not implement a
-- correct AXIS handshake - it ignores m_axis_tready. dac_100m_bd already had
-- an assumption that the AXIS handshake is unneeded: duc_saturate does not
-- accept _tready from the following component. Also, registered_dac_data has
-- never accepted _tready from dac_2_1_clk_converter, so dac_100m_bd has
-- never supported complete AXIS dataflow.
subtype Word_t is std_logic_vector(s_axis_tdata'range);
signal s_axis_tvalid_pipe : std_logic_vector(1 downto 0);
signal s_axis_tdata_reg : Word_t;
-- These _CDC signals will be sampled in the destination clock domain, but
-- will not produce any metastability because the input clocks must be
-- synchronous.
--
-- These signals must be driven by registers not to prevent glitches (as in
-- an asynchronous CDC), but to improve timing closure.
signal s_axis_tvalid_CDC : std_logic;
signal s_axis_tdata_CDC : Word_t;
-- m_axis_aclk and s_axis_aclk are nominally aligned by their rising edges.
-- Because m_axis_aclk is more heavily loaded than s_axis_aclk, m_axis_aclk
-- has a larger distribution delay, which causes a large hold violation using
-- post-place timing estimates. The Ultrafast method (UG 949) recommends
-- addressing such hold violations when WHS < -0.5 ns. By resampling on the
-- falling edge of the destination clock, we get nominally half a period of
-- setup and half a period of hold. The destination clock delay reduces the
-- hold margin, and increases the setup margin.
signal m_axis_tvalid_fall : std_logic;
signal m_axis_tdata_fall : Word_t;
begin
-- In the source clock domain, we capture incoming valid data and keep a
-- history of _tvalid over the last three clock cycles. If s_axis_tvalid has
-- been asserted once in the last three clock cycles, assert
-- s_axis_tvalid_CDC to be sampled in the output clock domain. The length of
-- s_axis_tvalid_pipe must match the ratio of the clock frequencies (2:1).
InputSampling:
process (s_axis_aclk) is
begin
if rising_edge(s_axis_aclk) then
if s_axis_tvalid='1' then
s_axis_tdata_reg <= s_axis_tdata;
end if;
s_axis_tdata_CDC <= s_axis_tdata_reg;
if s_axis_aresetn='0' then
s_axis_tvalid_pipe <= (others => '0');
s_axis_tvalid_CDC <= '0';
else
s_axis_tvalid_pipe <= s_axis_tvalid_pipe(0) & s_axis_tvalid;
if (s_axis_tvalid_pipe /= "00") then
s_axis_tvalid_CDC <= '1';
else
s_axis_tvalid_CDC <= '0';
end if;
end if;
end if;
end process InputSampling;
FallingEdgeSampling:
process (m_axis_aclk) is
begin
if falling_edge(m_axis_aclk) then
m_axis_tvalid_fall <= s_axis_tvalid_CDC;
m_axis_tdata_fall <= s_axis_tdata_CDC;
end if;
end process FallingEdgeSampling;
OutputRegisters:
process (m_axis_aclk) is
begin
if rising_edge(m_axis_aclk) then
m_axis_tdata <= m_axis_tdata_fall;
if m_axis_aresetn='0' then
m_axis_tvalid <= '0';
else
m_axis_tvalid <= m_axis_tvalid_fall;
end if;
end if;
end process OutputRegisters;
end RTL;
+83
View File
@@ -0,0 +1,83 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: ddc_saturate
--
-- Description:
--
-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
-- comments below for full description.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity ddc_saturate is
port(
Clk : in std_logic;
cDataIn : in std_logic_vector(47 downto 0); -- [Q,I] (I in LSBs)
cDataValidIn : in std_logic;
cDataOut : out std_logic_vector(31 downto 0); -- [Q,I] (I in LSBs)
cDataValidOut : out std_logic
);
end ddc_saturate;
architecture RTL of ddc_saturate is
signal cDataOutI : std_logic_vector(15 downto 0) := (others => '0');
signal cDataOutQ : std_logic_vector(15 downto 0) := (others => '0');
begin
-----------------------------------------------------------------------------
-- Saturation
--
-- The output of the Xilinx FIR Compiler has already been rounded on the LSB
-- side, but hasn't been saturated on the MSB side.
-- Coefficients = 18 bit, 1 integer bit (1.17)
-- Data In = 16 bits, 1 integer bit (1.15)
-- 1.17 * 1.15 = 2.32, and the Xilinx FIR core rounds to 2.15
-- Data Out = 17 bits, 2 integer bits (2.15), with 17 LSBs already rounded
-- off inside the FIR core.
-- We need to manually saturate the 2.15 number back to a 1.15 number
--
-- 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
-----------------------------------------------------------------------------
Saturation:
process(Clk)
begin
if rising_edge(Clk) then
-- Pipeline data valid to match the data
cDataValidOut <= cDataValidIn;
-- I, from cDataIn(16 downto 0)
if cDataIn(16 downto 15) = "01" then
cDataOutI <= "0111111111111111";
elsif cDataIn(16 downto 15) = "10" then
cDataOutI <= "1000000000000000";
else
cDataOutI <= cDataIn(15 downto 0);
end if;
-- Q, from cDataIn(40 downto 24)
if cDataIn(40 downto 39) = "01" then
cDataOutQ <= "0111111111111111";
elsif cDataIn(40 downto 39) = "10" then
cDataOutQ <= "1000000000000000";
else
cDataOutQ <= cDataIn(39 downto 24);
end if;
end if;
end process Saturation;
cDataOut <= cDataOutQ & cDataOutI;
end RTL;
+87
View File
@@ -0,0 +1,87 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: duc_saturate
--
-- Description:
--
-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
-- comments below for full description.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity duc_saturate is
port(
Clk : in std_logic;
cDataIn : in std_logic_vector(47 downto 0);
cDataValidIn : in std_logic;
cReadyForInput : out std_logic;
cDataOut : out std_logic_vector(31 downto 0);
cDataValidOut : out std_logic := '0'
);
end duc_saturate;
architecture RTL of duc_saturate is
signal cDataOutI : std_logic_vector(15 downto 0) := (others => '0');
signal cDataOutQ : std_logic_vector(15 downto 0) := (others => '0');
begin
-----------------------------------------------------------------------------
-- Saturation
--
-- The output of the Xilinx FIR Compiler has already been rounded on the LSB
-- side, but hasn't been saturated on the MSB side.
-- Coefficients = 18 bit, 1 integer bit (1.17)
-- Data In = 16 bits, 1 integer bit (1.15)
-- Xilinx FIR core rounds to output to 3.31. The filter coefficients has a
-- gain of 3 to compensate for the amplitude loss in interpolation, the
-- Xilinx FIR core rounds the output to 3.15.
-- Data Out = 18 bits, 3 integer bits (3.15), with 16 LSBs already rounded
-- off inside the FIR core.
-- We need to manually saturate the 3.15 number back to a 1.15 number
--
-- 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
-----------------------------------------------------------------------------
Saturation:
process(Clk)
begin
if rising_edge(Clk) then
-- Pipeline data valid to match the data
cDataValidOut <= cDataValidIn;
-- I, from cDataIn(17 downto 0)
if cDataIn(17) = '0' and cDataIn(16 downto 15) /= "00" then
cDataOutI <= "0111111111111111";
elsif cDataIn(17) = '1' and cDataIn(16) /= cDataIn(15) then
cDataOutI <= "1000000000000000";
else
cDataOutI <= cDataIn(15 downto 0);
end if;
-- Q, from cDataIn(41 downto 24)
if cDataIn(41) = '0' and cDataIn(40 downto 39) /= "00" then
cDataOutQ <= "0111111111111111";
elsif cDataIn(41) = '1' and
(not (cDataIn(40 downto 39) = "11")) then
cDataOutQ <= "1000000000000000";
else
cDataOutQ <= cDataIn(39 downto 24);
end if;
end if;
end process Saturation;
cDataOut <= cDataOutQ & cDataOutI;
cReadyForInput <= '1';
end RTL;
+362
View File
@@ -0,0 +1,362 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rf_core_100m
//
// Description:
//
// Top-level wrapper for the ADC/DAC processing logic. One of these wrappers
// exists for every supported Data Rate. An instance of this core should
// exist per dboard.
//
// Data/RF Specs:
// DBs: 1
// RX/DB: 2
// TX/DB: 2
// Data Rate: 122.88 or 125 MSps @ 1 SPC
//
// Input Clocks, all aligned to one another and coming from same MMCM
// rfdc_clk: 184.32 or 187.5 MHz (3x pll_ref_clk)
// rfdc_clk_2x: 368.64 or 375 MHz (6x pll_ref_clk)
// data_clk: 122.88 or 125 MHz (2x pll_ref_clk)
//
`default_nettype none
module rf_core_100m (
//---------------------------------------------------------------------------
// Clocking
//---------------------------------------------------------------------------
// Main Clock Inputs
input wire rfdc_clk,
input wire rfdc_clk_2x,
input wire data_clk,
input wire data_clk_2x, // Unused, kept for rf_core_* interface consistency.
// AXI4-Lite Configuration Clock
// This clock is used to synchronize status bits for the RFDC registers in
// the AXI-S clock domain.
input wire s_axi_config_clk,
//---------------------------------------------------------------------------
// RFDC Data Interfaces
//---------------------------------------------------------------------------
// All ports here are in the rfdc_clk domain.
// ADC
input wire [31:0] adc_data_in_i_tdata_0,
output wire adc_data_in_i_tready_0,
input wire adc_data_in_i_tvalid_0,
input wire [31:0] adc_data_in_q_tdata_0,
output wire adc_data_in_q_tready_0,
input wire adc_data_in_q_tvalid_0,
input wire [31:0] adc_data_in_i_tdata_1,
output wire adc_data_in_i_tready_1,
input wire adc_data_in_i_tvalid_1,
input wire [31:0] adc_data_in_q_tdata_1,
output wire adc_data_in_q_tready_1,
input wire adc_data_in_q_tvalid_1,
// DAC
output wire [63:0] dac_data_out_tdata_0,
input wire dac_data_out_tready_0,
output wire dac_data_out_tvalid_0,
output wire [63:0] dac_data_out_tdata_1,
input wire dac_data_out_tready_1,
output wire dac_data_out_tvalid_1,
//---------------------------------------------------------------------------
// User Data Interfaces
//---------------------------------------------------------------------------
// All ports here are in the data_clk domain.
// ADC
output wire [31:0] adc_data_out_tdata_0, // Packed [Q,I] with Q in MSBs
output wire adc_data_out_tvalid_0,
output wire [31:0] adc_data_out_tdata_1, // Packed [Q,I] with Q in MSBs
output wire adc_data_out_tvalid_1,
// DAC
input wire [31:0] dac_data_in_tdata_0, // Packed [Q,I] with Q in MSBs
output wire dac_data_in_tready_0,
input wire dac_data_in_tvalid_0,
input wire [31:0] dac_data_in_tdata_1, // Packed [Q,I] with Q in MSBs
output wire dac_data_in_tready_1,
input wire dac_data_in_tvalid_1,
//---------------------------------------------------------------------------
// Miscellaneous
//---------------------------------------------------------------------------
// Invert I/Q control signals from RFDC to DSP chain.
input wire [3:0] invert_adc_iq_rclk2,
input wire [3:0] invert_dac_iq_rclk2,
// Control/status vectors from/to RFDC.
// Notice these are all in the s_axi_config_clk domain.
output wire [15:0] dsp_info_sclk,
output wire [15:0] axi_status_sclk,
// Resets.
input wire adc_data_out_resetn_dclk,
input wire adc_enable_data_rclk,
input wire adc_rfdc_axi_resetn_rclk,
input wire dac_data_in_resetn_dclk,
input wire dac_data_in_resetn_rclk,
input wire dac_data_in_resetn_rclk2x,
input wire fir_resetn_rclk2x,
// Version (Constant)
output wire [95:0] version_info
);
`include "../../regmap/rfdc_regs_regmap_utils.vh"
`include "../../regmap/versioning_regs_regmap_utils.vh"
`include "../../regmap/versioning_utils.vh"
// Fixed for this implementation.
localparam NUM_ADC_CHANNELS = 2;
localparam NUM_DAC_CHANNELS = 2;
// ADC data interface from RFDC.
wire [31:0] adc_data_in_i_tdata [0:7]; // 2 SPC (I)
wire [31:0] adc_data_in_q_tdata [0:7]; // 2 SPC (Q)
wire [ 7:0] adc_data_in_i_tready;
wire [ 7:0] adc_data_in_q_tready;
wire [ 7:0] adc_data_in_i_tvalid;
wire [ 7:0] adc_data_in_q_tvalid;
// DAC data interface to RFDC.
wire [63:0] dac_data_out_tdata [0:7]; // 2 SPC (I + Q)
wire [ 7:0] dac_data_out_tready;
wire [ 7:0] dac_data_out_tvalid;
// ADC data interface to user.
wire [31:0] adc_data_out_tdata [0:7]; // 1 SPC (I + Q)
wire [ 7:0] adc_data_out_tready;
wire [ 7:0] adc_data_out_tvalid;
// DAC data interface from user.
wire [31:0] dac_data_in_tdata_preswap [0:7]; // 1 SPC (I + Q)
wire [31:0] dac_data_in_tdata [0:7]; // 1 SPC (I + Q)
wire [ 7:0] dac_data_in_tready;
wire [ 7:0] dac_data_in_tvalid;
wire [ 7:0] invert_dac_iq_dclk;
wire [15:0] axi_status;
//---------------------------------------------------------------------------
// Resets, Debug and Misc.
//---------------------------------------------------------------------------
// Group all these status bits together. They don't toggle frequently so data
// coherency is not an issue here.
// Using constants for DB0 since the bits are the 16 LSBs in a 32-bit vector.
// DB1 simply uses the 16 MSBs when wiring the status vector.
assign axi_status[USER_ADC_TREADY_MSB :USER_ADC_TREADY ] = adc_data_out_tready [1:0];
assign axi_status[USER_ADC_TVALID_MSB :USER_ADC_TVALID ] = adc_data_out_tvalid [1:0];
assign axi_status[RFDC_ADC_I_TVALID_MSB:RFDC_ADC_I_TVALID] = adc_data_in_i_tvalid[1:0];
assign axi_status[RFDC_ADC_Q_TVALID_MSB:RFDC_ADC_Q_TVALID] = adc_data_in_q_tvalid[1:0];
assign axi_status[RFDC_ADC_I_TREADY_MSB:RFDC_ADC_I_TREADY] = adc_data_in_i_tready[1:0];
assign axi_status[RFDC_ADC_Q_TREADY_MSB:RFDC_ADC_Q_TREADY] = adc_data_in_q_tready[1:0];
assign axi_status[RFDC_DAC_TVALID_MSB :RFDC_DAC_TVALID ] = dac_data_out_tvalid [1:0];
assign axi_status[RFDC_DAC_TREADY_MSB :RFDC_DAC_TREADY ] = dac_data_out_tready [1:0];
synchronizer #(
.WIDTH (16),
.STAGES (2),
.INITIAL_VAL (0),
.FALSE_PATH_TO_IN (1)
) synchronizer_axis_status (
.clk (s_axi_config_clk),
.rst (1'b0),
.in (axi_status),
.out (axi_status_sclk)
);
// Drive the DSP info vector with information on this specific DSP chain.
assign dsp_info_sclk[FABRIC_DSP_BW_MSB :FABRIC_DSP_BW] = FABRIC_DSP_BW_100M;
assign dsp_info_sclk[FABRIC_DSP_RX_CNT_MSB:FABRIC_DSP_RX_CNT] = NUM_ADC_CHANNELS;
assign dsp_info_sclk[FABRIC_DSP_TX_CNT_MSB:FABRIC_DSP_TX_CNT] = NUM_DAC_CHANNELS;
//---------------------------------------------------------------------------
// ADC Post-Processing
//---------------------------------------------------------------------------
// Data comes from the RFDC as 2 SPC, separate streams for each channel and
// I/Q.
assign adc_data_in_i_tdata[0] = adc_data_in_i_tdata_0;
assign adc_data_in_q_tdata[0] = adc_data_in_q_tdata_0;
assign adc_data_in_i_tdata[1] = adc_data_in_i_tdata_1;
assign adc_data_in_q_tdata[1] = adc_data_in_q_tdata_1;
assign adc_data_in_i_tready_0 = adc_data_in_i_tready[0];
assign adc_data_in_i_tvalid[0] = adc_data_in_i_tvalid_0;
assign adc_data_in_q_tready_0 = adc_data_in_q_tready[0];
assign adc_data_in_q_tvalid[0] = adc_data_in_q_tvalid_0;
assign adc_data_in_i_tready_1 = adc_data_in_i_tready[1];
assign adc_data_in_i_tvalid[1] = adc_data_in_i_tvalid_1;
assign adc_data_in_q_tready_1 = adc_data_in_q_tready[1];
assign adc_data_in_q_tvalid[1] = adc_data_in_q_tvalid_1;
// ADC Data from the RFDC arrives here as 2 SPC with separate I and Q
// streams. It leaves the adc_100m_bd as 1 SPC with I and Q packed into a
// single 32 bit word.
genvar adc_num;
generate
for (adc_num=0; adc_num < (NUM_ADC_CHANNELS); adc_num = adc_num + 1)
begin : adc_gen
adc_100m_bd adc_100m_bd_gen (
.adc_data_out_resetn_dclk (adc_data_out_resetn_dclk),
.data_clk (data_clk),
.enable_data_to_fir_rclk (adc_enable_data_rclk),
.fir_resetn_rclk2x (fir_resetn_rclk2x),
.rfdc_adc_axi_resetn_rclk (adc_rfdc_axi_resetn_rclk),
.rfdc_clk (rfdc_clk),
.rfdc_clk_2x (rfdc_clk_2x),
.swap_iq_2x (invert_adc_iq_rclk2 [adc_num]),
.adc_data_out_tvalid (adc_data_out_tvalid [adc_num]),
.adc_data_out_tdata (adc_data_out_tdata [adc_num]),
.adc_i_data_in_tvalid (adc_data_in_i_tvalid[adc_num]),
.adc_i_data_in_tready (adc_data_in_i_tready[adc_num]),
.adc_i_data_in_tdata (adc_data_in_i_tdata [adc_num]),
.adc_q_data_in_tvalid (adc_data_in_q_tvalid[adc_num]),
.adc_q_data_in_tready (adc_data_in_q_tready[adc_num]),
.adc_q_data_in_tdata (adc_data_in_q_tdata [adc_num])
);
end
endgenerate
// Data is released to the user as 1 SPC, separate streams for each channel.
assign adc_data_out_tdata_0 = adc_data_out_tdata[0];
assign adc_data_out_tdata_1 = adc_data_out_tdata[1];
// There is no tready going to the ADC (one has to be always ready for ADC
// data), but it is still a component of the axi_status vector as a generic
// AXI stream status. Report 1'b1 to the status vector consistent with being
// always ready
assign adc_data_out_tready[0] = 1'b1;
assign adc_data_out_tvalid_0 = adc_data_out_tvalid[0];
assign adc_data_out_tready[1] = 1'b1;
assign adc_data_out_tvalid_1 = adc_data_out_tvalid[1];
//---------------------------------------------------------------------------
// DAC Pre-Processing
//---------------------------------------------------------------------------
// Data comes from the user as 1 SPC, separate streams for each channel.
assign dac_data_in_tdata_preswap[0] = dac_data_in_tdata_0;
assign dac_data_in_tdata_preswap[1] = dac_data_in_tdata_1;
assign dac_data_in_tready_0 = dac_data_in_tready[0];
assign dac_data_in_tvalid[0] = dac_data_in_tvalid_0;
assign dac_data_in_tready_1 = dac_data_in_tready[1];
assign dac_data_in_tvalid[1] = dac_data_in_tvalid_1;
// Optionally swap IQ data positions in the vector. First cross the swap
// vector over to the data_clk domain.
synchronizer #(
.WIDTH (8),
.STAGES (2),
.INITIAL_VAL (0),
.FALSE_PATH_TO_IN (1)
) synchronizer_invert_dac_iq (
.clk (data_clk),
.rst (1'b0),
.in (invert_dac_iq_rclk2),
.out (invert_dac_iq_dclk)
);
genvar dac_num;
generate
for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
begin : dac_swap_gen
assign dac_data_in_tdata[dac_num][15:00] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][31:16]) : (dac_data_in_tdata_preswap[dac_num][15:0]);
assign dac_data_in_tdata[dac_num][31:16] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][15:00]) : (dac_data_in_tdata_preswap[dac_num][31:16]);
end
endgenerate
// These streams are then interpolated by dac_100m_bd, and form a single
// stream per channel, 2 SPC, packed: MSB [Sample1Q, Sample1I, Sample0Q,
// Sample0I] LSB.
generate
for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
begin : dac_gen
dac_100m_bd dac_100m_bd_gen (
.dac_data_in_resetn_dclk (dac_data_in_resetn_dclk),
.dac_data_in_resetn_rclk (dac_data_in_resetn_rclk),
.dac_data_in_resetn_rclk2x (dac_data_in_resetn_rclk2x),
.data_clk (data_clk),
.rfdc_clk (rfdc_clk),
.rfdc_clk_2x (rfdc_clk_2x),
.dac_data_out_tdata (dac_data_out_tdata [dac_num]),
.dac_data_out_tvalid (dac_data_out_tvalid[dac_num]),
.dac_data_out_tready (dac_data_out_tready[dac_num]),
.dac_data_in_tdata (dac_data_in_tdata [dac_num]),
.dac_data_in_tvalid (dac_data_in_tvalid [dac_num]),
.dac_data_in_tready (dac_data_in_tready [dac_num])
);
end
endgenerate
// Data is released to the RFDC as 2 SPC, separate streams per channel (I/Q
// together).
assign dac_data_out_tdata_0 = dac_data_out_tdata[0];
assign dac_data_out_tdata_1 = dac_data_out_tdata[1];
assign dac_data_out_tready[0] = dac_data_out_tready_0;
assign dac_data_out_tvalid_0 = dac_data_out_tvalid[0];
assign dac_data_out_tready[1] = dac_data_out_tready_1;
assign dac_data_out_tvalid_1 = dac_data_out_tvalid[1];
//---------------------------------------------------------------------------
// Version
//---------------------------------------------------------------------------
// Version metadata, constants come from auto-generated
// versioning_regs_regmap_utils.vh
assign version_info = build_component_versions(
RF_CORE_100M_VERSION_LAST_MODIFIED_TIME,
build_version(
RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MAJOR,
RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MINOR,
RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_BUILD
),
build_version(
RF_CORE_100M_CURRENT_VERSION_MAJOR,
RF_CORE_100M_CURRENT_VERSION_MINOR,
RF_CORE_100M_CURRENT_VERSION_BUILD
)
);
endmodule
`default_nettype wire
//XmlParse xml_on
//<regmap name="VERSIONING_REGS_REGMAP">
// <group name="VERSIONING_CONSTANTS">
// <enumeratedtype name="RF_CORE_100M_VERSION" showhex="true">
// <info>
// 100 MHz RF core.{BR/}
// For guidance on when to update these revision numbers,
// please refer to the register map documentation accordingly:
// <li> Current version: @.VERSIONING_REGS_REGMAP..CURRENT_VERSION
// <li> Oldest compatible version: @.VERSIONING_REGS_REGMAP..OLDEST_COMPATIBLE_VERSION
// <li> Version last modified: @.VERSIONING_REGS_REGMAP..VERSION_LAST_MODIFIED
// </info>
// <value name="RF_CORE_100M_CURRENT_VERSION_MAJOR" integer="1"/>
// <value name="RF_CORE_100M_CURRENT_VERSION_MINOR" integer="0"/>
// <value name="RF_CORE_100M_CURRENT_VERSION_BUILD" integer="0"/>
// <value name="RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MAJOR" integer="1"/>
// <value name="RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MINOR" integer="0"/>
// <value name="RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_BUILD" integer="0"/>
// <value name="RF_CORE_100M_VERSION_LAST_MODIFIED_TIME" integer="0x20102617"/>
// </enumeratedtype>
// </group>
//</regmap>
//XmlParse xml_off
+220
View File
@@ -0,0 +1,220 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rf_core_200m
//
// Description:
//
// Implementation of rf_core with 200 MHz bandwidth. It presents an interface
// that inputs/outputs 2 samples per cycle. This version is implemented by
// instantiating rf_core_400m and adding up-conversion and down-conversion
// filters.
//
`default_nettype none
module rf_core_200m (
//---------------------------------------------------------------------------
// Clocking
//---------------------------------------------------------------------------
// Main Clock Inputs
input wire rfdc_clk,
input wire rfdc_clk_2x,
input wire data_clk,
input wire data_clk_2x,
// AXI4-Lite Config Clock
// This clock is used to synchronize status bits for the RFDC
// registers in the AXI-S clock domain.
input wire s_axi_config_clk,
//---------------------------------------------------------------------------
// RFDC Data Interfaces
//---------------------------------------------------------------------------
// All ports here are in the rfdc_clk domain.
// ADC
input wire [127:0] adc_data_in_i_tdata_0,
output wire adc_data_in_i_tready_0,
input wire adc_data_in_i_tvalid_0,
input wire [127:0] adc_data_in_q_tdata_0,
output wire adc_data_in_q_tready_0,
input wire adc_data_in_q_tvalid_0,
input wire [127:0] adc_data_in_i_tdata_1,
output wire adc_data_in_i_tready_1,
input wire adc_data_in_i_tvalid_1,
input wire [127:0] adc_data_in_q_tdata_1,
output wire adc_data_in_q_tready_1,
input wire adc_data_in_q_tvalid_1,
// DAC
output wire [255:0] dac_data_out_tdata_0,
input wire dac_data_out_tready_0,
output wire dac_data_out_tvalid_0,
output wire [255:0] dac_data_out_tdata_1,
input wire dac_data_out_tready_1,
output wire dac_data_out_tvalid_1,
//---------------------------------------------------------------------------
// User Data Interface
//---------------------------------------------------------------------------
// All ports here are in the data_clk domain.
// ADC
output wire [63:0] adc_data_out_tdata_0, // Packed {Q1,I1,Q0,I0}
output wire adc_data_out_tvalid_0,
output wire [63:0] adc_data_out_tdata_1, // Packed {Q1,I1,Q0,I0}
output wire adc_data_out_tvalid_1,
// DAC
input wire [63:0] dac_data_in_tdata_0, // Packed {Q1,I1,Q0,I0} with Q in MSBs
output wire dac_data_in_tready_0,
input wire dac_data_in_tvalid_0,
input wire [63:0] dac_data_in_tdata_1, // Packed {Q1,I1,Q0,I0} with Q in MSBs
output wire dac_data_in_tready_1,
input wire dac_data_in_tvalid_1,
//---------------------------------------------------------------------------
// Miscellaneous
//---------------------------------------------------------------------------
// Invert I/Q control signals from RFDC to DSP chain.
input wire [3:0] invert_adc_iq_rclk2,
input wire [3:0] invert_dac_iq_rclk2,
// Control/status vectors from/to RFDC.
// Notice these are all in the s_axi_config_clk domain.
output reg [15:0] dsp_info_sclk,
output wire [15:0] axi_status_sclk,
// Resets.
input wire adc_data_out_resetn_dclk,
input wire adc_enable_data_rclk,
input wire adc_rfdc_axi_resetn_rclk,
input wire dac_data_in_resetn_dclk,
input wire dac_data_in_resetn_dclk2x,
input wire dac_data_in_resetn_rclk,
input wire fir_resetn_rclk2x,
// Version (Constant)
output wire [95:0] version_info
);
`include "../../regmap/rfdc_regs_regmap_utils.vh"
//---------------------------------------------------------------------------
// 400 MHz RF Core
//---------------------------------------------------------------------------
wire [127:0] adc_400m_tdata_0;
wire adc_400m_tvalid_0;
wire [127:0] adc_400m_tdata_1;
wire adc_400m_tvalid_1;
wire [127:0] dac_400m_tdata_0;
wire dac_400m_tvalid_0;
wire [127:0] dac_400m_tdata_1;
wire dac_400m_tvalid_1;
wire [ 15:0] dsp_info_sclk_400m;
rf_core_400m rf_core_400m_i (
.rfdc_clk (rfdc_clk),
.rfdc_clk_2x (rfdc_clk_2x),
.data_clk (data_clk),
.data_clk_2x (data_clk_2x),
.s_axi_config_clk (s_axi_config_clk),
.adc_data_in_i_tdata_0 (adc_data_in_i_tdata_0),
.adc_data_in_i_tready_0 (adc_data_in_i_tready_0),
.adc_data_in_i_tvalid_0 (adc_data_in_i_tvalid_0),
.adc_data_in_q_tdata_0 (adc_data_in_q_tdata_0),
.adc_data_in_q_tready_0 (adc_data_in_q_tready_0),
.adc_data_in_q_tvalid_0 (adc_data_in_q_tvalid_0),
.adc_data_in_i_tdata_1 (adc_data_in_i_tdata_1),
.adc_data_in_i_tready_1 (adc_data_in_i_tready_1),
.adc_data_in_i_tvalid_1 (adc_data_in_i_tvalid_1),
.adc_data_in_q_tdata_1 (adc_data_in_q_tdata_1),
.adc_data_in_q_tready_1 (adc_data_in_q_tready_1),
.adc_data_in_q_tvalid_1 (adc_data_in_q_tvalid_1),
.dac_data_out_tdata_0 (dac_data_out_tdata_0),
.dac_data_out_tready_0 (dac_data_out_tready_0),
.dac_data_out_tvalid_0 (dac_data_out_tvalid_0),
.dac_data_out_tdata_1 (dac_data_out_tdata_1),
.dac_data_out_tready_1 (dac_data_out_tready_1),
.dac_data_out_tvalid_1 (dac_data_out_tvalid_1),
.adc_data_out_tdata_0 (adc_400m_tdata_0),
.adc_data_out_tvalid_0 (adc_400m_tvalid_0),
.adc_data_out_tdata_1 (adc_400m_tdata_1),
.adc_data_out_tvalid_1 (adc_400m_tvalid_1),
.dac_data_in_tdata_0 (dac_400m_tdata_0),
.dac_data_in_tready_0 (),
.dac_data_in_tvalid_0 (dac_400m_tvalid_0),
.dac_data_in_tdata_1 (dac_400m_tdata_1),
.dac_data_in_tready_1 (),
.dac_data_in_tvalid_1 (dac_400m_tvalid_1),
.invert_adc_iq_rclk2 (invert_adc_iq_rclk2),
.invert_dac_iq_rclk2 (invert_dac_iq_rclk2),
.dsp_info_sclk (dsp_info_sclk_400m),
.axi_status_sclk (axi_status_sclk),
.adc_data_out_resetn_dclk (adc_data_out_resetn_dclk),
.adc_enable_data_rclk (adc_enable_data_rclk),
.adc_rfdc_axi_resetn_rclk (adc_rfdc_axi_resetn_rclk),
.dac_data_in_resetn_dclk (dac_data_in_resetn_dclk),
.dac_data_in_resetn_dclk2x (dac_data_in_resetn_dclk2x),
.dac_data_in_resetn_rclk (dac_data_in_resetn_rclk),
.fir_resetn_rclk2x (fir_resetn_rclk2x),
.version_info (version_info)
);
// Change reported bandwidth 200 MHz
always @(*) begin
dsp_info_sclk <= dsp_info_sclk_400m;
dsp_info_sclk[FABRIC_DSP_BW_MSB : FABRIC_DSP_BW] <= FABRIC_DSP_BW_200M;
end
//---------------------------------------------------------------------------
// ADC Down-conversion
//---------------------------------------------------------------------------
rf_down_4to2 #(
.NUM_CHANNELS (2)
) rf_down_4to2_i (
.clk (data_clk),
.clk_2x (data_clk_2x),
.rst (~adc_data_out_resetn_dclk),
.rst_2x (~adc_data_out_resetn_dclk), // 1x clk reset is safe to use
.i_tdata ({ adc_400m_tdata_1, adc_400m_tdata_0 }),
.i_tvalid ({ adc_400m_tvalid_1, adc_400m_tvalid_0 }),
.o_tdata ({ adc_data_out_tdata_1, adc_data_out_tdata_0 }),
.o_tvalid ({ adc_data_out_tvalid_1, adc_data_out_tvalid_0 })
);
//---------------------------------------------------------------------------
// DAC Up-conversion
//---------------------------------------------------------------------------
assign dac_data_in_tready_0 = 1'b1;
assign dac_data_in_tready_1 = 1'b1;
rf_up_2to4 #(
.NUM_CHANNELS (2)
) rf_up_2to4_i (
.clk (data_clk),
.clk_2x (data_clk_2x),
.rst (~dac_data_in_resetn_dclk),
.rst_2x (~dac_data_in_resetn_dclk2x),
.i_tdata ({ dac_data_in_tdata_1, dac_data_in_tdata_0 }),
.i_tvalid ({ dac_data_in_tvalid_1, dac_data_in_tvalid_0 }),
.o_tdata ({ dac_400m_tdata_1, dac_400m_tdata_0 }),
.o_tvalid ({ dac_400m_tvalid_1, dac_400m_tvalid_0 })
);
endmodule
`default_nettype wire
+149
View File
@@ -0,0 +1,149 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rf_down_4to2
//
// Description:
//
// Implements a down-sampling filter that accepts 4 samples per cycle on the
// input and outputs 2 samples per cycle. A 2x speed clock is used to perform
// the DSP computation, so that less logic can be used to implement the
// half-band filter.
//
// Data Path : In --> Gearbox --> Filter --> Gearbox --> Out
// SPC : 4 2 1 2
// Clock Rate : 1x 2x 2x 1x
//
`default_nettype none
module rf_down_4to2 #(
parameter NUM_CHANNELS = 1
) (
input wire clk,
input wire clk_2x,
// Synchronous resets
input wire rst,
input wire rst_2x,
// Input - 4 SPC, synchronous to "clk"
input wire [NUM_CHANNELS*128-1:0] i_tdata,
input wire [NUM_CHANNELS* 1-1:0] i_tvalid,
// Output - 2 SPC, synchronous to "clk"
output wire [NUM_CHANNELS*64-1:0] o_tdata,
output wire [NUM_CHANNELS* 1-1:0] o_tvalid
);
generate
genvar ch;
for (ch = 0; ch < NUM_CHANNELS; ch = ch + 1) begin : gen_channel
//-----------------------------------------------------------------------
// Input Gearbox
//-----------------------------------------------------------------------
//
// Convert from 4 SPC on clk to 2 SPC on clk_2x.
//
//-----------------------------------------------------------------------
wire [63:0] gear_to_filt_tdata;
wire gear_to_filt_tvalid;
gearbox_2x1 #(
.WORD_W (32),
.IN_WORDS (4),
.OUT_WORDS (2),
.BIG_ENDIAN (0)
) gearbox_2x1_in (
.i_clk (clk),
.i_rst (rst),
.i_tdata (i_tdata[ch*128 +: 128]),
.i_tvalid (i_tvalid[ch]),
.o_clk (clk_2x),
.o_rst (rst_2x),
.o_tdata (gear_to_filt_tdata),
.o_tvalid (gear_to_filt_tvalid)
);
//-----------------------------------------------------------------------
// Interpolating Filter
//-----------------------------------------------------------------------
wire [47:0] filt_to_clip_tdata;
wire filt_to_clip_tvalid;
hb47_2to1 hb47_2to1_i (
.aresetn (~rst_2x),
.aclk (clk_2x),
.s_axis_data_tvalid (gear_to_filt_tvalid),
.s_axis_data_tready (),
.s_axis_data_tdata (gear_to_filt_tdata),
.m_axis_data_tvalid (filt_to_clip_tvalid),
.m_axis_data_tuser (),
.m_axis_data_tdata (filt_to_clip_tdata)
);
//-----------------------------------------------------------------------
// Saturation
//-----------------------------------------------------------------------
wire [31:0] clip_to_gear_tdata;
wire clip_to_gear_tvalid;
genvar word;
for (word = 0; word < 2; word = word+1) begin : gen_sat
axi_clip #(
.WIDTH_IN (24),
.WIDTH_OUT (16),
.FIFOSIZE (0)
) axi_clip_i (
.clk (clk_2x),
.reset (rst_2x),
.i_tdata (filt_to_clip_tdata[word*24 +: 24]),
.i_tlast (1'b0),
.i_tvalid (filt_to_clip_tvalid),
.i_tready (),
.o_tdata (clip_to_gear_tdata[word*16 +: 16]),
.o_tlast (),
.o_tvalid (clip_to_gear_tvalid),
.o_tready (1'b1)
);
end
//-----------------------------------------------------------------------
// Output Gearbox
//-----------------------------------------------------------------------
//
// Convert from 1 SPC on clk_2x to 2 SPC on clk.
//
//-----------------------------------------------------------------------
gearbox_2x1 #(
.WORD_W (32),
.IN_WORDS (1),
.OUT_WORDS (2),
.BIG_ENDIAN (0)
) gearbox_2x1_out (
.i_clk (clk_2x),
.i_rst (rst_2x),
.i_tdata (clip_to_gear_tdata),
.i_tvalid (clip_to_gear_tvalid),
.o_clk (clk),
.o_rst (rst),
.o_tdata (o_tdata[ch*64 +: 64]),
.o_tvalid (o_tvalid[ch])
);
end // for
endgenerate
endmodule
`default_nettype wire
+149
View File
@@ -0,0 +1,149 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rf_up_2to4
//
// Description:
//
// Implements an up-sampling filter that accepts 2 samples per cycle on the
// input and outputs 4 samples per cycle. A 2x speed clock is used to perform
// the DSP computation, so that less logic can be used to implement the
// half-band filter.
//
// Data Path : In --> Gearbox --> Filter --> Gearbox --> Out
// SPC : 2 1 2 4
// Clock Rate : 1x 2x 2x 1x
//
`default_nettype none
module rf_up_2to4 #(
parameter NUM_CHANNELS = 1
) (
input wire clk,
input wire clk_2x,
// Synchronous resets
input wire rst,
input wire rst_2x,
// Input - 2 SPC, synchronous to "clk"
input wire [NUM_CHANNELS*64-1:0] i_tdata,
input wire [NUM_CHANNELS* 1-1:0] i_tvalid,
// Output - 4 SPC, synchronous to "clk"
output wire [NUM_CHANNELS*128-1:0] o_tdata,
output wire [NUM_CHANNELS* 1-1:0] o_tvalid
);
generate
genvar ch;
for (ch = 0; ch < NUM_CHANNELS; ch = ch + 1) begin : gen_channel
//-----------------------------------------------------------------------
// Input Gearbox
//-----------------------------------------------------------------------
//
// Convert from 2 SPC on clk to 1 SPC on clk_2x.
//
//-----------------------------------------------------------------------
wire [31:0] gear_to_filt_tdata;
wire gear_to_filt_tvalid;
gearbox_2x1 #(
.WORD_W (32),
.IN_WORDS (2),
.OUT_WORDS (1),
.BIG_ENDIAN (0)
) gearbox_2x1_in (
.i_clk (clk),
.i_rst (rst),
.i_tdata (i_tdata[ch*64 +: 64]),
.i_tvalid (i_tvalid[ch]),
.o_clk (clk_2x),
.o_rst (rst_2x),
.o_tdata (gear_to_filt_tdata),
.o_tvalid (gear_to_filt_tvalid)
);
//-----------------------------------------------------------------------
// Interpolating Filter
//-----------------------------------------------------------------------
wire [95:0] filt_to_clip_tdata;
wire filt_to_clip_tvalid;
hb47_1to2 hb47_1to2_i (
.aresetn (~rst_2x),
.aclk (clk_2x),
.s_axis_data_tvalid (gear_to_filt_tvalid),
.s_axis_data_tready (),
.s_axis_data_tdata (gear_to_filt_tdata),
.m_axis_data_tvalid (filt_to_clip_tvalid),
.m_axis_data_tuser (),
.m_axis_data_tdata (filt_to_clip_tdata)
);
//-----------------------------------------------------------------------
// Saturation
//-----------------------------------------------------------------------
wire [63:0] clip_to_gear_tdata;
wire clip_to_gear_tvalid;
genvar word;
for (word = 0; word < 4; word = word+1) begin : gen_sat
axi_clip #(
.WIDTH_IN (24),
.WIDTH_OUT (16),
.FIFOSIZE (0)
) axi_clip_i (
.clk (clk_2x),
.reset (rst_2x),
.i_tdata (filt_to_clip_tdata[word*24 +: 24]),
.i_tlast (1'b0),
.i_tvalid (filt_to_clip_tvalid),
.i_tready (),
.o_tdata (clip_to_gear_tdata[word*16 +: 16]),
.o_tlast (),
.o_tvalid (clip_to_gear_tvalid),
.o_tready (1'b1)
);
end
//-----------------------------------------------------------------------
// Output Gearbox
//-----------------------------------------------------------------------
//
// Convert from 2 SPC on clk_2x to 4 SPC on clk.
//
//-----------------------------------------------------------------------
gearbox_2x1 #(
.WORD_W (32),
.IN_WORDS (2),
.OUT_WORDS (4),
.BIG_ENDIAN (0)
) gearbox_2x1_out (
.i_clk (clk_2x),
.i_rst (rst_2x),
.i_tdata (clip_to_gear_tdata),
.i_tvalid (clip_to_gear_tvalid),
.o_clk (clk),
.o_rst (rst),
.o_tdata (o_tdata[ch*128 +: 128]),
.o_tvalid (o_tvalid[ch])
);
end // for
endgenerate
endmodule
`default_nettype wire
+142
View File
@@ -0,0 +1,142 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: adc_gearbox_2x4
--
-- Description:
--
-- Gearbox to expand the data width from 2 SPC to 4 SPC.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity adc_gearbox_2x4 is
port(
Clk1x : in std_logic;
Clk3x : in std_logic;
-- Resets with synchronous de-assertion.
ac1Reset_n : in std_logic;
ac3Reset_n : in std_logic;
-- Data packing: [Q1,I1,Q0,I0] (I in LSBs).
c3DataIn : in std_logic_vector(95 downto 0);
c3DataValidIn : in std_logic;
-- Data packing: [Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs).
c1DataOut : out std_logic_vector(191 downto 0);
c1DataValidOut : out std_logic
);
end adc_gearbox_2x4;
architecture RTL of adc_gearbox_2x4 is
signal c1DataValidInDly, c3DataValidInDly
: std_logic_vector(3 downto 0) := (others => '0');
subtype Word_t is std_logic_vector(95 downto 0);
type Words_t is array(natural range<>) of Word_t;
signal c3DataInDly, c1DataInDly : Words_t(3 downto 0);
begin
-- Pipeline input data. We will need four pipeline stages to account for the
-- three possible Clk1x and Clk3x phases and the nature of data packing done
-- in the DDC filter. The DDC asserts data valid for two clock cycles and
-- de-asserted for one clock cycle. This requires us to have shift register
-- that is 4 sample words (each sample word is 2 SPC) deep.
InputValidPipeline: process(Clk3x, ac3Reset_n)
begin
if ac3Reset_n = '0' then
c3DataValidInDly <= (others => '0');
-- These registers are on the falling edge to prevent a hold violation at
-- the input to the following Clk1x FF (which may arrive late when more
-- heavily loaded than Clk3x)
elsif falling_edge(Clk3x) then
c3DataValidInDly <= c3DataValidInDly(c3DataValidInDly'left-1 downto 0) &
c3DataValidIn;
end if;
end process;
InputDataPipeline: process(Clk3x)
begin
-- These registers are on the falling edge to prevent a hold violation at
-- the input to the following Clk1x FF (which may arrive late when more
-- heavily loaded than Clk3x).
if falling_edge(Clk3x) then
c3DataInDly <= c3DataInDly(c3DataInDly'high-1 downto 0) & c3DataIn;
end if;
end process InputDataPipeline;
-- Data valid clock crossing from Clk3x to Clk1x
Clk3xToClk1xValidCrossing: process(Clk1x, ac1Reset_n)
begin
if ac1Reset_n = '0' then
c1DataValidInDly <= (others => '0');
elsif rising_edge(Clk1x) then
c1DataValidInDly <= c3DataValidInDly;
end if;
end process;
-- Data clock crossing from Clk3x to Clk1x
Clk3xToClk1xDataCrossing: process(Clk1x)
begin
if rising_edge(Clk1x) then
c1DataInDly <= c3DataInDly;
end if;
end process;
-----------------------------------------------------------------------------
--
-- p0 p1 p2 p0
-- Clk3x _______/¯¯¯¯¯¯¯\_______/¯¯¯¯¯¯¯\_______/¯¯¯¯¯¯¯\_______/¯¯¯
--
-- Clk1x _______/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\_______________________/¯¯¯
--
-- c3DataValidIn _/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\_______________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- This gearbox connect the DDC filter output to the remaining RX data path.
-- For efficient use of DSP slices we run the DDC at 3x clock rate. Both
-- Clk3x and Clk1x are sourced from the same PLL and is phase locked as shown
-- in the above timing diagram. The output of DDC filter is asserted for two
-- clock cycles and is de-asserted for one clock cycle. The remaining part of
-- the design cannot run at 3x clock rate. So, we increase the number of
-- samples per clock cycle and decrease the clock frequency to 1x. Depending
-- upon the pipeline delay through the filter and RF section, the phase of
-- data valid assertion could be on either p0, p1, or p2 edge. And depending
-- upon the phase, data packing to Clk1x domain will vary. Since there are
-- three possible phase, we will need three different data packing options.
--
-- Data packing is done by looking for two consecutive ones in the data valid
-- shift register (c1DataValidInDly).This pattern can be used only because of
-- the way output data is packed in the filter. If we see two consecutive
-- ones, then we know that we have enough data to be packed for the output of
-- this gearbox. This is because, we need two Clk3x cycles of 2 SPC data to
-- pack a 4 SPC data output on Clk1x. The location of two consecutive ones in
-- the data valid shift register will provide the location of valid data in
-- data shift register (c1DataInDly).
DataPacker: process(Clk1x)
begin
if rising_edge(Clk1x) then
-- Data valid is asserted when both Clk1x and Clk3x are phase aligned
-- (p0). In this case, c1DataValidInDly will have consecutive ones in
-- index 1 and 2.
c1DataValidOut <= c1DataValidInDly(1) and c1DataValidInDly(2);
c1DataOut <= c1DataInDly(1) & c1DataInDly(2);
-- Data valid asserted on phase p1.
if c1DataValidInDly(1 downto 0) = "11" then
c1DataOut <= c1DataInDly(0) & c1DataInDly(1);
c1DataValidOut <= '1';
-- Data valid asserted on phase p2.
elsif c1DataValidInDly(3 downto 2) = "11" then
c1DataOut <= c1DataInDly(2) & c1DataInDly(3);
c1DataValidOut <= '1';
end if;
end if;
end process;
end RTL;
+105
View File
@@ -0,0 +1,105 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: adc_gearbox_8x4
//
// Description:
//
// Gearbox ADC data from 8 SPC to 4 SPC and corresponding 2x clock to 1x
// clock. Also implement data swapping to format packets to fit the FIR
// filter input requirements.
//
// This modules incurs one clk1x cycle of delay on the data and valid signals
// from input on the 1x domain to output on the 2x domain.
//
`default_nettype none
module adc_gearbox_8x4 (
input wire clk1x,
input wire reset_n_1x,
// Data is _presumed_ to be packed [Sample7, ..., Sample0] (Sample0 in LSBs).
input wire [127:0] adc_q_in_1x,
input wire [127:0] adc_i_in_1x,
input wire valid_in_1x,
// De-assert enable_1x to clear the data valid output synchronously.
input wire enable_1x,
input wire clk2x,
// Data is packed [Q3,I3, ... , Q0, I0] (I in LSBs) when swap_iq_1x is '0'
input wire swap_iq_2x,
output wire [127:0] adc_out_2x,
output wire valid_out_2x
);
// Re-create the 1x clock in the 2x domain to produce a deterministic
// crossing.
reg toggle_1x, toggle_2x = 1'b0, toggle_2x_dly = 1'b0, valid_2x = 1'b0, valid_dly_2x = 1'b0;
reg [127:0] data_out_2x = 128'b0, adc_q_data_in_2x = 128'b0, adc_i_data_in_2x = 128'b0;
// Create a toggle in the 1x clock domain (clock divider /2).
always @(posedge clk1x or negedge reset_n_1x) begin
if ( ! reset_n_1x) begin
toggle_1x <= 1'b0;
end else begin
toggle_1x <= ! toggle_1x;
end
end
// Transfer the toggle from the 1x to the 2x domain. Delay the toggle in the
// 2x domain by one cycle and compare it to the non-delayed version. When
// they differ, push data_in[63:0] onto the output. When the match, push
// [127:64] onto the output. The datasheet is unclear on the exact
// implementation.
//
// It is safe to not reset this domain because all of the input signals will
// be cleared by the 1x reset. Safe default values are assigned to all these
// registers.
always @(posedge clk2x) begin
toggle_2x <= toggle_1x;
toggle_2x_dly <= toggle_2x;
adc_q_data_in_2x <= adc_q_in_1x;
adc_i_data_in_2x <= adc_i_in_1x;
data_out_2x <= 128'b0;
// Place Q in the MSBs, I in the LSBs by default, unless swapped = 1.
if (valid_2x) begin
if (swap_iq_2x) begin
if (toggle_2x != toggle_2x_dly) begin
data_out_2x <= {adc_i_data_in_2x[63:48], adc_q_data_in_2x[63:48],
adc_i_data_in_2x[47:32], adc_q_data_in_2x[47:32],
adc_i_data_in_2x[31:16], adc_q_data_in_2x[31:16],
adc_i_data_in_2x[15: 0], adc_q_data_in_2x[15: 0]};
end else begin
data_out_2x <= {adc_i_data_in_2x[127:112], adc_q_data_in_2x[127:112],
adc_i_data_in_2x[111: 96], adc_q_data_in_2x[111: 96],
adc_i_data_in_2x[95 : 80], adc_q_data_in_2x[95 : 80],
adc_i_data_in_2x[79 : 64], adc_q_data_in_2x[79 : 64]};
end
end else begin
if (toggle_2x != toggle_2x_dly) begin
data_out_2x <= {adc_q_data_in_2x[63:48], adc_i_data_in_2x[63:48],
adc_q_data_in_2x[47:32], adc_i_data_in_2x[47:32],
adc_q_data_in_2x[31:16], adc_i_data_in_2x[31:16],
adc_q_data_in_2x[15: 0], adc_i_data_in_2x[15: 0]};
end else begin
data_out_2x <= {adc_q_data_in_2x[127:112], adc_i_data_in_2x[127:112],
adc_q_data_in_2x[111: 96], adc_i_data_in_2x[111: 96],
adc_q_data_in_2x[95 : 80], adc_i_data_in_2x[95 : 80],
adc_q_data_in_2x[79 : 64], adc_i_data_in_2x[79 : 64]};
end
end
end
// Valid is simply a transferred version of the 1x clock's valid. Delay it one
// more cycle to align outputs.
valid_2x <= valid_in_1x && enable_1x;
valid_dly_2x <= valid_2x;
end
assign adc_out_2x = data_out_2x;
assign valid_out_2x = valid_dly_2x;
endmodule
`default_nettype wire
+233
View File
@@ -0,0 +1,233 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_gearbox_12x8
--
-- Description:
--
-- Gearbox to expand the data width from 12 SPC to 8 SPC.
-- Input Clocks, all aligned to one another and coming from same MMCM.
-- PLL reference clock = 61.44 or 62.5 MHz.
-- RfClk: 184.32 or 187.5 MHz (3x PLL reference clock)
-- Clk1x: 122.88 or 125 MHz (2x PLL reference clock)
-- Clk2x: 245.76 or 250 MHz (4x PLL reference clock)
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity dac_gearbox_12x8 is
port(
Clk1x : in std_logic;
RfClk : in std_logic;
ac1Reset_n : in std_logic;
arReset_n : in std_logic;
-- Data packing: [Q11,I11,Q10,I10,...,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c1DataIn : in std_logic_vector(383 downto 0);
c1DataValidIn : in std_logic;
-- Data packing: [Q7,I7,Q6,I6,...,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
rDataOut : out std_logic_vector(255 downto 0) := (others => '0');
rReadyForOutput : in std_logic;
rDataValidOut : out std_logic
);
end dac_gearbox_12x8;
architecture RTL of dac_gearbox_12x8 is
constant kDataWidth : natural := 16;
constant kDataI0Lsb : natural := 0;
constant kDataI0Msb : natural := kDataWidth-1;
constant kDataQ0Lsb : natural := kDataI0Msb+1;
constant kDataQ0Msb : natural := kDataQ0Lsb+kDataWidth-1;
constant kDataI1Lsb : natural := kDataQ0Msb+1;
constant kDataI1Msb : natural := kDataI1Lsb+kDataWidth-1;
constant kDataQ1Lsb : natural := kDataI1Msb+1;
constant kDataQ1Msb : natural := kDataQ1Lsb+kDataWidth-1;
constant kDataI2Lsb : natural := kDataQ1Msb+1;
constant kDataI2Msb : natural := kDataI2Lsb+kDataWidth-1;
constant kDataQ2Lsb : natural := kDataI2Msb+1;
constant kDataQ2Msb : natural := kDataQ2Lsb+kDataWidth-1;
constant kDataI3Lsb : natural := kDataQ2Msb+1;
constant kDataI3Msb : natural := kDataI3Lsb+kDataWidth-1;
constant kDataQ3Lsb : natural := kDataI3Msb+1;
constant kDataQ3Msb : natural := kDataQ3Lsb+kDataWidth-1;
constant kDataI4Lsb : natural := kDataQ3Msb+1;
constant kDataI4Msb : natural := kDataI4Lsb+kDataWidth-1;
constant kDataQ4Lsb : natural := kDataI4Msb+1;
constant kDataQ4Msb : natural := kDataQ4Lsb+kDataWidth-1;
constant kDataI5Lsb : natural := kDataQ4Msb+1;
constant kDataI5Msb : natural := kDataI5Lsb+kDataWidth-1;
constant kDataQ5Lsb : natural := kDataI5Msb+1;
constant kDataQ5Msb : natural := kDataQ5Lsb+kDataWidth-1;
constant kDataI6Lsb : natural := kDataQ5Msb+1;
constant kDataI6Msb : natural := kDataI6Lsb+kDataWidth-1;
constant kDataQ6Lsb : natural := kDataI6Msb+1;
constant kDataQ6Msb : natural := kDataQ6Lsb+kDataWidth-1;
constant kDataI7Lsb : natural := kDataQ6Msb+1;
constant kDataI7Msb : natural := kDataI7Lsb+kDataWidth-1;
constant kDataQ7Lsb : natural := kDataI7Msb+1;
constant kDataQ7Msb : natural := kDataQ7Lsb+kDataWidth-1;
subtype Word_t is std_logic_vector(383 downto 0);
type Words_t is array(natural range<>) of Word_t;
signal rDataInDly : Words_t(3 downto 0);
signal rDataValidDly : std_logic_vector(3 downto 0) := (others => '0');
signal c1PhaseCount, c1DataValidInDly : std_logic := '0';
signal rPhaseShiftReg : std_logic_vector(2 downto 0);
begin
-----------------------------------------------------------------------------
-- Data Packing 12 SPC to 8 SPC
-----------------------------------------------------------------------------
Clk1xDataCount: process(ac1Reset_n, Clk1x)
begin
if ac1Reset_n = '0' then
c1PhaseCount <= '0';
c1DataValidInDly <= '0';
elsif rising_edge(Clk1x) then
c1DataValidInDly <= c1DataValidIn;
c1PhaseCount <= (not c1PhaseCount) and (c1DataValidIn or c1DataValidInDly);
end if;
end process;
DataClkCrossing: process(RfClk)
begin
if rising_edge(RfClk) then
rDataInDly <= rDataInDly(rDataInDly'high-1 downto 0) & c1DataIn;
end if;
end process;
-- Store clock phase information in a shift register. The shift register
-- is a 3 bit register and it used in output data packer.
PhaseClkCrossing: process(arReset_n,RfClk)
begin
if arReset_n = '0' then
rPhaseShiftReg <= (others => '0');
elsif rising_edge(RfClk) then
rPhaseShiftReg(2 downto 1) <= rPhaseShiftReg(1 downto 0);
rPhaseShiftReg(0) <= c1PhaseCount;
end if;
end process;
-----------------------------------------------------------------------------
--
-- Timing diagram: Data valid is asserted when both clock are edge aligned.
--
-- | | |
-- v <-Clocks edge aligned v v
-- Clk1x ¯¯\____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\___
-- |
-- v <- O/p data valid assertion
-- RfClk ¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯
-- | | |
-- c1DataValid _/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | |
-- c1DValidDly _________/¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | |
-- c1PhaseCount _______/¯¯¯¯¯¯¯¯¯¯¯¯\|_______|__/¯¯¯¯|¯¯¯¯¯¯¯\__________/¯¯
-- | | |
-- v <- rPhaseSR= "001"
-- rPhaseSR(0) ________________/¯¯¯¯¯¯¯¯\_____|_______|_/¯¯¯¯¯¯¯\_________________
-- | |
-- v <- rPhaseSR= "010"
-- rPhaseSR(1) _________________________/¯¯¯¯¯¯¯¯\____|__________/¯¯¯¯¯¯¯\____________
-- |
-- v <- rPhaseSR= "100"
-- rPhaseSR(2) __________________________________/¯¯¯¯¯¯¯¯\_______________/¯¯¯¯¯¯¯\___
--
-- rDValidDly0 _________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- rDValidDly1 _________________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- rDValidDly2 __________________________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- In this design use a single bit counter on the input clock (Clk1x) domain
-- and pass it to the RfClk domain. When data valid is asserted when both
-- clocks are rising edge aligned, only one bit in rPhaseSR high, the
-- remaining bits are zero. We use the position of the bit counter in the
-- shift register to do data packing.
--
--
-- Timing diagram: When data valid is asserted when both clock are NOT edge
-- aligned.
--
-- | | |
-- v <-Clocks edge aligned v v
-- Clk1x ¯¯\____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\___
-- |
-- v <- O/p data valid assertion
-- RfClk ¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯
-- | | | |
-- c1DataValid ________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | | |
-- c1DValidDly ___________________/¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | | |
-- c1PhaseCount ___________________/¯¯¯¯¯¯¯¯¯¯|¯\_____|_____/¯|¯¯¯¯¯¯¯|¯¯\__________/¯¯
-- | | | |
-- v <- rPhaseSR= "001" |
-- rPhaseSR(0) ________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯\_____|_/¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯
-- | | |
-- v <- rPhaseSR= "011"
-- rPhaseSR(1) ________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯\_____|_/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | |
-- v <- rPhaseSR= "110"
-- rPhaseSR(2) ________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\_______/¯¯¯¯¯¯¯¯¯¯
-- ^
-- | <- rPhaseSR= "101"
--
-- rDValidDly0 _________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- rDValidDly1 _________________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- The above timing diagram is when input data valid is asserted when both
-- clocks rising edges are not aligned. In this case the more than one bit in
-- rPhaseSR is asserted which is unique to this case. As mentioned in the
-- above case, we use rPhaseSR value to determine data packing.
-- Output Data Packer
DataOut: process(RfClk)
begin
if rising_edge(RfClk) then
-- rPhaseShiftReg = "011"
rDataOut <= rDataInDly(2)(kDataQ7Msb downto kDataI0Lsb);
if rPhaseShiftReg = "110" or rPhaseShiftReg = "100" then
rDataOut <= rDataInDly(2)(kDataQ3Msb downto kDataI0Lsb) &
rDataInDly(3)(c1DataIn'length-1 downto kDataQ7Msb+1);
elsif rPhaseShiftReg = "101" or rPhaseShiftReg = "001" then
rDataOut <= rDataInDly(3)(c1DataIn'length-1 downto kDataI4Lsb);
elsif rPhaseShiftReg = "010" then
rDataOut <= rDataInDly(3)(kDataQ7Msb downto kDataI0Lsb);
end if;
end if;
end process;
DataValidOut: process(RfClk, arReset_n)
begin
if arReset_n = '0' then
rDataValidDly <= (others => '0');
rDataValidOut <= '0';
elsif rising_edge(RfClk) then
rDataValidDly <= rDataValidDly(rDataValidDly'left-1 downto 0) &
c1DataValidIn;
-- Data valid out asserting based on phase alignment RfClk and Clk1x.
-- When RfClk and Clk1x are not phase aligned.
rDataValidOut <= rDataValidDly(2) and rReadyForOutput;
-- When RfClk and Clk1x are phase aligned.
if (rPhaseShiftReg(2) xor rPhaseShiftReg(1) xor rPhaseShiftReg(0)) = '1' then
rDataValidOut <= rDataValidDly(2) and rDataValidDly(3) and rReadyForOutput;
end if;
end if;
end process;
end RTL;
+80
View File
@@ -0,0 +1,80 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: dac_gearbox_4x2
//
// Description:
//
// Gearbox DAC data from 4 SPC to 2 SPC and corresponding 2x clock to 1x
// clock.
// This module incurs in one clk1x cycle of delay on the data and valid
// signals from input on the 1x domain to output on the 2x domain.
//
`default_nettype none
module dac_gearbox_4x2 (
input wire clk1x,
input wire reset_n_1x,
// Data is _presumed_ to be packed [Q3,I3,Q2,I2,Q1,I1,Q0,I0]
input wire [127:0] data_in_1x,
input wire valid_in_1x,
output wire ready_out_1x,
input wire clk2x,
// Data is packed [Q1,I1,Q0,I0] (I in LSBs)
output wire [ 63:0] data_out_2x,
output wire valid_out_2x
);
// Re-create the 1x clock in the 2x domain to produce a deterministic
// crossing.
reg toggle_1x, toggle_2x = 1'b0, toggle_2x_dly = 1'b0, valid_2x = 1'b0, valid_dly_2x = 1'b0;
reg [127:0] data_in_2x_dly0 = 128'b0, data_in_2x_dly1 = 32'b0;
reg [63 :0] data_2x_dly = 64'b0;
// Create a toggle in the 1x clock domain (clock divider /2).
always @(posedge clk1x or negedge reset_n_1x) begin
if ( ! reset_n_1x) begin
toggle_1x <= 1'b0;
end else begin
toggle_1x <= ! toggle_1x;
end
end
// Transfer the toggle from the 1x to the 2x domain. Delay the toggle in the
// 2x domain by one cycle and compare it to the non-delayed version. When
// they differ, push data_in[63:0] onto the output. When they match, push
// [127:64] onto the output.
//
// It is safe to not reset this domain because all of the input signals will
// be cleared by the 1x reset. Safe default values are assigned to all these
// registers.
always @(posedge clk2x) begin
toggle_2x <= toggle_1x;
toggle_2x_dly <= toggle_2x;
data_in_2x_dly0 <= data_in_1x;
data_in_2x_dly1 <= data_in_2x_dly0 ;
data_2x_dly <= 64'b0;
if (valid_2x) begin
data_2x_dly <= data_in_2x_dly1[127:64];
if (toggle_2x != toggle_2x_dly) begin
data_2x_dly <= data_in_2x_dly0[63:0];
end
end
// Valid is simply a transferred version of the 1x clock's valid. Delay it
// one more cycle to align outputs.
valid_2x <= valid_in_1x;
valid_dly_2x <= valid_2x;
end
assign valid_out_2x = valid_dly_2x;
assign data_out_2x = data_2x_dly;
assign ready_out_1x = 1'b1;
endmodule
`default_nettype wire
+124
View File
@@ -0,0 +1,124 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_gearbox_6x12
--
-- Description:
--
-- Gearbox to expand the data width from 6 SPC to 12 SPC.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity dac_gearbox_6x12 is
port(
Clk1x : in std_logic;
Clk2x : in std_logic;
ac1Reset_n : in std_logic;
ac2Reset_n : in std_logic;
-- 16 bit data packing: [Q5,I5,Q4,I4,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c2DataIn : in std_logic_vector(191 downto 0);
c2DataValidIn : in std_logic;
-- 16 bit data packing: [Q11,I11,Q10,I10,..,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c1DataOut : out std_logic_vector(383 downto 0) := (others => '0');
c1DataValidOut : out std_logic := '0'
);
end dac_gearbox_6x12;
architecture RTL of dac_gearbox_6x12 is
subtype Word_t is std_logic_vector(191 downto 0);
type Words_t is array(natural range<>) of Word_t;
signal c1DataInDly, c2DataInDly : Words_t(2 downto 0);
signal c2DataValidInDly : std_logic_vector(1 downto 0) := (others => '0');
signal c1PhaseCount, c2PhaseCount : std_logic := '0';
signal c1DataValidIn, c1DataValidDly0 : std_logic := '0';
begin
-- Input data pipeline.
InputValidPipeline: process(Clk2x, ac2Reset_n)
begin
if ac2Reset_n = '0' then
c2DataValidInDly <= (others => '0');
elsif rising_edge(Clk2x) then
c2DataValidInDly <= c2DataValidInDly(c2DataValidInDly'left-1 downto 0) &
c2DataValidIn;
end if;
end process;
InputDataPipeline: process(Clk2x)
begin
if rising_edge(Clk2x) then
c2DataInDly <= c2DataInDly(c2DataInDly'high-1 downto 0) & c2DataIn;
end if;
end process;
-- Process to determine if data valid was asserted when both clocks were
-- in-phase. Since we are crossing a 2x clock domain to a 1x clock domain,
-- there are only two possible phase. One is data valid assertion when both
-- clocks rising edges are aligned. The other case is data valid assertion
-- when Clk2x is aligned to the falling edge.
Clock2xPhaseCount: process(ac2Reset_n, Clk2x)
begin
if ac2Reset_n = '0' then
c2PhaseCount <= '0';
elsif rising_edge(Clk2x) then
-- This is a single bit counter. This counter is enabled for an extra
-- clock cycle to account for the output pipeline delay.
c2PhaseCount <= (not c2PhaseCount) and
(c2DataValidInDly(1) or c2DataValidInDly(0));
end if;
end process;
-- Crossing clock from Clk2x to Clk1x.
Clk2xToClk1xCrossing: process(Clk1x)
begin
if rising_edge(Clk1x) then
c1DataInDly <= c2DataInDly;
c1PhaseCount <= c2PhaseCount;
c1DataValidIn <= c2DataValidInDly(0);
end if;
end process;
-- Output data packing is determined based on when input data valid was
-- asserted. c1PhaseCount is '1' when input data valid was asserted when both
-- clocks are rising edge aligned. In this case, we can send data from the
-- with 1 and 2 pipeline delays.
-- When data valid is asserted when the two clock are not rising edge
-- aligned, we will use data from 2 and 3 pipeline delays.
DataOut: process(Clk1x)
begin
if rising_edge(Clk1x) then
c1DataOut <= c1DataInDly(1) & c1DataInDly(2);
if c1PhaseCount = '1' then
c1DataOut <= c1DataInDly(0) & c1DataInDly(1);
end if;
end if;
end process;
-- Similar to data output, when input data valid is asserted and both clocks
-- are rising edge aligned, the output data valid is asserted with a single
-- pipeline stage. If not, output data valid is asserted with two pipeline
-- stages.
DataValidOut: process(Clk1x, ac1Reset_n)
begin
if ac1Reset_n = '0' then
c1DataValidDly0 <= '0';
c1DataValidOut <= '0';
elsif rising_edge(Clk1x) then
c1DataValidDly0 <= c1DataValidIn;
c1DataValidOut <= c1DataValidDly0;
if c1PhaseCount = '1' then
c1DataValidOut <= c1DataValidIn;
end if;
end if;
end process;
end RTL;
+99
View File
@@ -0,0 +1,99 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_gearbox_6x8
--
-- Description:
--
-- Gearbox to expand the data width from 6 SPC to 8 SPC.
-- Input Clocks, all aligned to one another and coming from same MMCM
-- PLL reference clock = 61.44 or 62.5 MHz.
-- RfClk: 184.32 or 187.5 MHz (3x PLL reference clock)
-- Clk1x: 122.88 or 125 MHz (2x PLL reference clock)
-- Clk2x: 245.76 or 250 MHz (4x PLL reference clock)
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity dac_gearbox_6x8 is
port(
Clk1x : in std_logic;
Clk2x : in std_logic;
RfClk : in std_logic;
ac1Reset_n : in std_logic;
ac2Reset_n : in std_logic;
arReset_n : in std_logic;
-- 16 bit data packing: [Q5,I5,Q4,I4,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c2DataIn : in std_logic_vector(191 downto 0);
c2DataValidIn : in std_logic;
-- 16 bit data packing: [Q7,I7,Q6,I6,..,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
rDataOut : out std_logic_vector(255 downto 0) := (others => '0');
rReadyForOutput : in std_logic;
rDataValidOut : out std_logic := '0'
);
end dac_gearbox_6x8;
architecture struct of dac_gearbox_6x8 is
signal c1DataOut : std_logic_vector(383 downto 0);
signal c1DataValidOut : std_logic;
begin
-- Clk1x, Clk2x, and RfClk are source from the same PLL and have a known
-- phase relationship between power cycles. Since, they have known phase
-- relationship, clock crossing as be done without a dual clock FIFO or any
-- other handshaking mechanism. We cannot move data from Clk2x to RfClk
-- because of the clock relation between these two clocks will make it almost
-- impossible to close timing. So, we move data from Clk2x to Clk1x and then
-- to RfClk domain. Since, we need deterministic delay in the data path, we
-- cannot use a FIFO to do data crossing.
--
-- Clk1x = Sample clock/24
-- Clk2x = Sample clock/12
-- RfClk = Sample clock/16
--
-- Clk1x __/-----\_____/-----\_____/-----\_____/-----\_____/-----\___
-- | |
-- Clk2x __/--\__/--\__/--\__/--\__/--\ | |
-- | | | |
-- | | <- Setup relationship | | <- Setup relationship
-- | | | |
-- RfClk __/---\___/---\___/---\___/---\___/---\___/---\___/---\___/-
--
-- As you can see the setup relationship for passing data synchronously from
-- Clk2x to RfClk is very small (Sample clock period * 4). It is not possible
-- to close timing with this requirement. For passing data from Clk1x to
-- RfClk the setup relationship is (Sample clock period * 8) which is
-- relatively easy to close timing.
dac_gearbox_6x12_i: entity work.dac_gearbox_6x12 (RTL)
port map (
Clk1x => Clk1x,
Clk2x => Clk2x,
ac1Reset_n => ac1Reset_n,
ac2Reset_n => ac2Reset_n,
c2DataIn => c2DataIn,
c2DataValidIn => c2DataValidIn,
c1DataOut => c1DataOut,
c1DataValidOut => c1DataValidOut
);
dac_gearbox_12x8_i: entity work.dac_gearbox_12x8 (RTL)
port map (
Clk1x => Clk1x,
RfClk => RfClk,
ac1Reset_n => ac1Reset_n,
arReset_n => arReset_n,
c1DataIn => c1DataOut,
c1DataValidIn => c1DataValidOut,
rDataOut => rDataOut,
rReadyForOutput => rReadyForOutput,
rDataValidOut => rDataValidOut
);
end struct;
+81
View File
@@ -0,0 +1,81 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: ddc_400m_saturate
--
-- Description:
--
-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
-- comments below for full description.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library work;
use work.PkgRf.all;
entity ddc_400m_saturate is
port(
Clk : in std_logic;
-- This data is from the DDC with a sample width of 17 bits and 7 bits of
-- padding. Data format is, [Q3,I3, ... , Q0,I0] (I in LSBs)
cDataIn : in std_logic_vector(191 downto 0);
cDataValidIn : in std_logic;
-- 16 bits saturated data. Data format is [Q3,I3, ... , Q0,I0] (I in LSBs)
cDataOut : out std_logic_vector(127 downto 0);
cDataValidOut : out std_logic );
end ddc_400m_saturate;
architecture RTL of ddc_400m_saturate is
signal cDataOutSamples : Samples16_t(7 downto 0) := (others => (others => '0'));
signal cDataInSamples : Samples17_t(cDataOutSamples'range);
begin
-----------------------------------------------------------------------------
-- Saturation
--
-- The output of the Xilinx FIR Compiler has already been rounded on the LSB
-- side, but hasn't been saturated on the MSB side.
-- Coefficients = 18 bit, 1 integer bit (1.17)
-- Data In = 16 bits, 1 integer bit (1.15)
-- 1.17 * 1.15 = 2.32, and the Xilinx FIR core rounds to 2.15
-- Data Out = 17 bits, 2 integer bits (2.15), with 17 LSBs already rounded
-- off inside the FIR core.
-- We need to manually saturate the 2.15 number back to a 1.15 number
--
-- 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
-----------------------------------------------------------------------------
-- Logic to saturate input data to 16-bit signed value. Information on DDC
-- data packer is in PkgRf.vhd.
cDataInSamples <= to_Samples17(cDataIn);
GenSat: for i in cDataOutSamples'range generate
Saturation:
process(Clk)
begin
if rising_edge(Clk) then
cDataOutSamples(i) <= Saturate(cDataInSamples(i));
end if;
end process;
end generate GenSat;
DValidPipeline: process(Clk)
begin
if rising_edge(Clk) then
-- Pipeline data valid to match the data.
cDataValidOut <= cDataValidIn;
end if;
end process;
cDataOut <= to_stdlogicvector(cDataOutSamples);
end RTL;
+86
View File
@@ -0,0 +1,86 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: duc_400m_saturate
--
-- Description:
--
-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
-- comments below for full description.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library work;
use work.PkgRf.all;
entity duc_400m_saturate is
port(
Clk : in std_logic;
-- This data is from the DDC with a sample width of 18 bits and 6 bits of
-- padding. Data format is, [Q5,I5, ... , Q0,I0] (I in LSBs)
cDataIn : in std_logic_vector(287 downto 0);
cDataValidIn : in std_logic;
cReadyForInput : out std_logic;
-- 16 bits saturated data. Data format is [Q5,I5, ... , Q0,I0] (I in LSBs)
cDataOut : out std_logic_vector(191 downto 0);
cDataValidOut : out std_logic := '0');
end duc_400m_saturate;
architecture RTL of duc_400m_saturate is
signal cDataOutSamples : Samples16_t(11 downto 0) := (others => (others => '0'));
signal cDataInputSamples : Samples18_t(cDataOutSamples'range);
begin
-----------------------------------------------------------------------------
-- Saturation
--
-- The output of the Xilinx FIR Compiler has already been rounded on the LSB
-- side, but hasn't been saturated on the MSB side.
-- Coefficients = 18 bit, 1 integer bit (1.17)
-- Data In = 16 bits, 1 integer bit (1.15)
-- Xilinx FIR core rounds to output to 3.31. The filter coefficients has a
-- gain of 3 to compensate for the amplitude loss in interpolation, the
-- Xilinx FIR core rounds the output to 3.15.
-- Data Out = 18 bits, 3 integer bits (3.15), with 16 LSBs already rounded
-- off inside the FIR core.
-- We need to manually saturate the 3.15 number back to a 1.15 number
--
-- 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
-----------------------------------------------------------------------------
-- Logic to saturate input data to 16-bit signed value. Information on DUC data packer is in
-- PkgRf.vhd.
cDataInputSamples <= to_Samples18(cDataIn);
GenSat: for i in cDataOutSamples'range generate
Saturation:
process(Clk)
begin
if rising_edge(Clk) then
cDataOutSamples(i) <= Saturate(cDataInputSamples(i));
end if;
end process;
end generate GenSat;
DValidPipeline: process(Clk)
begin
if rising_edge(Clk) then
-- Pipeline data valid to match the data.
cDataValidOut <= cDataValidIn;
end if;
end process;
cDataOut <= to_stdlogicvector(cDataOutSamples);
cReadyForInput <= '1';
end RTL;
+387
View File
@@ -0,0 +1,387 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rf_core_400m
//
// Description:
//
// Top-level wrapper for the ADC/DAC processing logic. One of these wrappers
// exists for every supported Data Rate. An instance of this core should
// exist per dboard.
//
// Data/RF Specs:
// DBs: 1
// RX/DB: 2
// TX/DB: 2
// Data Rate: 122.88 or 125 MSps @ 4 SPC
//
// Input Clocks, all aligned to one another and coming from same MMCM
// rfdc_clk: 184.32 or 187.5 MHz (3x pll_ref_clk)
// rfdc_clk_2x: 368.64 or 375 MHz (6x pll_ref_clk)
// data_clk: 122.88 or 125 MHz (2x pll_ref_clk)
// data_clk_2x: 245.76 or 250 MHz (4x pll_ref_clk)
//
`default_nettype none
module rf_core_400m (
//---------------------------------------------------------------------------
// Clocking
//---------------------------------------------------------------------------
// Main Clock Inputs
input wire rfdc_clk,
input wire rfdc_clk_2x,
input wire data_clk,
input wire data_clk_2x,
// AXI4-Lite Config Clock
// This clock is used to synchronize status bits for the RFDC
// registers in the AXI-S clock domain.
input wire s_axi_config_clk,
//---------------------------------------------------------------------------
// RFDC Data Interfaces
//---------------------------------------------------------------------------
// All ports here are in the rfdc_clk domain.
// ADC
input wire [127:0] adc_data_in_i_tdata_0,
output wire adc_data_in_i_tready_0,
input wire adc_data_in_i_tvalid_0,
input wire [127:0] adc_data_in_q_tdata_0,
output wire adc_data_in_q_tready_0,
input wire adc_data_in_q_tvalid_0,
input wire [127:0] adc_data_in_i_tdata_1,
output wire adc_data_in_i_tready_1,
input wire adc_data_in_i_tvalid_1,
input wire [127:0] adc_data_in_q_tdata_1,
output wire adc_data_in_q_tready_1,
input wire adc_data_in_q_tvalid_1,
// DAC
output wire [255:0] dac_data_out_tdata_0,
input wire dac_data_out_tready_0,
output wire dac_data_out_tvalid_0,
output wire [255:0] dac_data_out_tdata_1,
input wire dac_data_out_tready_1,
output wire dac_data_out_tvalid_1,
//---------------------------------------------------------------------------
// User Data Interfaces
//---------------------------------------------------------------------------
// All ports here are in the data_clk domain.
// ADC
output wire [127:0] adc_data_out_tdata_0, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire adc_data_out_tvalid_0,
output wire [127:0] adc_data_out_tdata_1, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire adc_data_out_tvalid_1,
// DAC
input wire [127:0] dac_data_in_tdata_0, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire dac_data_in_tready_0,
input wire dac_data_in_tvalid_0,
input wire [127:0] dac_data_in_tdata_1, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire dac_data_in_tready_1,
input wire dac_data_in_tvalid_1,
//---------------------------------------------------------------------------
// Miscellaneous
//---------------------------------------------------------------------------
// Invert I/Q control signals from RFDC to DSP chain.
input wire [3:0] invert_adc_iq_rclk2,
input wire [3:0] invert_dac_iq_rclk2,
// Control/status vectors from/to RFDC.
// Notice these are all in the s_axi_config_clk domain.
output wire [15:0] dsp_info_sclk,
output wire [15:0] axi_status_sclk,
// Resets.
input wire adc_data_out_resetn_dclk,
input wire adc_enable_data_rclk,
input wire adc_rfdc_axi_resetn_rclk,
input wire dac_data_in_resetn_dclk,
input wire dac_data_in_resetn_dclk2x,
input wire dac_data_in_resetn_rclk,
input wire fir_resetn_rclk2x,
// Version (Constant)
output wire [95:0] version_info
);
`include "../../regmap/rfdc_regs_regmap_utils.vh"
`include "../../regmap/versioning_regs_regmap_utils.vh"
`include "../../regmap/versioning_utils.vh"
// Fixed for this implementation
localparam NUM_ADC_CHANNELS = 2;
localparam NUM_DAC_CHANNELS = 2;
// ADC data interface from RFDC.
wire [127:0] adc_data_in_i_tdata [0:7]; // 8 SPC (I)
wire [127:0] adc_data_in_q_tdata [0:7]; // 8 SPC (Q)
wire [ 7:0] adc_data_in_i_tready;
wire [ 7:0] adc_data_in_q_tready;
wire [ 7:0] adc_data_in_i_tvalid;
wire [ 7:0] adc_data_in_q_tvalid;
// DAC data interface to RFDC.
wire [255:0] dac_data_out_tdata [0:7]; // 8 SPC (I + Q)
wire [ 7:0] dac_data_out_tready;
wire [ 7:0] dac_data_out_tvalid;
// ADC data interface to user.
wire [127:0] adc_data_out_tdata [0:7]; // 4 SPC (I + Q)
wire [ 7:0] adc_data_out_tready;
wire [ 7:0] adc_data_out_tvalid;
// DAC data interface from user.
wire [127:0] dac_data_in_tdata_preswap [0:7]; // 4 SPC (I + Q)
wire [127:0] dac_data_in_tdata [0:7]; // 4 SPC (I + Q)
wire [ 7:0] dac_data_in_tready;
wire [ 7:0] dac_data_in_tvalid;
wire [ 7:0] invert_dac_iq_dclk;
wire [15:0] axi_status;
//---------------------------------------------------------------------------
// Resets, Debug and Misc.
//---------------------------------------------------------------------------
// Group all these status bits together. They don't toggle frequently so data
// coherency is not an issue here.
// Using constants for DB0 since the bits are the 16 LSBs in a 32-bit vector.
// DB1 simply uses the 16 MSBs when wiring the status vector.
assign axi_status[USER_ADC_TREADY_MSB :USER_ADC_TREADY ] = adc_data_out_tready[1:0];
assign axi_status[USER_ADC_TVALID_MSB :USER_ADC_TVALID ] = adc_data_out_tvalid[1:0];
assign axi_status[RFDC_ADC_I_TVALID_MSB:RFDC_ADC_I_TVALID] = adc_data_in_i_tvalid[1:0];
assign axi_status[RFDC_ADC_Q_TVALID_MSB:RFDC_ADC_Q_TVALID] = adc_data_in_q_tvalid[1:0];
assign axi_status[RFDC_ADC_I_TREADY_MSB:RFDC_ADC_I_TREADY] = adc_data_in_i_tready[1:0];
assign axi_status[RFDC_ADC_Q_TREADY_MSB:RFDC_ADC_Q_TREADY] = adc_data_in_q_tready[1:0];
assign axi_status[RFDC_DAC_TVALID_MSB :RFDC_DAC_TVALID ] = dac_data_out_tvalid[1:0];
assign axi_status[RFDC_DAC_TREADY_MSB :RFDC_DAC_TREADY ] = dac_data_out_tready[1:0];
synchronizer #(
.WIDTH (16),
.STAGES (2),
.INITIAL_VAL (0),
.FALSE_PATH_TO_IN (1)
) synchronizer_axis_status (
.clk (s_axi_config_clk),
.rst (1'b0),
.in (axi_status),
.out (axi_status_sclk)
);
// Drive the DSP info vector with information on this specific DSP chain.
assign dsp_info_sclk[FABRIC_DSP_BW_MSB :FABRIC_DSP_BW] = FABRIC_DSP_BW_400M;
assign dsp_info_sclk[FABRIC_DSP_RX_CNT_MSB:FABRIC_DSP_RX_CNT] = NUM_ADC_CHANNELS;
assign dsp_info_sclk[FABRIC_DSP_TX_CNT_MSB:FABRIC_DSP_TX_CNT] = NUM_DAC_CHANNELS;
//---------------------------------------------------------------------------
// ADC Post-Processing
//---------------------------------------------------------------------------
// Data comes from the RFDC as 8 SPC, separate streams for each channel and
// I/Q.
assign adc_data_in_i_tdata[0] = adc_data_in_i_tdata_0;
assign adc_data_in_q_tdata[0] = adc_data_in_q_tdata_0;
assign adc_data_in_i_tdata[1] = adc_data_in_i_tdata_1;
assign adc_data_in_q_tdata[1] = adc_data_in_q_tdata_1;
assign adc_data_in_i_tready_0 = adc_data_in_i_tready[0];
assign adc_data_in_i_tvalid[0] = adc_data_in_i_tvalid_0;
assign adc_data_in_q_tready_0 = adc_data_in_q_tready[0];
assign adc_data_in_q_tvalid[0] = adc_data_in_q_tvalid_0;
assign adc_data_in_i_tready_1 = adc_data_in_i_tready[1];
assign adc_data_in_i_tvalid[1] = adc_data_in_i_tvalid_1;
assign adc_data_in_q_tready_1 = adc_data_in_q_tready[1];
assign adc_data_in_q_tvalid[1] = adc_data_in_q_tvalid_1;
// ADC Data from the RFDC arrives here as 8 SPC with separate I and Q
// streams. It leaves the adc_100m_bd as 4 SPC with I and Q packed into a
// single 128 bit word.
genvar adc_num;
generate
for (adc_num=0; adc_num < (NUM_ADC_CHANNELS); adc_num = adc_num + 1)
begin : adc_gen
adc_400m_bd adc_400m_bd_gen (
.adc_data_out_resetn_dclk (adc_data_out_resetn_dclk),
.data_clk (data_clk),
.enable_data_to_fir_rclk (adc_enable_data_rclk),
.fir_resetn_rclk2x (fir_resetn_rclk2x),
.rfdc_adc_axi_resetn_rclk (adc_rfdc_axi_resetn_rclk),
.rfdc_clk (rfdc_clk),
.rfdc_clk_2x (rfdc_clk_2x),
.swap_iq_2x (invert_adc_iq_rclk2 [adc_num]),
.adc_q_data_in_tvalid (adc_data_in_q_tvalid[adc_num]),
.adc_q_data_in_tready (adc_data_in_q_tready[adc_num]),
.adc_q_data_in_tdata (adc_data_in_q_tdata [adc_num]),
.adc_i_data_in_tvalid (adc_data_in_i_tvalid[adc_num]),
.adc_i_data_in_tready (adc_data_in_i_tready[adc_num]),
.adc_i_data_in_tdata (adc_data_in_i_tdata [adc_num]),
.adc_data_out_tvalid (adc_data_out_tvalid [adc_num]),
.adc_data_out_tdata (adc_data_out_tdata [adc_num])
);
end
endgenerate
// Data is released to the user as 4 SPC, separate streams for each channel.
assign adc_data_out_tdata_0 = adc_data_out_tdata[0];
assign adc_data_out_tdata_1 = adc_data_out_tdata[1];
// There is no tready going to the ADC (one has to be always ready for ADC
// data), but it is still a component of the axi_status vector as a generic
// AXI stream status. Report 1'b1 to the status vector consistent with being
// always ready
assign adc_data_out_tready[0] = 1'b1;
assign adc_data_out_tvalid_0 = adc_data_out_tvalid[0];
assign adc_data_out_tready[1] = 1'b1;
assign adc_data_out_tvalid_1 = adc_data_out_tvalid[1];
//---------------------------------------------------------------------------
// DAC Pre-Processing
//---------------------------------------------------------------------------
// Data comes from the user as 4 SPC, separate streams for each channel.
assign dac_data_in_tdata_preswap[0] = dac_data_in_tdata_0;
assign dac_data_in_tdata_preswap[1] = dac_data_in_tdata_1;
assign dac_data_in_tready_0 = dac_data_in_tready[0];
assign dac_data_in_tvalid[0] = dac_data_in_tvalid_0;
assign dac_data_in_tready_1 = dac_data_in_tready[1];
assign dac_data_in_tvalid[1] = dac_data_in_tvalid_1;
// Optionally swap IQ data positions in the vector. First cross the swap
// vector over to the data_clk domain.
synchronizer #(
.WIDTH (8),
.STAGES (2),
.INITIAL_VAL (0),
.FALSE_PATH_TO_IN (1)
) synchronizer_invert_dac_iq (
.clk (data_clk),
.rst (1'b0),
.in (invert_dac_iq_rclk2),
.out (invert_dac_iq_dclk)
);
genvar dac_num;
generate
for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
begin : dac_swap_gen
//IO and Q0 swap
assign dac_data_in_tdata[dac_num][15:00] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][31:16]) : (dac_data_in_tdata_preswap[dac_num][15:0]);
assign dac_data_in_tdata[dac_num][31:16] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][15:00]) : (dac_data_in_tdata_preswap[dac_num][31:16]);
//I1 and Q1 swap
assign dac_data_in_tdata[dac_num][47:32] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][63:48]) : (dac_data_in_tdata_preswap[dac_num][47:32]);
assign dac_data_in_tdata[dac_num][63:48] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][47:32]) : (dac_data_in_tdata_preswap[dac_num][63:48]);
//I2 and Q2 swap
assign dac_data_in_tdata[dac_num][79:64] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][95:80]) : (dac_data_in_tdata_preswap[dac_num][79:64]);
assign dac_data_in_tdata[dac_num][95:80] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][79:64]) : (dac_data_in_tdata_preswap[dac_num][95:80]);
//I3 and Q3 swap
assign dac_data_in_tdata[dac_num][111:96] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][127:112]) : (dac_data_in_tdata_preswap[dac_num][111:96]);
assign dac_data_in_tdata[dac_num][127:112] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][111:96]) : (dac_data_in_tdata_preswap[dac_num][127:112]);
end
endgenerate
// These streams are then interpolated by dac_400m_bd, and form a single
// stream per channel, 8 SPC, packed: MSB [Sample7Q, Sample7I, ... ,
// Sample0Q, Sample0I] LSB.
generate
for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
begin : dac_gen
dac_400m_bd dac_400m_bd_gen (
.dac_data_in_resetn_dclk (dac_data_in_resetn_dclk),
.dac_data_in_resetn_dclk2x (dac_data_in_resetn_dclk2x),
.dac_data_in_resetn_rclk (dac_data_in_resetn_rclk),
.dac_data_in_tdata (dac_data_in_tdata [dac_num]),
.dac_data_in_tready (dac_data_in_tready [dac_num]),
.dac_data_in_tvalid (dac_data_in_tvalid [dac_num]),
.dac_data_out_tdata (dac_data_out_tdata [dac_num]),
.dac_data_out_tready (dac_data_out_tready[dac_num]),
.dac_data_out_tvalid (dac_data_out_tvalid[dac_num]),
.data_clk (data_clk),
.data_clk_2x (data_clk_2x),
.rfdc_clk (rfdc_clk)
);
end
endgenerate
// Data is released to the RFDC as 8 SPC, separate streams per channel (I/Q
// together).
assign dac_data_out_tdata_0 = dac_data_out_tdata[0];
assign dac_data_out_tdata_1 = dac_data_out_tdata[1];
assign dac_data_out_tready[0] = dac_data_out_tready_0;
assign dac_data_out_tvalid_0 = dac_data_out_tvalid[0];
assign dac_data_out_tready[1] = dac_data_out_tready_1;
assign dac_data_out_tvalid_1 = dac_data_out_tvalid[1];
//---------------------------------------------------------------------------
// Version
//---------------------------------------------------------------------------
// Version metadata, constants come from auto-generated
// versioning_regs_regmap_utils.vh
assign version_info = build_component_versions(
RF_CORE_400M_VERSION_LAST_MODIFIED_TIME,
build_version(
RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MAJOR,
RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MINOR,
RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_BUILD
),
build_version(
RF_CORE_400M_CURRENT_VERSION_MAJOR,
RF_CORE_400M_CURRENT_VERSION_MINOR,
RF_CORE_400M_CURRENT_VERSION_BUILD
)
);
endmodule
`default_nettype wire
//XmlParse xml_on
//<regmap name="VERSIONING_REGS_REGMAP">
// <group name="VERSIONING_CONSTANTS">
// <enumeratedtype name="RF_CORE_400M_VERSION" showhex="true">
// <info>
// 400 MHz RF core.{BR/}
// For guidance on when to update these revision numbers,
// please refer to the register map documentation accordingly:
// <li> Current version: @.VERSIONING_REGS_REGMAP..CURRENT_VERSION
// <li> Oldest compatible version: @.VERSIONING_REGS_REGMAP..OLDEST_COMPATIBLE_VERSION
// <li> Version last modified: @.VERSIONING_REGS_REGMAP..VERSION_LAST_MODIFIED
// </info>
// <value name="RF_CORE_400M_CURRENT_VERSION_MAJOR" integer="1"/>
// <value name="RF_CORE_400M_CURRENT_VERSION_MINOR" integer="0"/>
// <value name="RF_CORE_400M_CURRENT_VERSION_BUILD" integer="0"/>
// <value name="RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MAJOR" integer="1"/>
// <value name="RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MINOR" integer="0"/>
// <value name="RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_BUILD" integer="0"/>
// <value name="RF_CORE_400M_VERSION_LAST_MODIFIED_TIME" integer="0x20102617"/>
// </enumeratedtype>
// </group>
//</regmap>
//XmlParse xml_off
+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
@@ -0,0 +1,186 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_adc_gearbox_2x1
--
-- Description:
--
-- Self-checking testbench for adc_gearbox_2x1.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_adc_gearbox_2x1 is
end tb_adc_gearbox_2x1;
architecture RTL of tb_adc_gearbox_2x1 is
component adc_gearbox_2x1
port (
clk1x : in std_logic;
reset_n_1x : in std_logic;
adc_q_in_1x : in std_logic_vector(31 downto 0);
adc_i_in_1x : in std_logic_vector(31 downto 0);
valid_in_1x : in std_logic;
enable_1x : in std_logic;
clk2x : in std_logic;
swap_iq_2x : in std_logic;
adc_out_2x : out std_logic_vector(31 downto 0);
valid_out_2x : out std_logic);
end component;
signal cDataCheckNxtLo, cDataCheckLo : std_logic_vector(31 downto 0);
signal cDataCheckNxtHi, cDataCheckHi1, cDataCheckHi2 : std_logic_vector(31 downto 0);
signal adc_i_in_1x : std_logic_vector(31 downto 0);
signal adc_out_2x : std_logic_vector(31 downto 0);
signal adc_q_in_1x : std_logic_vector(31 downto 0);
signal enable_1x : std_logic;
signal reset_n_1x : std_logic;
signal swap_iq_2x : std_logic;
signal valid_in_1x : std_logic;
signal valid_out_2x : std_logic;
signal StopSim : boolean;
constant kPer : time := 10 ns;
signal Clk : std_logic := '1';
signal Clk2x : std_logic := '1';
procedure ClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk);
end loop;
end procedure ClkWait;
begin
Clk <= not Clk after kPer/2 when not StopSim else '0';
Clk2x <= not Clk2x after kPer/4 when not StopSim else '0';
dut: adc_gearbox_2x1
port map (
clk1x => Clk,
reset_n_1x => reset_n_1x,
adc_q_in_1x => adc_q_in_1x,
adc_i_in_1x => adc_i_in_1x,
valid_in_1x => valid_in_1x,
enable_1x => enable_1x,
clk2x => Clk2x,
swap_iq_2x => swap_iq_2x,
adc_out_2x => adc_out_2x,
valid_out_2x => valid_out_2x
);
main: process
begin
swap_iq_2x <= '0';
valid_in_1x <= '0';
enable_1x <= '0';
reset_n_1x <= '0';
ClkWait(5);
reset_n_1x <= '1';
ClkWait(5);
-- Ensure the outputs are quiet.
ClkWait(20);
assert valid_out_2x'stable(kPer*20) and valid_out_2x = '0'
report "valid not stable at de-asserted at startup"
severity error;
assert adc_out_2x'stable(kPer*20) and (adc_out_2x = x"00000000")
report "data not stable at zero at startup"
severity error;
-- Valid asserted, Enable asserted, Enable de-asserted, Valid de-asserted.
ClkWait(10);
valid_in_1x <= '1';
ClkWait(10);
enable_1x <= '1';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '1'
report "valid not stable at asserted"
severity error;
ClkWait(10);
enable_1x <= '0';
ClkWait(10);
valid_in_1x <= '0';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '0'
report "valid not stable at de-asserted"
severity error;
-- Enable asserted, Valid asserted, Enable de-asserted, Valid de-asserted.
ClkWait(10);
enable_1x <= '1';
ClkWait(10);
valid_in_1x <= '1';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '1'
report "valid not stable at asserted"
severity error;
ClkWait(10);
enable_1x <= '0';
ClkWait(10);
valid_in_1x <= '0';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '0'
report "valid not stable at de-asserted"
severity error;
StopSim <= true;
wait;
end process;
driver: process(Clk)
variable tempQdata : integer := 1;
variable tempIdata : integer := 128;
begin
if rising_edge(Clk) then
adc_q_in_1x <= std_logic_vector(to_unsigned(tempQdata+1,16)) & std_logic_vector(to_unsigned(tempQdata, 16));
adc_i_in_1x <= std_logic_vector(to_unsigned(tempIdata+1,16)) & std_logic_vector(to_unsigned(tempIdata, 16));
cDataCheckNxtLo <= std_logic_vector(to_unsigned(tempQdata,16)) & std_logic_vector(to_unsigned(tempIdata, 16));
cDataCheckNxtHi <= std_logic_vector(to_unsigned(tempQdata+1,16)) & std_logic_vector(to_unsigned(tempIdata+1,16));
tempQdata := tempQdata+2;
tempIdata := tempIdata+2;
end if;
end process;
checker: process(Clk2x)
variable tempout : integer := 1;
variable ExpectedData : std_logic_vector(31 downto 0) := (others => '0');
begin
if falling_edge(Clk2x) then
if Clk = '1' then
ExpectedData := cDataCheckLo;
else
ExpectedData := cDataCheckHi2;
end if;
if valid_out_2x = '1' then
assert adc_out_2x = ExpectedData
report "ADC data out mismatch from expected"
severity error;
tempout := tempout +1;
end if;
cDataCheckLo <= cDataCheckNxtLo;
cDataCheckHi1 <= cDataCheckNxtHi;
cDataCheckHi2 <= cDataCheckHi1;
end if;
end process;
end RTL;
@@ -0,0 +1,197 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_adc_gearbox_2x4
--
-- Description:
--
-- Self-checking testbench for the gearbox that expands the data width from 2
-- SPC to 4 SPC.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_adc_gearbox_2x4 is
end tb_adc_gearbox_2x4;
architecture RTL of tb_adc_gearbox_2x4 is
component adc_gearbox_2x4
port (
Clk1x : in std_logic;
Clk3x : in std_logic;
ac1Reset_n : in std_logic;
ac3Reset_n : in std_logic;
c3DataIn : in std_logic_vector(95 downto 0);
c3DataValidIn : in std_logic;
c1DataOut : out std_logic_vector(191 downto 0);
c1DataValidOut : out std_logic);
end component;
signal aTestReset : boolean;
signal ac1Reset_n : std_logic := '1';
signal ac3Reset_n : std_logic := '1';
signal c3DataIn : std_logic_vector( 95 downto 0) := (others => '0');
signal c3DataValidIn : std_logic := '0';
signal c1ExpectedData : std_logic_vector(191 downto 0) := (others => '0');
signal c1DataOut : std_logic_vector(191 downto 0) := (others => '0');
signal c1DataValidOut : std_logic;
signal StopSim : boolean;
constant kPer : time := 12 ns;
signal Clk1x : std_logic := '1';
signal Clk3x : std_logic := '1';
procedure Clk3xWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk3x);
end loop;
end procedure Clk3xWait;
procedure Clk1xWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk1x);
end loop;
end procedure Clk1xWait;
begin
Clk1x <= not Clk1x after kPer/2 when not StopSim else '0';
Clk3x <= not Clk3x after kPer/6 when not StopSim else '0';
dut: adc_gearbox_2x4
port map (
Clk1x => Clk1x,
Clk3x => Clk3x,
ac1Reset_n => ac1Reset_n,
ac3Reset_n => ac3Reset_n,
c3DataIn => c3DataIn,
c3DataValidIn => c3DataValidIn,
c1DataOut => c1DataOut,
c1DataValidOut => c1DataValidOut
);
main: process
procedure PhaseTest(WaitCycles : positive := 1) is
begin
-- Stop data generation by asserting this reset.
aTestReset <= true;
Clk1xWait;
ac1Reset_n <= '0';
ac3Reset_n <= '0';
Clk1xWait;
ac1Reset_n <= '1';
ac3Reset_n <= '1';
-- This wait is in Clk3x domain. This is used to change phase in which
-- data valid is asserted with respect to Clk3x and Clk1x rising edge.
-- Wait an additional 12 Clk3x cycles for the output data valid to be
-- de-asserted.
Clk3xWait(WaitCycles+12);
-- De-asserting test reset will start data generation.
aTestReset <= false;
-- Wait for a random time before we stop the test.
Clk3xWait(1000);
end procedure;
begin
-- Change phase between Clk1x and Clk3x. See details in the DUT.
-- The wait in each phase test is used to move the de-assertion of data
-- generation logic reset. By doing this, we can change data valid
-- assertion phase between Clk3x and Clk1x.
-- p0.
PhaseTest(1);
-- p1
PhaseTest(2);
-- p2.
PhaseTest(6);
-- Stop simulation
StopSim <= true;
wait;
end process;
-- Process to generate data to the DUT.
driver: process(Clk3x, aTestReset)
variable tempQdata : integer := 1;
variable tempIdata : integer := 128;
variable dataCount : integer := 0;
begin
if aTestReset then
tempQdata := 1;
tempIdata := 128;
dataCount := 0;
c3DataIn <= (others => '0');
c3DataValidIn <= '0';
elsif rising_edge(Clk3x) then
if dataCount < 2 then
c3DataIn <= "0000000" & std_logic_vector(to_unsigned(tempQdata+1,17)) &
"0000000" & std_logic_vector(to_unsigned(tempIdata+1,17)) &
"0000000" & std_logic_vector(to_unsigned(tempQdata+0,17)) &
"0000000" & std_logic_vector(to_unsigned(tempIdata+0,17));
dataCount := dataCount + 1;
c3DataValidIn <= '1';
tempQdata := tempQdata +2;
tempIdata := tempIdata +2;
elsif dataCount = 2 then
c3DataIn <= (others => '0');
dataCount := 0;
c3DataValidIn <= '0';
end if;
end if;
end process;
-- Process to generate expected data that is used to verify the DUT output.
expected_data: process(Clk1x)
variable tempQdata : integer := 1;
variable tempIdata : integer := 128;
begin
if rising_edge(Clk1x) then
if aTestReset and c1DataValidOut = '0' then
tempQdata := 1;
tempIdata := 128;
elsif c1DataValidOut = '1' then
tempQdata := tempQdata+4;
tempIdata := tempIdata+4;
end if;
c1ExpectedData <= "0000000" & std_logic_vector(to_unsigned(tempQdata+3,17)) &
"0000000" & std_logic_vector(to_unsigned(tempIdata+3,17)) &
"0000000" & std_logic_vector(to_unsigned(tempQdata+2,17)) &
"0000000" & std_logic_vector(to_unsigned(tempIdata+2,17)) &
"0000000" & std_logic_vector(to_unsigned(tempQdata+1,17)) &
"0000000" & std_logic_vector(to_unsigned(tempIdata+1,17)) &
"0000000" & std_logic_vector(to_unsigned(tempQdata+0,17)) &
"0000000" & std_logic_vector(to_unsigned(tempIdata+0,17));
end if;
end process;
-- Process to continuously check output data from the DUT.
checker: process(Clk1x)
begin
if falling_edge(Clk1x) then
if c1DataValidOut = '1' then
assert c1DataOut = c1ExpectedData
report "ADC data out mismatch from expected"
severity error;
end if;
end if;
end process;
end RTL;
@@ -0,0 +1,206 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_adc_gearbox_8x4
--
-- Description:
--
-- Self-checking testbench for adc_gearbox_8x4.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_adc_gearbox_8x4 is
end tb_adc_gearbox_8x4;
architecture RTL of tb_adc_gearbox_8x4 is
component adc_gearbox_8x4
port (
clk1x : in std_logic;
reset_n_1x : in std_logic;
adc_q_in_1x : in std_logic_vector(127 downto 0);
adc_i_in_1x : in std_logic_vector(127 downto 0);
valid_in_1x : in std_logic;
enable_1x : in std_logic;
clk2x : in std_logic;
swap_iq_2x : in std_logic;
adc_out_2x : out std_logic_vector(127 downto 0);
valid_out_2x : out std_logic);
end component;
signal cDataCheckNxtLo, cDataCheckLo: std_logic_vector(127 downto 0);
signal cDataCheckNxtHi : std_logic_vector(127 downto 0);
signal cDataCheckHi1, cDataCheckHi2: std_logic_vector(127 downto 0);
signal adc_i_in_1x : std_logic_vector(127 downto 0);
signal adc_out_2x : std_logic_vector(127 downto 0);
signal adc_q_in_1x : std_logic_vector(127 downto 0);
signal enable_1x : std_logic;
signal reset_n_1x : std_logic;
signal swap_iq_2x : std_logic;
signal valid_in_1x : std_logic;
signal valid_out_2x : std_logic;
signal StopSim : boolean;
constant kPer : time := 10 ns;
signal Clk : std_logic := '1';
signal Clk2x : std_logic := '1';
procedure ClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk);
end loop;
end procedure ClkWait;
begin
Clk <= not Clk after kPer/2 when not StopSim else '0';
Clk2x <= not Clk2x after kPer/4 when not StopSim else '0';
dut: adc_gearbox_8x4
port map (
clk1x => Clk,
reset_n_1x => reset_n_1x,
adc_q_in_1x => adc_q_in_1x,
adc_i_in_1x => adc_i_in_1x,
valid_in_1x => valid_in_1x,
enable_1x => enable_1x,
clk2x => Clk2x,
swap_iq_2x => swap_iq_2x,
adc_out_2x => adc_out_2x,
valid_out_2x => valid_out_2x
);
main: process
begin
swap_iq_2x <= '0';
valid_in_1x <= '0';
enable_1x <= '0';
reset_n_1x <= '0';
ClkWait(5);
reset_n_1x <= '1';
ClkWait(5);
-- Ensure the outputs are quiet.
ClkWait(20);
assert valid_out_2x'stable(kPer*20) and valid_out_2x = '0'
report "valid not stable at de-asserted at startup"
severity error;
assert adc_out_2x'stable(kPer*20) and (adc_out_2x = std_logic_vector(to_unsigned(0,128)))
report "data not stable at zero at startup"
severity error;
-- Valid asserted, Enable asserted, Enable de-asserted, Valid de-asserted.
ClkWait(10);
valid_in_1x <= '1';
ClkWait(10);
enable_1x <= '1';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '1'
report "valid not stable at asserted"
severity error;
ClkWait(10);
enable_1x <= '0';
ClkWait(10);
valid_in_1x <= '0';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '0'
report "valid not stable at de-asserted"
severity error;
-- Enable asserted, Valid asserted, Enable de-asserted, Valid de-asserted.
ClkWait(10);
enable_1x <= '1';
ClkWait(10);
valid_in_1x <= '1';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '1'
report "valid not stable at asserted"
severity error;
ClkWait(10);
enable_1x <= '0';
ClkWait(10);
valid_in_1x <= '0';
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '0'
report "valid not stable at de-asserted"
severity error;
StopSim <= true;
wait;
end process;
-- Process to generate input data to DUT and expected output data.
driver: process(Clk)
variable tempQdata : integer := 1;
variable tempIdata : integer := 128;
variable qData8spc : std_logic_vector(127 downto 0);
variable iData8spc : std_logic_vector(127 downto 0);
begin
if rising_edge(Clk) then
qdata8Spc := std_logic_vector(to_unsigned(tempQdata+7,16)) & std_logic_vector(to_unsigned(tempQdata+6,16)) &
std_logic_vector(to_unsigned(tempQdata+5,16)) & std_logic_vector(to_unsigned(tempQdata+4,16)) &
std_logic_vector(to_unsigned(tempQdata+3,16)) & std_logic_vector(to_unsigned(tempQdata+2,16)) &
std_logic_vector(to_unsigned(tempQdata+1,16)) & std_logic_vector(to_unsigned(tempQdata ,16));
adc_q_in_1x <= qData8Spc;
iData8spc := std_logic_vector(to_unsigned(tempIdata+7,16)) & std_logic_vector(to_unsigned(tempIdata+6,16)) &
std_logic_vector(to_unsigned(tempIdata+5,16)) & std_logic_vector(to_unsigned(tempIdata+4,16)) &
std_logic_vector(to_unsigned(tempIdata+3,16)) & std_logic_vector(to_unsigned(tempIdata+2,16)) &
std_logic_vector(to_unsigned(tempIdata+1,16)) & std_logic_vector(to_unsigned(tempIdata ,16));
adc_i_in_1x <= iData8Spc;
cDataCheckNxtLo <= qData8spc( 63 downto 48) & iData8spc( 63 downto 48) &
qData8spc( 47 downto 32) & iData8spc( 47 downto 32) &
qData8spc( 31 downto 16) & iData8spc( 31 downto 16) &
qData8spc( 15 downto 0) & iData8spc( 15 downto 0);
cDataCheckNxtHi <= qData8spc(127 downto 112) & iData8spc(127 downto 112) &
qData8spc(111 downto 96) & iData8spc(111 downto 96) &
qData8spc( 95 downto 80) & iData8spc( 95 downto 80) &
qData8spc( 79 downto 64) & iData8spc( 79 downto 64);
tempQdata := tempQdata+8;
tempIdata := tempIdata+8;
end if;
end process;
-- Process to check DUT output with expected data.
checker: process(Clk2x)
variable tempout : integer := 1;
variable ExpectedData : std_logic_vector(127 downto 0) := (others => '0');
begin
if falling_edge(Clk2x) then
if Clk = '1' then
ExpectedData := cDataCheckLo;
else
ExpectedData := cDataCheckHi2;
end if;
if valid_out_2x = '1' then
assert adc_out_2x = ExpectedData
report "ADC data out mismatch from expected"
severity error;
tempout := tempout +1;
end if;
cDataCheckLo <= cDataCheckNxtLo;
cDataCheckHi1 <= cDataCheckNxtHi;
cDataCheckHi2 <= cDataCheckHi1;
end if;
end process;
end RTL;
+119
View File
@@ -0,0 +1,119 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_capture_sysref
--
-- Description:
--
-- Self-checking testbench for tb_capture_sysref.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_capture_sysref is
end tb_capture_sysref;
architecture RTL of tb_capture_sysref is
component capture_sysref
port (
pll_ref_clk : in std_logic;
rfdc_clk : in std_logic;
sysref_in : in std_logic;
enable_rclk : in std_logic;
sysref_out_pclk : out std_logic;
sysref_out_rclk : out std_logic);
end component;
signal enable_rclk : std_logic := '0';
signal sysref_out_pclk : std_logic := '0';
signal sysref_out_rclk : std_logic := '0';
signal sysref_in : std_logic := '0';
signal SysrefDly, SysrefDlyDly, rSysref : std_logic := '0';
signal StopSim : boolean;
constant kPerPRC : time := 30 ns;
constant kPerRF : time := 10 ns;
signal PllRefClk : std_logic := '1';
signal RfdcClk : std_logic := '1';
procedure ClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(PllRefClk);
end loop;
end procedure ClkWait;
begin
PllRefClk <= not PllRefClk after kPerPRC/2 when not StopSim else '0';
RfdcClk <= not RfdcClk after kPerRF/2 when not StopSim else '0';
dut: capture_sysref
port map (
pll_ref_clk => PllRefClk,
rfdc_clk => RfdcClk,
sysref_in => sysref_in,
enable_rclk => enable_rclk,
sysref_out_pclk => sysref_out_pclk,
sysref_out_rclk => sysref_out_rclk
);
main: process
begin
enable_rclk <= '1';
ClkWait(100);
wait until falling_edge(sysref_out_rclk);
ClkWait;
wait until falling_edge(RfdcClk);
enable_rclk <= '0';
ClkWait(100);
wait until falling_edge(RfdcClk);
enable_rclk <= '1';
ClkWait(100);
StopSim <= true;
wait;
end process;
sysref: process(PllRefClk)
variable count : integer := 1;
begin
if rising_edge(PllRefClk) then
count := count +1;
if count = 10 then
sysref_in <= not sysref_in;
count := 1;
end if;
end if;
end process;
checker_pll_ref_clk: process(PllRefClk)
begin
if falling_edge(PllRefClk) then
SysrefDly <= sysref_in;
SysrefDlyDly <= SysrefDly;
assert SysrefDlyDly = sysref_out_pclk
report "SYSREF incorrectly captured in the PllRefClk domain"
severity error;
end if;
end process;
checker_rfdc_clk: process(RfdcClk)
begin
if falling_edge(RfdcClk) then
rSysref <= sysref_out_pclk;
assert (rSysref = sysref_out_rclk) or (enable_rclk = '0')
report "SYSREF incorrectly captured in the RfdcClk domain."
severity error;
end if;
end process;
end RTL;
@@ -0,0 +1,197 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_dac_gearbox_12x8
--
-- Description:
--
-- Self-checking testbench for a gearbox that decreases the SPCs from 12 to
-- 8.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_dac_gearbox_12x8 is
end tb_dac_gearbox_12x8;
architecture RTL of tb_dac_gearbox_12x8 is
signal TestStart : boolean;
signal ac1Reset_n : std_logic := '0';
signal arReset_n : std_logic := '0';
signal c1DataIn : std_logic_vector(383 downto 0) := (others => '0');
signal c1DataValidIn : std_logic := '0';
signal rDataOut : std_logic_vector(255 downto 0);
signal rReadyForOutput : std_logic := '1';
signal rDataValidOut : std_logic;
signal rDataToCheck, rDataToCheckDly0, rDataToCheckDly1, rDataToCheckDly2,
rDataToCheckDly3, rDataToCheckDly4
: std_logic_vector(255 downto 0) := (others => '0');
signal StopSim : boolean;
constant kPer : time := 12 ns;
signal Clk1x: std_logic := '1';
signal RfClk: std_logic := '1';
procedure RfClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(RfClk);
end loop;
end procedure RfClkWait;
procedure Clk1xWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk1x);
end loop;
end procedure Clk1xWait;
begin
Clk1x <= not Clk1x after kPer/4 when not StopSim else '0';
RfClk <= not RfClk after kPer/6 when not StopSim else '0';
dut: entity WORK.dac_gearbox_12x8 (RTL)
port map (
Clk1x => Clk1x,
RfClk => RfClk,
ac1Reset_n => ac1Reset_n,
arReset_n => arReset_n,
c1DataIn => c1DataIn,
c1DataValidIn => c1DataValidIn,
rDataOut => rDataOut,
rReadyForOutput => rReadyForOutput,
rDataValidOut => rDataValidOut
);
main: process
-- Procedure to start and stop data generation.
-- WaitCycles : This is a wait in Clk1x cycle. This is used to shift data
-- valid assertion. Depending on the Clk1x cycle, data valid
-- will be asserted either when both RfClk and Clk1x are phase
-- aligned or when both clocks are not phase aligned.
procedure PhaseTest(WaitCycles : positive := 1) is
begin
for i in 0 to 31 loop
-- Wait for certain RfClk cycles before starting the test.
Clk1xWait(WaitCycles);
TestStart <= true;
-- Random wait
Clk1xWait(1000+i);
TestStart <= false;
-- wait for few clock cycles for the output data valid to de-assert.
Clk1xWait(10);
end loop;
end procedure;
begin
ac1Reset_n <= '0';
arReset_n <= '0';
TestStart <= false;
Clk1xWait(5);
ac1Reset_n <= '1';
arReset_n <= '1';
rReadyForOutput <= '1';
-- RfClk and Clk1x are phase aligned
PhaseTest(1);
-- RfClk and Clk1x are phase aligned
PhaseTest(2);
-- RfClk and Clk1x are not phase aligned
PhaseTest(3);
-- Stop data input to the DUT and wait for few clock cycles for the output
-- data valid to be de-asserted.
TestStart <= false;
RfClkWait(10);
StopSim <= true;
wait;
end process;
-- Process to generate input data.
driver: process(Clk1x)
variable qDataIn : unsigned(15 downto 0) := x"0001";
variable iDataIn : unsigned(15 downto 0) := x"0080";
begin
if rising_edge(Clk1x) then
c1DataValidIn <= '0';
if TestStart then
c1DataValidIn <= '1';
c1DataIn <= std_logic_vector((qDataIn+11) & (iDataIn+11) &
(qDataIn+10) & (iDataIn+10) &
(qDataIn+9) & (iDataIn+9) &
(qDataIn+8) & (iDataIn+8) &
(qDataIn+7) & (iDataIn+7) &
(qDataIn+6) & (iDataIn+6) &
(qDataIn+5) & (iDataIn+5) &
(qDataIn+4) & (iDataIn+4) &
(qDataIn+3) & (iDataIn+3) &
(qDataIn+2) & (iDataIn+2) &
(qDataIn+1) & (iDataIn+1) &
(qDataIn+0) & (iDataIn+0));
qDataIn := qDataIn+12;
iDataIn := iDataIn+12;
else
c1DataValidIn <= '0';
qDataIn := x"0001";
iDataIn := x"0080";
end if;
end if;
end process;
-- Process to generate expected output data.
ExpectedData: process(RfClk)
variable qDataOut : unsigned(15 downto 0) := x"0001";
variable iDataOut : unsigned(15 downto 0) := x"0080";
begin
if rising_edge(RfClk) then
if TestStart then
rDataToCheck <= std_logic_vector((qDataOut+7) & (iDataOut+7) &
(qDataOut+6) & (iDataOut+6) &
(qDataOut+5) & (iDataOut+5) &
(qDataOut+4) & (iDataOut+4) &
(qDataOut+3) & (iDataOut+3) &
(qDataOut+2) & (iDataOut+2) &
(qDataOut+1) & (iDataOut+1) &
(qDataOut+0) & (iDataOut+0));
-- Data output that has to be verified.
qDataOut := qDataOut+8;
iDataOut := iDataOut+8;
else
qDataOut := x"0001";
iDataOut := x"0080";
end if;
rDataToCheckDly0 <= rDataToCheck;
rDataToCheckDly1 <= rDataToCheckDly0;
rDataToCheckDly2 <= rDataToCheckDly1;
rDataToCheckDly3 <= rDataToCheckDly2;
rDataToCheckDly4 <= rDataToCheckDly3;
end if;
end process;
-- Process to check output data with expected data.
checker: process(RfClk)
begin
if falling_edge(RfClk) then
if rDataValidOut = '1' then
assert rDataOut = rDataToCheckDly4
report "DAC data out mismatch from expected"
severity error;
end if;
end if;
end process;
end RTL;
@@ -0,0 +1,168 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_dac_gearbox_4x2
--
-- Description:
--
-- Self-checking testbench used to test the gearbox that reduces a 4 SPC data
-- into a 2 SPC data.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_dac_gearbox_4x2 is
end tb_dac_gearbox_4x2;
architecture RTL of tb_dac_gearbox_4x2 is
component dac_gearbox_4x2
port (
clk1x : in std_logic;
reset_n_1x : in std_logic;
data_in_1x : in std_logic_vector(127 downto 0);
valid_in_1x : in std_logic;
ready_out_1x : out std_logic;
clk2x : in std_logic;
data_out_2x : out std_logic_vector(63 downto 0);
valid_out_2x : out std_logic);
end component;
signal TestStart : boolean;
signal data_in_1x : std_logic_vector(127 downto 0);
signal data_out_2x : std_logic_vector(63 downto 0);
signal ready_out_1x : std_logic;
signal reset_n_1x : std_logic;
signal valid_in_1x : std_logic;
signal valid_out_2x : std_logic;
signal StopSim : boolean;
constant kPer : time := 10 ns;
signal Clk: std_logic := '1';
signal Clk2x: std_logic := '1';
signal c2DataToCheck, c2DataToCheckDly0, c2DataToCheckDly1, c2DataToCheckDly2
: std_logic_vector(63 downto 0) := (others => '0');
procedure ClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk);
end loop;
end procedure ClkWait;
begin
Clk <= not Clk after kPer/2 when not StopSim else '0';
Clk2x <= not Clk2x after kPer/4 when not StopSim else '0';
dut: dac_gearbox_4x2
port map (
clk1x => Clk,
reset_n_1x => reset_n_1x,
data_in_1x => data_in_1x,
valid_in_1x => valid_in_1x,
ready_out_1x => ready_out_1x,
clk2x => Clk2x,
data_out_2x => data_out_2x,
valid_out_2x => valid_out_2x
);
main: process
begin
reset_n_1x <= '0';
TestStart <= false;
ClkWait(5);
reset_n_1x <= '1';
ClkWait(5);
-- Ensure the outputs are quiet.
ClkWait(20);
assert valid_out_2x'stable(kPer*20) and valid_out_2x = '0'
report "valid not stable at de-asserted at startup"
severity error;
assert data_out_2x'stable(kPer*20) and (data_out_2x = x"0000000000000000")
report "data not stable at zero at startup"
severity error;
-- Valid asserted, Enable asserted, Enable de-asserted, Valid de-asserted.
ClkWait(10);
TestStart <= true;
ClkWait(110);
assert valid_out_2x'stable(kPer*100) and valid_out_2x = '1'
report "valid not stable at asserted"
severity error;
TestStart <= false;
ClkWait(10);
StopSim <= true;
wait;
end process;
-- Process to generate input data to DUT.
driver: process(Clk)
variable tempQdata : integer := 1;
variable tempIdata : integer := 128;
begin
if rising_edge(Clk) then
valid_in_1x <= '0';
if TestStart then
valid_in_1x <= '1';
data_in_1x <= std_logic_vector(to_unsigned(tempQdata+3,16)) & std_logic_vector(to_unsigned(tempIdata+3,16)) &
std_logic_vector(to_unsigned(tempQdata+2,16)) & std_logic_vector(to_unsigned(tempIdata+2,16)) &
std_logic_vector(to_unsigned(tempQdata+1,16)) & std_logic_vector(to_unsigned(tempIdata+1,16)) &
std_logic_vector(to_unsigned(tempQdata+0,16)) & std_logic_vector(to_unsigned(tempIdata+0,16));
tempQdata := tempQdata+4;
tempIdata := tempIdata+4;
end if;
end if;
end process;
-- Process to generate expected data out of the DUT.
ExpectedData: process(Clk2x)
variable qDataOut : unsigned(15 downto 0) := x"0001";
variable iDataOut : unsigned(15 downto 0) := x"0080";
begin
if rising_edge(Clk2x) then
if TestStart then
c2DataToCheck <= std_logic_vector((qDataOut+1) & (iDataOut+1) &
(qDataOut+0) & (iDataOut+0));
qDataOut := qDataOut+2;
iDataOut := iDataOut+2;
else
qDataOut := x"0001";
iDataOut := x"0080";
end if;
c2DataToCheckDly0 <= c2DataToCheck;
c2DataToCheckDly1 <= c2DataToCheckDly0;
c2DataToCheckDly2 <= c2DataToCheckDly1;
end if;
end process;
-- Process to check DUT output data with expected data.
checker: process(Clk2x)
begin
if falling_edge(Clk2x) then
if valid_out_2x = '1' then
assert data_out_2x = c2DataToCheckDly2
report "DAC data out mismatch from expected"
severity error;
end if;
assert ready_out_1x = '1'
report "Ready for output is not asserted"
severity error;
end if;
end process;
end RTL;
@@ -0,0 +1,187 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_dac_gearbox_6x12
--
-- Description:
--
-- Self-checking testbench used to test the gearbox that expands a 6 SPC data
-- into a 12 SPC data.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_dac_gearbox_6x12 is
end tb_dac_gearbox_6x12;
architecture RTL of tb_dac_gearbox_6x12 is
signal TestStart : boolean;
signal ac1Reset_n : std_logic;
signal ac2Reset_n : std_logic;
signal c1DataOut : std_logic_vector(383 downto 0);
signal c1DataValidOut : std_logic;
signal c2DataIn : std_logic_vector(191 downto 0) := (others => '0');
signal c2DataValidIn : std_logic := '0';
signal InPhase : boolean := false;
signal c1DataToCheck, c1DataToCheckDly0, c1DataToCheckDly1, c1DataToCheckDly2
: std_logic_vector(383 downto 0) := (others => '0');
signal StopSim : boolean;
constant kPer : time := 12 ns;
signal Clk1x: std_logic := '1';
signal Clk2x: std_logic := '1';
procedure Clk2xWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk2x);
end loop;
end procedure Clk2xWait;
begin
Clk1x <= not Clk1x after kPer/4 when not StopSim else '0';
Clk2x <= not Clk2x after kPer/8 when not StopSim else '0';
dut: entity WORK.dac_gearbox_6x12 (RTL)
port map (
Clk1x => Clk1x,
Clk2x => Clk2x,
ac1Reset_n => ac1Reset_n,
ac2Reset_n => ac2Reset_n,
c2DataIn => c2DataIn,
c2DataValidIn => c2DataValidIn,
c1DataOut => c1DataOut,
c1DataValidOut => c1DataValidOut
);
main: process
-- Procedure to start and stop data generation.
-- WaitCycles : This is a wait in Clk2x cycle. This is used to shift data
-- valid assertion. Depending on the Clk2x cycle, data valid
-- will be asserted either when both Clk1x and Clk2x are phase
-- aligned or when both clocks are not phase aligned.
-- Phase : This input is used in the logic that is used to check
-- output data with expected data. If data valid was asserted
-- when both clocks were phase aligned, then this input is
-- set to true and vice versa.
procedure PhaseTest(WaitCycles : positive := 1;
Phase : boolean := false) is
begin
-- Wait for certain Clk2x cycles before starting the test.
Clk2xWait(WaitCycles);
InPhase <= Phase;
TestStart <= true;
Clk2xWait(1000); -- Random wait.
TestStart <= false;
-- wait for few clock cycles for the output data valid to de-assert.
Clk2xWait(10);
end procedure;
begin
-- Assert and de-assert reset.
ac1Reset_n <= '0';
ac2Reset_n <= '0';
TestStart <= false;
Clk2xWait(5);
ac1Reset_n <= '1';
ac2Reset_n <= '1';
PhaseTest(1, true);
PhaseTest(3, false);
PhaseTest(5, true);
-- Stop data input to the DUT and wait for few clock cycles for the output
-- data valid to be de-asserted.
TestStart <= false;
Clk2xWait(10);
StopSim <= true;
wait;
end process;
driver: process(Clk2x)
variable tempQdata : unsigned(15 downto 0) := x"0001";
variable tempIdata : unsigned(15 downto 0) := x"0080";
begin
if rising_edge(Clk2x) then
c2DataValidIn <= '0';
if TestStart then
c2DataValidIn <= '1';
c2DataIn <= std_logic_vector((tempQdata+5) & (tempIdata+5) &
(tempQdata+4) & (tempIdata+4) &
(tempQdata+3) & (tempIdata+3) &
(tempQdata+2) & (tempIdata+2) &
(tempQdata+1) & (tempIdata+1) &
(tempQdata+0) & (tempIdata+0));
tempQdata := tempQdata +6;
tempIdata := tempIdata +6;
else
c2DataValidIn <= '0';
tempQdata := x"0001";
tempIdata := x"0080";
end if;
end if;
end process;
-- Process to generate expected data out of the DUT.
ExpectedData: process(Clk1x)
variable qDataOut : unsigned(15 downto 0) := x"0001";
variable iDataOut : unsigned(15 downto 0) := x"0080";
begin
if rising_edge(Clk1x) then
if TestStart then
c1DataToCheck <= std_logic_vector((qDataOut+11) & (iDataOut+11) &
(qDataOut+10) & (iDataOut+10) &
(qDataOut+9) & (iDataOut+9) &
(qDataOut+8) & (iDataOut+8) &
(qDataOut+7) & (iDataOut+7) &
(qDataOut+6) & (iDataOut+6) &
(qDataOut+5) & (iDataOut+5) &
(qDataOut+4) & (iDataOut+4) &
(qDataOut+3) & (iDataOut+3) &
(qDataOut+2) & (iDataOut+2) &
(qDataOut+1) & (iDataOut+1) &
(qDataOut+0) & (iDataOut+0));
qDataOut := qDataOut+12;
iDataOut := iDataOut+12;
else
qDataOut := x"0001";
iDataOut := x"0080";
end if;
c1DataToCheckDly0 <= c1DataToCheck;
c1DataToCheckDly1 <= c1DataToCheckDly0;
c1DataToCheckDly2 <= c1DataToCheckDly1;
end if;
end process;
-- Process to check output data with expected data.
checker: process(Clk1x)
begin
if falling_edge(Clk1x) then
if c1DataValidOut = '1' and InPhase then
assert c1DataOut = c1DataToCheckDly1
report "ADC data out mismatch from expected"
severity warning;
elsif c1DataValidOut = '1' and (not InPhase) then
assert c1DataOut = c1DataToCheckDly2
report "ADC data out mismatch from expected"
severity warning;
end if;
end if;
end process;
end RTL;
@@ -0,0 +1,125 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_ddc_400m_saturate
--
-- Description:
--
-- Self-checking testbench used to check the saturation logic needed in DDC.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library WORK;
use WORK.PkgRf.all;
entity tb_ddc_400m_saturate is
end tb_ddc_400m_saturate;
architecture RTL of tb_ddc_400m_saturate is
component ddc_400m_saturate
port (
Clk : in std_logic;
cDataIn : in std_logic_vector(191 downto 0);
cDataValidIn : in std_logic;
cDataOut : out std_logic_vector(127 downto 0);
cDataValidOut : out std_logic);
end component;
signal TestStart : boolean := false;
signal cDataIn : std_logic_vector(191 downto 0);
signal cDataOut : std_logic_vector(127 downto 0);
signal cDataValidIn : std_logic;
signal cDataValidOut : std_logic;
signal StopSim : boolean;
constant kPer : time := 10 ns;
constant kSamplesPerClock : integer := 8;
signal Clk: std_logic := '1';
procedure ClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk);
end loop;
end procedure ClkWait;
begin
Clk <= not Clk after kPer/2 when not StopSim else '0';
dut: ddc_400m_saturate
port map (
Clk => Clk,
cDataIn => cDataIn,
cDataValidIn => cDataValidIn,
cDataOut => cDataOut,
cDataValidOut => cDataValidOut);
main: process
begin
ClkWait;
TestStart <= false;
ClkWait;
TestStart <= true;
-- This wait is needed to sweep through the entire range of 17 bits signed
-- value. Since we operate the saturation logic with 8 samples per cycle,
-- we need to wait for 2^kDdcDataOutWidth/8. We are adding an extra 10
-- clock cycles wait just as a buffer for the DUT latency.
ClkWait(2**kDdcDataOutWidth/kSamplesPerClock + 10);
StopSim <= true;
wait;
end process;
-- Process to generate 17-bit signed data.
DataGen: process(Clk)
variable Sample : Sample17_t := kSmallest17;
begin
if falling_edge(Clk) then
if TestStart then
cDataValidIn <= '1';
cDataIn <= "0000000" & std_logic_vector(Sample+kSamplesPerClock-1) &
"0000000" & std_logic_vector(Sample+kSamplesPerClock-2) &
"0000000" & std_logic_vector(Sample+kSamplesPerClock-3) &
"0000000" & std_logic_vector(Sample+kSamplesPerClock-4) &
"0000000" & std_logic_vector(Sample+kSamplesPerClock-5) &
"0000000" & std_logic_vector(Sample+kSamplesPerClock-6) &
"0000000" & std_logic_vector(Sample+kSamplesPerClock-7) &
"0000000" & std_logic_vector(Sample+kSamplesPerClock-8);
Sample := Sample +8;
end if;
end if;
end process;
-- Check if saturation and data packing is done correctly.
DataCheck: process(Clk)
variable Sample : Sample17_t := kSmallest17;
variable ExpectedData : std_logic_vector(15 downto 0);
begin
if falling_edge(Clk) then
if cDataValidOut then
for i in 1 to 8 loop
ExpectedData := tb_saturate(std_logic_vector(Sample));
assert cDataOut(kSatDataWidth*i-1 downto kSatDataWidth*(i-1)) = ExpectedData
report "Saturation data out mismatch in index : " & to_string(i) & LF &
"Expected data is : " & to_hstring(ExpectedData) & LF &
"Received data is : " & to_hstring(cDataOut(kSatDataWidth*i-1 downto kSatDataWidth*(i-1)))
severity error;
Sample := Sample+1;
end loop;
end if;
end if;
end process;
end RTL;
@@ -0,0 +1,133 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_duc_400m_saturate
--
-- Description:
--
-- Self-checking testbench used to check the saturation logic needed in DDC.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library WORK;
use WORK.PkgRf.all;
entity tb_duc_400m_saturate is
end tb_duc_400m_saturate;
architecture RTL of tb_duc_400m_saturate is
component duc_400m_saturate
port (
Clk : in std_logic;
cDataIn : in std_logic_vector(287 downto 0);
cDataValidIn : in std_logic;
cReadyForInput : out std_logic;
cDataOut : out std_logic_vector(191 downto 0);
cDataValidOut : out std_logic := '0');
end component;
signal TestStart : boolean := false;
signal cDataIn : std_logic_vector(287 downto 0);
signal cDataOut : std_logic_vector(191 downto 0);
signal cDataValidIn : std_logic;
signal cDataValidOut : std_logic;
signal StopSim : boolean;
constant kPer : time := 10 ns;
constant kSamplesPerClock : integer := 12;
signal Clk: std_logic := '1';
procedure ClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(Clk);
end loop;
end procedure ClkWait;
begin
Clk <= not Clk after kPer/2 when not StopSim else '0';
-- cReadyForInput is a constant in the design and is not being tested.
dut: duc_400m_saturate
port map (
Clk => Clk,
cDataIn => cDataIn,
cDataValidIn => cDataValidIn,
cReadyForInput => open,
cDataOut => cDataOut,
cDataValidOut => cDataValidOut);
main: process
begin
ClkWait;
TestStart <= false;
ClkWait;
TestStart <= true;
-- This wait is needed to sweep through the entire range of 18 bits signed
-- value. Since we operate the saturation logic with 12 samples per cycle,
-- we need to wait for 2^kDucDataOutWidth/12. We are adding an extra 10
-- clock cycles wait just as a buffer for the DUT latency.
ClkWait(2**kDucDataOutWidth/kSamplesPerClock + 10);
StopSim <= true;
wait;
end process;
-- Process to generate 18-bit signed data.
DataGen: process(Clk)
variable Sample : Sample18_t := kSmallest18;
begin
if falling_edge(Clk) then
if TestStart then
cDataValidIn <= '1';
cDataIn <= "000000" & std_logic_vector(Sample+kSamplesPerClock-1) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-2) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-3) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-4) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-5) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-6) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-7) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-8) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-9) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-10) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-11) &
"000000" & std_logic_vector(Sample+kSamplesPerClock-12);
Sample := Sample +12;
end if;
end if;
end process;
-- Check if saturation and data packing is done correctly.
DataCheck: process(Clk)
variable Sample : Sample18_t := kSmallest18;
variable ExpectedData : std_logic_vector(15 downto 0);
begin
if falling_edge(Clk) then
if cDataValidOut then
for i in 1 to 12 loop
ExpectedData := tb_saturate(std_logic_vector(Sample));
assert cDataOut(kSatDataWidth*i-1 downto kSatDataWidth*(i-1)) = ExpectedData
report "Saturation data out mismatch in index : " & to_string(i) & LF &
"Expected data is : " & to_hstring(ExpectedData) & LF &
"Received data is : " & to_hstring(cDataOut(kSatDataWidth*i-1 downto kSatDataWidth*(i-1)))
severity error;
Sample := Sample+1;
end loop;
end if;
end if;
end process;
end RTL;
+281
View File
@@ -0,0 +1,281 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_rf_nco_reset
--
-- Description:
--
-- Self-checking testbench for NCO reset sequencing.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity tb_rf_nco_reset is
end tb_rf_nco_reset;
architecture RTL of tb_rf_nco_reset is
signal cAdc0xNcoUpdateReq : std_logic;
signal cAdc2xNcoUpdateReq : std_logic;
signal cDac0xNcoUpdateReq : std_logic;
signal cDac0xSysrefIntGating : std_logic;
signal cDac0xSysrefIntReenable : std_logic;
signal cDac1xNcoUpdateReq : std_logic;
signal cNcoPhaseRst : std_logic;
signal cNcoUpdateEn : std_logic_vector(5 downto 0);
signal dNcoResetDone : std_logic;
signal cDac0xNcoUpdateBusy : std_logic_vector(1 downto 0) := "00";
signal dStartNcoReset : std_logic := '0';
signal cAdc0xNcoUpdateBusy : std_logic := '0';
signal cAdc2xNcoUpdateBusy : std_logic := '0';
signal cDac1xNcoUpdateBusy : std_logic := '0';
signal cSysref_ms, cSysref : std_logic := '0';
signal cSysrefDlyd : std_logic_vector(1 downto 0) := "00";
signal cDac0xSysrefIntGatingDlyd : std_logic := '0';
signal cNcoPhaseRstDlyd : std_logic_vector(2 downto 0) := "000";
signal cWrCount : integer := 0;
type RfdcNcoState_t is (Idle, GateSysref, UpdateReq, CheckUpdate,
SysrefEn, WaitForSysref, ResetDone);
signal cRfdcNcoState : RfdcNcoState_t := Idle;
signal StopSim : boolean;
constant kConfigClkPer : time := 25 ns;
-- SYSREF period is 2.5 MHz.
constant kSysrefPer : time := 400 ns;
-- DataClk period is 125 MHz and generated from the same clocking chip that
-- generated SYSREF and are related.
constant kDataClkPer : time := kSysrefPer/50;
signal ConfigClk : std_logic := '0';
signal DataClk : std_logic := '0';
signal dSysref : std_logic := '0';
procedure DataClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(DataClk);
end loop;
end procedure DataClkWait;
procedure ConfigClkWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(ConfigClk);
end loop;
end procedure ConfigClkWait;
procedure SysrefWait(X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(dSysref);
end loop;
end procedure SysrefWait;
begin
ConfigClk <= not ConfigClk after kConfigClkPer/2 when not StopSim else '0';
DataClk <= not DataClk after kDataClkPer/2 when not StopSim else '0';
dSysref <= not dSysref after kSysrefPer/2 when not StopSim else '0';
-- Both cNcoPhaseRst and cNcoUpdateEn are constants in the DUT.
dut: entity WORK.rf_nco_reset (RTL)
port map (
ConfigClk => ConfigClk,
DataClk => DataClk,
dSysref => dSysref,
dStartNcoReset => dStartNcoReset,
cDac0xNcoUpdateBusy => cDac0xNcoUpdateBusy,
cDac0xNcoUpdateReq => cDac0xNcoUpdateReq,
cDac0xSysrefIntGating => cDac0xSysrefIntGating,
cDac0xSysrefIntReenable => cDac0xSysrefIntReenable,
cDac1xNcoUpdateBusy => cDac1xNcoUpdateBusy,
cDac1xNcoUpdateReq => cDac1xNcoUpdateReq,
cAdc0xNcoUpdateBusy => cAdc0xNcoUpdateBusy,
cAdc0xNcoUpdateReq => cAdc0xNcoUpdateReq,
cAdc2xNcoUpdateBusy => cAdc2xNcoUpdateBusy,
cAdc2xNcoUpdateReq => cAdc2xNcoUpdateReq,
cNcoPhaseRst => cNcoPhaseRst,
cNcoUpdateEn => cNcoUpdateEn,
dNcoResetDone => dNcoResetDone
);
main: process
-- Procedure to sweep the entire SYSREF period.
-- When we strobe dStartNcoReset for one DataClk cycle. NCO reset sequence
-- is initiated. In this procedure, we sweep the dStartNcoReset strobe the
-- entire SYSREF cycle.
procedure SysrefSweep is
constant kSysrefInRfCycles : integer := kSysrefPer/kDataClkPer;
begin
for i in 1 to kSysrefInRfCycles loop
wait until cDac0xSysrefIntGating = '0' for 1 us;
assert cDac0xSysrefIntGating = '0'
report "NCO phase reset does not de-assert"
severity error;
SysrefWait;
DataClkWait(i);
dStartNcoReset <= '0';
DataClkWait;
dStartNcoReset <= '1';
DataClkWait;
dStartNcoReset <= '0';
-- Wait for a minimum of 3 SYSREF period. 1 SYSREF edge is used to
-- initiate NCO reset, 1 SYSREF edge is used to re-enable SYSREF and 1
-- SYSREF edge is used by RFDC to reset all NCOs.
SysrefWait(3);
end loop;
end procedure;
begin
-- Strobe dStartNcoReset across entire SYSREF period.
SysrefSweep;
-- Wait for a minimum of 3 SYSREF cycles to make sure NCO reset is complete.
SysrefWait(3);
StopSim <= true;
wait;
end process;
-- Process to mimic RFDC NCO reset
-- This state machine is based of "NCO frequency hopping" section in PG269
-- (v2.2). Refer to multi-mode subsection for more details.
MimicRfdc: process(ConfigClk)
begin
if falling_edge(ConfigClk) then
cRfdcNcoState <= Idle;
case cRfdcNcoState is
-- Wait until SYSREF internal gating is asserted.
when Idle =>
cWrCount <= 0;
if cDac0xSysrefIntGating = '1' then
cRfdcNcoState <= GateSysref;
end if;
-- Change cDac0xNcoUpdateBusy to "11" to indicate SYSREF is gated
-- internally when NCO update is requested on DAC tile 228.
-- cDac0xNcoUpdateBusy(0) is set to '1', the SYSREF is gated and
-- cDac0xNcoUpdateBusy(1) is set to '1', to indicate the NCO reset
-- process has started, but not complete.
when GateSysref =>
cRfdcNcoState <= GateSysref;
if cDac0xNcoUpdateReq = '1' then
cRfdcNcoState <= UpdateReq;
cDac0xNcoUpdateBusy <= "11";
end if;
-- If NCO reset is requested on other tiles, assert NCO update busy on
-- other tiles as well.
when UpdateReq =>
cRfdcNcoState <= CheckUpdate;
cDac1xNcoUpdateBusy <= cDac1xNcoUpdateReq;
cAdc0xNcoUpdateBusy <= cAdc0xNcoUpdateReq;
cAdc2xNcoUpdateBusy <= cAdc2xNcoUpdateReq;
-- It takes 5 clock cycles to update each RFDC internal registers with
-- the used request change. In rf_nco_reset entity, we only want to
-- reset the NCO, which is a single bit. So, it should take only 5
-- ConfigClk for the update. When the internal register is updated, set
-- cDac0xNcoUpdateBusy(0) to '0'.
when CheckUpdate =>
cRfdcNcoState <= CheckUpdate;
if cWrCount > 4 then
cRfdcNcoState <= SysrefEn;
cDac0xNcoUpdateBusy <= "10"; --Indicates that SYSREF is gated.
cDac1xNcoUpdateBusy <= '0';
cAdc0xNcoUpdateBusy <= '0';
cAdc2xNcoUpdateBusy <= '0';
end if;
cWrCount <= cWrCount + 1;
-- Wait until internal SYSREF gating is disabled.
when SysrefEn =>
cWrCount <= 0;
cRfdcNcoState <= SysrefEn;
if cDac0xSysrefIntReenable = '1' then
if cSysrefDlyd(0) = '0' and cSysref = '1' then
cDac0xNcoUpdateBusy <= "00"; --Indicates that NCO reset is complete.
cRfdcNcoState <= ResetDone;
else
cRfdcNcoState <= WaitForSysref;
end if;
end if;
-- NCO reset is done on the rising edge of SYSREF. When NCO reset is
-- complete, set cDac0xNcoUpdateBusy(1) to '0'.
when WaitForSysref =>
cRfdcNcoState <= WaitForSysref;
if cSysrefDlyd(0) = '0' and cSysref = '1' then
cDac0xNcoUpdateBusy <= "00"; --Indicates that NCO reset is complete.
cRfdcNcoState <= ResetDone;
end if;
-- Wait in this state, until the next NCO reset is requested.
when ResetDone =>
cRfdcNcoState <= ResetDone;
if cDac0xSysrefIntGating = '1' then
cRfdcNcoState <= GateSysref;
end if;
end case;
end if;
end process;
-- SYSREF clock crossing from DataClk to ConfigClk and some pipelines.
ConfigClkSysref: process(ConfigClk)
begin
if rising_edge(ConfigClk) then
cSysref_ms <= dSysref;
cSysref <= cSysref_ms;
cSysrefDlyd <= cSysrefDlyd(cSysrefDlyd'high-1) & cSysref;
cDac0xSysrefIntGatingDlyd <= cDac0xSysrefIntGating;
cNcoPhaseRstDlyd <= cNcoPhaseRstDlyd(cNcoPhaseRstDlyd'high downto 1)
& cDac0xNcoUpdateBusy(1);
end if;
end process;
-- Assertions
process(ConfigClk)
begin
if falling_edge(ConfigClk) then
--Check if cNcoPhaseRst is a constant of '1'.
assert cNcoPhaseRst = '1'
report "NCO phase reset signal should be constant."
severity error;
-- Check if cNcoUpdateEn is a constant of "100000".
assert cNcoUpdateEn = "100000"
report "NCO phase reset signal should be constant."
severity error;
-- Check if NCO reset was requested on the rising edge of SYSREF.
if cDac0xSysrefIntGating = '1' and cDac0xSysrefIntGatingDlyd = '0' then
assert cSysrefDlyd = "01"
report "NCO reset did not start on SYSREF rising edge"
severity error;
end if;
-- We wait for couple of clock cycles after NCO done signal is toggled in
-- from the RFDC. RFDC uses cDac0xNcoUpdateBusy(1) to indicate NCO reset
-- process is done. It is important to wait a minimum of three clock
-- cycles before this check is done. This wait is needed for clock
-- crossing.
if cNcoPhaseRstDlyd(2) = '1' and cNcoPhaseRstDlyd(1) = '0' then
assert dNcoResetDone = '1'
report "NCO Reset done should have been asserted after NCO " &
"reset request is de-asserted"
severity error;
end if;
end if;
end process;
end RTL;
@@ -0,0 +1,436 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: tb_rf_reset_controller
--
-- Description:
--
-- Testbench for rf_reset_controller.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library WORK;
use WORK.PkgRFDC_REGS_REGMAP.all;
entity tb_rf_reset_controller is
end tb_rf_reset_controller;
architecture RTL of tb_rf_reset_controller is
component rf_reset_controller
port (
ConfigClk : in std_logic;
DataClk : in std_logic;
PllRefClk : in std_logic;
RfClk : in std_logic;
RfClk2x : in std_logic;
DataClk2x : in std_logic;
dAdcResetPulse : in std_logic;
dDacResetPulse : in std_logic;
dAdcDataOutReset_n : out std_logic;
r2AdcFirReset_n : out std_logic;
rAdcRfdcAxiReset_n : out std_logic;
rAdcEnableData : out std_logic;
rAdcGearboxReset_n : out std_logic;
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;
cSoftwareControl : in std_logic_vector(31 downto 0);
cSoftwareStatus : out std_logic_vector(31 downto 0));
end component;
signal cSoftwareStatus : std_logic_vector(31 downto 0);
signal r2AdcFirReset_n : std_logic;
signal r2DacFirReset_n : std_logic;
signal rAdcGearboxReset_n : std_logic;
signal rDacGearboxReset_n : std_logic;
signal cSoftwareControl : std_logic_vector(31 downto 0) := (others => '0');
signal dAdcResetPulse : std_logic := '0';
signal dDacResetPulse : std_logic := '0';
constant kSwReset : std_logic := '0';
constant kTimedReset : std_logic := '1';
-- All constants mentioned below are number of the particular clock cycles
-- PllRefClk period. For example, kDataClkCycles is the total number of
-- DataClk cycles in the PllRefClk period.
constant kDataClkCycles : integer := 2;
constant kDataClk2xCycles : integer := 4;
constant kRfClkCycles : integer := 3;
constant kRfClk2xCycles : integer := 6;
constant kConfigPer : time := 25 ns;
-- Make sure the PllRefClk period is a least common multiple of all the other
-- derived clock.
constant kPllRefClkPer : time := 12 ns;
constant kDataClkPer : time := kPllRefClkPer/2;
constant kDataClk2xPer : time := kPllRefClkPer/4;
constant kRfClkPer : time := kPllRefClkPer/3;
constant kRfClk2xPer : time := kPllRefClkPer/6;
signal pReset : boolean := false;
signal dCount : integer := 0;
signal d2Count : integer := 0;
signal rCount : integer := 0;
signal r2Count : integer := 0;
signal StopSim : boolean;
signal ConfigClk : std_logic := '1';
signal RfClk : std_logic := '1';
signal RfClk2x : std_logic := '1';
signal DataClk : std_logic := '1';
signal DataClk2x : std_logic := '1';
signal PllRefClk : std_logic := '1';
signal dAdcDataOutReset_n : std_logic := '0';
signal dAdcDataOutResetDlyd_n : std_logic := '0';
signal dDacDataInReset_n : std_logic := '0';
signal dDacDataInResetDlyd_n : std_logic := '0';
signal d2DacFirReset_n : std_logic := '0';
signal d2DacFirResetDlyd_n : std_logic := '0';
signal rAdcRfdcAxiReset_n : std_logic := '0';
signal rAdcRfdcAxiResetDlyd_n : std_logic := '0';
signal rDacRfdcAxiReset_n : std_logic := '0';
signal rDacRfdcAxiResetDlyd_n : std_logic := '0';
signal r2AdcFirResetDlyd_n : std_logic := '0';
signal r2DacFirResetDlyd_n : std_logic := '0';
signal ExpectedSwAdcResetDone : std_logic := '0';
signal ExpectedAdcReset : std_logic := '0';
signal ExpectedSwDacResetDone : std_logic := '0';
signal ExpectedDacReset : std_logic := '0';
signal ExpectedAxiAdcResetOut : std_logic := '0';
signal ExpectedAxiDacResetOut : std_logic := '0';
-- Make sure the wait time for reset done check is at least 10 ConfigClk
-- cycles to account for all clock domain crossings. We also have some status
-- check in the testbench which requires the wait to be additional ConfigClk
-- cycles. This wait is in ConfigClk period.
constant kResetDoneWait : positive := 10;
procedure ClkWait(signal clk : in std_logic; X : positive := 1) is
begin
for i in 1 to X loop
wait until rising_edge(clk);
end loop;
end procedure ClkWait;
-- Check phase alignment of reset. We want to make sure the reset is asserted
-- on the 1st rising clock edge after the rising edge of PllRefClk.
procedure CheckAlignment(
signal Clk : in std_logic; -- Synchronous reset clock
signal Reset_n : in std_logic; -- Synchronous reset
signal ResetDlyd_n : inout std_logic; -- Delayed synchronous reset
signal PhaseCount : in integer; -- Phase count used to check alignment
Message : string) is -- Assertion message
begin
-- Check if reset is asserted on the 1st Clk after the rising edge of
-- PllRefClk.
if falling_edge(Clk) then
ResetDlyd_n <= Reset_n;
if Reset_n = '0' and ResetDlyd_n = '1' then
assert PhaseCount = 1
report Message & " reset is not asserted in the expected time" severity error;
end if;
end if;
end procedure CheckAlignment;
-- Procedure to generate phase counter that is used to check the alignment of
-- phase of all clocks related to PllRefClk.
procedure PhaseCounter(
signal Clk : in std_logic; -- Clock related to PllRefClk
signal Reset : in boolean; -- Reset synchronous to PllRefClk
signal PhaseCount : inout integer; -- Phase count of Clk with respect to PllRefClk
ClockCycles : integer) is -- Number of Clk clock cycles in PllRefClk period
begin
if rising_edge(Clk) then
if Reset or PhaseCount = ClockCycles-1 then
PhaseCount <= 0;
else
PhaseCount <= PhaseCount+1;
end if;
end if;
end procedure PhaseCounter;
procedure CheckExpectedValue(
signal Clk : in std_logic;
signal Actual : in std_logic;
signal Expected : in std_logic;
Message : string) is
begin
if falling_edge(Clk) then
-- Check if the actual value is as expected.
assert std_match(Actual, Expected)
report Message & " not as expected" & LF
& "Expected = " & std_logic'image(Expected) & LF
& "Actual = " & std_logic'image(Actual) severity error;
end if;
end procedure CheckExpectedValue;
begin
ConfigClk <= not ConfigClk after kConfigPer/2 when not StopSim else '0';
RfClk <= not RfClk after kRfClkPer/2 when not StopSim else '0';
RfClk2x <= not RfClk2x after kRfClk2xPer/2 when not StopSim else '0';
DataClk <= not DataClk after kDataClkPer/2 when not StopSim else '0';
DataClk2x <= not DataClk2x after kDataClk2xPer/2 when not StopSim else '0';
PllRefClk <= not PllRefClk after kPllRefClkPer/2 when not StopSim else '0';
-- rAdcEnableData is a constant and is not tested.
dut: rf_reset_controller
port map (
ConfigClk => ConfigClk,
DataClk => DataClk,
PllRefClk => PllRefClk,
RfClk => RfClk,
RfClk2x => RfClk2x,
DataClk2x => DataClk2x,
dAdcResetPulse => dAdcResetPulse,
dDacResetPulse => dDacResetPulse,
dAdcDataOutReset_n => dAdcDataOutReset_n,
r2AdcFirReset_n => r2AdcFirReset_n,
rAdcRfdcAxiReset_n => rAdcRfdcAxiReset_n,
rAdcEnableData => open,
rAdcGearboxReset_n => rAdcGearboxReset_n,
dDacDataInReset_n => dDacDataInReset_n,
r2DacFirReset_n => r2DacFirReset_n,
d2DacFirReset_n => d2DacFirReset_n,
rDacRfdcAxiReset_n => rDacRfdcAxiReset_n,
rDacGearboxReset_n => rDacGearboxReset_n,
cSoftwareControl => cSoftwareControl,
cSoftwareStatus => cSoftwareStatus
);
main: process
-- Procedure to generate software reset and expected DUR reset output.
procedure StrobeReset(
signal TimedReset : out std_logic; -- SW Reset control
signal ExpectedResetOut : out std_logic; -- Expected reset values
signal ExpectedAxiResetOut : out std_logic; -- Expected reset values
signal SwResetStatus : out std_logic; -- Expected SW reset status
SwReset : integer; -- SW Reset control
ResetType : std_logic; -- 0 = SW reset, 1 = UHD timed reset
ResetWait : positive := 1) is -- Wait time for test iteration
begin
if ResetType = kSwReset then
-- Assert software reset control on the rising edge of ConfigClk. Also
-- change the expected status to don't care as the status will change
-- only after few ConfigClk period.
ClkWait(ConfigClk);
TimedReset <= '0';
cSoftwareControl(SwReset) <= '1';
SwResetStatus <= '-';
ExpectedResetOut <= '-';
ExpectedAxiResetOut <= '-';
ClkWait(ConfigClk, 1);
SwResetStatus <= '0';
-- Wait for additional ConfigClk before changing the expected reset
-- value to '0'. This wait is needed to account for pipeline and clock
-- crossing delays.
ClkWait(ConfigClk, 1);
-- Changed expected reset output to '0' (active low).
ExpectedResetOut <= '0';
ExpectedAxiResetOut <= '0';
ClkWait(ConfigClk,1);
-- SW reset status should be asserted after 3 ConfigClk periods. This
-- wait is needed to account for pipeline and clock crossings.
SwResetStatus <= '1';
-- De-assert software reset
ClkWait(ConfigClk,2);
cSoftwareControl(SwReset) <= '0';
-- Change the expected reset outputs to don't care as it will take few
-- PllRefClk cycles and ConfigClk to DataClock crossing.
ExpectedAxiResetOut <= '-';
ClkWait(ConfigClk,1);
ExpectedAxiResetOut <= '1';
-- After few ConfigClk cycles, all reset outputs should be de-asserted.
ClkWait(ConfigClk,1);
ExpectedResetOut <= '-';
ClkWait(ConfigClk,2);
ExpectedResetOut <= '1';
-- Wait for ResetWait time before exiting the test iteration.
ClkWait(ConfigClk,ResetWait);
else -- Timed command.
ClkWait(DataClk,ResetWait);
TimedReset <= '1';
-- RFDC should not be asserted with timed reset.
ExpectedAxiResetOut <= '1';
-- Strobe the reset pulse only for one DataClk period.
ClkWait(DataClk,1);
TimedReset <= '0';
ClkWait(PllRefClk,2);
ExpectedResetOut <= '-';
-- Wait for 3 PllRefClk to account for pipeline delays.
ClkWait(PllRefClk,1);
ExpectedResetOut <= '0';
ClkWait(PllRefClk,2);
ExpectedResetOut <= '-';
-- Reset should be asserted only for two PllRefClk cycles.
ClkWait(PllRefClk,2);
ExpectedResetOut <= '1';
ClkWait(DataClk,ResetWait); -- Wait between test.
end if;
end procedure StrobeReset;
begin
-- Expected power on reset values.
ExpectedAdcReset <= '0';
ExpectedAxiAdcResetOut <= '0';
ExpectedDacReset <= '0';
ExpectedAxiDacResetOut <= '0';
ClkWait(ConfigClk,1);
ClkWait(RfClk,1);
ExpectedAxiAdcResetOut <= '1';
ExpectedAxiDacResetOut <= '1';
ClkWait(ConfigClk,1);
ExpectedAdcReset <= '-';
ExpectedDacReset <= '-';
ClkWait(ConfigClk,1);
ExpectedAdcReset <= '1';
ExpectedDacReset <= '1';
ClkWait(ConfigClk,5);
-- This reset is for simulation to have a common reference to check for
-- clock alignment.
ClkWait(PllRefClk,1);
pReset <= true;
ClkWait(PllRefClk,1);
pReset <= false;
ClkWait(PllRefClk,1);
---------------------------------------------------------------------------
-- Test resets from software
---------------------------------------------------------------------------
-----------------------------------
-- ADC
-----------------------------------
StrobeReset(dAdcResetPulse, ExpectedAdcReset, ExpectedAxiAdcResetOut,
ExpectedSwAdcResetDone, kADC_RESET, kSwReset, kResetDoneWait);
-- Align reset to the rising edge of PllRefClk
ClkWait(PllRefClk,1);
StrobeReset(dAdcResetPulse, ExpectedAdcReset, ExpectedAxiAdcResetOut,
ExpectedSwAdcResetDone, kADC_RESET, kTimedReset, kResetDoneWait);
StrobeReset(dAdcResetPulse, ExpectedAdcReset, ExpectedAxiAdcResetOut,
ExpectedSwAdcResetDone, kADC_RESET, kSwReset, kResetDoneWait);
-- Align reset to the falling edge of PllRefClk.
ClkWait(PllRefClk,1);
ClkWait(DataClk,1);
StrobeReset(dAdcResetPulse, ExpectedAdcReset, ExpectedAxiAdcResetOut,
ExpectedSwAdcResetDone, kADC_RESET, kTimedReset, kResetDoneWait);
-----------------------------------
-- DAC
-----------------------------------
StrobeReset(dDacResetPulse, ExpectedDacReset, ExpectedAxiDacResetOut,
ExpectedSwDacResetDone, kDAC_RESET, kSwReset, kResetDoneWait);
-- Align reset to the rising edge of PllRefClk.
ClkWait(PllRefClk,1);
StrobeReset(dDacResetPulse, ExpectedDacReset, ExpectedAxiDacResetOut,
ExpectedSwDacResetDone, kDAC_RESET, kTimedReset, kResetDoneWait);
StrobeReset(dDacResetPulse, ExpectedDacReset, ExpectedAxiDacResetOut,
ExpectedSwDacResetDone, kDAC_RESET, kSwReset, kResetDoneWait);
-- Align reset to the falling edge of PllRefClk.
ClkWait(PllRefClk,1);
ClkWait(DataClk,1);
StrobeReset(dDacResetPulse, ExpectedDacReset, ExpectedAxiDacResetOut,
ExpectedSwDacResetDone, kDAC_RESET, kTimedReset, kResetDoneWait);
StopSim <= true;
wait;
end process main;
-----------------------------------------------------------------------------
-- Reset from software and UHD timed command
-----------------------------------------------------------------------------
-- Check if the correct resets are getting asserted when UHD timed reset or
-- software reset is asserted. Except for RFDC AXI-S reset all other resets
-- should be strobed for UHD timed reset.
-----------------------------------------------------------------------------
-- Check if the reset done status is getting asserted as expected.
CheckExpectedValue(ConfigClk, cSoftwareStatus(kADC_SEQ_DONE),
ExpectedSwAdcResetDone, "ADC reset done status");
CheckExpectedValue(ConfigClk, cSoftwareStatus(kDAC_SEQ_DONE),
ExpectedSwDacResetDone, "DAC reset done status");
-- Check if resets state in DataClk is as expected.
CheckExpectedValue(DataClk, dAdcDataOutReset_n, ExpectedAdcReset,
"ADC data out reset");
CheckExpectedValue(DataClk, dDacDataInReset_n, ExpectedDacReset,
"DAC data out reset");
-- Check if resets state in DataClk2x is as expected.
CheckExpectedValue(DataClk2x, d2DacFirReset_n, ExpectedDacReset,
"400M interpolator reset");
---- Check if resets state in RfClk2x is as expected.
CheckExpectedValue(RfClk2x, r2AdcFirReset_n, ExpectedAdcReset,
"ADC re-sampler reset");
CheckExpectedValue(RfClk2x, r2DacFirReset_n, ExpectedDacReset,
"DAC re-sampler reset");
---- Check if resets state in RfClk is as expected.
CheckExpectedValue(RfClk, rAdcRfdcAxiReset_n, ExpectedAxiAdcResetOut,
"ADC RFDC AXI-S interface reset");
CheckExpectedValue(RfClk, rDacRfdcAxiReset_n, ExpectedAxiDacResetOut,
"DAC RFDC AXI-S interface reset");
CheckExpectedValue(RfClk, rAdcGearboxReset_n, ExpectedAdcReset,
"ADC gearbox reset");
CheckExpectedValue(RfClk, rDacGearboxReset_n, ExpectedDacReset,
"DAC gearbox reset");
-----------------------------------------------------------------------------
-- Reset alignment checks for resets
-----------------------------------------------------------------------------
-----------------------------------
-- Clock counter
-----------------------------------
-- We use counters to check the phase of all the derived clocks with respect
-- to PllRefClk. Each counter will rollover at the rising edge of PllRefClk.
-----------------------------------
PhaseCounter(DataClk, pReset, dCount, kDataClkCycles);
PhaseCounter(DataClk2x, pReset, d2Count, kDataClk2xCycles);
PhaseCounter(RfClk, pReset, rCount, kRfClkCycles);
PhaseCounter(RfClk2x, pReset, r2Count, kRfClk2xCycles);
-- Check for DataClk based synchronous reset alignment to PllRefClk.
CheckAlignment(DataClk, dAdcDataOutReset_n, dAdcDataOutResetDlyd_n, dCount,
"ADC data out");
CheckAlignment(DataClk, dDacDataInReset_n, dDacDataInResetDlyd_n, dCount,
"DAC data in");
-- Check for DataClk2x based synchronous reset alignment to PllRefClk.
CheckAlignment(DataClk2x, d2DacFirReset_n, d2DacFirResetDlyd_n, d2Count,
"400M DAC FIR Filter");
-- Check for RfClk based synchronous reset alignment to PllRefClk.
CheckAlignment(RfClk, rAdcRfdcAxiReset_n, rAdcRfdcAxiResetDlyd_n, rCount,
"ADC RFDC reset ");
CheckAlignment(RfClk, rDacRfdcAxiReset_n, rDacRfdcAxiResetDlyd_n, rCount,
"DAC RFDC reset ");
-- Check for RfClk2x based synchronous reset alignment to PllRefClk.
CheckAlignment(RfClk2x, r2AdcFirReset_n, r2AdcFirResetDlyd_n, r2Count,
"ADC decimation filter reset ");
CheckAlignment(RfClk2x, r2DacFirReset_n, r2DacFirResetDlyd_n, r2Count,
"DAC interpolation filter reset ");
end RTL;