Files
b210-k7-fpga/top/x400/rf/400m/dac_gearbox_12x8.vhd
T
61782b02d7 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
2021-06-10 11:56:58 -05:00

234 lines
10 KiB
VHDL

--
-- 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;