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