aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/rf/100m
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/100m
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/100m')
-rw-r--r--fpga/usrp3/top/x400/rf/100m/adc_3_1_clk_converter.vhd114
-rw-r--r--fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v120
-rw-r--r--fpga/usrp3/top/x400/rf/100m/dac_1_3_clk_converter.vhd143
-rw-r--r--fpga/usrp3/top/x400/rf/100m/dac_2_1_clk_converter.vhd118
-rw-r--r--fpga/usrp3/top/x400/rf/100m/ddc_saturate.vhd83
-rw-r--r--fpga/usrp3/top/x400/rf/100m/duc_saturate.vhd87
-rw-r--r--fpga/usrp3/top/x400/rf/100m/rf_core_100m.v362
7 files changed, 1027 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/rf/100m/adc_3_1_clk_converter.vhd b/fpga/usrp3/top/x400/rf/100m/adc_3_1_clk_converter.vhd
new file mode 100644
index 000000000..a941fcb8f
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/adc_3_1_clk_converter.vhd
@@ -0,0 +1,114 @@
+--
+-- Copyright 2021 Ettus Research, a National Instruments Brand
+--
+-- SPDX-License-Identifier: LGPL-3.0-or-later
+--
+-- Module: adc_3_1_clk_converter
+--
+-- Description:
+--
+-- This module transfers data from s_axis_aclk to m_axis_aclk. s_axis_aclk
+-- must be three times the frequency of m_axis_aclk, and the two clocks must
+-- be related (this module requires timing closure across the clock domain
+-- boundary).
+--
+
+library IEEE;
+ use IEEE.std_logic_1164.all;
+
+entity adc_3_1_clk_converter is
+ port(
+ s_axis_clk : in std_logic;
+ s_axis_resetn : in std_logic;
+ s_axis_tdata : in std_logic_vector(47 downto 0);
+ s_axis_tvalid : in std_logic;
+
+ m_axis_clk : in std_logic;
+ m_axis_resetn : in std_logic;
+ m_axis_tvalid : out std_logic;
+ m_axis_tdata : out std_logic_vector(47 downto 0)
+ );
+end entity;
+
+architecture RTL of adc_3_1_clk_converter is
+
+ -- To keep the implementation simple, this module does not implement a
+ -- correct AXIS handshake - it ignores m_axis_tready. adc_100m_bd already had
+ -- an assumption that the AXIS handshake is unneeded: ddc_saturate does not
+ -- accept _tready from the following component.
+ subtype Word_t is std_logic_vector(s_axis_tdata'range);
+ signal s_axis_tvalid_pipe : std_logic_vector(2 downto 0);
+ signal s_axis_tdata_reg : Word_t;
+
+ -- These _CDC signals will be sampled in the destination clock domain, but
+ -- will not produce any metastability because the input clocks must be
+ -- synchronous.
+ --
+ -- These signals must be driven by registers not to prevent glitches (as in
+ -- an asynchronous CDC), but to improve timing closure.
+ signal s_axis_tvalid_CDC : std_logic;
+ signal s_axis_tdata_CDC : Word_t;
+
+ -- m_axis_clk and s_axis_clk are nominally aligned by their rising edges.
+ -- Because m_axis_clk is more heavily loaded than s_axis_clk, m_axis_clk has
+ -- a larger distribution delay, which causes a large hold violation using
+ -- post-place timing estimates. The Ultrafast method (UG 949) recommends
+ -- addressing such hold violations when WHS < -0.5 ns. By resampling on the
+ -- falling edge of the destination clock, we get nominally half a period of
+ -- setup and half a period of hold. The destination clock delay reduces the
+ -- hold margin, and increases the setup margin.
+ signal m_axis_tvalid_fall : std_logic;
+ signal m_axis_tdata_fall : Word_t;
+
+begin
+
+ -- In the source clock domain, we capture incoming valid data and keep a
+ -- history of _tvalid over the last three clock cycles. If s_axis_tvalid has
+ -- been asserted once in the last three clock cycles, assert
+ -- s_axis_tvalid_CDC to be sampled in the output clock domain. The length of
+ -- s_axis_tvalid_pipe must match the ratio of the clock frequencies (3:1).
+ InputSampling:
+ process (s_axis_clk) is
+ begin
+ if rising_edge(s_axis_clk) then
+ if s_axis_tvalid='1' then
+ s_axis_tdata_reg <= s_axis_tdata;
+ end if;
+ s_axis_tdata_CDC <= s_axis_tdata_reg;
+ if s_axis_resetn='0' then
+ s_axis_tvalid_pipe <= (others => '0');
+ s_axis_tvalid_CDC <= '0';
+ else
+ s_axis_tvalid_pipe <= s_axis_tvalid_pipe(1 downto 0) & s_axis_tvalid;
+ if (s_axis_tvalid_pipe /= "000") then
+ s_axis_tvalid_CDC <= '1';
+ else
+ s_axis_tvalid_CDC <= '0';
+ end if;
+ end if;
+ end if;
+ end process InputSampling;
+
+ FallingEdgeSampling:
+ process (m_axis_clk) is
+ begin
+ if falling_edge(m_axis_clk) then
+ m_axis_tvalid_fall <= s_axis_tvalid_CDC;
+ m_axis_tdata_fall <= s_axis_tdata_CDC;
+ end if;
+ end process FallingEdgeSampling;
+
+ OutputRegisters:
+ process (m_axis_clk) is
+ begin
+ if rising_edge(m_axis_clk) then
+ m_axis_tdata <= m_axis_tdata_fall;
+ if m_axis_resetn='0' then
+ m_axis_tvalid <= '0';
+ else
+ m_axis_tvalid <= m_axis_tvalid_fall;
+ end if;
+ end if;
+ end process OutputRegisters;
+
+end RTL;
diff --git a/fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v b/fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v
new file mode 100644
index 000000000..604c04f50
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v
@@ -0,0 +1,120 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: adc_gearbox_2x1
+//
+// Description:
+//
+// Gearbox ADC data from 2 SPC to 1 SPC and corresponding 2x clock to 1x
+// clock. Also implement data swapping to format packets to fit the FIR
+// filter input requirements.
+//
+// This modules incurs one clk1x cycle of delay on the data and valid signals
+// from input on the 1x domain to output on the 2x domain.
+//
+
+`default_nettype none
+
+module adc_gearbox_2x1 (
+ input wire clk1x,
+ input wire reset_n_1x,
+ // Data is _presumed_ to be packed [Sample1, Sample0] (Sample0 in LSBs).
+ input wire [31:0] adc_q_in_1x,
+ input wire [31:0] adc_i_in_1x,
+ input wire valid_in_1x,
+ // De-assert enable_1x to clear the data synchronously from this module.
+ input wire enable_1x,
+
+ input wire clk2x,
+ // Data is packed [Q,I] (I in LSBs) when swap_iq_1x is '0'.
+ input wire swap_iq_2x,
+ output wire [31:0] adc_out_2x,
+ output wire valid_out_2x
+);
+
+ // Re-create the 1x clock in the 2x domain to produce a deterministic
+ // crossing.
+ reg toggle_1x, toggle_2x = 1'b0, toggle_2x_dly = 1'b0, valid_2x = 1'b0, valid_dly_2x = 1'b0;
+ reg [31:0] data_out_2x = 32'b0, adc_q_data_in_2x = 32'b0, adc_i_data_in_2x = 32'b0;
+
+ // Create a toggle in the 1x clock domain (clock divider /2).
+ always @(posedge clk1x or negedge reset_n_1x) begin
+ if ( ! reset_n_1x) begin
+ toggle_1x <= 1'b0;
+ end else begin
+ toggle_1x <= ! toggle_1x;
+ end
+ end
+
+ // clk1x and clk2x are nominally aligned on their rising edges, but clk2x is
+ // more heavily loaded, which results in a later arrival time. That late
+ // arrival causes large estimated hold violations after place. The Ultrafast
+ // method (UG 949) suggests fixing post-place hold violations that are worse
+ // than -0.5 ns.
+ // Resampling 1x signals on the falling edge of clk2x provides nominally half
+ // a period of setup and half a period of hold. The late arrival of clk2x
+ // shifts some of that margin away from hold slack and into setup slack.
+ reg toggle_2x_fall = 1'b0;
+ reg [31:0] adc_q_in_2x_fall = 32'b0;
+ reg [31:0] adc_i_in_2x_fall = 32'b0;
+ reg valid_in_2x_fall = 1'b0;
+ reg enable_2x_fall = 1'b0;
+
+ always @(negedge clk2x) begin
+ toggle_2x_fall <= toggle_1x;
+ adc_q_in_2x_fall <= adc_q_in_1x;
+ adc_i_in_2x_fall <= adc_i_in_1x;
+ valid_in_2x_fall <= valid_in_1x;
+ enable_2x_fall <= enable_1x;
+ end
+
+ // Transfer the toggle from the 1x to the 2x domain. Delay the toggle in the
+ // 2x domain by one cycle and compare it to the non-delayed version. When
+ // they differ, push data_in[15:0] onto the output. When the match, push
+ // [31:16] onto the output. The datasheet is unclear on the exact
+ // implementation.
+ //
+ // It is safe to not reset this domain because all of the input signals will
+ // be cleared by the 1x reset. Safe default values are assigned to all these
+ // registers.
+ always @(posedge clk2x) begin
+ toggle_2x <= toggle_2x_fall;
+ toggle_2x_dly <= toggle_2x;
+ adc_q_data_in_2x <= adc_q_in_2x_fall;
+ adc_i_data_in_2x <= adc_i_in_2x_fall;
+ // Place Q in the MSBs, I in the LSBs by default, unless swapped = 1.
+ if (valid_2x) begin
+ if (swap_iq_2x) begin
+ if (toggle_2x != toggle_2x_dly) begin
+ data_out_2x[31:16] <= adc_i_data_in_2x[15:0];
+ data_out_2x[15: 0] <= adc_q_data_in_2x[15:0];
+ end else begin
+ data_out_2x[31:16] <= adc_i_data_in_2x[31:16];
+ data_out_2x[15: 0] <= adc_q_data_in_2x[31:16];
+ end
+ end else begin
+ if (toggle_2x != toggle_2x_dly) begin
+ data_out_2x[31:16] <= adc_q_data_in_2x[15:0];
+ data_out_2x[15: 0] <= adc_i_data_in_2x[15:0];
+ end else begin
+ data_out_2x[31:16] <= adc_q_data_in_2x[31:16];
+ data_out_2x[15: 0] <= adc_i_data_in_2x[31:16];
+ end
+ end
+ end else begin
+ data_out_2x <= 32'b0;
+ end
+ // Valid is simply a transferred version of the 1x clock's valid. Delay it
+ // one more cycle to align outputs.
+ valid_2x <= valid_in_2x_fall && enable_2x_fall;
+ valid_dly_2x <= valid_2x;
+ end
+
+ assign adc_out_2x = data_out_2x;
+ assign valid_out_2x = valid_dly_2x;
+
+endmodule
+
+`default_nettype wire
diff --git a/fpga/usrp3/top/x400/rf/100m/dac_1_3_clk_converter.vhd b/fpga/usrp3/top/x400/rf/100m/dac_1_3_clk_converter.vhd
new file mode 100644
index 000000000..b5df20a08
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/dac_1_3_clk_converter.vhd
@@ -0,0 +1,143 @@
+--
+-- Copyright 2021 Ettus Research, a National Instruments Brand
+--
+-- SPDX-License-Identifier: LGPL-3.0-or-later
+--
+-- Module: dac_1_3_clk_converter
+--
+-- Description:
+--
+-- This module transfers data from s_axis_aclk to m_axis_aclk. m_axis_aclk
+-- must be three times the frequency of s_axis_aclk, and the two clocks must
+-- be related (this module requires timing closure across the clock domain
+-- boundary).
+--
+
+library IEEE;
+ use IEEE.std_logic_1164.all;
+
+entity dac_1_3_clk_converter is
+ port(
+ s_axis_aclk : in std_logic;
+ s_axis_aresetn : in std_logic;
+ s_axis_tvalid : in std_logic;
+ s_axis_tdata : in std_logic_vector(31 downto 0);
+ s_axis_tready : out std_logic := '1';
+
+ m_axis_aclk : in std_logic;
+ m_axis_aresetn : in std_logic;
+ m_axis_tready : in std_logic;
+ m_axis_tdata : out std_logic_vector(31 downto 0);
+ m_axis_tvalid : out std_logic
+ );
+end entity dac_1_3_clk_converter;
+
+architecture RTL of dac_1_3_clk_converter is
+
+ -- I was unable to think of a simple implementation that implements a correct
+ -- AXIS handshake on both ports. All my ideas became equivalent to a two
+ -- clock FIFO (although the clocks are synchronous, so the write-to-read
+ -- latency would have been certain).
+ --
+ -- We don't expect the DAC to ever hold off incoming data, and dac_100m_bd
+ -- already has the AXIS handshake disconnected: the FIR is configured to
+ -- disallow back pressure - it has no m_axis_data_tready pin.
+ --
+ -- I'm going with the simple, but not strictly correct, implementation.
+ -- s_axis_tready will be constantly true, even when it shouldn't be. The
+ -- bottom line is this component is likely useless for any application but
+ -- dac_100m_bd.
+
+ type output_fsm is (
+ idle,
+ got_data,
+ -- The recovery state of delay ensures that we don't re-use an old input
+ -- valid signal (remember the output clock is 3x the frequency of the input
+ -- clock)
+ recovery
+ );
+
+ subtype word is std_logic_vector(s_axis_tdata'range);
+ signal output_state_mclk : output_fsm;
+ signal axis_tdata_sclk : word;
+ signal axis_tvalid_sclk : std_logic;
+
+ signal axis_tdata_mclk : word;
+ signal axis_tvalid_mclk : std_logic;
+
+begin
+
+ s_axis_tready <= '1';
+
+ input_valid_register:
+ process(s_axis_aclk, s_axis_aresetn) is
+ begin
+ if s_axis_aresetn='0' then
+ axis_tvalid_sclk <= '0';
+ elsif rising_edge(s_axis_aclk) then
+ axis_tvalid_sclk <= s_axis_tvalid;
+ end if;
+ end process;
+
+ input_data_register:
+ process (s_axis_aclk) is
+ begin
+ if rising_edge(s_axis_aclk) then
+ axis_tdata_sclk <= s_axis_tdata;
+ end if;
+ end process input_data_register;
+
+ -- These CDC registers will not become metastable because the two clock
+ -- domains are related.
+ cdc_input_valid_register:
+ process (m_axis_aclk, m_axis_aresetn) is
+ begin
+ if m_axis_aresetn='0' then
+ axis_tvalid_mclk <= '0';
+ elsif rising_edge(m_axis_aclk) then
+ axis_tvalid_mclk <= axis_tvalid_sclk;
+ end if;
+ end process cdc_input_valid_register;
+
+ cdc_input_data_register:
+ process (m_axis_aclk) is
+ begin
+ if rising_edge(m_axis_aclk) then
+ axis_tdata_mclk <= axis_tdata_sclk;
+ end if;
+ end process cdc_input_data_register;
+
+ output_data_register:
+ process (m_axis_aclk) is
+ begin
+ if rising_edge(m_axis_aclk) then
+ if output_state_mclk=idle then
+ m_axis_tdata <= axis_tdata_mclk;
+ end if;
+ end if;
+ end process output_data_register;
+
+ fsm: process(m_axis_aresetn, m_axis_aclk) is
+ begin
+ if m_axis_aresetn='0' then
+ output_state_mclk <= idle;
+ m_axis_tvalid <= '0';
+ elsif rising_edge(m_axis_aclk) then
+ m_axis_tvalid <= '0';
+ case output_state_mclk is
+ when idle =>
+ if axis_tvalid_mclk='1' then
+ output_state_mclk <= got_data;
+ end if;
+
+ when got_data =>
+ m_axis_tvalid <= '1';
+ output_state_mclk <= recovery;
+
+ when recovery =>
+ output_state_mclk <= idle;
+ end case;
+ end if;
+ end process fsm;
+
+end RTL;
diff --git a/fpga/usrp3/top/x400/rf/100m/dac_2_1_clk_converter.vhd b/fpga/usrp3/top/x400/rf/100m/dac_2_1_clk_converter.vhd
new file mode 100644
index 000000000..fcef4b4e0
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/dac_2_1_clk_converter.vhd
@@ -0,0 +1,118 @@
+--
+-- Copyright 2021 Ettus Research, a National Instruments Brand
+--
+-- SPDX-License-Identifier: LGPL-3.0-or-later
+--
+-- Module: dac_2_1_clk_converter
+--
+-- Description:
+--
+-- This module transfers data from s_axis_aclk to m_axis_aclk. s_axis_aclk
+-- must be two times the frequency of m_axis_aclk, and the two clocks must be
+-- related (this module requires timing closure across the clock domain
+-- boundary).
+--
+
+library IEEE;
+ use IEEE.std_logic_1164.all;
+
+entity dac_2_1_clk_converter is
+ port (
+ s_axis_aclk : in std_logic;
+ s_axis_aresetn : in std_logic;
+ s_axis_tvalid : in std_logic;
+ s_axis_tdata : in std_logic_vector(63 downto 0);
+
+ m_axis_aclk : in std_logic;
+ m_axis_aresetn : in std_logic;
+ m_axis_tready : in std_logic;
+ m_axis_tvalid : out std_logic;
+ m_axis_tdata : out std_logic_vector(63 downto 0)
+ );
+end entity dac_2_1_clk_converter;
+
+architecture RTL of dac_2_1_clk_converter is
+
+ -- To keep the implementation simple, this module does not implement a
+ -- correct AXIS handshake - it ignores m_axis_tready. dac_100m_bd already had
+ -- an assumption that the AXIS handshake is unneeded: duc_saturate does not
+ -- accept _tready from the following component. Also, registered_dac_data has
+ -- never accepted _tready from dac_2_1_clk_converter, so dac_100m_bd has
+ -- never supported complete AXIS dataflow.
+
+ subtype Word_t is std_logic_vector(s_axis_tdata'range);
+ signal s_axis_tvalid_pipe : std_logic_vector(1 downto 0);
+ signal s_axis_tdata_reg : Word_t;
+
+ -- These _CDC signals will be sampled in the destination clock domain, but
+ -- will not produce any metastability because the input clocks must be
+ -- synchronous.
+ --
+ -- These signals must be driven by registers not to prevent glitches (as in
+ -- an asynchronous CDC), but to improve timing closure.
+ signal s_axis_tvalid_CDC : std_logic;
+ signal s_axis_tdata_CDC : Word_t;
+
+ -- m_axis_aclk and s_axis_aclk are nominally aligned by their rising edges.
+ -- Because m_axis_aclk is more heavily loaded than s_axis_aclk, m_axis_aclk
+ -- has a larger distribution delay, which causes a large hold violation using
+ -- post-place timing estimates. The Ultrafast method (UG 949) recommends
+ -- addressing such hold violations when WHS < -0.5 ns. By resampling on the
+ -- falling edge of the destination clock, we get nominally half a period of
+ -- setup and half a period of hold. The destination clock delay reduces the
+ -- hold margin, and increases the setup margin.
+ signal m_axis_tvalid_fall : std_logic;
+ signal m_axis_tdata_fall : Word_t;
+
+begin
+
+ -- In the source clock domain, we capture incoming valid data and keep a
+ -- history of _tvalid over the last three clock cycles. If s_axis_tvalid has
+ -- been asserted once in the last three clock cycles, assert
+ -- s_axis_tvalid_CDC to be sampled in the output clock domain. The length of
+ -- s_axis_tvalid_pipe must match the ratio of the clock frequencies (2:1).
+ InputSampling:
+ process (s_axis_aclk) is
+ begin
+ if rising_edge(s_axis_aclk) then
+ if s_axis_tvalid='1' then
+ s_axis_tdata_reg <= s_axis_tdata;
+ end if;
+ s_axis_tdata_CDC <= s_axis_tdata_reg;
+ if s_axis_aresetn='0' then
+ s_axis_tvalid_pipe <= (others => '0');
+ s_axis_tvalid_CDC <= '0';
+ else
+ s_axis_tvalid_pipe <= s_axis_tvalid_pipe(0) & s_axis_tvalid;
+ if (s_axis_tvalid_pipe /= "00") then
+ s_axis_tvalid_CDC <= '1';
+ else
+ s_axis_tvalid_CDC <= '0';
+ end if;
+ end if;
+ end if;
+ end process InputSampling;
+
+ FallingEdgeSampling:
+ process (m_axis_aclk) is
+ begin
+ if falling_edge(m_axis_aclk) then
+ m_axis_tvalid_fall <= s_axis_tvalid_CDC;
+ m_axis_tdata_fall <= s_axis_tdata_CDC;
+ end if;
+ end process FallingEdgeSampling;
+
+ OutputRegisters:
+ process (m_axis_aclk) is
+ begin
+ if rising_edge(m_axis_aclk) then
+ m_axis_tdata <= m_axis_tdata_fall;
+ if m_axis_aresetn='0' then
+ m_axis_tvalid <= '0';
+ else
+ m_axis_tvalid <= m_axis_tvalid_fall;
+ end if;
+ end if;
+ end process OutputRegisters;
+
+end RTL;
diff --git a/fpga/usrp3/top/x400/rf/100m/ddc_saturate.vhd b/fpga/usrp3/top/x400/rf/100m/ddc_saturate.vhd
new file mode 100644
index 000000000..9235f4fec
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/ddc_saturate.vhd
@@ -0,0 +1,83 @@
+--
+-- Copyright 2021 Ettus Research, a National Instruments Brand
+--
+-- SPDX-License-Identifier: LGPL-3.0-or-later
+--
+-- Module: ddc_saturate
+--
+-- Description:
+--
+-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
+-- comments below for full description.
+--
+
+library IEEE;
+ use IEEE.std_logic_1164.all;
+ use IEEE.numeric_std.all;
+
+entity ddc_saturate is
+ port(
+ Clk : in std_logic;
+ cDataIn : in std_logic_vector(47 downto 0); -- [Q,I] (I in LSBs)
+ cDataValidIn : in std_logic;
+ cDataOut : out std_logic_vector(31 downto 0); -- [Q,I] (I in LSBs)
+ cDataValidOut : out std_logic
+ );
+end ddc_saturate;
+
+architecture RTL of ddc_saturate is
+
+ signal cDataOutI : std_logic_vector(15 downto 0) := (others => '0');
+ signal cDataOutQ : std_logic_vector(15 downto 0) := (others => '0');
+
+begin
+
+
+ -----------------------------------------------------------------------------
+ -- Saturation
+ --
+ -- The output of the Xilinx FIR Compiler has already been rounded on the LSB
+ -- side, but hasn't been saturated on the MSB side.
+ -- Coefficients = 18 bit, 1 integer bit (1.17)
+ -- Data In = 16 bits, 1 integer bit (1.15)
+ -- 1.17 * 1.15 = 2.32, and the Xilinx FIR core rounds to 2.15
+ -- Data Out = 17 bits, 2 integer bits (2.15), with 17 LSBs already rounded
+ -- off inside the FIR core.
+ -- We need to manually saturate the 2.15 number back to a 1.15 number
+ --
+ -- If 2 MSBs = 00, output <= input without MSB, e.g. positive number < 1
+ -- If 2 MSBs = 01, output <= 0.111111111111111, e.g. positive number >= 1
+ -- If 2 MSBs = 10, output <= 1.000000000000000, e.g. negative number < -1
+ -- If 2 MSBs = 11, output <= input without MSB, e.g. negative number >= -1
+ -----------------------------------------------------------------------------
+ Saturation:
+ process(Clk)
+ begin
+ if rising_edge(Clk) then
+ -- Pipeline data valid to match the data
+ cDataValidOut <= cDataValidIn;
+
+ -- I, from cDataIn(16 downto 0)
+ if cDataIn(16 downto 15) = "01" then
+ cDataOutI <= "0111111111111111";
+ elsif cDataIn(16 downto 15) = "10" then
+ cDataOutI <= "1000000000000000";
+ else
+ cDataOutI <= cDataIn(15 downto 0);
+ end if;
+
+ -- Q, from cDataIn(40 downto 24)
+ if cDataIn(40 downto 39) = "01" then
+ cDataOutQ <= "0111111111111111";
+ elsif cDataIn(40 downto 39) = "10" then
+ cDataOutQ <= "1000000000000000";
+ else
+ cDataOutQ <= cDataIn(39 downto 24);
+ end if;
+
+ end if;
+ end process Saturation;
+
+ cDataOut <= cDataOutQ & cDataOutI;
+
+end RTL;
diff --git a/fpga/usrp3/top/x400/rf/100m/duc_saturate.vhd b/fpga/usrp3/top/x400/rf/100m/duc_saturate.vhd
new file mode 100644
index 000000000..5cb1bc1fc
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/duc_saturate.vhd
@@ -0,0 +1,87 @@
+--
+-- Copyright 2021 Ettus Research, a National Instruments Brand
+--
+-- SPDX-License-Identifier: LGPL-3.0-or-later
+--
+-- Module: duc_saturate
+--
+-- Description:
+--
+-- Saturation logic for reducing 2x24 bit words to 2x16 bit words. See
+-- comments below for full description.
+--
+
+library IEEE;
+ use IEEE.std_logic_1164.all;
+ use IEEE.numeric_std.all;
+
+entity duc_saturate is
+ port(
+ Clk : in std_logic;
+ cDataIn : in std_logic_vector(47 downto 0);
+ cDataValidIn : in std_logic;
+ cReadyForInput : out std_logic;
+ cDataOut : out std_logic_vector(31 downto 0);
+ cDataValidOut : out std_logic := '0'
+ );
+end duc_saturate;
+
+architecture RTL of duc_saturate is
+
+ signal cDataOutI : std_logic_vector(15 downto 0) := (others => '0');
+ signal cDataOutQ : std_logic_vector(15 downto 0) := (others => '0');
+
+begin
+
+ -----------------------------------------------------------------------------
+ -- Saturation
+ --
+ -- The output of the Xilinx FIR Compiler has already been rounded on the LSB
+ -- side, but hasn't been saturated on the MSB side.
+ -- Coefficients = 18 bit, 1 integer bit (1.17)
+ -- Data In = 16 bits, 1 integer bit (1.15)
+ -- Xilinx FIR core rounds to output to 3.31. The filter coefficients has a
+ -- gain of 3 to compensate for the amplitude loss in interpolation, the
+ -- Xilinx FIR core rounds the output to 3.15.
+ -- Data Out = 18 bits, 3 integer bits (3.15), with 16 LSBs already rounded
+ -- off inside the FIR core.
+ -- We need to manually saturate the 3.15 number back to a 1.15 number
+ --
+ -- If 3 MSBs = 000, output <= input without MSB, e.g. positive number < 1
+ -- If 3 MSBs = 0x1/01x, output <= 0.111111111111111, e.g. positive number >= 1
+ -- If 3 MSBs = 1x0/10x, output <= 1.000000000000000, e.g. negative number < -1
+ -- If 3 MSBs = 111, output <= input without MSB, e.g. negative number >= -1
+ -----------------------------------------------------------------------------
+Saturation:
+ process(Clk)
+ begin
+ if rising_edge(Clk) then
+ -- Pipeline data valid to match the data
+ cDataValidOut <= cDataValidIn;
+
+ -- I, from cDataIn(17 downto 0)
+ if cDataIn(17) = '0' and cDataIn(16 downto 15) /= "00" then
+ cDataOutI <= "0111111111111111";
+ elsif cDataIn(17) = '1' and cDataIn(16) /= cDataIn(15) then
+ cDataOutI <= "1000000000000000";
+ else
+ cDataOutI <= cDataIn(15 downto 0);
+ end if;
+
+ -- Q, from cDataIn(41 downto 24)
+ if cDataIn(41) = '0' and cDataIn(40 downto 39) /= "00" then
+ cDataOutQ <= "0111111111111111";
+ elsif cDataIn(41) = '1' and
+ (not (cDataIn(40 downto 39) = "11")) then
+ cDataOutQ <= "1000000000000000";
+ else
+ cDataOutQ <= cDataIn(39 downto 24);
+ end if;
+
+ end if;
+ end process Saturation;
+
+ cDataOut <= cDataOutQ & cDataOutI;
+ cReadyForInput <= '1';
+
+end RTL;
diff --git a/fpga/usrp3/top/x400/rf/100m/rf_core_100m.v b/fpga/usrp3/top/x400/rf/100m/rf_core_100m.v
new file mode 100644
index 000000000..9ca837e47
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/rf_core_100m.v
@@ -0,0 +1,362 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rf_core_100m
+//
+// Description:
+//
+// Top-level wrapper for the ADC/DAC processing logic. One of these wrappers
+// exists for every supported Data Rate. An instance of this core should
+// exist per dboard.
+//
+// Data/RF Specs:
+// DBs: 1
+// RX/DB: 2
+// TX/DB: 2
+// Data Rate: 122.88 or 125 MSps @ 1 SPC
+//
+// Input Clocks, all aligned to one another and coming from same MMCM
+// rfdc_clk: 184.32 or 187.5 MHz (3x pll_ref_clk)
+// rfdc_clk_2x: 368.64 or 375 MHz (6x pll_ref_clk)
+// data_clk: 122.88 or 125 MHz (2x pll_ref_clk)
+//
+
+`default_nettype none
+
+module rf_core_100m (
+
+ //---------------------------------------------------------------------------
+ // Clocking
+ //---------------------------------------------------------------------------
+
+ // Main Clock Inputs
+ input wire rfdc_clk,
+ input wire rfdc_clk_2x,
+ input wire data_clk,
+ input wire data_clk_2x, // Unused, kept for rf_core_* interface consistency.
+
+ // AXI4-Lite Configuration Clock
+ // This clock is used to synchronize status bits for the RFDC registers in
+ // the AXI-S clock domain.
+ input wire s_axi_config_clk,
+
+ //---------------------------------------------------------------------------
+ // RFDC Data Interfaces
+ //---------------------------------------------------------------------------
+ // All ports here are in the rfdc_clk domain.
+
+ // ADC
+ input wire [31:0] adc_data_in_i_tdata_0,
+ output wire adc_data_in_i_tready_0,
+ input wire adc_data_in_i_tvalid_0,
+ input wire [31:0] adc_data_in_q_tdata_0,
+ output wire adc_data_in_q_tready_0,
+ input wire adc_data_in_q_tvalid_0,
+ input wire [31:0] adc_data_in_i_tdata_1,
+ output wire adc_data_in_i_tready_1,
+ input wire adc_data_in_i_tvalid_1,
+ input wire [31:0] adc_data_in_q_tdata_1,
+ output wire adc_data_in_q_tready_1,
+ input wire adc_data_in_q_tvalid_1,
+
+ // DAC
+ output wire [63:0] dac_data_out_tdata_0,
+ input wire dac_data_out_tready_0,
+ output wire dac_data_out_tvalid_0,
+ output wire [63:0] dac_data_out_tdata_1,
+ input wire dac_data_out_tready_1,
+ output wire dac_data_out_tvalid_1,
+
+ //---------------------------------------------------------------------------
+ // User Data Interfaces
+ //---------------------------------------------------------------------------
+ // All ports here are in the data_clk domain.
+
+ // ADC
+ output wire [31:0] adc_data_out_tdata_0, // Packed [Q,I] with Q in MSBs
+ output wire adc_data_out_tvalid_0,
+ output wire [31:0] adc_data_out_tdata_1, // Packed [Q,I] with Q in MSBs
+ output wire adc_data_out_tvalid_1,
+
+ // DAC
+ input wire [31:0] dac_data_in_tdata_0, // Packed [Q,I] with Q in MSBs
+ output wire dac_data_in_tready_0,
+ input wire dac_data_in_tvalid_0,
+ input wire [31:0] dac_data_in_tdata_1, // Packed [Q,I] with Q in MSBs
+ output wire dac_data_in_tready_1,
+ input wire dac_data_in_tvalid_1,
+
+ //---------------------------------------------------------------------------
+ // Miscellaneous
+ //---------------------------------------------------------------------------
+
+ // Invert I/Q control signals from RFDC to DSP chain.
+ input wire [3:0] invert_adc_iq_rclk2,
+ input wire [3:0] invert_dac_iq_rclk2,
+
+ // Control/status vectors from/to RFDC.
+ // Notice these are all in the s_axi_config_clk domain.
+ output wire [15:0] dsp_info_sclk,
+ output wire [15:0] axi_status_sclk,
+
+ // Resets.
+ input wire adc_data_out_resetn_dclk,
+ input wire adc_enable_data_rclk,
+ input wire adc_rfdc_axi_resetn_rclk,
+ input wire dac_data_in_resetn_dclk,
+ input wire dac_data_in_resetn_rclk,
+ input wire dac_data_in_resetn_rclk2x,
+ input wire fir_resetn_rclk2x,
+
+ // Version (Constant)
+ output wire [95:0] version_info
+);
+
+ `include "../../regmap/rfdc_regs_regmap_utils.vh"
+ `include "../../regmap/versioning_regs_regmap_utils.vh"
+ `include "../../regmap/versioning_utils.vh"
+
+ // Fixed for this implementation.
+ localparam NUM_ADC_CHANNELS = 2;
+ localparam NUM_DAC_CHANNELS = 2;
+
+ // ADC data interface from RFDC.
+ wire [31:0] adc_data_in_i_tdata [0:7]; // 2 SPC (I)
+ wire [31:0] adc_data_in_q_tdata [0:7]; // 2 SPC (Q)
+ wire [ 7:0] adc_data_in_i_tready;
+ wire [ 7:0] adc_data_in_q_tready;
+ wire [ 7:0] adc_data_in_i_tvalid;
+ wire [ 7:0] adc_data_in_q_tvalid;
+ // DAC data interface to RFDC.
+ wire [63:0] dac_data_out_tdata [0:7]; // 2 SPC (I + Q)
+ wire [ 7:0] dac_data_out_tready;
+ wire [ 7:0] dac_data_out_tvalid;
+
+ // ADC data interface to user.
+ wire [31:0] adc_data_out_tdata [0:7]; // 1 SPC (I + Q)
+ wire [ 7:0] adc_data_out_tready;
+ wire [ 7:0] adc_data_out_tvalid;
+ // DAC data interface from user.
+ wire [31:0] dac_data_in_tdata_preswap [0:7]; // 1 SPC (I + Q)
+ wire [31:0] dac_data_in_tdata [0:7]; // 1 SPC (I + Q)
+ wire [ 7:0] dac_data_in_tready;
+ wire [ 7:0] dac_data_in_tvalid;
+
+ wire [ 7:0] invert_dac_iq_dclk;
+ wire [15:0] axi_status;
+
+ //---------------------------------------------------------------------------
+ // Resets, Debug and Misc.
+ //---------------------------------------------------------------------------
+
+ // Group all these status bits together. They don't toggle frequently so data
+ // coherency is not an issue here.
+ // Using constants for DB0 since the bits are the 16 LSBs in a 32-bit vector.
+ // DB1 simply uses the 16 MSBs when wiring the status vector.
+ assign axi_status[USER_ADC_TREADY_MSB :USER_ADC_TREADY ] = adc_data_out_tready [1:0];
+ assign axi_status[USER_ADC_TVALID_MSB :USER_ADC_TVALID ] = adc_data_out_tvalid [1:0];
+ assign axi_status[RFDC_ADC_I_TVALID_MSB:RFDC_ADC_I_TVALID] = adc_data_in_i_tvalid[1:0];
+ assign axi_status[RFDC_ADC_Q_TVALID_MSB:RFDC_ADC_Q_TVALID] = adc_data_in_q_tvalid[1:0];
+ assign axi_status[RFDC_ADC_I_TREADY_MSB:RFDC_ADC_I_TREADY] = adc_data_in_i_tready[1:0];
+ assign axi_status[RFDC_ADC_Q_TREADY_MSB:RFDC_ADC_Q_TREADY] = adc_data_in_q_tready[1:0];
+ assign axi_status[RFDC_DAC_TVALID_MSB :RFDC_DAC_TVALID ] = dac_data_out_tvalid [1:0];
+ assign axi_status[RFDC_DAC_TREADY_MSB :RFDC_DAC_TREADY ] = dac_data_out_tready [1:0];
+
+ synchronizer #(
+ .WIDTH (16),
+ .STAGES (2),
+ .INITIAL_VAL (0),
+ .FALSE_PATH_TO_IN (1)
+ ) synchronizer_axis_status (
+ .clk (s_axi_config_clk),
+ .rst (1'b0),
+ .in (axi_status),
+ .out (axi_status_sclk)
+ );
+
+ // Drive the DSP info vector with information on this specific DSP chain.
+ assign dsp_info_sclk[FABRIC_DSP_BW_MSB :FABRIC_DSP_BW] = FABRIC_DSP_BW_100M;
+ assign dsp_info_sclk[FABRIC_DSP_RX_CNT_MSB:FABRIC_DSP_RX_CNT] = NUM_ADC_CHANNELS;
+ assign dsp_info_sclk[FABRIC_DSP_TX_CNT_MSB:FABRIC_DSP_TX_CNT] = NUM_DAC_CHANNELS;
+
+ //---------------------------------------------------------------------------
+ // ADC Post-Processing
+ //---------------------------------------------------------------------------
+
+ // Data comes from the RFDC as 2 SPC, separate streams for each channel and
+ // I/Q.
+ assign adc_data_in_i_tdata[0] = adc_data_in_i_tdata_0;
+ assign adc_data_in_q_tdata[0] = adc_data_in_q_tdata_0;
+ assign adc_data_in_i_tdata[1] = adc_data_in_i_tdata_1;
+ assign adc_data_in_q_tdata[1] = adc_data_in_q_tdata_1;
+
+ assign adc_data_in_i_tready_0 = adc_data_in_i_tready[0];
+ assign adc_data_in_i_tvalid[0] = adc_data_in_i_tvalid_0;
+ assign adc_data_in_q_tready_0 = adc_data_in_q_tready[0];
+ assign adc_data_in_q_tvalid[0] = adc_data_in_q_tvalid_0;
+ assign adc_data_in_i_tready_1 = adc_data_in_i_tready[1];
+ assign adc_data_in_i_tvalid[1] = adc_data_in_i_tvalid_1;
+ assign adc_data_in_q_tready_1 = adc_data_in_q_tready[1];
+ assign adc_data_in_q_tvalid[1] = adc_data_in_q_tvalid_1;
+
+ // ADC Data from the RFDC arrives here as 2 SPC with separate I and Q
+ // streams. It leaves the adc_100m_bd as 1 SPC with I and Q packed into a
+ // single 32 bit word.
+ genvar adc_num;
+ generate
+ for (adc_num=0; adc_num < (NUM_ADC_CHANNELS); adc_num = adc_num + 1)
+ begin : adc_gen
+ adc_100m_bd adc_100m_bd_gen (
+ .adc_data_out_resetn_dclk (adc_data_out_resetn_dclk),
+ .data_clk (data_clk),
+ .enable_data_to_fir_rclk (adc_enable_data_rclk),
+ .fir_resetn_rclk2x (fir_resetn_rclk2x),
+ .rfdc_adc_axi_resetn_rclk (adc_rfdc_axi_resetn_rclk),
+ .rfdc_clk (rfdc_clk),
+ .rfdc_clk_2x (rfdc_clk_2x),
+ .swap_iq_2x (invert_adc_iq_rclk2 [adc_num]),
+ .adc_data_out_tvalid (adc_data_out_tvalid [adc_num]),
+ .adc_data_out_tdata (adc_data_out_tdata [adc_num]),
+ .adc_i_data_in_tvalid (adc_data_in_i_tvalid[adc_num]),
+ .adc_i_data_in_tready (adc_data_in_i_tready[adc_num]),
+ .adc_i_data_in_tdata (adc_data_in_i_tdata [adc_num]),
+ .adc_q_data_in_tvalid (adc_data_in_q_tvalid[adc_num]),
+ .adc_q_data_in_tready (adc_data_in_q_tready[adc_num]),
+ .adc_q_data_in_tdata (adc_data_in_q_tdata [adc_num])
+ );
+ end
+ endgenerate
+
+ // Data is released to the user as 1 SPC, separate streams for each channel.
+ assign adc_data_out_tdata_0 = adc_data_out_tdata[0];
+ assign adc_data_out_tdata_1 = adc_data_out_tdata[1];
+
+ // There is no tready going to the ADC (one has to be always ready for ADC
+ // data), but it is still a component of the axi_status vector as a generic
+ // AXI stream status. Report 1'b1 to the status vector consistent with being
+ // always ready
+ assign adc_data_out_tready[0] = 1'b1;
+ assign adc_data_out_tvalid_0 = adc_data_out_tvalid[0];
+ assign adc_data_out_tready[1] = 1'b1;
+ assign adc_data_out_tvalid_1 = adc_data_out_tvalid[1];
+
+ //---------------------------------------------------------------------------
+ // DAC Pre-Processing
+ //---------------------------------------------------------------------------
+
+ // Data comes from the user as 1 SPC, separate streams for each channel.
+ assign dac_data_in_tdata_preswap[0] = dac_data_in_tdata_0;
+ assign dac_data_in_tdata_preswap[1] = dac_data_in_tdata_1;
+
+ assign dac_data_in_tready_0 = dac_data_in_tready[0];
+ assign dac_data_in_tvalid[0] = dac_data_in_tvalid_0;
+ assign dac_data_in_tready_1 = dac_data_in_tready[1];
+ assign dac_data_in_tvalid[1] = dac_data_in_tvalid_1;
+
+ // Optionally swap IQ data positions in the vector. First cross the swap
+ // vector over to the data_clk domain.
+ synchronizer #(
+ .WIDTH (8),
+ .STAGES (2),
+ .INITIAL_VAL (0),
+ .FALSE_PATH_TO_IN (1)
+ ) synchronizer_invert_dac_iq (
+ .clk (data_clk),
+ .rst (1'b0),
+ .in (invert_dac_iq_rclk2),
+ .out (invert_dac_iq_dclk)
+ );
+
+ genvar dac_num;
+ generate
+ for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
+ begin : dac_swap_gen
+ assign dac_data_in_tdata[dac_num][15:00] = invert_dac_iq_dclk[dac_num] ?
+ (dac_data_in_tdata_preswap[dac_num][31:16]) : (dac_data_in_tdata_preswap[dac_num][15:0]);
+ assign dac_data_in_tdata[dac_num][31:16] = invert_dac_iq_dclk[dac_num] ?
+ (dac_data_in_tdata_preswap[dac_num][15:00]) : (dac_data_in_tdata_preswap[dac_num][31:16]);
+ end
+ endgenerate
+
+ // These streams are then interpolated by dac_100m_bd, and form a single
+ // stream per channel, 2 SPC, packed: MSB [Sample1Q, Sample1I, Sample0Q,
+ // Sample0I] LSB.
+ generate
+ for (dac_num=0; dac_num < (NUM_DAC_CHANNELS); dac_num = dac_num + 1)
+ begin : dac_gen
+ dac_100m_bd dac_100m_bd_gen (
+ .dac_data_in_resetn_dclk (dac_data_in_resetn_dclk),
+ .dac_data_in_resetn_rclk (dac_data_in_resetn_rclk),
+ .dac_data_in_resetn_rclk2x (dac_data_in_resetn_rclk2x),
+ .data_clk (data_clk),
+ .rfdc_clk (rfdc_clk),
+ .rfdc_clk_2x (rfdc_clk_2x),
+ .dac_data_out_tdata (dac_data_out_tdata [dac_num]),
+ .dac_data_out_tvalid (dac_data_out_tvalid[dac_num]),
+ .dac_data_out_tready (dac_data_out_tready[dac_num]),
+ .dac_data_in_tdata (dac_data_in_tdata [dac_num]),
+ .dac_data_in_tvalid (dac_data_in_tvalid [dac_num]),
+ .dac_data_in_tready (dac_data_in_tready [dac_num])
+ );
+ end
+ endgenerate
+
+ // Data is released to the RFDC as 2 SPC, separate streams per channel (I/Q
+ // together).
+ assign dac_data_out_tdata_0 = dac_data_out_tdata[0];
+ assign dac_data_out_tdata_1 = dac_data_out_tdata[1];
+
+ assign dac_data_out_tready[0] = dac_data_out_tready_0;
+ assign dac_data_out_tvalid_0 = dac_data_out_tvalid[0];
+ assign dac_data_out_tready[1] = dac_data_out_tready_1;
+ assign dac_data_out_tvalid_1 = dac_data_out_tvalid[1];
+
+ //---------------------------------------------------------------------------
+ // Version
+ //---------------------------------------------------------------------------
+
+ // Version metadata, constants come from auto-generated
+ // versioning_regs_regmap_utils.vh
+ assign version_info = build_component_versions(
+ RF_CORE_100M_VERSION_LAST_MODIFIED_TIME,
+ build_version(
+ RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MAJOR,
+ RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MINOR,
+ RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_BUILD
+ ),
+ build_version(
+ RF_CORE_100M_CURRENT_VERSION_MAJOR,
+ RF_CORE_100M_CURRENT_VERSION_MINOR,
+ RF_CORE_100M_CURRENT_VERSION_BUILD
+ )
+ );
+
+endmodule
+
+`default_nettype wire
+
+//XmlParse xml_on
+//<regmap name="VERSIONING_REGS_REGMAP">
+// <group name="VERSIONING_CONSTANTS">
+// <enumeratedtype name="RF_CORE_100M_VERSION" showhex="true">
+// <info>
+// 100 MHz RF core.{BR/}
+// For guidance on when to update these revision numbers,
+// please refer to the register map documentation accordingly:
+// <li> Current version: @.VERSIONING_REGS_REGMAP..CURRENT_VERSION
+// <li> Oldest compatible version: @.VERSIONING_REGS_REGMAP..OLDEST_COMPATIBLE_VERSION
+// <li> Version last modified: @.VERSIONING_REGS_REGMAP..VERSION_LAST_MODIFIED
+// </info>
+// <value name="RF_CORE_100M_CURRENT_VERSION_MAJOR" integer="1"/>
+// <value name="RF_CORE_100M_CURRENT_VERSION_MINOR" integer="0"/>
+// <value name="RF_CORE_100M_CURRENT_VERSION_BUILD" integer="0"/>
+// <value name="RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MAJOR" integer="1"/>
+// <value name="RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_MINOR" integer="0"/>
+// <value name="RF_CORE_100M_OLDEST_COMPATIBLE_VERSION_BUILD" integer="0"/>
+// <value name="RF_CORE_100M_VERSION_LAST_MODIFIED_TIME" integer="0x20102617"/>
+// </enumeratedtype>
+// </group>
+//</regmap>
+//XmlParse xml_off