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
234 lines
10 KiB
VHDL
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;
|