fpga: x400: Add support for X410 motherboard FPGA

Co-authored-by: Andrew Moch <Andrew.Moch@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Javier Valenzuela <javier.valenzuela@ni.com>
Co-authored-by: Joerg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Kumaran Subramoniam <kumaran.subramoniam@ni.com>
Co-authored-by: Max Köhler <max.koehler@ni.com>
Co-authored-by: Michael Auchter <michael.auchter@ni.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>
Co-authored-by: Hector Rubio <hrubio@ni.com>


Original-commit: 6d3765605262016a80f71e36357f749ea35cbe5a
This commit is contained in:
Wade Fife
2021-06-10 11:56:58 -05:00
committed by Aaron Rossetto
co-authored by Andrew Moch Daniel Jepson Javier Valenzuela Joerg Hofrichter Kumaran Subramoniam Max Köhler Michael Auchter Paul Butler Hector Rubio
parent bfef20ea45
commit 61782b02d7
205 changed files with 299634 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: adc_gearbox_2x4
--
-- Description:
--
-- Gearbox to expand the data width from 2 SPC to 4 SPC.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity adc_gearbox_2x4 is
port(
Clk1x : in std_logic;
Clk3x : in std_logic;
-- Resets with synchronous de-assertion.
ac1Reset_n : in std_logic;
ac3Reset_n : in std_logic;
-- Data packing: [Q1,I1,Q0,I0] (I in LSBs).
c3DataIn : in std_logic_vector(95 downto 0);
c3DataValidIn : in std_logic;
-- Data packing: [Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs).
c1DataOut : out std_logic_vector(191 downto 0);
c1DataValidOut : out std_logic
);
end adc_gearbox_2x4;
architecture RTL of adc_gearbox_2x4 is
signal c1DataValidInDly, c3DataValidInDly
: std_logic_vector(3 downto 0) := (others => '0');
subtype Word_t is std_logic_vector(95 downto 0);
type Words_t is array(natural range<>) of Word_t;
signal c3DataInDly, c1DataInDly : Words_t(3 downto 0);
begin
-- Pipeline input data. We will need four pipeline stages to account for the
-- three possible Clk1x and Clk3x phases and the nature of data packing done
-- in the DDC filter. The DDC asserts data valid for two clock cycles and
-- de-asserted for one clock cycle. This requires us to have shift register
-- that is 4 sample words (each sample word is 2 SPC) deep.
InputValidPipeline: process(Clk3x, ac3Reset_n)
begin
if ac3Reset_n = '0' then
c3DataValidInDly <= (others => '0');
-- These registers are on the falling edge to prevent a hold violation at
-- the input to the following Clk1x FF (which may arrive late when more
-- heavily loaded than Clk3x)
elsif falling_edge(Clk3x) then
c3DataValidInDly <= c3DataValidInDly(c3DataValidInDly'left-1 downto 0) &
c3DataValidIn;
end if;
end process;
InputDataPipeline: process(Clk3x)
begin
-- These registers are on the falling edge to prevent a hold violation at
-- the input to the following Clk1x FF (which may arrive late when more
-- heavily loaded than Clk3x).
if falling_edge(Clk3x) then
c3DataInDly <= c3DataInDly(c3DataInDly'high-1 downto 0) & c3DataIn;
end if;
end process InputDataPipeline;
-- Data valid clock crossing from Clk3x to Clk1x
Clk3xToClk1xValidCrossing: process(Clk1x, ac1Reset_n)
begin
if ac1Reset_n = '0' then
c1DataValidInDly <= (others => '0');
elsif rising_edge(Clk1x) then
c1DataValidInDly <= c3DataValidInDly;
end if;
end process;
-- Data clock crossing from Clk3x to Clk1x
Clk3xToClk1xDataCrossing: process(Clk1x)
begin
if rising_edge(Clk1x) then
c1DataInDly <= c3DataInDly;
end if;
end process;
-----------------------------------------------------------------------------
--
-- p0 p1 p2 p0
-- Clk3x _______/¯¯¯¯¯¯¯\_______/¯¯¯¯¯¯¯\_______/¯¯¯¯¯¯¯\_______/¯¯¯
--
-- Clk1x _______/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\_______________________/¯¯¯
--
-- c3DataValidIn _/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\_______________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- This gearbox connect the DDC filter output to the remaining RX data path.
-- For efficient use of DSP slices we run the DDC at 3x clock rate. Both
-- Clk3x and Clk1x are sourced from the same PLL and is phase locked as shown
-- in the above timing diagram. The output of DDC filter is asserted for two
-- clock cycles and is de-asserted for one clock cycle. The remaining part of
-- the design cannot run at 3x clock rate. So, we increase the number of
-- samples per clock cycle and decrease the clock frequency to 1x. Depending
-- upon the pipeline delay through the filter and RF section, the phase of
-- data valid assertion could be on either p0, p1, or p2 edge. And depending
-- upon the phase, data packing to Clk1x domain will vary. Since there are
-- three possible phase, we will need three different data packing options.
--
-- Data packing is done by looking for two consecutive ones in the data valid
-- shift register (c1DataValidInDly).This pattern can be used only because of
-- the way output data is packed in the filter. If we see two consecutive
-- ones, then we know that we have enough data to be packed for the output of
-- this gearbox. This is because, we need two Clk3x cycles of 2 SPC data to
-- pack a 4 SPC data output on Clk1x. The location of two consecutive ones in
-- the data valid shift register will provide the location of valid data in
-- data shift register (c1DataInDly).
DataPacker: process(Clk1x)
begin
if rising_edge(Clk1x) then
-- Data valid is asserted when both Clk1x and Clk3x are phase aligned
-- (p0). In this case, c1DataValidInDly will have consecutive ones in
-- index 1 and 2.
c1DataValidOut <= c1DataValidInDly(1) and c1DataValidInDly(2);
c1DataOut <= c1DataInDly(1) & c1DataInDly(2);
-- Data valid asserted on phase p1.
if c1DataValidInDly(1 downto 0) = "11" then
c1DataOut <= c1DataInDly(0) & c1DataInDly(1);
c1DataValidOut <= '1';
-- Data valid asserted on phase p2.
elsif c1DataValidInDly(3 downto 2) = "11" then
c1DataOut <= c1DataInDly(2) & c1DataInDly(3);
c1DataValidOut <= '1';
end if;
end if;
end process;
end RTL;
+105
View File
@@ -0,0 +1,105 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: adc_gearbox_8x4
//
// Description:
//
// Gearbox ADC data from 8 SPC to 4 SPC and corresponding 2x clock to 1x
// clock. Also implement data swapping to format packets to fit the FIR
// filter input requirements.
//
// This modules incurs one clk1x cycle of delay on the data and valid signals
// from input on the 1x domain to output on the 2x domain.
//
`default_nettype none
module adc_gearbox_8x4 (
input wire clk1x,
input wire reset_n_1x,
// Data is _presumed_ to be packed [Sample7, ..., Sample0] (Sample0 in LSBs).
input wire [127:0] adc_q_in_1x,
input wire [127:0] adc_i_in_1x,
input wire valid_in_1x,
// De-assert enable_1x to clear the data valid output synchronously.
input wire enable_1x,
input wire clk2x,
// Data is packed [Q3,I3, ... , Q0, I0] (I in LSBs) when swap_iq_1x is '0'
input wire swap_iq_2x,
output wire [127:0] adc_out_2x,
output wire valid_out_2x
);
// Re-create the 1x clock in the 2x domain to produce a deterministic
// crossing.
reg toggle_1x, toggle_2x = 1'b0, toggle_2x_dly = 1'b0, valid_2x = 1'b0, valid_dly_2x = 1'b0;
reg [127:0] data_out_2x = 128'b0, adc_q_data_in_2x = 128'b0, adc_i_data_in_2x = 128'b0;
// Create a toggle in the 1x clock domain (clock divider /2).
always @(posedge clk1x or negedge reset_n_1x) begin
if ( ! reset_n_1x) begin
toggle_1x <= 1'b0;
end else begin
toggle_1x <= ! toggle_1x;
end
end
// Transfer the toggle from the 1x to the 2x domain. Delay the toggle in the
// 2x domain by one cycle and compare it to the non-delayed version. When
// they differ, push data_in[63:0] onto the output. When the match, push
// [127:64] onto the output. The datasheet is unclear on the exact
// implementation.
//
// It is safe to not reset this domain because all of the input signals will
// be cleared by the 1x reset. Safe default values are assigned to all these
// registers.
always @(posedge clk2x) begin
toggle_2x <= toggle_1x;
toggle_2x_dly <= toggle_2x;
adc_q_data_in_2x <= adc_q_in_1x;
adc_i_data_in_2x <= adc_i_in_1x;
data_out_2x <= 128'b0;
// Place Q in the MSBs, I in the LSBs by default, unless swapped = 1.
if (valid_2x) begin
if (swap_iq_2x) begin
if (toggle_2x != toggle_2x_dly) begin
data_out_2x <= {adc_i_data_in_2x[63:48], adc_q_data_in_2x[63:48],
adc_i_data_in_2x[47:32], adc_q_data_in_2x[47:32],
adc_i_data_in_2x[31:16], adc_q_data_in_2x[31:16],
adc_i_data_in_2x[15: 0], adc_q_data_in_2x[15: 0]};
end else begin
data_out_2x <= {adc_i_data_in_2x[127:112], adc_q_data_in_2x[127:112],
adc_i_data_in_2x[111: 96], adc_q_data_in_2x[111: 96],
adc_i_data_in_2x[95 : 80], adc_q_data_in_2x[95 : 80],
adc_i_data_in_2x[79 : 64], adc_q_data_in_2x[79 : 64]};
end
end else begin
if (toggle_2x != toggle_2x_dly) begin
data_out_2x <= {adc_q_data_in_2x[63:48], adc_i_data_in_2x[63:48],
adc_q_data_in_2x[47:32], adc_i_data_in_2x[47:32],
adc_q_data_in_2x[31:16], adc_i_data_in_2x[31:16],
adc_q_data_in_2x[15: 0], adc_i_data_in_2x[15: 0]};
end else begin
data_out_2x <= {adc_q_data_in_2x[127:112], adc_i_data_in_2x[127:112],
adc_q_data_in_2x[111: 96], adc_i_data_in_2x[111: 96],
adc_q_data_in_2x[95 : 80], adc_i_data_in_2x[95 : 80],
adc_q_data_in_2x[79 : 64], adc_i_data_in_2x[79 : 64]};
end
end
end
// Valid is simply a transferred version of the 1x clock's valid. Delay it one
// more cycle to align outputs.
valid_2x <= valid_in_1x && enable_1x;
valid_dly_2x <= valid_2x;
end
assign adc_out_2x = data_out_2x;
assign valid_out_2x = valid_dly_2x;
endmodule
`default_nettype wire
+233
View File
@@ -0,0 +1,233 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_gearbox_12x8
--
-- Description:
--
-- Gearbox to expand the data width from 12 SPC to 8 SPC.
-- Input Clocks, all aligned to one another and coming from same MMCM.
-- PLL reference clock = 61.44 or 62.5 MHz.
-- RfClk: 184.32 or 187.5 MHz (3x PLL reference clock)
-- Clk1x: 122.88 or 125 MHz (2x PLL reference clock)
-- Clk2x: 245.76 or 250 MHz (4x PLL reference clock)
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity dac_gearbox_12x8 is
port(
Clk1x : in std_logic;
RfClk : in std_logic;
ac1Reset_n : in std_logic;
arReset_n : in std_logic;
-- Data packing: [Q11,I11,Q10,I10,...,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c1DataIn : in std_logic_vector(383 downto 0);
c1DataValidIn : in std_logic;
-- Data packing: [Q7,I7,Q6,I6,...,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
rDataOut : out std_logic_vector(255 downto 0) := (others => '0');
rReadyForOutput : in std_logic;
rDataValidOut : out std_logic
);
end dac_gearbox_12x8;
architecture RTL of dac_gearbox_12x8 is
constant kDataWidth : natural := 16;
constant kDataI0Lsb : natural := 0;
constant kDataI0Msb : natural := kDataWidth-1;
constant kDataQ0Lsb : natural := kDataI0Msb+1;
constant kDataQ0Msb : natural := kDataQ0Lsb+kDataWidth-1;
constant kDataI1Lsb : natural := kDataQ0Msb+1;
constant kDataI1Msb : natural := kDataI1Lsb+kDataWidth-1;
constant kDataQ1Lsb : natural := kDataI1Msb+1;
constant kDataQ1Msb : natural := kDataQ1Lsb+kDataWidth-1;
constant kDataI2Lsb : natural := kDataQ1Msb+1;
constant kDataI2Msb : natural := kDataI2Lsb+kDataWidth-1;
constant kDataQ2Lsb : natural := kDataI2Msb+1;
constant kDataQ2Msb : natural := kDataQ2Lsb+kDataWidth-1;
constant kDataI3Lsb : natural := kDataQ2Msb+1;
constant kDataI3Msb : natural := kDataI3Lsb+kDataWidth-1;
constant kDataQ3Lsb : natural := kDataI3Msb+1;
constant kDataQ3Msb : natural := kDataQ3Lsb+kDataWidth-1;
constant kDataI4Lsb : natural := kDataQ3Msb+1;
constant kDataI4Msb : natural := kDataI4Lsb+kDataWidth-1;
constant kDataQ4Lsb : natural := kDataI4Msb+1;
constant kDataQ4Msb : natural := kDataQ4Lsb+kDataWidth-1;
constant kDataI5Lsb : natural := kDataQ4Msb+1;
constant kDataI5Msb : natural := kDataI5Lsb+kDataWidth-1;
constant kDataQ5Lsb : natural := kDataI5Msb+1;
constant kDataQ5Msb : natural := kDataQ5Lsb+kDataWidth-1;
constant kDataI6Lsb : natural := kDataQ5Msb+1;
constant kDataI6Msb : natural := kDataI6Lsb+kDataWidth-1;
constant kDataQ6Lsb : natural := kDataI6Msb+1;
constant kDataQ6Msb : natural := kDataQ6Lsb+kDataWidth-1;
constant kDataI7Lsb : natural := kDataQ6Msb+1;
constant kDataI7Msb : natural := kDataI7Lsb+kDataWidth-1;
constant kDataQ7Lsb : natural := kDataI7Msb+1;
constant kDataQ7Msb : natural := kDataQ7Lsb+kDataWidth-1;
subtype Word_t is std_logic_vector(383 downto 0);
type Words_t is array(natural range<>) of Word_t;
signal rDataInDly : Words_t(3 downto 0);
signal rDataValidDly : std_logic_vector(3 downto 0) := (others => '0');
signal c1PhaseCount, c1DataValidInDly : std_logic := '0';
signal rPhaseShiftReg : std_logic_vector(2 downto 0);
begin
-----------------------------------------------------------------------------
-- Data Packing 12 SPC to 8 SPC
-----------------------------------------------------------------------------
Clk1xDataCount: process(ac1Reset_n, Clk1x)
begin
if ac1Reset_n = '0' then
c1PhaseCount <= '0';
c1DataValidInDly <= '0';
elsif rising_edge(Clk1x) then
c1DataValidInDly <= c1DataValidIn;
c1PhaseCount <= (not c1PhaseCount) and (c1DataValidIn or c1DataValidInDly);
end if;
end process;
DataClkCrossing: process(RfClk)
begin
if rising_edge(RfClk) then
rDataInDly <= rDataInDly(rDataInDly'high-1 downto 0) & c1DataIn;
end if;
end process;
-- Store clock phase information in a shift register. The shift register
-- is a 3 bit register and it used in output data packer.
PhaseClkCrossing: process(arReset_n,RfClk)
begin
if arReset_n = '0' then
rPhaseShiftReg <= (others => '0');
elsif rising_edge(RfClk) then
rPhaseShiftReg(2 downto 1) <= rPhaseShiftReg(1 downto 0);
rPhaseShiftReg(0) <= c1PhaseCount;
end if;
end process;
-----------------------------------------------------------------------------
--
-- Timing diagram: Data valid is asserted when both clock are edge aligned.
--
-- | | |
-- v <-Clocks edge aligned v v
-- Clk1x ¯¯\____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\___
-- |
-- v <- O/p data valid assertion
-- RfClk ¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯
-- | | |
-- c1DataValid _/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | |
-- c1DValidDly _________/¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | |
-- c1PhaseCount _______/¯¯¯¯¯¯¯¯¯¯¯¯\|_______|__/¯¯¯¯|¯¯¯¯¯¯¯\__________/¯¯
-- | | |
-- v <- rPhaseSR= "001"
-- rPhaseSR(0) ________________/¯¯¯¯¯¯¯¯\_____|_______|_/¯¯¯¯¯¯¯\_________________
-- | |
-- v <- rPhaseSR= "010"
-- rPhaseSR(1) _________________________/¯¯¯¯¯¯¯¯\____|__________/¯¯¯¯¯¯¯\____________
-- |
-- v <- rPhaseSR= "100"
-- rPhaseSR(2) __________________________________/¯¯¯¯¯¯¯¯\_______________/¯¯¯¯¯¯¯\___
--
-- rDValidDly0 _________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- rDValidDly1 _________________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- rDValidDly2 __________________________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- In this design use a single bit counter on the input clock (Clk1x) domain
-- and pass it to the RfClk domain. When data valid is asserted when both
-- clocks are rising edge aligned, only one bit in rPhaseSR high, the
-- remaining bits are zero. We use the position of the bit counter in the
-- shift register to do data packing.
--
--
-- Timing diagram: When data valid is asserted when both clock are NOT edge
-- aligned.
--
-- | | |
-- v <-Clocks edge aligned v v
-- Clk1x ¯¯\____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\_____/¯¯¯¯¯\___
-- |
-- v <- O/p data valid assertion
-- RfClk ¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯¯¯\___/¯
-- | | | |
-- c1DataValid ________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | | |
-- c1DValidDly ___________________/¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | | | |
-- c1PhaseCount ___________________/¯¯¯¯¯¯¯¯¯¯|¯\_____|_____/¯|¯¯¯¯¯¯¯|¯¯\__________/¯¯
-- | | | |
-- v <- rPhaseSR= "001" |
-- rPhaseSR(0) ________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯\_____|_/¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯
-- | | |
-- v <- rPhaseSR= "011"
-- rPhaseSR(1) ________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯\_____|_/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
-- | |
-- v <- rPhaseSR= "110"
-- rPhaseSR(2) ________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\_______/¯¯¯¯¯¯¯¯¯¯
-- ^
-- | <- rPhaseSR= "101"
--
-- rDValidDly0 _________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- rDValidDly1 _________________________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
--
-- The above timing diagram is when input data valid is asserted when both
-- clocks rising edges are not aligned. In this case the more than one bit in
-- rPhaseSR is asserted which is unique to this case. As mentioned in the
-- above case, we use rPhaseSR value to determine data packing.
-- Output Data Packer
DataOut: process(RfClk)
begin
if rising_edge(RfClk) then
-- rPhaseShiftReg = "011"
rDataOut <= rDataInDly(2)(kDataQ7Msb downto kDataI0Lsb);
if rPhaseShiftReg = "110" or rPhaseShiftReg = "100" then
rDataOut <= rDataInDly(2)(kDataQ3Msb downto kDataI0Lsb) &
rDataInDly(3)(c1DataIn'length-1 downto kDataQ7Msb+1);
elsif rPhaseShiftReg = "101" or rPhaseShiftReg = "001" then
rDataOut <= rDataInDly(3)(c1DataIn'length-1 downto kDataI4Lsb);
elsif rPhaseShiftReg = "010" then
rDataOut <= rDataInDly(3)(kDataQ7Msb downto kDataI0Lsb);
end if;
end if;
end process;
DataValidOut: process(RfClk, arReset_n)
begin
if arReset_n = '0' then
rDataValidDly <= (others => '0');
rDataValidOut <= '0';
elsif rising_edge(RfClk) then
rDataValidDly <= rDataValidDly(rDataValidDly'left-1 downto 0) &
c1DataValidIn;
-- Data valid out asserting based on phase alignment RfClk and Clk1x.
-- When RfClk and Clk1x are not phase aligned.
rDataValidOut <= rDataValidDly(2) and rReadyForOutput;
-- When RfClk and Clk1x are phase aligned.
if (rPhaseShiftReg(2) xor rPhaseShiftReg(1) xor rPhaseShiftReg(0)) = '1' then
rDataValidOut <= rDataValidDly(2) and rDataValidDly(3) and rReadyForOutput;
end if;
end if;
end process;
end RTL;
+80
View File
@@ -0,0 +1,80 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: dac_gearbox_4x2
//
// Description:
//
// Gearbox DAC data from 4 SPC to 2 SPC and corresponding 2x clock to 1x
// clock.
// This module incurs in one clk1x cycle of delay on the data and valid
// signals from input on the 1x domain to output on the 2x domain.
//
`default_nettype none
module dac_gearbox_4x2 (
input wire clk1x,
input wire reset_n_1x,
// Data is _presumed_ to be packed [Q3,I3,Q2,I2,Q1,I1,Q0,I0]
input wire [127:0] data_in_1x,
input wire valid_in_1x,
output wire ready_out_1x,
input wire clk2x,
// Data is packed [Q1,I1,Q0,I0] (I in LSBs)
output wire [ 63:0] data_out_2x,
output wire valid_out_2x
);
// Re-create the 1x clock in the 2x domain to produce a deterministic
// crossing.
reg toggle_1x, toggle_2x = 1'b0, toggle_2x_dly = 1'b0, valid_2x = 1'b0, valid_dly_2x = 1'b0;
reg [127:0] data_in_2x_dly0 = 128'b0, data_in_2x_dly1 = 32'b0;
reg [63 :0] data_2x_dly = 64'b0;
// Create a toggle in the 1x clock domain (clock divider /2).
always @(posedge clk1x or negedge reset_n_1x) begin
if ( ! reset_n_1x) begin
toggle_1x <= 1'b0;
end else begin
toggle_1x <= ! toggle_1x;
end
end
// Transfer the toggle from the 1x to the 2x domain. Delay the toggle in the
// 2x domain by one cycle and compare it to the non-delayed version. When
// they differ, push data_in[63:0] onto the output. When they match, push
// [127:64] onto the output.
//
// It is safe to not reset this domain because all of the input signals will
// be cleared by the 1x reset. Safe default values are assigned to all these
// registers.
always @(posedge clk2x) begin
toggle_2x <= toggle_1x;
toggle_2x_dly <= toggle_2x;
data_in_2x_dly0 <= data_in_1x;
data_in_2x_dly1 <= data_in_2x_dly0 ;
data_2x_dly <= 64'b0;
if (valid_2x) begin
data_2x_dly <= data_in_2x_dly1[127:64];
if (toggle_2x != toggle_2x_dly) begin
data_2x_dly <= data_in_2x_dly0[63:0];
end
end
// Valid is simply a transferred version of the 1x clock's valid. Delay it
// one more cycle to align outputs.
valid_2x <= valid_in_1x;
valid_dly_2x <= valid_2x;
end
assign valid_out_2x = valid_dly_2x;
assign data_out_2x = data_2x_dly;
assign ready_out_1x = 1'b1;
endmodule
`default_nettype wire
+124
View File
@@ -0,0 +1,124 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_gearbox_6x12
--
-- Description:
--
-- Gearbox to expand the data width from 6 SPC to 12 SPC.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity dac_gearbox_6x12 is
port(
Clk1x : in std_logic;
Clk2x : in std_logic;
ac1Reset_n : in std_logic;
ac2Reset_n : in std_logic;
-- 16 bit data packing: [Q5,I5,Q4,I4,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c2DataIn : in std_logic_vector(191 downto 0);
c2DataValidIn : in std_logic;
-- 16 bit data packing: [Q11,I11,Q10,I10,..,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c1DataOut : out std_logic_vector(383 downto 0) := (others => '0');
c1DataValidOut : out std_logic := '0'
);
end dac_gearbox_6x12;
architecture RTL of dac_gearbox_6x12 is
subtype Word_t is std_logic_vector(191 downto 0);
type Words_t is array(natural range<>) of Word_t;
signal c1DataInDly, c2DataInDly : Words_t(2 downto 0);
signal c2DataValidInDly : std_logic_vector(1 downto 0) := (others => '0');
signal c1PhaseCount, c2PhaseCount : std_logic := '0';
signal c1DataValidIn, c1DataValidDly0 : std_logic := '0';
begin
-- Input data pipeline.
InputValidPipeline: process(Clk2x, ac2Reset_n)
begin
if ac2Reset_n = '0' then
c2DataValidInDly <= (others => '0');
elsif rising_edge(Clk2x) then
c2DataValidInDly <= c2DataValidInDly(c2DataValidInDly'left-1 downto 0) &
c2DataValidIn;
end if;
end process;
InputDataPipeline: process(Clk2x)
begin
if rising_edge(Clk2x) then
c2DataInDly <= c2DataInDly(c2DataInDly'high-1 downto 0) & c2DataIn;
end if;
end process;
-- Process to determine if data valid was asserted when both clocks were
-- in-phase. Since we are crossing a 2x clock domain to a 1x clock domain,
-- there are only two possible phase. One is data valid assertion when both
-- clocks rising edges are aligned. The other case is data valid assertion
-- when Clk2x is aligned to the falling edge.
Clock2xPhaseCount: process(ac2Reset_n, Clk2x)
begin
if ac2Reset_n = '0' then
c2PhaseCount <= '0';
elsif rising_edge(Clk2x) then
-- This is a single bit counter. This counter is enabled for an extra
-- clock cycle to account for the output pipeline delay.
c2PhaseCount <= (not c2PhaseCount) and
(c2DataValidInDly(1) or c2DataValidInDly(0));
end if;
end process;
-- Crossing clock from Clk2x to Clk1x.
Clk2xToClk1xCrossing: process(Clk1x)
begin
if rising_edge(Clk1x) then
c1DataInDly <= c2DataInDly;
c1PhaseCount <= c2PhaseCount;
c1DataValidIn <= c2DataValidInDly(0);
end if;
end process;
-- Output data packing is determined based on when input data valid was
-- asserted. c1PhaseCount is '1' when input data valid was asserted when both
-- clocks are rising edge aligned. In this case, we can send data from the
-- with 1 and 2 pipeline delays.
-- When data valid is asserted when the two clock are not rising edge
-- aligned, we will use data from 2 and 3 pipeline delays.
DataOut: process(Clk1x)
begin
if rising_edge(Clk1x) then
c1DataOut <= c1DataInDly(1) & c1DataInDly(2);
if c1PhaseCount = '1' then
c1DataOut <= c1DataInDly(0) & c1DataInDly(1);
end if;
end if;
end process;
-- Similar to data output, when input data valid is asserted and both clocks
-- are rising edge aligned, the output data valid is asserted with a single
-- pipeline stage. If not, output data valid is asserted with two pipeline
-- stages.
DataValidOut: process(Clk1x, ac1Reset_n)
begin
if ac1Reset_n = '0' then
c1DataValidDly0 <= '0';
c1DataValidOut <= '0';
elsif rising_edge(Clk1x) then
c1DataValidDly0 <= c1DataValidIn;
c1DataValidOut <= c1DataValidDly0;
if c1PhaseCount = '1' then
c1DataValidOut <= c1DataValidIn;
end if;
end if;
end process;
end RTL;
+99
View File
@@ -0,0 +1,99 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_gearbox_6x8
--
-- Description:
--
-- Gearbox to expand the data width from 6 SPC to 8 SPC.
-- Input Clocks, all aligned to one another and coming from same MMCM
-- PLL reference clock = 61.44 or 62.5 MHz.
-- RfClk: 184.32 or 187.5 MHz (3x PLL reference clock)
-- Clk1x: 122.88 or 125 MHz (2x PLL reference clock)
-- Clk2x: 245.76 or 250 MHz (4x PLL reference clock)
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity dac_gearbox_6x8 is
port(
Clk1x : in std_logic;
Clk2x : in std_logic;
RfClk : in std_logic;
ac1Reset_n : in std_logic;
ac2Reset_n : in std_logic;
arReset_n : in std_logic;
-- 16 bit data packing: [Q5,I5,Q4,I4,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c2DataIn : in std_logic_vector(191 downto 0);
c2DataValidIn : in std_logic;
-- 16 bit data packing: [Q7,I7,Q6,I6,..,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
rDataOut : out std_logic_vector(255 downto 0) := (others => '0');
rReadyForOutput : in std_logic;
rDataValidOut : out std_logic := '0'
);
end dac_gearbox_6x8;
architecture struct of dac_gearbox_6x8 is
signal c1DataOut : std_logic_vector(383 downto 0);
signal c1DataValidOut : std_logic;
begin
-- Clk1x, Clk2x, and RfClk are source from the same PLL and have a known
-- phase relationship between power cycles. Since, they have known phase
-- relationship, clock crossing as be done without a dual clock FIFO or any
-- other handshaking mechanism. We cannot move data from Clk2x to RfClk
-- because of the clock relation between these two clocks will make it almost
-- impossible to close timing. So, we move data from Clk2x to Clk1x and then
-- to RfClk domain. Since, we need deterministic delay in the data path, we
-- cannot use a FIFO to do data crossing.
--
-- Clk1x = Sample clock/24
-- Clk2x = Sample clock/12
-- RfClk = Sample clock/16
--
-- Clk1x __/-----\_____/-----\_____/-----\_____/-----\_____/-----\___
-- | |
-- Clk2x __/--\__/--\__/--\__/--\__/--\ | |
-- | | | |
-- | | <- Setup relationship | | <- Setup relationship
-- | | | |
-- RfClk __/---\___/---\___/---\___/---\___/---\___/---\___/---\___/-
--
-- As you can see the setup relationship for passing data synchronously from
-- Clk2x to RfClk is very small (Sample clock period * 4). It is not possible
-- to close timing with this requirement. For passing data from Clk1x to
-- RfClk the setup relationship is (Sample clock period * 8) which is
-- relatively easy to close timing.
dac_gearbox_6x12_i: entity work.dac_gearbox_6x12 (RTL)
port map (
Clk1x => Clk1x,
Clk2x => Clk2x,
ac1Reset_n => ac1Reset_n,
ac2Reset_n => ac2Reset_n,
c2DataIn => c2DataIn,
c2DataValidIn => c2DataValidIn,
c1DataOut => c1DataOut,
c1DataValidOut => c1DataValidOut
);
dac_gearbox_12x8_i: entity work.dac_gearbox_12x8 (RTL)
port map (
Clk1x => Clk1x,
RfClk => RfClk,
ac1Reset_n => ac1Reset_n,
arReset_n => arReset_n,
c1DataIn => c1DataOut,
c1DataValidIn => c1DataValidOut,
rDataOut => rDataOut,
rReadyForOutput => rReadyForOutput,
rDataValidOut => rDataValidOut
);
end struct;
+81
View File
@@ -0,0 +1,81 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: ddc_400m_saturate
--
-- Description:
--
-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
-- comments below for full description.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library work;
use work.PkgRf.all;
entity ddc_400m_saturate is
port(
Clk : in std_logic;
-- This data is from the DDC with a sample width of 17 bits and 7 bits of
-- padding. Data format is, [Q3,I3, ... , Q0,I0] (I in LSBs)
cDataIn : in std_logic_vector(191 downto 0);
cDataValidIn : in std_logic;
-- 16 bits saturated data. Data format is [Q3,I3, ... , Q0,I0] (I in LSBs)
cDataOut : out std_logic_vector(127 downto 0);
cDataValidOut : out std_logic );
end ddc_400m_saturate;
architecture RTL of ddc_400m_saturate is
signal cDataOutSamples : Samples16_t(7 downto 0) := (others => (others => '0'));
signal cDataInSamples : Samples17_t(cDataOutSamples'range);
begin
-----------------------------------------------------------------------------
-- Saturation
--
-- The output of the Xilinx FIR Compiler has already been rounded on the LSB
-- side, but hasn't been saturated on the MSB side.
-- Coefficients = 18 bit, 1 integer bit (1.17)
-- Data In = 16 bits, 1 integer bit (1.15)
-- 1.17 * 1.15 = 2.32, and the Xilinx FIR core rounds to 2.15
-- Data Out = 17 bits, 2 integer bits (2.15), with 17 LSBs already rounded
-- off inside the FIR core.
-- We need to manually saturate the 2.15 number back to a 1.15 number
--
-- If 2 MSBs = 00, output <= input without MSB, e.g. positive number < 1
-- If 2 MSBs = 01, output <= 0.111111111111111, e.g. positive number >= 1
-- If 2 MSBs = 10, output <= 1.000000000000000, e.g. negative number < -1
-- If 2 MSBs = 11, output <= input without MSB, e.g. negative number >= -1
-----------------------------------------------------------------------------
-- Logic to saturate input data to 16-bit signed value. Information on DDC
-- data packer is in PkgRf.vhd.
cDataInSamples <= to_Samples17(cDataIn);
GenSat: for i in cDataOutSamples'range generate
Saturation:
process(Clk)
begin
if rising_edge(Clk) then
cDataOutSamples(i) <= Saturate(cDataInSamples(i));
end if;
end process;
end generate GenSat;
DValidPipeline: process(Clk)
begin
if rising_edge(Clk) then
-- Pipeline data valid to match the data.
cDataValidOut <= cDataValidIn;
end if;
end process;
cDataOut <= to_stdlogicvector(cDataOutSamples);
end RTL;
+86
View File
@@ -0,0 +1,86 @@
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: duc_400m_saturate
--
-- Description:
--
-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
-- comments below for full description.
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library work;
use work.PkgRf.all;
entity duc_400m_saturate is
port(
Clk : in std_logic;
-- This data is from the DDC with a sample width of 18 bits and 6 bits of
-- padding. Data format is, [Q5,I5, ... , Q0,I0] (I in LSBs)
cDataIn : in std_logic_vector(287 downto 0);
cDataValidIn : in std_logic;
cReadyForInput : out std_logic;
-- 16 bits saturated data. Data format is [Q5,I5, ... , Q0,I0] (I in LSBs)
cDataOut : out std_logic_vector(191 downto 0);
cDataValidOut : out std_logic := '0');
end duc_400m_saturate;
architecture RTL of duc_400m_saturate is
signal cDataOutSamples : Samples16_t(11 downto 0) := (others => (others => '0'));
signal cDataInputSamples : Samples18_t(cDataOutSamples'range);
begin
-----------------------------------------------------------------------------
-- Saturation
--
-- The output of the Xilinx FIR Compiler has already been rounded on the LSB
-- side, but hasn't been saturated on the MSB side.
-- Coefficients = 18 bit, 1 integer bit (1.17)
-- Data In = 16 bits, 1 integer bit (1.15)
-- Xilinx FIR core rounds to output to 3.31. The filter coefficients has a
-- gain of 3 to compensate for the amplitude loss in interpolation, the
-- Xilinx FIR core rounds the output to 3.15.
-- Data Out = 18 bits, 3 integer bits (3.15), with 16 LSBs already rounded
-- off inside the FIR core.
-- We need to manually saturate the 3.15 number back to a 1.15 number
--
-- If 3 MSBs = 000, output <= input without MSB, e.g. positive number < 1
-- If 3 MSBs = 0x1/01x, output <= 0.111111111111111, e.g. positive number >= 1
-- If 3 MSBs = 1x0/10x, output <= 1.000000000000000, e.g. negative number < -1
-- If 3 MSBs = 111, output <= input without MSB, e.g. negative number >= -1
-----------------------------------------------------------------------------
-- Logic to saturate input data to 16-bit signed value. Information on DUC data packer is in
-- PkgRf.vhd.
cDataInputSamples <= to_Samples18(cDataIn);
GenSat: for i in cDataOutSamples'range generate
Saturation:
process(Clk)
begin
if rising_edge(Clk) then
cDataOutSamples(i) <= Saturate(cDataInputSamples(i));
end if;
end process;
end generate GenSat;
DValidPipeline: process(Clk)
begin
if rising_edge(Clk) then
-- Pipeline data valid to match the data.
cDataValidOut <= cDataValidIn;
end if;
end process;
cDataOut <= to_stdlogicvector(cDataOutSamples);
cReadyForInput <= '1';
end RTL;
+387
View File
@@ -0,0 +1,387 @@
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rf_core_400m
//
// Description:
//
// Top-level wrapper for the ADC/DAC processing logic. One of these wrappers
// exists for every supported Data Rate. An instance of this core should
// exist per dboard.
//
// Data/RF Specs:
// DBs: 1
// RX/DB: 2
// TX/DB: 2
// Data Rate: 122.88 or 125 MSps @ 4 SPC
//
// Input Clocks, all aligned to one another and coming from same MMCM
// rfdc_clk: 184.32 or 187.5 MHz (3x pll_ref_clk)
// rfdc_clk_2x: 368.64 or 375 MHz (6x pll_ref_clk)
// data_clk: 122.88 or 125 MHz (2x pll_ref_clk)
// data_clk_2x: 245.76 or 250 MHz (4x pll_ref_clk)
//
`default_nettype none
module rf_core_400m (
//---------------------------------------------------------------------------
// Clocking
//---------------------------------------------------------------------------
// Main Clock Inputs
input wire rfdc_clk,
input wire rfdc_clk_2x,
input wire data_clk,
input wire data_clk_2x,
// AXI4-Lite Config Clock
// This clock is used to synchronize status bits for the RFDC
// registers in the AXI-S clock domain.
input wire s_axi_config_clk,
//---------------------------------------------------------------------------
// RFDC Data Interfaces
//---------------------------------------------------------------------------
// All ports here are in the rfdc_clk domain.
// ADC
input wire [127:0] adc_data_in_i_tdata_0,
output wire adc_data_in_i_tready_0,
input wire adc_data_in_i_tvalid_0,
input wire [127:0] adc_data_in_q_tdata_0,
output wire adc_data_in_q_tready_0,
input wire adc_data_in_q_tvalid_0,
input wire [127:0] adc_data_in_i_tdata_1,
output wire adc_data_in_i_tready_1,
input wire adc_data_in_i_tvalid_1,
input wire [127:0] adc_data_in_q_tdata_1,
output wire adc_data_in_q_tready_1,
input wire adc_data_in_q_tvalid_1,
// DAC
output wire [255:0] dac_data_out_tdata_0,
input wire dac_data_out_tready_0,
output wire dac_data_out_tvalid_0,
output wire [255:0] dac_data_out_tdata_1,
input wire dac_data_out_tready_1,
output wire dac_data_out_tvalid_1,
//---------------------------------------------------------------------------
// User Data Interfaces
//---------------------------------------------------------------------------
// All ports here are in the data_clk domain.
// ADC
output wire [127:0] adc_data_out_tdata_0, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire adc_data_out_tvalid_0,
output wire [127:0] adc_data_out_tdata_1, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire adc_data_out_tvalid_1,
// DAC
input wire [127:0] dac_data_in_tdata_0, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire dac_data_in_tready_0,
input wire dac_data_in_tvalid_0,
input wire [127:0] dac_data_in_tdata_1, // Packed [Q3,I3, ... , Q0,I0] with Q in MSBs
output wire dac_data_in_tready_1,
input wire dac_data_in_tvalid_1,
//---------------------------------------------------------------------------
// Miscellaneous
//---------------------------------------------------------------------------
// Invert I/Q control signals from RFDC to DSP chain.
input wire [3:0] invert_adc_iq_rclk2,
input wire [3:0] invert_dac_iq_rclk2,
// Control/status vectors from/to RFDC.
// Notice these are all in the s_axi_config_clk domain.
output wire [15:0] dsp_info_sclk,
output wire [15:0] axi_status_sclk,
// Resets.
input wire adc_data_out_resetn_dclk,
input wire adc_enable_data_rclk,
input wire adc_rfdc_axi_resetn_rclk,
input wire dac_data_in_resetn_dclk,
input wire dac_data_in_resetn_dclk2x,
input wire dac_data_in_resetn_rclk,
input wire fir_resetn_rclk2x,
// Version (Constant)
output wire [95:0] version_info
);
`include "../../regmap/rfdc_regs_regmap_utils.vh"
`include "../../regmap/versioning_regs_regmap_utils.vh"
`include "../../regmap/versioning_utils.vh"
// Fixed for this implementation
localparam NUM_ADC_CHANNELS = 2;
localparam NUM_DAC_CHANNELS = 2;
// ADC data interface from RFDC.
wire [127:0] adc_data_in_i_tdata [0:7]; // 8 SPC (I)
wire [127:0] adc_data_in_q_tdata [0:7]; // 8 SPC (Q)
wire [ 7:0] adc_data_in_i_tready;
wire [ 7:0] adc_data_in_q_tready;
wire [ 7:0] adc_data_in_i_tvalid;
wire [ 7:0] adc_data_in_q_tvalid;
// DAC data interface to RFDC.
wire [255:0] dac_data_out_tdata [0:7]; // 8 SPC (I + Q)
wire [ 7:0] dac_data_out_tready;
wire [ 7:0] dac_data_out_tvalid;
// ADC data interface to user.
wire [127:0] adc_data_out_tdata [0:7]; // 4 SPC (I + Q)
wire [ 7:0] adc_data_out_tready;
wire [ 7:0] adc_data_out_tvalid;
// DAC data interface from user.
wire [127:0] dac_data_in_tdata_preswap [0:7]; // 4 SPC (I + Q)
wire [127:0] dac_data_in_tdata [0:7]; // 4 SPC (I + Q)
wire [ 7:0] dac_data_in_tready;
wire [ 7:0] dac_data_in_tvalid;
wire [ 7:0] invert_dac_iq_dclk;
wire [15:0] axi_status;
//---------------------------------------------------------------------------
// Resets, Debug and Misc.
//---------------------------------------------------------------------------
// Group all these status bits together. They don't toggle frequently so data
// coherency is not an issue here.
// Using constants for DB0 since the bits are the 16 LSBs in a 32-bit vector.
// DB1 simply uses the 16 MSBs when wiring the status vector.
assign axi_status[USER_ADC_TREADY_MSB :USER_ADC_TREADY ] = adc_data_out_tready[1:0];
assign axi_status[USER_ADC_TVALID_MSB :USER_ADC_TVALID ] = adc_data_out_tvalid[1:0];
assign axi_status[RFDC_ADC_I_TVALID_MSB:RFDC_ADC_I_TVALID] = adc_data_in_i_tvalid[1:0];
assign axi_status[RFDC_ADC_Q_TVALID_MSB:RFDC_ADC_Q_TVALID] = adc_data_in_q_tvalid[1:0];
assign axi_status[RFDC_ADC_I_TREADY_MSB:RFDC_ADC_I_TREADY] = adc_data_in_i_tready[1:0];
assign axi_status[RFDC_ADC_Q_TREADY_MSB:RFDC_ADC_Q_TREADY] = adc_data_in_q_tready[1:0];
assign axi_status[RFDC_DAC_TVALID_MSB :RFDC_DAC_TVALID ] = dac_data_out_tvalid[1:0];
assign axi_status[RFDC_DAC_TREADY_MSB :RFDC_DAC_TREADY ] = dac_data_out_tready[1:0];
synchronizer #(
.WIDTH (16),
.STAGES (2),
.INITIAL_VAL (0),
.FALSE_PATH_TO_IN (1)
) synchronizer_axis_status (
.clk (s_axi_config_clk),
.rst (1'b0),
.in (axi_status),
.out (axi_status_sclk)
);
// Drive the DSP info vector with information on this specific DSP chain.
assign dsp_info_sclk[FABRIC_DSP_BW_MSB :FABRIC_DSP_BW] = FABRIC_DSP_BW_400M;
assign dsp_info_sclk[FABRIC_DSP_RX_CNT_MSB:FABRIC_DSP_RX_CNT] = NUM_ADC_CHANNELS;
assign dsp_info_sclk[FABRIC_DSP_TX_CNT_MSB:FABRIC_DSP_TX_CNT] = NUM_DAC_CHANNELS;
//---------------------------------------------------------------------------
// ADC Post-Processing
//---------------------------------------------------------------------------
// Data comes from the RFDC as 8 SPC, separate streams for each channel and
// I/Q.
assign adc_data_in_i_tdata[0] = adc_data_in_i_tdata_0;
assign adc_data_in_q_tdata[0] = adc_data_in_q_tdata_0;
assign adc_data_in_i_tdata[1] = adc_data_in_i_tdata_1;
assign adc_data_in_q_tdata[1] = adc_data_in_q_tdata_1;
assign adc_data_in_i_tready_0 = adc_data_in_i_tready[0];
assign adc_data_in_i_tvalid[0] = adc_data_in_i_tvalid_0;
assign adc_data_in_q_tready_0 = adc_data_in_q_tready[0];
assign adc_data_in_q_tvalid[0] = adc_data_in_q_tvalid_0;
assign adc_data_in_i_tready_1 = adc_data_in_i_tready[1];
assign adc_data_in_i_tvalid[1] = adc_data_in_i_tvalid_1;
assign adc_data_in_q_tready_1 = adc_data_in_q_tready[1];
assign adc_data_in_q_tvalid[1] = adc_data_in_q_tvalid_1;
// ADC Data from the RFDC arrives here as 8 SPC with separate I and Q
// streams. It leaves the adc_100m_bd as 4 SPC with I and Q packed into a
// single 128 bit word.
genvar adc_num;
generate
for (adc_num=0; adc_num < (NUM_ADC_CHANNELS); adc_num = adc_num + 1)
begin : adc_gen
adc_400m_bd adc_400m_bd_gen (
.adc_data_out_resetn_dclk (adc_data_out_resetn_dclk),
.data_clk (data_clk),
.enable_data_to_fir_rclk (adc_enable_data_rclk),
.fir_resetn_rclk2x (fir_resetn_rclk2x),
.rfdc_adc_axi_resetn_rclk (adc_rfdc_axi_resetn_rclk),
.rfdc_clk (rfdc_clk),
.rfdc_clk_2x (rfdc_clk_2x),
.swap_iq_2x (invert_adc_iq_rclk2 [adc_num]),
.adc_q_data_in_tvalid (adc_data_in_q_tvalid[adc_num]),
.adc_q_data_in_tready (adc_data_in_q_tready[adc_num]),
.adc_q_data_in_tdata (adc_data_in_q_tdata [adc_num]),
.adc_i_data_in_tvalid (adc_data_in_i_tvalid[adc_num]),
.adc_i_data_in_tready (adc_data_in_i_tready[adc_num]),
.adc_i_data_in_tdata (adc_data_in_i_tdata [adc_num]),
.adc_data_out_tvalid (adc_data_out_tvalid [adc_num]),
.adc_data_out_tdata (adc_data_out_tdata [adc_num])
);
end
endgenerate
// Data is released to the user as 4 SPC, separate streams for each channel.
assign adc_data_out_tdata_0 = adc_data_out_tdata[0];
assign adc_data_out_tdata_1 = adc_data_out_tdata[1];
// There is no tready going to the ADC (one has to be always ready for ADC
// data), but it is still a component of the axi_status vector as a generic
// AXI stream status. Report 1'b1 to the status vector consistent with being
// always ready
assign adc_data_out_tready[0] = 1'b1;
assign adc_data_out_tvalid_0 = adc_data_out_tvalid[0];
assign adc_data_out_tready[1] = 1'b1;
assign adc_data_out_tvalid_1 = adc_data_out_tvalid[1];
//---------------------------------------------------------------------------
// DAC Pre-Processing
//---------------------------------------------------------------------------
// Data comes from the user as 4 SPC, separate streams for each channel.
assign dac_data_in_tdata_preswap[0] = dac_data_in_tdata_0;
assign dac_data_in_tdata_preswap[1] = dac_data_in_tdata_1;
assign dac_data_in_tready_0 = dac_data_in_tready[0];
assign dac_data_in_tvalid[0] = dac_data_in_tvalid_0;
assign dac_data_in_tready_1 = dac_data_in_tready[1];
assign dac_data_in_tvalid[1] = dac_data_in_tvalid_1;
// Optionally swap IQ data positions in the vector. First cross the swap
// vector over to the data_clk domain.
synchronizer #(
.WIDTH (8),
.STAGES (2),
.INITIAL_VAL (0),
.FALSE_PATH_TO_IN (1)
) synchronizer_invert_dac_iq (
.clk (data_clk),
.rst (1'b0),
.in (invert_dac_iq_rclk2),
.out (invert_dac_iq_dclk)
);
genvar dac_num;
generate
for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
begin : dac_swap_gen
//IO and Q0 swap
assign dac_data_in_tdata[dac_num][15:00] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][31:16]) : (dac_data_in_tdata_preswap[dac_num][15:0]);
assign dac_data_in_tdata[dac_num][31:16] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][15:00]) : (dac_data_in_tdata_preswap[dac_num][31:16]);
//I1 and Q1 swap
assign dac_data_in_tdata[dac_num][47:32] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][63:48]) : (dac_data_in_tdata_preswap[dac_num][47:32]);
assign dac_data_in_tdata[dac_num][63:48] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][47:32]) : (dac_data_in_tdata_preswap[dac_num][63:48]);
//I2 and Q2 swap
assign dac_data_in_tdata[dac_num][79:64] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][95:80]) : (dac_data_in_tdata_preswap[dac_num][79:64]);
assign dac_data_in_tdata[dac_num][95:80] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][79:64]) : (dac_data_in_tdata_preswap[dac_num][95:80]);
//I3 and Q3 swap
assign dac_data_in_tdata[dac_num][111:96] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][127:112]) : (dac_data_in_tdata_preswap[dac_num][111:96]);
assign dac_data_in_tdata[dac_num][127:112] = invert_dac_iq_dclk[dac_num] ?
(dac_data_in_tdata_preswap[dac_num][111:96]) : (dac_data_in_tdata_preswap[dac_num][127:112]);
end
endgenerate
// These streams are then interpolated by dac_400m_bd, and form a single
// stream per channel, 8 SPC, packed: MSB [Sample7Q, Sample7I, ... ,
// Sample0Q, Sample0I] LSB.
generate
for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
begin : dac_gen
dac_400m_bd dac_400m_bd_gen (
.dac_data_in_resetn_dclk (dac_data_in_resetn_dclk),
.dac_data_in_resetn_dclk2x (dac_data_in_resetn_dclk2x),
.dac_data_in_resetn_rclk (dac_data_in_resetn_rclk),
.dac_data_in_tdata (dac_data_in_tdata [dac_num]),
.dac_data_in_tready (dac_data_in_tready [dac_num]),
.dac_data_in_tvalid (dac_data_in_tvalid [dac_num]),
.dac_data_out_tdata (dac_data_out_tdata [dac_num]),
.dac_data_out_tready (dac_data_out_tready[dac_num]),
.dac_data_out_tvalid (dac_data_out_tvalid[dac_num]),
.data_clk (data_clk),
.data_clk_2x (data_clk_2x),
.rfdc_clk (rfdc_clk)
);
end
endgenerate
// Data is released to the RFDC as 8 SPC, separate streams per channel (I/Q
// together).
assign dac_data_out_tdata_0 = dac_data_out_tdata[0];
assign dac_data_out_tdata_1 = dac_data_out_tdata[1];
assign dac_data_out_tready[0] = dac_data_out_tready_0;
assign dac_data_out_tvalid_0 = dac_data_out_tvalid[0];
assign dac_data_out_tready[1] = dac_data_out_tready_1;
assign dac_data_out_tvalid_1 = dac_data_out_tvalid[1];
//---------------------------------------------------------------------------
// Version
//---------------------------------------------------------------------------
// Version metadata, constants come from auto-generated
// versioning_regs_regmap_utils.vh
assign version_info = build_component_versions(
RF_CORE_400M_VERSION_LAST_MODIFIED_TIME,
build_version(
RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MAJOR,
RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MINOR,
RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_BUILD
),
build_version(
RF_CORE_400M_CURRENT_VERSION_MAJOR,
RF_CORE_400M_CURRENT_VERSION_MINOR,
RF_CORE_400M_CURRENT_VERSION_BUILD
)
);
endmodule
`default_nettype wire
//XmlParse xml_on
//<regmap name="VERSIONING_REGS_REGMAP">
// <group name="VERSIONING_CONSTANTS">
// <enumeratedtype name="RF_CORE_400M_VERSION" showhex="true">
// <info>
// 400 MHz RF core.{BR/}
// For guidance on when to update these revision numbers,
// please refer to the register map documentation accordingly:
// <li> Current version: @.VERSIONING_REGS_REGMAP..CURRENT_VERSION
// <li> Oldest compatible version: @.VERSIONING_REGS_REGMAP..OLDEST_COMPATIBLE_VERSION
// <li> Version last modified: @.VERSIONING_REGS_REGMAP..VERSION_LAST_MODIFIED
// </info>
// <value name="RF_CORE_400M_CURRENT_VERSION_MAJOR" integer="1"/>
// <value name="RF_CORE_400M_CURRENT_VERSION_MINOR" integer="0"/>
// <value name="RF_CORE_400M_CURRENT_VERSION_BUILD" integer="0"/>
// <value name="RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MAJOR" integer="1"/>
// <value name="RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_MINOR" integer="0"/>
// <value name="RF_CORE_400M_OLDEST_COMPATIBLE_VERSION_BUILD" integer="0"/>
// <value name="RF_CORE_400M_VERSION_LAST_MODIFIED_TIME" integer="0x20102617"/>
// </enumeratedtype>
// </group>
//</regmap>
//XmlParse xml_off