aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd
diff options
context:
space:
mode:
authorWade Fife <wade.fife@ettus.com>2021-06-08 19:40:46 -0500
committerAaron Rossetto <aaron.rossetto@ni.com>2021-06-10 11:56:58 -0500
commit6d3765605262016a80f71e36357f749ea35cbe5a (patch)
tree7d62d6622befd4132ac1ee085effa1426f7f53e5 /fpga/usrp3/top/x400/rf/400m/dac_gearbox_6x12.vhd
parentf706b89e6974e28ce76aadeeb06169becc86acba (diff)
downloaduhd-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.vhd124
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;