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