fpga: x400: Add support for X410 motherboard FPGA
Co-authored-by: Andrew Moch <Andrew.Moch@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com> Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com> Co-authored-by: Max Köhler <max.koehler@ni.com> Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Wade Fife <wade.fife@ettus.com> Co-authored-by: Hector Rubio <hrubio@ni.com> Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
This commit is contained in:
committed by
Aaron Rossetto
co-authored by
Andrew Moch
Daniel Jepson
Javier Valenzuela
Joerg Hofrichter
Kumaran Subramoniam
Max Köhler
Michael Auchter
Paul Butler
Hector Rubio
parent
bfef20ea45
commit
61782b02d7
@@ -0,0 +1,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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -0,0 +1,220 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: PkgRf
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- This package has some type definition and functions used in the RF data
|
||||
-- chain.
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
use IEEE.numeric_std.all;
|
||||
|
||||
package PkgRf is
|
||||
|
||||
-- DDC sample data out width.
|
||||
constant kDdcDataOutWidth : natural := 17;
|
||||
-- Each sample is padded in MSB with 7 extra bits of zero to byte align.
|
||||
constant kDdcDataWordWidth : natural := kDdcDataOutWidth+7;
|
||||
-- DUC sample data out width.
|
||||
constant kDucDataOutWidth : natural := 18;
|
||||
-- Each sample is padded in MSB with 6 extra bits of zero to byte align.
|
||||
constant kDucDataWordWidth : natural := kDucDataOutWidth+6;
|
||||
-- Saturated data output width.
|
||||
constant kSatDataWidth : natural := 16;
|
||||
-- ADC sample resolution.
|
||||
constant kAdcSampleRes : natural := 16;
|
||||
|
||||
subtype Sample18_t is signed(17 downto 0);
|
||||
subtype Sample17_t is signed(16 downto 0);
|
||||
subtype Sample16_t is signed(15 downto 0);
|
||||
subtype Sample16slv_t is std_logic_vector(15 downto 0);
|
||||
|
||||
type Samples16_t is array(natural range<>) of Sample16_t;
|
||||
type Samples17_t is array(natural range<>) of Sample17_t;
|
||||
type Samples18_t is array(natural range<>) of Sample18_t;
|
||||
|
||||
-- These constants have the largest and smallest 18-bit, 17-bit, and 16-bit
|
||||
-- signed values.
|
||||
constant kLargest18 : Sample18_t := to_signed(2**17 - 1, 18);
|
||||
constant kSmallest18 : Sample18_t := to_signed(-2**17, 18);
|
||||
constant kLargest17 : Sample17_t := to_signed(2**16 - 1, 17);
|
||||
constant kSmallest17 : Sample17_t := to_signed(-2**16, 17);
|
||||
constant kLargest16 : Sample16_t := to_signed(2**15 - 1, 16);
|
||||
constant kSmallest16 : Sample16_t := to_signed(-2**15, 16);
|
||||
|
||||
function Saturate(s : signed ) return Sample16_t;
|
||||
function to_stdlogicvector(d : Samples16_t) return std_logic_vector;
|
||||
function to_Samples16(d : std_logic_vector) return Samples16_t;
|
||||
function to_Samples17(d : std_logic_vector) return Samples17_t;
|
||||
function to_Samples18(d : std_logic_vector) return Samples18_t;
|
||||
-- Shift the ADC sample to the left by 1 bit.
|
||||
function Gain2x(d : std_logic_vector) return std_logic_vector;
|
||||
function Gain2x(s : Samples16_t) return Samples16_t;
|
||||
--synopsys translate_off
|
||||
function tb_saturate(s: std_logic_vector) return Sample16slv_t;
|
||||
--synopsys translate_on
|
||||
|
||||
end package PkgRf;
|
||||
|
||||
|
||||
package body PkgRf is
|
||||
|
||||
-- Function to saturate any signed number greater then 16 bits.
|
||||
-- A saturated 16-bit data is returned.
|
||||
function Saturate ( s : signed) return Sample16_t is
|
||||
begin
|
||||
if s > kLargest16 then
|
||||
return kLargest16;
|
||||
elsif s < kSmallest16 then
|
||||
return kSmallest16;
|
||||
else
|
||||
return resize(s, 16);
|
||||
end if;
|
||||
end function Saturate;
|
||||
|
||||
-- This function will convert 16 bit signed array into a single
|
||||
-- std_logic_vector.
|
||||
function to_stdlogicvector(d : Samples16_t) return std_logic_vector is
|
||||
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
|
||||
alias normalD : Samples16_t(d'length-1 downto 0) is d;
|
||||
variable rval : std_logic_vector(d'length * 16 - 1 downto 0);
|
||||
constant dataWidth : natural := Sample16_t'length;
|
||||
begin
|
||||
for i in normalD'range loop
|
||||
rval(i*dataWidth + dataWidth-1 downto i*dataWidth)
|
||||
:= std_logic_vector(normalD(i));
|
||||
end loop;
|
||||
return rval;
|
||||
end function to_stdlogicvector;
|
||||
|
||||
-- This function will convert a std_logic_vector into an array of 18 bit
|
||||
-- signed array. The input std_logic_vector has data packed in 24 bits. But
|
||||
-- only 18 bits has valid data and remaining 6 MSB bits are padded with
|
||||
-- zeros.
|
||||
function to_Samples18(d : std_logic_vector) return Samples18_t is
|
||||
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
|
||||
alias normalD : std_logic_vector(d'length-1 downto 0) is d;
|
||||
variable rval : Samples18_t(d'length / kDucDataWordWidth - 1 downto 0);
|
||||
begin
|
||||
--synopsys translate_off
|
||||
assert (((d'length) mod kDucDataWordWidth) = 0)
|
||||
report "Input to the function to_Samples18 must be a multiple of kDucDataWordWidth"
|
||||
severity error;
|
||||
--synopsys translate_on
|
||||
for i in rval'range loop
|
||||
rval(i) := Sample18_t(normalD(i*kDucDataWordWidth + Sample18_t'length-1
|
||||
downto i*kDucDataWordWidth));
|
||||
end loop;
|
||||
return rval;
|
||||
end function to_Samples18;
|
||||
|
||||
-- This function will convert a std_logic_vector into an array of 16 bit
|
||||
-- signed array. The input std_logic_vector has data packed in 16 bits. But
|
||||
-- only 15 bits has valid data and the uper two bits only have the signed
|
||||
-- bit.
|
||||
function to_Samples16(d : std_logic_vector) return Samples16_t is
|
||||
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
|
||||
alias normalD : std_logic_vector(d'length-1 downto 0) is d;
|
||||
variable rval : Samples16_t(d'length / kAdcSampleRes - 1 downto 0);
|
||||
begin
|
||||
--synopsys translate_off
|
||||
assert (((d'length) mod kAdcSampleRes) = 0)
|
||||
report "Input to the function to_Samples16 must be a multiple of kAdcSampleRes"
|
||||
severity error;
|
||||
--synopsys translate_on
|
||||
for i in rval'range loop
|
||||
rval(i) := Sample16_t(normalD(i*kAdcSampleRes + Sample16_t'length-1
|
||||
downto i*kAdcSampleRes));
|
||||
end loop;
|
||||
return rval;
|
||||
end function to_Samples16;
|
||||
|
||||
-- This function will convert a std_logic_vector into an array of 19 bit
|
||||
-- signed array. The input std_logic_vector has data packed in 24 bits. But
|
||||
-- only 17 bits has valid data and remaining 7 MSB bits are padded with
|
||||
-- zeros.
|
||||
function to_Samples17(d : std_logic_vector) return Samples17_t is
|
||||
-- This alias is used to normalize the input vector to [d'length-1 downto 0]
|
||||
alias normalD : std_logic_vector(d'length-1 downto 0) is d;
|
||||
variable rval : Samples17_t(d'length / kDdcDataWordWidth - 1 downto 0);
|
||||
begin
|
||||
--synopsys translate_off
|
||||
assert (((d'length) mod kDdcDataWordWidth) = 0)
|
||||
report "Input to the function to_Samples17 must be a multiple of kDdcDataWordWidth"
|
||||
severity error;
|
||||
--synopsys translate_on
|
||||
for i in rval'range loop
|
||||
rval(i) := Sample17_t(normalD(i*kDdcDataWordWidth + Sample17_t'length-1
|
||||
downto i*kDdcDataWordWidth));
|
||||
end loop;
|
||||
return rval;
|
||||
end function to_Samples17;
|
||||
|
||||
-- Function to shift the sample to the left by one bit and effectively
|
||||
-- multiply by 2.
|
||||
function Gain2x(s : Samples16_t) return Samples16_t is
|
||||
variable rval : Samples16_t(s'range);
|
||||
begin
|
||||
for i in rval'range loop
|
||||
rval(i) := s(i)(kAdcSampleRes-2 downto 0) & '0';
|
||||
end loop;
|
||||
return rval;
|
||||
end function Gain2x;
|
||||
|
||||
function Gain2x (d : std_logic_vector) return std_logic_vector is
|
||||
begin
|
||||
return to_stdlogicvector(Gain2x(to_Samples16(d)));
|
||||
end function;
|
||||
|
||||
--synopsys translate_off
|
||||
---------------------------------------------------------------
|
||||
-- Function below this comment is used only for testbench.
|
||||
---------------------------------------------------------------
|
||||
-- This function does saturation of a signed number in std_logic_vector data
|
||||
-- type. The current implementation supports only 17 or 18 bit signed
|
||||
-- number.
|
||||
function tb_saturate(s: std_logic_vector) return Sample16slv_t is
|
||||
-- This alias is used to normalize the input vector to [s'length-1 downto 0]
|
||||
alias normalS : std_logic_vector(s'length-1 downto 0) is s;
|
||||
variable rval : Sample16slv_t;
|
||||
constant len : integer := s'length;
|
||||
begin
|
||||
|
||||
-- If 2 MSBs = 00, output <= input without MSB, e.g. positive number < 1
|
||||
-- If 2 MSBs = 01, output <= 0.111111111111111, e.g. positive number >= 1
|
||||
-- If 2 MSBs = 10, output <= 1.000000000000000, e.g. negative number < -1
|
||||
-- If 2 MSBs = 11, output <= input without MSB, e.g. negative number >= -1
|
||||
if len = kDdcDataOutWidth then
|
||||
if normalS(len-1 downto len-2) = "01" then
|
||||
rval := "0111111111111111";
|
||||
elsif normalS(len-1 downto len-2) = "10" then
|
||||
rval := "1000000000000000";
|
||||
else
|
||||
rval := normalS(len-2 downto 0);
|
||||
end if;
|
||||
|
||||
-- If 3 MSBs = 000, output <= input without MSB, e.g. positive number < 1
|
||||
-- If 3 MSBs = 0x1/01x, output <= 0.111111111111111, e.g. positive number >= 1
|
||||
-- If 3 MSBs = 1x0/10x, output <= 1.000000000000000, e.g. negative number < -1
|
||||
-- If 3 MSBs = 111, output <= input without MSB, e.g. negative number >= -1
|
||||
else -- len = kDucDataOutWidth
|
||||
if normalS(len-1) = '0' and normalS(len-2 downto len-3) /= "00" then
|
||||
rval := "0111111111111111";
|
||||
elsif (normalS(len-1 downto len-3) = "000") or
|
||||
(normalS(len-1 downto len-3) = "111") then
|
||||
rval := normalS(len-3 downto 0);
|
||||
else
|
||||
rval := "1000000000000000";
|
||||
end if;
|
||||
end if;
|
||||
return rval;
|
||||
end function tb_saturate;
|
||||
--synopsys translate_on
|
||||
|
||||
end package body;
|
||||
@@ -0,0 +1,98 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: axis_mux
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- This module implements a data mux for a single AXIS bus. When
|
||||
-- mux_select='0' m_axis_tdata comes from s_axis_tdata. mux_select='1'
|
||||
-- chooses GPIO as the output data.
|
||||
--
|
||||
-- This module IS NOT useful for crossing clock domain boundaries s_axis_aclk
|
||||
-- and m_axis_mclk must be connected to the same clock.
|
||||
--
|
||||
-- This mux is intended for muxing in constant calibration data from gpio.
|
||||
-- gpio and mux_select are expected to be asynchronous to s_axis_aclk, but
|
||||
-- this module includes no synchronization logic. When mux_select or gpio
|
||||
-- change, m_axis_tvalid and m_axis_tdata are undefined in the first few
|
||||
-- clock cycles. You must wait for bad axis cycles to flush through the
|
||||
-- remainder of the pipeline before performing calibration and again after
|
||||
-- exiting calibration mode.
|
||||
--
|
||||
-- kAxiWidth must be an integer multiple of kGpioWidth. A concurrent assert
|
||||
-- statement checks this assumption and should produce a synthesis warning if
|
||||
-- that requirement is not met.
|
||||
--
|
||||
-- Parameters:
|
||||
--
|
||||
-- kGpioWidth : GPIO width.
|
||||
-- kAxiWidth : AXI bus width. Must be an integer multiple of kGpioWidth
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
|
||||
entity axis_mux is
|
||||
generic (
|
||||
kGpioWidth : natural := 32;
|
||||
kAxiWidth : natural := 256
|
||||
);
|
||||
port(
|
||||
gpio : in std_logic_vector(kGpioWidth-1 downto 0);
|
||||
mux_select : in std_logic;
|
||||
|
||||
-- s_axis_aclk MUST be the same as m_axis_aclk.
|
||||
-- Declaring an unused clock allows the BD tool to identify the
|
||||
-- synchronicity of the slave AXIS port signals.
|
||||
s_axis_aclk : in std_logic;
|
||||
s_axis_tdata : in std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
s_axis_tvalid : in std_logic;
|
||||
s_axis_tready : out std_logic;
|
||||
m_axis_aclk : in std_logic;
|
||||
m_axis_tvalid : out std_logic;
|
||||
m_axis_tdata : out std_logic_vector(kAxiWidth - 1 downto 0)
|
||||
);
|
||||
end entity axis_mux;
|
||||
|
||||
architecture RTL of axis_mux is
|
||||
|
||||
constant kWordSize : natural := gpio'length;
|
||||
constant kWordCount : natural := kAxiWidth / kWordSize;
|
||||
|
||||
subtype AxiData_t is std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
|
||||
impure function ConcatenatedData return AxiData_t is
|
||||
variable rval : AxiData_t;
|
||||
begin
|
||||
for i in 0 to kWordCount - 1 loop
|
||||
rval(i*kWordSize + kWordSize - 1 downto i*kWordSize) := gpio;
|
||||
end loop;
|
||||
return rval;
|
||||
end function ConcatenatedData;
|
||||
|
||||
begin
|
||||
|
||||
assert kWordSize * kWordCount = kAxiWidth
|
||||
report "m_axis_tdata'length is not an integer multiple of gpio'length"
|
||||
severity failure;
|
||||
|
||||
MuxOutputRegister:
|
||||
process (m_axis_aclk) is
|
||||
begin
|
||||
if rising_edge(m_axis_aclk) then
|
||||
if mux_select='1' then
|
||||
m_axis_tdata <= ConcatenatedData;
|
||||
m_axis_tvalid <= '1';
|
||||
else
|
||||
m_axis_tdata <= s_axis_tdata;
|
||||
m_axis_tvalid <= s_axis_tvalid;
|
||||
end if;
|
||||
end if;
|
||||
end process MuxOutputRegister;
|
||||
|
||||
s_axis_tready <= '1';
|
||||
|
||||
end RTL;
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: capture_sysref
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Capture SYSREF and transfer it to the higher clock domain. Module incurs
|
||||
// in 2 pll_ref_clk cycles + 1 rfdc_clk cycle of delay.
|
||||
//
|
||||
|
||||
module capture_sysref (
|
||||
// Clocks
|
||||
input wire pll_ref_clk,
|
||||
input wire rfdc_clk,
|
||||
|
||||
// SYSREF input and control
|
||||
input wire sysref_in, // Single-ended SYSREF (previously buffered)
|
||||
input wire enable_rclk, // Enables SYSREF output in the rfdc_clk domain.
|
||||
|
||||
// Captured SYSREF outputs
|
||||
output wire sysref_out_pclk, // Debug output (Domain: pll_ref_clk).
|
||||
output wire sysref_out_rclk // RFDC output (Domain: rfdc_clk).
|
||||
);
|
||||
|
||||
reg sysref_pclk_ms = 1'b0, sysref_pclk = 1'b0, sysref_rclk = 1'b0;
|
||||
|
||||
// Capture SYSREF synchronously with the pll_ref_clk, but double-sync it just
|
||||
// in case static timing isn't met so as not to destroy downstream logic.
|
||||
always @ (posedge pll_ref_clk) begin
|
||||
sysref_pclk_ms <= sysref_in;
|
||||
sysref_pclk <= sysref_pclk_ms;
|
||||
end
|
||||
|
||||
assign sysref_out_pclk = sysref_pclk;
|
||||
|
||||
// Transfer to faster clock which is edge-aligned with the pll_ref_clk.
|
||||
always @ (posedge rfdc_clk) begin
|
||||
if (enable_rclk) begin
|
||||
sysref_rclk <= sysref_pclk;
|
||||
end else begin
|
||||
sysref_rclk <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
assign sysref_out_rclk = sysref_rclk;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,300 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: clock_gates
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- Gate propagation of DataClk and RfdcClk instances until the PLL lock
|
||||
-- status signal is stable and software has acknowledged it by asserting the
|
||||
-- pertinent controls.
|
||||
--
|
||||
-- RfdcClks are used on other Xilinx IP components in the Board Design, and
|
||||
-- Vivado fails to detect their frequency correctly their buffer is
|
||||
-- explicitly instantiated in the Block Design. Therefore, we only generate
|
||||
-- the buffer enable signals for these clocks within this component.
|
||||
--
|
||||
-- Since DataClk are only used in other Custom IP blocks within the Block
|
||||
-- design, it is possible to instantiate the clock buffers within this block
|
||||
-- for without running into IP generation failures.
|
||||
--
|
||||
-- Parameters:
|
||||
--
|
||||
-- kReliableClkPeriodNs: Clock period (ns) for ReliableClk.
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.ALL;
|
||||
use IEEE.numeric_std.ALL;
|
||||
|
||||
library UNISIM;
|
||||
use UNISIM.Vcomponents.ALL;
|
||||
|
||||
library WORK;
|
||||
use WORK.PkgRFDC_REGS_REGMAP.all;
|
||||
|
||||
|
||||
entity clock_gates is
|
||||
generic (
|
||||
kReliableClkPeriodNs : integer := 25
|
||||
);
|
||||
port (
|
||||
-- MMCM reset
|
||||
-- This clock will be asserted via AXI access before any clocking
|
||||
-- configuration done, signals coming into this component will not change
|
||||
-- immediately after this reset is de-asserted.
|
||||
rPllReset_n : in std_logic;
|
||||
|
||||
aPllLocked : in std_logic;
|
||||
|
||||
-- Input Clocks (from MMCM)
|
||||
ReliableClk : in std_logic;
|
||||
DataClk1xPll : in std_logic;
|
||||
DataClk2xPll : in std_logic;
|
||||
|
||||
-- Buffered Clock Outputs (to design)
|
||||
DataClk1x : out std_logic;
|
||||
DataClk2x : out std_logic;
|
||||
|
||||
-- Buffers for these signals must be instantiated on Block design for clock
|
||||
-- rates to be identified. The Utility Buffers instantiated on the Block
|
||||
-- Design require signals to be of type std_logic_vector.
|
||||
aEnableRfBufg1x : out std_logic_vector(0 downto 0);
|
||||
aEnableRfBufg2x : out std_logic_vector(0 downto 0);
|
||||
|
||||
-- PLL Status Signals
|
||||
rPllLocked : out std_logic;
|
||||
|
||||
-- Window Interface
|
||||
rSafeToEnableGatedClks : in std_logic;
|
||||
rGatedBaseClksValid : out std_logic;
|
||||
|
||||
-- AXI GPIO interface
|
||||
rSoftwareControl : in std_logic_vector(31 downto 0);
|
||||
rSoftwareStatus : out std_logic_vector(31 downto 0)
|
||||
);
|
||||
end clock_gates;
|
||||
|
||||
architecture STRUCT of clock_gates is
|
||||
|
||||
component sync_wrapper
|
||||
generic (
|
||||
WIDTH : integer := 1;
|
||||
STAGES : integer := 2;
|
||||
INITIAL_VAL : integer := 0;
|
||||
FALSE_PATH_TO_IN : integer := 1);
|
||||
port (
|
||||
clk : in std_logic;
|
||||
rst : in std_logic;
|
||||
signal_in : in std_logic_vector((WIDTH-1) downto 0);
|
||||
signal_out : out std_logic_vector((WIDTH-1) downto 0));
|
||||
end component;
|
||||
|
||||
component BUFGCE
|
||||
generic(
|
||||
CE_TYPE : string);
|
||||
port (
|
||||
O : out std_ulogic;
|
||||
CE : in std_ulogic;
|
||||
I : in std_ulogic);
|
||||
end component;
|
||||
|
||||
-- UltraScale MMCM max lock time = 100 us / 25 ns = 4,000 clk cycles. If the
|
||||
-- division kPllLockTimeNs / kReliableClkPeriodNs does not evaluate to an
|
||||
-- integer, Vivado could either round up or down. In case they round down, we
|
||||
-- add '1' to the result to ensure we have the full lock time accounted for.
|
||||
-- In this case, it is better to count 1 more than necessary than kill the
|
||||
-- process prematurely.
|
||||
constant kPllLockTimeNs : integer := 100000;
|
||||
constant kMaxPllLockCount : integer := kPllLockTimeNs / kReliableClkPeriodNs + 1;
|
||||
signal rLockedFilterCount : integer range 0 to kMaxPllLockCount-1 := kMaxPllLockCount-1;
|
||||
|
||||
signal rClearDataClkUnlockedSticky : std_logic;
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- PLL locked signals
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-- Synchronizer signals
|
||||
signal aPllLockedLcl : std_logic_vector(0 downto 0);
|
||||
signal rPllLockedDs : std_logic_vector(0 downto 0) := (others => '0');
|
||||
|
||||
-- Lock status indicators
|
||||
signal rPllLockedLcl : std_logic := '0';
|
||||
signal rPllUnlockedSticky : std_logic := '0';
|
||||
|
||||
-- Safe BUFG enable signals
|
||||
signal rEnableDataClk1x,
|
||||
rEnableDataClk2x,
|
||||
rEnableRfdcClk1x,
|
||||
rEnableRfdcClk2x : std_logic;
|
||||
|
||||
signal rEnableDataBufg1x : std_logic := '0';
|
||||
signal rEnableDataBufg2x : std_logic := '0';
|
||||
signal rEnableRfdcBufg1xLcl : std_logic := '0';
|
||||
signal rEnableRfdcBufg2xLcl : std_logic := '0';
|
||||
|
||||
-- Active high version of reset required for synchronizer blocks.
|
||||
signal rPllReset : std_logic;
|
||||
|
||||
-- Since these signals control sensitive components (clock enables), we apply
|
||||
-- a dont_touch attribute to preserve the signals through both synthesis and
|
||||
-- P&R. Implementation of "dont_touch" has been confirmed after P&R.
|
||||
attribute dont_touch : string;
|
||||
attribute dont_touch of rEnableDataBufg1x : signal is "TRUE";
|
||||
attribute dont_touch of rEnableDataBufg2x : signal is "TRUE";
|
||||
attribute dont_touch of aEnableRfBufg1x : signal is "TRUE";
|
||||
attribute dont_touch of aEnableRfBufg2x : signal is "TRUE";
|
||||
|
||||
attribute X_INTERFACE_INFO : string;
|
||||
attribute X_INTERFACE_PARAMETER : string;
|
||||
|
||||
attribute X_INTERFACE_INFO of DataClk1xPll : signal is
|
||||
"xilinx.com:signal:clock:1.0 DataClk1xPll CLK";
|
||||
attribute X_INTERFACE_INFO of DataClk2xPll : signal is
|
||||
"xilinx.com:signal:clock:1.0 DataClk2xPll CLK";
|
||||
|
||||
begin
|
||||
|
||||
rPllReset <= not rPllReset_n;
|
||||
|
||||
-- Assert rGatedBaseClksValid once the PLL has been locked for the specified
|
||||
-- time.
|
||||
rGatedBaseClksValid <= rPllLockedLcl;
|
||||
|
||||
DataClkEnables : process(ReliableClk)
|
||||
begin
|
||||
if rising_edge(ReliableClk) then
|
||||
if rPllReset_n = '0' then
|
||||
rEnableDataBufg1x <= '0';
|
||||
rEnableDataBufg2x <= '0';
|
||||
rEnableRfdcBufg1xLcl <= '0';
|
||||
rEnableRfdcBufg2xLcl <= '0';
|
||||
else
|
||||
rEnableDataBufg1x <=
|
||||
rSafeToEnableGatedClks and
|
||||
rEnableDataClk1x and
|
||||
(not rPllUnlockedSticky);
|
||||
|
||||
rEnableDataBufg2x <=
|
||||
rSafeToEnableGatedClks and
|
||||
rEnableDataClk2x and
|
||||
(not rPllUnlockedSticky);
|
||||
|
||||
rEnableRfdcBufg1xLcl <=
|
||||
rSafeToEnableGatedClks and
|
||||
rEnableRfdcClk1x and
|
||||
(not rPllUnlockedSticky);
|
||||
|
||||
rEnableRfdcBufg2xLcl <=
|
||||
rSafeToEnableGatedClks and
|
||||
rEnableRfdcClk2x and
|
||||
(not rPllUnlockedSticky);
|
||||
end if;
|
||||
end if;
|
||||
end process DataClkEnables;
|
||||
|
||||
aEnableRfBufg1x(0) <= rEnableRfdcBufg1xLcl;
|
||||
aEnableRfBufg2x(0) <= rEnableRfdcBufg2xLcl;
|
||||
|
||||
DataClk1xSafeBufg: BUFGCE
|
||||
generic map(
|
||||
CE_TYPE => "ASYNC"
|
||||
)
|
||||
port map (
|
||||
I => DataClk1xPll,
|
||||
CE => rEnableDataBufg1x,
|
||||
O => DataClk1x
|
||||
);
|
||||
|
||||
DataClk2xSafeBufg: BUFGCE
|
||||
generic map(
|
||||
CE_TYPE => "ASYNC"
|
||||
)
|
||||
port map (
|
||||
I => DataClk2xPll,
|
||||
CE => rEnableDataBufg2x,
|
||||
O => DataClk2x
|
||||
);
|
||||
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Create PLL Lock Signal
|
||||
-----------------------------------------------------------------------------
|
||||
-- Double-sync the incoming aPllLocked signal from the PLL.
|
||||
|
||||
aPllLockedLcl(0) <= aPllLocked;
|
||||
|
||||
DataClkPllLockedDS: sync_wrapper
|
||||
generic map (
|
||||
WIDTH => 1,
|
||||
STAGES => open,
|
||||
INITIAL_VAL => open,
|
||||
FALSE_PATH_TO_IN => open)
|
||||
port map (
|
||||
clk => ReliableClk,
|
||||
rst => rPllReset,
|
||||
signal_in => aPllLockedLcl,
|
||||
signal_out => rPllLockedDs
|
||||
);
|
||||
|
||||
-- Filter the Lock signal. Assert a lock when the PLL lock signal has been
|
||||
-- asserted for kPllLockTimeNs
|
||||
--
|
||||
-- !!! SAFE COUNTER STARTUP !!!
|
||||
-- rLockedFilterCount cannot start incrementing until rPllReset_n is
|
||||
-- de-asserted. Once rPllReset_n is de-asserted through a AXI access, input
|
||||
-- values for the registers in this state machine will not change until the
|
||||
-- MMCM locks and the double synchronizer reflects a locked status, making
|
||||
-- this start-up safe.
|
||||
PllLockFilter: process (ReliableClk)
|
||||
begin
|
||||
if rising_edge(ReliableClk) then
|
||||
if rPllReset_n = '0' then
|
||||
rLockedFilterCount <= kMaxPllLockCount-1;
|
||||
rPllLockedLcl <= '0';
|
||||
else
|
||||
if rPllLockedDs(0) = '1' then
|
||||
if rLockedFilterCount = 0 then
|
||||
rPllLockedLcl <= '1';
|
||||
else
|
||||
rPllLockedLcl <= '0';
|
||||
rLockedFilterCount <= rLockedFilterCount - 1;
|
||||
end if;
|
||||
else
|
||||
rLockedFilterCount <= kMaxPllLockCount-1;
|
||||
rPllLockedLcl <= '0';
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
end process PllLockFilter;
|
||||
|
||||
-- Sticky bit to hold '1' if PLL ever comes unlocked
|
||||
PllStickyBit: process (ReliableClk)
|
||||
begin
|
||||
if rising_edge(ReliableClk) then
|
||||
if (not rPllReset_n or rClearDataClkUnlockedSticky) = '1' then
|
||||
rPllUnlockedSticky <= '0';
|
||||
else
|
||||
if rPllLockedLcl = '1' and rPllLockedDs(0) = '0' then
|
||||
rPllUnlockedSticky <= '1';
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
rPllLocked <= rPllLockedLcl;
|
||||
|
||||
-- AXI transaction decoding
|
||||
rClearDataClkUnlockedSticky <= rSoftwareControl(kCLEAR_DATA_CLK_UNLOCKED);
|
||||
rEnableDataClk1x <= rSoftwareControl(kENABLE_DATA_CLK);
|
||||
rEnableDataClk2x <= rSoftwareControl(kENABLE_DATA_CLK_2X);
|
||||
rEnableRfdcClk1x <= rSoftwareControl(kENABLE_RF_CLK);
|
||||
rEnableRfdcClk2x <= rSoftwareControl(kENABLE_RF_CLK_2X);
|
||||
|
||||
rSoftwareStatus(kDATA_CLK_PLL_LOCKED) <= rPllLockedLcl;
|
||||
rSoftwareStatus(kDATA_CLK_PLL_UNLOCKED_STICKY) <= rPllUnlockedSticky;
|
||||
|
||||
end STRUCT;
|
||||
@@ -0,0 +1,147 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: gpio_to_axis_mux
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- This module either drives the AXIS outputs with the corresponding AXIS
|
||||
-- slave inputs, or it drives the output AXIS with data provided by the GPIO
|
||||
-- lines. This allows the calibration process to drive a constant value to
|
||||
-- the DAC's. Although every AXIS interface has its own clock, all the clocks
|
||||
-- must be connected to the same source. Independent clock inputs allows the
|
||||
-- block design editor to automatically detect the clock domain of the
|
||||
-- corresponding interface.
|
||||
--
|
||||
-- kAxiWidth must be an integer multiple of kGpioWidth. A concurrent assert
|
||||
-- statement in axis_mux checks this assumption and should produce a
|
||||
-- synthesis warning if that requirement is not met.
|
||||
--
|
||||
-- Parameters:
|
||||
--
|
||||
-- kGpioWidth : GPIO width.
|
||||
-- kAxiWidth : AXI bus width. Must be an integer multiple of kGpioWidth
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
|
||||
entity gpio_to_axis_mux is
|
||||
generic (
|
||||
kGpioWidth : natural := 32;
|
||||
kAxiWidth : natural := 256
|
||||
);
|
||||
port(
|
||||
gpio : in std_logic_vector(kGpioWidth-1 downto 0);
|
||||
|
||||
-- mux_select(n) chooses the data source for AXIS interface n.
|
||||
-- '0' chooses s_axis_tdata_n. '1' chooses gpio as the data source.
|
||||
-- The only used bits are 0, 1, 4, 5. The remaining bits are reserved for
|
||||
-- future expansion.
|
||||
mux_select : in std_logic_vector(7 downto 0);
|
||||
|
||||
s_axis_0_aclk : in std_logic;
|
||||
s_axis_tdata_0 : in std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
s_axis_tvalid_0 : in std_logic;
|
||||
s_axis_tready_0 : out std_logic;
|
||||
m_axis_0_aclk : in std_logic;
|
||||
m_axis_tvalid_0 : out std_logic;
|
||||
m_axis_tdata_0 : out std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
|
||||
s_axis_1_aclk : in std_logic;
|
||||
s_axis_tdata_1 : in std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
s_axis_tvalid_1 : in std_logic;
|
||||
s_axis_tready_1 : out std_logic;
|
||||
m_axis_1_aclk : in std_logic;
|
||||
m_axis_tvalid_1 : out std_logic;
|
||||
m_axis_tdata_1 : out std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
|
||||
s_axis_2_aclk : in std_logic;
|
||||
s_axis_tdata_2 : in std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
s_axis_tvalid_2 : in std_logic;
|
||||
s_axis_tready_2 : out std_logic;
|
||||
m_axis_2_aclk : in std_logic;
|
||||
m_axis_tvalid_2 : out std_logic;
|
||||
m_axis_tdata_2 : out std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
|
||||
s_axis_3_aclk : in std_logic;
|
||||
s_axis_tdata_3 : in std_logic_vector(kAxiWidth - 1 downto 0);
|
||||
s_axis_tvalid_3 : in std_logic;
|
||||
s_axis_tready_3 : out std_logic;
|
||||
m_axis_3_aclk : in std_logic;
|
||||
m_axis_tvalid_3 : out std_logic;
|
||||
m_axis_tdata_3 : out std_logic_vector(kAxiWidth - 1 downto 0)
|
||||
);
|
||||
end entity;
|
||||
|
||||
architecture RTL of gpio_to_axis_mux is
|
||||
|
||||
begin
|
||||
|
||||
axis_mux0: entity work.axis_mux (RTL)
|
||||
generic map (
|
||||
kGpioWidth => kGpioWidth,
|
||||
kAxiWidth => kAxiWidth)
|
||||
port map (
|
||||
gpio => gpio,
|
||||
mux_select => mux_select(0),
|
||||
s_axis_aclk => s_axis_0_aclk,
|
||||
s_axis_tdata => s_axis_tdata_0,
|
||||
s_axis_tvalid => s_axis_tvalid_0,
|
||||
s_axis_tready => s_axis_tready_0,
|
||||
m_axis_aclk => m_axis_0_aclk,
|
||||
m_axis_tvalid => m_axis_tvalid_0,
|
||||
m_axis_tdata => m_axis_tdata_0
|
||||
);
|
||||
|
||||
axis_mux1: entity work.axis_mux (RTL)
|
||||
generic map (
|
||||
kGpioWidth => kGpioWidth,
|
||||
kAxiWidth => kAxiWidth)
|
||||
port map (
|
||||
gpio => gpio,
|
||||
mux_select => mux_select(1),
|
||||
s_axis_aclk => s_axis_1_aclk,
|
||||
s_axis_tdata => s_axis_tdata_1,
|
||||
s_axis_tvalid => s_axis_tvalid_1,
|
||||
s_axis_tready => s_axis_tready_1,
|
||||
m_axis_aclk => m_axis_1_aclk,
|
||||
m_axis_tvalid => m_axis_tvalid_1,
|
||||
m_axis_tdata => m_axis_tdata_1
|
||||
);
|
||||
|
||||
axis_mux2: entity work.axis_mux (RTL)
|
||||
generic map (
|
||||
kGpioWidth => kGpioWidth,
|
||||
kAxiWidth => kAxiWidth)
|
||||
port map (
|
||||
gpio => gpio,
|
||||
mux_select => mux_select(4),
|
||||
s_axis_aclk => s_axis_2_aclk,
|
||||
s_axis_tdata => s_axis_tdata_2,
|
||||
s_axis_tvalid => s_axis_tvalid_2,
|
||||
s_axis_tready => s_axis_tready_2,
|
||||
m_axis_aclk => m_axis_2_aclk,
|
||||
m_axis_tvalid => m_axis_tvalid_2,
|
||||
m_axis_tdata => m_axis_tdata_2
|
||||
);
|
||||
|
||||
axis_mux3: entity work.axis_mux (RTL)
|
||||
generic map (
|
||||
kGpioWidth => kGpioWidth,
|
||||
kAxiWidth => kAxiWidth)
|
||||
port map (
|
||||
gpio => gpio,
|
||||
mux_select => mux_select(5),
|
||||
s_axis_aclk => s_axis_3_aclk,
|
||||
s_axis_tdata => s_axis_tdata_3,
|
||||
s_axis_tvalid => s_axis_tvalid_3,
|
||||
s_axis_tready => s_axis_tready_3,
|
||||
m_axis_aclk => m_axis_3_aclk,
|
||||
m_axis_tvalid => m_axis_tvalid_3,
|
||||
m_axis_tdata => m_axis_tdata_3
|
||||
);
|
||||
|
||||
end RTL;
|
||||
@@ -0,0 +1,228 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: rf_nco_reset
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- This entity has the logic needed to synchronously reset the NCO inside the
|
||||
-- RF section.
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
use IEEE.numeric_std.all;
|
||||
|
||||
entity rf_nco_reset is
|
||||
port(
|
||||
-- AXI-lite clock used for RFDC configuration.
|
||||
ConfigClk : in std_logic;
|
||||
|
||||
-- Radio clock used in the converter data path.
|
||||
DataClk : in std_logic;
|
||||
|
||||
-- PL SYSREF
|
||||
dSysref : in std_logic;
|
||||
|
||||
--Strobe dNcoResetEn for one DataClk cycle to initiate NCO reset.
|
||||
dStartNcoReset : in std_logic;
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- NCO reset controls and status
|
||||
---------------------------------------------------------------------------
|
||||
-- Port naming convention:
|
||||
-- cDac<Tile Number><Converter Number><signal name>
|
||||
-- cAdc<Tile Number><Converter Number><signal name>
|
||||
|
||||
-----------------------------------
|
||||
-- DAC Tile 228
|
||||
-----------------------------------
|
||||
-- DAC common NCO update controls and status.
|
||||
cDac0xNcoUpdateBusy : in std_logic_vector(1 downto 0);
|
||||
cDac0xNcoUpdateReq : out std_logic := '0';
|
||||
cDac0xSysrefIntGating : out std_logic := '0';
|
||||
cDac0xSysrefIntReenable : out std_logic := '0';
|
||||
|
||||
-----------------------------------
|
||||
-- DAC Tile 229
|
||||
-----------------------------------
|
||||
-- DAC common NCO update controls and status.
|
||||
cDac1xNcoUpdateBusy : in std_logic;
|
||||
cDac1xNcoUpdateReq : out std_logic := '0';
|
||||
|
||||
-----------------------------------
|
||||
--ADC Tile 224
|
||||
-----------------------------------
|
||||
-- ADC common NCO update controls and status.
|
||||
cAdc0xNcoUpdateBusy : in std_logic;
|
||||
cAdc0xNcoUpdateReq : out std_logic := '0';
|
||||
|
||||
-----------------------------------
|
||||
--ADC Tile 226
|
||||
-----------------------------------
|
||||
-- ADC common NCO update controls and status.
|
||||
cAdc2xNcoUpdateBusy : in std_logic;
|
||||
cAdc2xNcoUpdateReq : out std_logic := '0';
|
||||
|
||||
-- NCO reset can be initiated only when cNcoPhaseRst is set to '1' and
|
||||
-- cNcoUpdateEn = 0x20. The FSM in this entity will set these values when
|
||||
-- an NCO reset is initiated during synchronization. These ports are common
|
||||
-- for all the converters. So, we will fan these signals out to each
|
||||
-- converter outside this entity.
|
||||
cNcoPhaseRst : out std_logic := '1';
|
||||
cNcoUpdateEn : out std_logic_vector(5 downto 0) := "100000";
|
||||
|
||||
-- NCO reset status back to the user.
|
||||
dNcoResetDone : out std_logic := '0'
|
||||
);
|
||||
end rf_nco_reset;
|
||||
|
||||
architecture RTL of rf_nco_reset is
|
||||
|
||||
-- State machine to sequence NCO reset across different RFDC tiles.
|
||||
type ResetState_t is (Idle, ReqGating, CheckGating, CheckUpdateDone,
|
||||
CheckResetDone, ResetDone);
|
||||
signal cResetState : ResetState_t := Idle;
|
||||
|
||||
signal dNcoResetDone_ms, cNcoResetDone : std_logic := '0';
|
||||
signal dStartNcoResetReg, cStartNcoReset_ms, cStartNcoReset : std_logic := '0';
|
||||
signal cSysref_ms, cSysref, cSysrefDlyd : std_logic := '0';
|
||||
signal cSysrefIntGating, dSysrefIntGating_ms,
|
||||
dSysrefIntGating : std_logic := '0';
|
||||
begin
|
||||
|
||||
-- NCO start signal from the user is a one DataClk cycle strobe. In this
|
||||
-- process, we register the NCO start request from the user. This NCO start
|
||||
-- request register is cleared after the NCO reset sequence is initiated. We
|
||||
-- used the signal used to gate SYSREF to clear this register.
|
||||
RegNcoStart: process(DataClk)
|
||||
begin
|
||||
if rising_edge(DataClk) then
|
||||
dSysrefIntGating_ms <= cSysrefIntGating;
|
||||
dSysrefIntGating <= dSysrefIntGating_ms;
|
||||
if dSysrefIntGating = '1' then
|
||||
dStartNcoResetReg <= '0';
|
||||
elsif dStartNcoReset = '1' then
|
||||
dStartNcoResetReg <= '1';
|
||||
end if;
|
||||
end if;
|
||||
end process RegNcoStart;
|
||||
|
||||
-- Irrespective of when NCO reset strobe is issued by the user, we need to
|
||||
-- initiate NCO reset only on the rising edge of SYSREF. This is because, we
|
||||
-- have to complete the reset within a SYSREF period.
|
||||
ConfigClkCross: process(ConfigClk)
|
||||
begin
|
||||
if rising_edge(ConfigClk) then
|
||||
cSysref_ms <= dSysref;
|
||||
cSysref <= cSysref_ms;
|
||||
cSysrefDlyd <= cSysref;
|
||||
cStartNcoReset_ms <= dStartNcoResetReg;
|
||||
cStartNcoReset <= cStartNcoReset_ms;
|
||||
end if;
|
||||
end process ConfigClkCross;
|
||||
|
||||
-- These signals can be set to a constant value as NCO phase reset is only
|
||||
-- initiated by *NcoUpdateReq signal.
|
||||
cNcoPhaseRst <= '1';
|
||||
cNcoUpdateEn <= "100000";
|
||||
|
||||
-- ! STATE MACHINE STARTUP !
|
||||
-- The state machine starts in Idle state and does not change state until
|
||||
-- cStartNcoReset is set to '1'. cStartNcoReset signal and cSysref are based
|
||||
-- of ConfigClock so changing state from Idle cannot go metastable. State
|
||||
-- machine to initiate NCO reset on all enabled RFDC tiles. This state
|
||||
-- machine was written based of the information provided in "NCO frequency
|
||||
-- hopping" section in PG269 (v2.2). We use multi-mode for NCO reset.
|
||||
ResetFsm: process(ConfigClk)
|
||||
begin
|
||||
if rising_edge(ConfigClk) then
|
||||
cResetState <= Idle;
|
||||
cNcoResetDone <= '0';
|
||||
cDac0xNcoUpdateReq <= '0';
|
||||
cSysrefIntGating <= '0';
|
||||
cDac0xSysrefIntReenable <= '0';
|
||||
cDac1xNcoUpdateReq <= '0';
|
||||
cAdc0xNcoUpdateReq <= '0';
|
||||
cAdc2xNcoUpdateReq <= '0';
|
||||
case cResetState is
|
||||
-- Stay in this state until NCO reset sequence is initiated. NCO reset
|
||||
-- is initiated only on the rising edge of SYSREF.
|
||||
when Idle =>
|
||||
if cSysref = '1' and cSysrefDlyd = '0' and cStartNcoReset = '1' then
|
||||
cResetState <= ReqGating;
|
||||
cSysrefIntGating <= '1';
|
||||
end if;
|
||||
|
||||
-- When NCO reset is initiated, gate the RFDC internal SYSREF. To gate
|
||||
-- internal SYSREF set cSysrefIntGating to '1'. To request NCO reset
|
||||
-- strobe cDac0xNcoUpdateReq for one ConfigClk period. At this point,
|
||||
-- we can only request NCO reset for RF-DAC tile 228.
|
||||
when ReqGating =>
|
||||
cResetState <= CheckGating;
|
||||
cDac0xNcoUpdateReq <= '1';
|
||||
cSysrefIntGating <= '1';
|
||||
|
||||
-- Since we are gating SYSREF inside RFDC, we need to wait until SYSREF
|
||||
-- is gated internally. RFDC sets cDac0xNcoUpdateBusy[0] to '1' when
|
||||
-- SYSREF is gated. cDac0xNcoUpdateBusy[1] is also set to '1' to
|
||||
-- indicate that NCO reset is still in progress. After the SYSREF is
|
||||
-- gated request NCO reset on all other converter tiles.
|
||||
when CheckGating =>
|
||||
cSysrefIntGating <= '1';
|
||||
cResetState <= CheckGating;
|
||||
if cDac0xNcoUpdateBusy = "11" then
|
||||
cResetState <= CheckUpdateDone;
|
||||
cDac1xNcoUpdateReq <= '1';
|
||||
cAdc0xNcoUpdateReq <= '1';
|
||||
cAdc2xNcoUpdateReq <= '1';
|
||||
end if;
|
||||
|
||||
-- In this state, we check if the RFDC block is ready for NCO reset.
|
||||
-- This check is done using the *Busy signal from RFDC. Once RFDC is
|
||||
-- ready for NCO reset, disable internal SYSREF gating.
|
||||
when CheckUpdateDone =>
|
||||
cSysrefIntGating <= '1';
|
||||
cResetState <= CheckUpdateDone;
|
||||
if cDac0xNcoUpdateBusy = "10" and cAdc0xNcoUpdateBusy = '0' and
|
||||
cAdc2xNcoUpdateBusy = '0' and cDac1xNcoUpdateBusy = '0' and
|
||||
cSysref = '1' and cSysrefDlyd = '0' then
|
||||
cDac0xSysrefIntReenable <= '1';
|
||||
cResetState <= CheckResetDone;
|
||||
end if;
|
||||
|
||||
-- NCO reset is done when cDac0xNcoUpdateBusy[1] is set to '0'. RFDC is
|
||||
-- programmed from software to reset the NCO on a SYSREF rising edge.
|
||||
when CheckResetDone =>
|
||||
cSysrefIntGating <= '1';
|
||||
cResetState <= CheckResetDone;
|
||||
if cDac0xNcoUpdateBusy = "00" then
|
||||
cResetState <= ResetDone;
|
||||
end if;
|
||||
|
||||
-- Wait in this state until another NCO reset request is issued.
|
||||
when ResetDone =>
|
||||
cNcoResetDone <= '1';
|
||||
cResetState <= ResetDone;
|
||||
if cSysref = '1' and cSysrefDlyd = '0' and cStartNcoReset = '1' then
|
||||
cResetState <= ReqGating;
|
||||
cSysrefIntGating <= '1';
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process ResetFsm;
|
||||
|
||||
cDac0xSysrefIntGating <= cSysrefIntGating;
|
||||
|
||||
-- Move the NCO reset done status to DataClk domain.
|
||||
DataClkCrossing: process(DataClk)
|
||||
begin
|
||||
if rising_edge(DataClk) then
|
||||
dNcoResetDone_ms <= cNcoResetDone;
|
||||
dNcoResetDone <= dNcoResetDone_ms;
|
||||
end if;
|
||||
end process DataClkCrossing;
|
||||
|
||||
end RTL;
|
||||
@@ -0,0 +1,216 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: rf_reset
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- Control RFDC, ADC, and DAC resets.
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
use IEEE.numeric_std.all;
|
||||
|
||||
entity rf_reset is
|
||||
port(
|
||||
-- Clocks used in the data path.
|
||||
DataClk : in std_logic;
|
||||
PllRefClk : in std_logic;
|
||||
RfClk : in std_logic;
|
||||
RfClk2x : in std_logic;
|
||||
DataClk2x : in std_logic;
|
||||
|
||||
-- Master resets from the Radio.
|
||||
dTimedReset : in std_logic;
|
||||
dSwReset : in std_logic;
|
||||
|
||||
-- Resets outputs.
|
||||
dReset_n : out std_logic := '0';
|
||||
d2Reset_n : out std_logic := '0';
|
||||
r2Reset_n : out std_logic := '0';
|
||||
rAxiReset_n : out std_logic := '0';
|
||||
rReset_n : out std_logic := '0'
|
||||
);
|
||||
end rf_reset;
|
||||
|
||||
|
||||
architecture RTL of rf_reset is
|
||||
|
||||
-- POR value for all resets are active high or low.
|
||||
signal dResetPulseDly : std_logic_vector(2 downto 0) := "111";
|
||||
signal dResetPulseStretch : std_logic := '1';
|
||||
signal pResetPulseStretch : std_logic_vector(1 downto 0) := "11";
|
||||
signal pResetPulse_n : std_logic := '0';
|
||||
signal pAxiReset_n : std_logic := '0';
|
||||
|
||||
|
||||
begin
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Clock Phase Diagram
|
||||
-----------------------------------------------------------------------------
|
||||
-- Before we look into the details of the clock alignment, here is the clock
|
||||
-- frequencies of all the synchronous clocks that is used in the design.
|
||||
-- PllRefClk is the reference clock for the FPGA PLL and all other clocks are
|
||||
-- derived from PllRefClk. PllRefClk for X410 is ~62.5 MHz
|
||||
-- PllRefClk = ~62.5 MHz (Sample clock/48. This is the X410 configuration and
|
||||
-- could be different for other x4xx variants.)
|
||||
-- DataClk = PllRefClk*2
|
||||
-- DataClkx2 = PllRefClk*4
|
||||
-- RfClk = PllRefClk*3
|
||||
-- RfClkx2 = PllRefClk*6
|
||||
-- DataClk = PllRefClk*4 for legacy mode. In legacy mode, we will not use
|
||||
-- DataClkx2 as the clock frequency will be too high to close timing.
|
||||
-- Five clocks with five different frequencies, all related and occasionally
|
||||
-- aligned. Rising edge of all clocks are aligned to the rising edge of
|
||||
-- PllRefClk. We will use the rising edge of PllRefClk as the reference to
|
||||
-- assert synchronous reset for all clock domains. The synchronous reset
|
||||
-- pulse is in the DataClk domain. As we can see from the timing diagram, the
|
||||
-- DataClk rising edge is not always aligned to the rising edge of all the
|
||||
-- other clocks. But, it is guaranteed that the DataClk will be aligned to
|
||||
-- all the other clock on the rising edge of PLL reference clock. In case 1,
|
||||
-- the synchronous reset pulse is on the DataClk edge where the data clock is
|
||||
-- not aligned to RfClk. We stretch the pulse from DataClk domain and send
|
||||
-- the reset out on the rising edge of PllRefClk where all the clocks rising
|
||||
-- edge is aligned. In case 2, the synchronous reset is received on the
|
||||
-- DataClk cycle where all the clocks are aligned. This is because, in
|
||||
-- case 2, the synchronous reset is received on the rising edge of PllRefClk.
|
||||
-- For case 1 and case 2, all the output resets are asserted only on the
|
||||
-- PllRefClk rising edge to guarantee a known relationship between the resets
|
||||
-- in different clock domains.
|
||||
--
|
||||
-- Alignment * * *
|
||||
-- ___________ ___________ ___________ ___________ ___________
|
||||
-- PllRefClk __| |___________| |___________| |___________| |___________| |
|
||||
-- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
-- RfClk2x __| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_
|
||||
-- ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
|
||||
-- RfClk __| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___| |___|
|
||||
-- __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
|
||||
-- DataClk2x __| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__|
|
||||
-- _____ _____ _____ _____ _____ _____ _____ _____ _____
|
||||
-- DataClk __| |_____| |_____| |_____| |_____| |_____| |_____| |_____| |_____| |_____|
|
||||
-- . : : : :
|
||||
-- --------- Case 1 ---------.-- : : : :
|
||||
-- ^ : : ^ :
|
||||
-- Reset Strobe --> | : Aligned reset strobe -->| :
|
||||
-- ____________ : : : :
|
||||
-- dResetPulse________| |______________________________________ : :
|
||||
-- : _____________________________________________________________________________
|
||||
-- dResetPulseStretch ______________________| :
|
||||
-- : ________________________________________________
|
||||
-- pResetPulseStretch ____________________________________________| : : |___
|
||||
-- _________________________________________________________________________ :
|
||||
-- pResetPulse_n : |________________________________
|
||||
-- : : : :
|
||||
-- --------- Case 2 ----------- : : : :
|
||||
-- ^ : ^ :
|
||||
-- Reset Strobe --> | : | <-- Aligned reset strobe
|
||||
-- ____________ : : :
|
||||
-- dResetPulse(0) ________| |______________________________________________________________________________
|
||||
-- _______________________________________________________________________________
|
||||
-- dResetPulseStretch ______________________| :
|
||||
-- ________________________________________________________
|
||||
-- pResetPulseStretch ____________________________________________| :
|
||||
-- _________________________________________________________________________
|
||||
-- pResetPulse_n |________________________________
|
||||
-- --------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Implementation
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-- Since the dTimedReset is asserted only for one DataClk cycle, we need to
|
||||
-- stretch the strobe to four DataClk cycles, so the strobe is wide enough to
|
||||
-- be sampled by PllRefClk which is four times the DataClk period. Pulse
|
||||
-- stretch is done for 4 DataClk periods to support the legacy mode. We also
|
||||
-- do a logical OR on resets from software. Software resets are from the
|
||||
-- ConfigClock domain which is a slower clock than the PllRefClk. So, we
|
||||
-- don't have to stretch the software reset.
|
||||
PulseStretch: process(DataClk)
|
||||
begin
|
||||
if rising_edge(DataClk) then
|
||||
dResetPulseDly <= dResetPulseDly(1 downto 0) & (dTimedReset or dSwReset);
|
||||
dResetPulseStretch <= '0';
|
||||
if (dResetPulseDly /= "000") or dTimedReset = '1' or dSwReset = '1' then
|
||||
dResetPulseStretch <= '1';
|
||||
end if;
|
||||
end if;
|
||||
end process PulseStretch;
|
||||
|
||||
-- Strobe reset pulse for 2 PllRefClk period to make sure we have the reset
|
||||
-- asserted for longer period. The FIR filter is the only design that
|
||||
-- requires reset to be asserted for 2 clock cycles. This requirement is
|
||||
-- satisfied with one PllRefClk period. RFDC does not have any AXI stream
|
||||
-- reset time requirement. We will reset all designs for two PllRefClk period
|
||||
-- just to be on the safer side. The same strategy is used for DAC resets as
|
||||
-- well.
|
||||
ResetOut: process(PllRefClk)
|
||||
begin
|
||||
if rising_edge(PllRefClk) then
|
||||
pResetPulseStretch <= pResetPulseStretch(0) & dResetPulseStretch;
|
||||
pResetPulse_n <= not (pResetPulseStretch(1) or pResetPulseStretch(0));
|
||||
end if;
|
||||
end process ResetOut;
|
||||
|
||||
-- We are using PllRefClk as the reference and issuing resets to all the
|
||||
-- other clock domains. We are not trying to align all the resets in
|
||||
-- different clock domains. We are making sure that all resets will be
|
||||
-- asserted with respect to each other at the same time from run to run.
|
||||
DataClkReset: process(DataClk)
|
||||
begin
|
||||
if rising_edge(DataClk) then
|
||||
dReset_n <= pResetPulse_n;
|
||||
end if;
|
||||
end process DataClkReset;
|
||||
|
||||
DataClk2xReset: process(DataClk2x)
|
||||
begin
|
||||
if rising_edge(DataClk2x) then
|
||||
d2Reset_n <= pResetPulse_n;
|
||||
end if;
|
||||
end process DataClk2xReset;
|
||||
|
||||
Rfclk2xReset: process(RfClk2x)
|
||||
begin
|
||||
if rising_edge(RfClk2x) then
|
||||
r2Reset_n <= pResetPulse_n;
|
||||
end if;
|
||||
end process Rfclk2xReset;
|
||||
|
||||
RfclkReset: process(RfClk)
|
||||
begin
|
||||
if rising_edge(RfClk) then
|
||||
rReset_n <= pResetPulse_n;
|
||||
end if;
|
||||
end process RfclkReset;
|
||||
|
||||
-------------------------------------
|
||||
-- RF Resets
|
||||
-------------------------------------
|
||||
-- RFDC resets are asserted only once and it should be done using the reset
|
||||
-- from software. This is because we want the RFDC AXI-S interface in reset
|
||||
-- until the RfClk is stable. The only way to know if the RfClk is stable is
|
||||
-- by reading the lock status of sample clock PLL and MMCM used to generate
|
||||
-- all clocks in the signal path. dSwReset is a software reset while is
|
||||
-- asserted for a longer period of time and it does not require any pulse
|
||||
-- stretch.
|
||||
|
||||
RfdcReset: process(PllRefClk)
|
||||
begin
|
||||
if rising_edge(PllRefClk) then
|
||||
pAxiReset_n <= not dSwReset;
|
||||
end if;
|
||||
end process RfdcReset;
|
||||
|
||||
RfclkAxiReset: process(RfClk)
|
||||
begin
|
||||
if rising_edge(RfClk) then
|
||||
rAxiReset_n <= pAxiReset_n;
|
||||
end if;
|
||||
end process RfclkAxiReset;
|
||||
|
||||
end RTL;
|
||||
@@ -0,0 +1,208 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: rf_reset_controller
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- Control RFDC, ADC, and DAC resets.
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
use IEEE.numeric_std.all;
|
||||
|
||||
library WORK;
|
||||
use WORK.PkgRFDC_REGS_REGMAP.all;
|
||||
|
||||
entity rf_reset_controller is
|
||||
port(
|
||||
-- Clocks
|
||||
-- Config clock is async to all the others.
|
||||
ConfigClk : in std_logic;
|
||||
DataClk : in std_logic;
|
||||
PllRefClk : in std_logic;
|
||||
RfClk : in std_logic;
|
||||
RfClk2x : in std_logic;
|
||||
DataClk2x : in std_logic;
|
||||
|
||||
-- Master resets from the Radio
|
||||
dAdcResetPulse : in std_logic;
|
||||
dDacResetPulse : in std_logic;
|
||||
|
||||
-- ADC Resets
|
||||
dAdcDataOutReset_n : out std_logic;
|
||||
r2AdcFirReset_n : out std_logic;
|
||||
rAdcRfdcAxiReset_n : out std_logic;
|
||||
rAdcEnableData : out std_logic;
|
||||
rAdcGearboxReset_n : out std_logic;
|
||||
|
||||
-- DAC Resets
|
||||
dDacDataInReset_n : out std_logic;
|
||||
r2DacFirReset_n : out std_logic;
|
||||
d2DacFirReset_n : out std_logic;
|
||||
rDacRfdcAxiReset_n : out std_logic;
|
||||
rDacGearboxReset_n : out std_logic;
|
||||
|
||||
-- SW Control and Status
|
||||
-- Control to initiate resets to RFDC and decimation block including the
|
||||
-- gearboxes. The reset status is a sticky status of both ADC and DAC.
|
||||
cSoftwareControl : in std_logic_vector(31 downto 0);
|
||||
cSoftwareStatus : out std_logic_vector(31 downto 0)
|
||||
);
|
||||
end rf_reset_controller;
|
||||
|
||||
|
||||
architecture RTL of rf_reset_controller is
|
||||
|
||||
-- POR value for all resets are high.
|
||||
signal cTriggerAdcReset : std_logic := '1';
|
||||
signal cTriggerAdcResetDlyd : std_logic := '1';
|
||||
signal cTriggerDacReset : std_logic := '1';
|
||||
signal cTriggerDacResetDlyd : std_logic := '1';
|
||||
|
||||
signal dTriggerAdcReset_ms : std_logic := '1';
|
||||
signal dTriggerAdcReset : std_logic := '1';
|
||||
signal dTriggerDacReset_ms : std_logic := '1';
|
||||
signal dTriggerDacReset : std_logic := '1';
|
||||
|
||||
-- POR value of all reset done signals are set to low.
|
||||
signal cTriggerAdcResetDone_ms : std_logic := '0';
|
||||
signal cTriggerAdcResetDone : std_logic := '0';
|
||||
signal cAdcResetDoneSticky : std_logic := '0';
|
||||
signal cTriggerDacResetDone_ms : std_logic := '0';
|
||||
signal cTriggerDacResetDone : std_logic := '0';
|
||||
signal cDacResetDoneSticky : std_logic := '0';
|
||||
|
||||
attribute ASYNC_REG : string;
|
||||
attribute ASYNC_REG of dTriggerAdcReset : signal is "TRUE";
|
||||
attribute ASYNC_REG of dTriggerDacReset : signal is "TRUE";
|
||||
attribute ASYNC_REG of cTriggerAdcResetDone : signal is "TRUE";
|
||||
attribute ASYNC_REG of cTriggerDacResetDone : signal is "TRUE";
|
||||
attribute ASYNC_REG of dTriggerAdcReset_ms : signal is "TRUE";
|
||||
attribute ASYNC_REG of dTriggerDacReset_ms : signal is "TRUE";
|
||||
attribute ASYNC_REG of cTriggerAdcResetDone_ms : signal is "TRUE";
|
||||
attribute ASYNC_REG of cTriggerDacResetDone_ms : signal is "TRUE";
|
||||
|
||||
begin
|
||||
|
||||
-- rAdcEnableData is set to '1' as we don't control the flow of RX data.
|
||||
rAdcEnableData <= '1';
|
||||
|
||||
cTriggerAdcReset <= cSoftwareControl(kADC_RESET);
|
||||
cTriggerDacReset <= cSoftwareControl(kDAC_RESET);
|
||||
|
||||
cSoftwareStatus <= (
|
||||
kADC_SEQ_DONE => cAdcResetDoneSticky,
|
||||
kDAC_SEQ_DONE => cDacResetDoneSticky,
|
||||
others => '0'
|
||||
);
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- High-Level Resets Using ConfigClk
|
||||
-----------------------------------------------------------------------------
|
||||
-- Pass the master FSM reset around to the other clock domains and then
|
||||
-- return them back to the ConfigClk domain. This is also a handy way to
|
||||
-- prove all your clocks are toggling to some extent.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
SeqResetDataClk : process(DataClk)
|
||||
begin
|
||||
if rising_edge(DataClk) then
|
||||
-- double-syncs have no sync reset!
|
||||
dTriggerAdcReset_ms <= cTriggerAdcReset;
|
||||
dTriggerAdcReset <= dTriggerAdcReset_ms;
|
||||
dTriggerDacReset_ms <= cTriggerDacReset;
|
||||
dTriggerDacReset <= dTriggerDacReset_ms;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Reset Sequence Done Status
|
||||
-----------------------------------------------------------------------------
|
||||
-- Now back to ConfigClk! We provide the status for all software controlled
|
||||
-- resets. We move the signal from ConfigClk to DataClk domain and move it
|
||||
-- back to ConfigClk domain. This just proves that DataClk is toggling and
|
||||
-- the reset requested by software is sampled in the DataClk.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
SeqResetDone : process(ConfigClk)
|
||||
begin
|
||||
if rising_edge(ConfigClk) then
|
||||
-- double-syncs have no sync reset!
|
||||
cTriggerAdcResetDone_ms <= dTriggerAdcReset;
|
||||
cTriggerAdcResetDone <= cTriggerAdcResetDone_ms;
|
||||
cTriggerDacResetDone_ms <= dTriggerDacReset;
|
||||
cTriggerDacResetDone <= cTriggerDacResetDone_ms;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
-- ADC reset done
|
||||
SwAdcResetDone: process(ConfigClk)
|
||||
begin
|
||||
if rising_edge(ConfigClk) then
|
||||
cTriggerAdcResetDlyd <= cTriggerAdcReset;
|
||||
-- De-assert reset status on the rising edge of SW ADC reset.
|
||||
if cTriggerAdcReset = '1' and cTriggerAdcResetDlyd = '0' then
|
||||
cAdcResetDoneSticky <= '0';
|
||||
-- Assert and hold the ADC reset status on ADC reset strobe.
|
||||
elsif cTriggerAdcResetDone = '1' then
|
||||
cAdcResetDoneSticky <= '1';
|
||||
end if;
|
||||
end if;
|
||||
end process SwAdcResetDone;
|
||||
|
||||
-- DAC reset done
|
||||
SwDacResetDone: process(ConfigClk)
|
||||
begin
|
||||
if rising_edge(ConfigClk) then
|
||||
cTriggerDacResetDlyd <= cTriggerDacReset;
|
||||
-- De-assert reset status on the rising edge of SW DAC reset.
|
||||
if cTriggerDacReset = '1' and cTriggerDacResetDlyd = '0' then
|
||||
cDacResetDoneSticky <= '0';
|
||||
-- Assert and hold the DAC reset status on DAC reset strobe.
|
||||
elsif cTriggerDacResetDone = '1' then
|
||||
cDacResetDoneSticky <= '1';
|
||||
end if;
|
||||
end if;
|
||||
end process SwDacResetDone;
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- rf_reset Instances
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
AdcResets: entity work.rf_reset (RTL)
|
||||
port map (
|
||||
DataClk => DataClk,
|
||||
PllRefClk => PllRefClk,
|
||||
RfClk => RfClk,
|
||||
RfClk2x => RfClk2x,
|
||||
DataClk2x => DataClk2x,
|
||||
dTimedReset => dAdcResetPulse,
|
||||
dSwReset => dTriggerAdcReset,
|
||||
dReset_n => dAdcDataOutReset_n,
|
||||
d2Reset_n => open,
|
||||
r2Reset_n => r2AdcFirReset_n,
|
||||
rAxiReset_n => rAdcRfdcAxiReset_n,
|
||||
rReset_n => rAdcGearboxReset_n
|
||||
);
|
||||
|
||||
DacResets: entity work.rf_reset (RTL)
|
||||
port map (
|
||||
DataClk => DataClk,
|
||||
PllRefClk => PllRefClk,
|
||||
RfClk => RfClk,
|
||||
RfClk2x => RfClk2x,
|
||||
DataClk2x => DataClk2x,
|
||||
dTimedReset => dDacResetPulse,
|
||||
dSwReset => dTriggerDacReset,
|
||||
dReset_n => dDacDataInReset_n,
|
||||
d2Reset_n => d2DacFirReset_n,
|
||||
r2Reset_n => r2DacFirReset_n,
|
||||
rAxiReset_n => rDacRfdcAxiReset_n,
|
||||
rReset_n => rDacGearboxReset_n
|
||||
);
|
||||
|
||||
end RTL;
|
||||
@@ -0,0 +1,51 @@
|
||||
--
|
||||
-- Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
--
|
||||
-- SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
--
|
||||
-- Module: scale_2x
|
||||
--
|
||||
-- Description:
|
||||
--
|
||||
-- This block does the scaling of IQ data by 2. The data from the mixer is
|
||||
-- 1/2 the full scale and the upper two bits will only have the signed bits,
|
||||
-- so it is okay to multiply the data by 2 and resize it back to 16 bits.
|
||||
--
|
||||
-- Parameters:
|
||||
--
|
||||
-- kDataWidth: Data width, should be a multiple of 16 bits.
|
||||
--
|
||||
|
||||
library IEEE;
|
||||
use IEEE.std_logic_1164.all;
|
||||
use IEEE.numeric_std.all;
|
||||
|
||||
library WORK;
|
||||
use WORK.PkgRf.all;
|
||||
|
||||
entity scale_2x is
|
||||
generic(
|
||||
kDataWidth : integer range 1 to 256 := 32
|
||||
);
|
||||
port(
|
||||
-- [..Q1,I1,Q0,I0] (I in LSBs). Each I and Q data is 16 bits wide, but
|
||||
-- since the data is only 1/2 full scale. Useful information is only
|
||||
-- in the lower 15 bits, with upper two bits used as a signed bit.
|
||||
cDataIn : in std_logic_vector(kDataWidth-1 downto 0);
|
||||
cDataValidIn : in std_logic;
|
||||
|
||||
-- [..Q1,I1,Q0,I0] (I in LSBs). 16 bit output with a gain of 2x.
|
||||
cDataOut : out std_logic_vector(kDataWidth-1 downto 0);
|
||||
cDataValidOut : out std_logic
|
||||
);
|
||||
end scale_2x;
|
||||
|
||||
architecture RTL of scale_2x is
|
||||
|
||||
begin
|
||||
|
||||
-- Scale the date by 2 by shifting the data to the left by 1 bit.
|
||||
cDataOut <= Gain2x(cDataIn);
|
||||
cDataValidOut <= cDataValidIn;
|
||||
|
||||
end RTL;
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright 2021 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: sync_wrapper
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// As the original synchronizer component has port signal names that are
|
||||
// incompatible with VHDL (in, out), this modules provides an an interface to
|
||||
// instantiate the synchronizer block in VHDL.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module sync_wrapper #(
|
||||
parameter WIDTH = 1,
|
||||
parameter STAGES = 2,
|
||||
parameter INITIAL_VAL = 0,
|
||||
parameter FALSE_PATH_TO_IN = 1
|
||||
)(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire [WIDTH-1:0] signal_in,
|
||||
output wire [WIDTH-1:0] signal_out
|
||||
);
|
||||
|
||||
synchronizer #(
|
||||
.WIDTH (WIDTH),
|
||||
.STAGES (STAGES),
|
||||
.INITIAL_VAL (INITIAL_VAL),
|
||||
.FALSE_PATH_TO_IN (FALSE_PATH_TO_IN)
|
||||
) synchronizer_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.in (signal_in),
|
||||
.out (signal_out)
|
||||
);
|
||||
|
||||
endmodule //sync_wrapper
|
||||
|
||||
`default_nettype wire
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user