diff options
author | Wade Fife <wade.fife@ettus.com> | 2021-06-08 19:40:46 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-06-10 11:56:58 -0500 |
commit | 6d3765605262016a80f71e36357f749ea35cbe5a (patch) | |
tree | 7d62d6622befd4132ac1ee085effa1426f7f53e5 /fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd | |
parent | f706b89e6974e28ce76aadeeb06169becc86acba (diff) | |
download | uhd-6d3765605262016a80f71e36357f749ea35cbe5a.tar.gz uhd-6d3765605262016a80f71e36357f749ea35cbe5a.tar.bz2 uhd-6d3765605262016a80f71e36357f749ea35cbe5a.zip |
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>
Diffstat (limited to 'fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd')
-rw-r--r-- | fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd b/fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd new file mode 100644 index 000000000..878720c8d --- /dev/null +++ b/fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd @@ -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; |