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