aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2019-07-03 20:15:35 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 12:16:25 -0800
commitc256b9df6502536c2e451e690f1ad5962c664d1a (patch)
treea83ad13e6f5978bbe14bb3ecf8294ba1e3d28db4
parent9a8435ed998fc5c65257f4c55768750b227ab19e (diff)
downloaduhd-c256b9df6502536c2e451e690f1ad5962c664d1a.tar.gz
uhd-c256b9df6502536c2e451e690f1ad5962c664d1a.tar.bz2
uhd-c256b9df6502536c2e451e690f1ad5962c664d1a.zip
x300/mpmd: Port all RFNoC devices to the new RFNoC framework
Co-Authored-By: Alex Williams <alex.williams@ni.com> Co-Authored-By: Sugandha Gupta <sugandha.gupta@ettus.com> Co-Authored-By: Brent Stapleton <brent.stapleton@ettus.com> Co-Authored-By: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
-rw-r--r--host/docs/configuration.dox2
-rw-r--r--host/include/uhd/erfnoc/blocks/e310_bsp.yml24
-rw-r--r--host/include/uhd/erfnoc/core/e320_bsp.yml27
-rw-r--r--host/include/uhd/erfnoc/core/n300_bsp.yml36
-rw-r--r--host/include/uhd/erfnoc/core/n310_bsp.yml48
-rw-r--r--host/include/uhd/erfnoc/core/n320_bsp.yml48
-rw-r--r--host/include/uhd/erfnoc/core/x300_bsp.yml49
-rw-r--r--host/include/uhd/erfnoc/core/x310_bsp.yml3
-rw-r--r--host/include/uhd/rfnoc/defaults.hpp3
-rw-r--r--host/lib/include/uhdlib/usrp/common/apply_corrections.hpp40
-rw-r--r--host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp101
-rw-r--r--host/lib/usrp/common/apply_corrections.cpp100
-rw-r--r--host/lib/usrp/dboard/e3xx/CMakeLists.txt9
-rw-r--r--host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.cpp212
-rw-r--r--host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.hpp (renamed from host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.hpp)55
-rw-r--r--host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp300
-rw-r--r--host/lib/usrp/dboard/e3xx/e320_radio_control_impl.cpp183
-rw-r--r--host/lib/usrp/dboard/e3xx/e320_radio_control_impl.hpp (renamed from host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.hpp)43
-rw-r--r--host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp272
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_bands.cpp10
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_constants.hpp14
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp621
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.hpp293
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp305
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp343
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp215
-rw-r--r--host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp427
-rw-r--r--host/lib/usrp/dboard/magnesium/CMakeLists.txt8
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_bands.cpp11
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_constants.hpp23
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp8
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp6
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp1151
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp (renamed from host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp)136
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_control_cpld.cpp (renamed from host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_cpld.cpp)34
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_control_gain.cpp (renamed from host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_gain.cpp)68
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp446
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp847
-rw-r--r--host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp713
-rw-r--r--host/lib/usrp/dboard/rhodium/CMakeLists.txt8
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_bands.cpp22
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_constants.hpp40
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp723
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control.hpp (renamed from host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp)213
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp252
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp611
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp713
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp298
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp677
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp843
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp726
-rw-r--r--host/lib/usrp/mpmd/CMakeLists.txt10
-rw-r--r--host/lib/usrp/mpmd/mpmd_find.cpp11
-rw-r--r--host/lib/usrp/mpmd/mpmd_image_loader.cpp2
-rw-r--r--host/lib/usrp/mpmd/mpmd_impl.cpp127
-rw-r--r--host/lib/usrp/mpmd/mpmd_impl.hpp100
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_ctrl_base.hpp56
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.cpp (renamed from host/lib/usrp/mpmd/mpmd_xport_ctrl_dpdk_udp.cpp)16
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.hpp (renamed from host/lib/usrp/mpmd/mpmd_xport_ctrl_dpdk_udp.hpp)10
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.cpp135
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.hpp64
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.cpp279
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.hpp61
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_mgr.cpp135
-rw-r--r--host/lib/usrp/mpmd/mpmd_link_if_mgr.hpp (renamed from host/lib/usrp/mpmd/mpmd_xport_mgr.hpp)80
-rw-r--r--host/lib/usrp/mpmd/mpmd_mb_controller.cpp142
-rw-r--r--host/lib/usrp/mpmd/mpmd_mb_controller.hpp73
-rw-r--r--host/lib/usrp/mpmd/mpmd_mb_iface.cpp301
-rw-r--r--host/lib/usrp/mpmd/mpmd_mb_iface.hpp68
-rw-r--r--host/lib/usrp/mpmd/mpmd_mboard_impl.cpp96
-rw-r--r--host/lib/usrp/mpmd/mpmd_prop_tree.cpp12
-rw-r--r--host/lib/usrp/mpmd/mpmd_xport.cpp62
-rw-r--r--host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp44
-rw-r--r--host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp194
-rw-r--r--host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp56
-rw-r--r--host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp265
-rw-r--r--host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp45
-rw-r--r--host/lib/usrp/mpmd/mpmd_xport_mgr.cpp173
-rw-r--r--host/lib/usrp/x300/CMakeLists.txt8
-rw-r--r--host/lib/usrp/x300/x300_conn_mgr.hpp11
-rw-r--r--host/lib/usrp/x300/x300_defaults.hpp2
-rw-r--r--host/lib/usrp/x300/x300_eth_mgr.cpp370
-rw-r--r--host/lib/usrp/x300/x300_eth_mgr.hpp34
-rw-r--r--host/lib/usrp/x300/x300_fw_common.h2
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp524
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp130
-rw-r--r--host/lib/usrp/x300/x300_io_impl.cpp59
-rw-r--r--host/lib/usrp/x300/x300_mb_controller.cpp731
-rw-r--r--host/lib/usrp/x300/x300_mb_controller.hpp153
-rw-r--r--host/lib/usrp/x300/x300_mb_iface.cpp226
-rw-r--r--host/lib/usrp/x300/x300_pcie_mgr.cpp233
-rw-r--r--host/lib/usrp/x300/x300_pcie_mgr.hpp31
-rw-r--r--host/lib/usrp/x300/x300_prop_tree.cpp117
-rw-r--r--host/lib/usrp/x300/x300_radio_control.cpp1906
-rw-r--r--host/lib/usrp/x300/x300_radio_mbc_iface.hpp64
-rw-r--r--host/lib/usrp/x300/x300_regs.hpp3
-rwxr-xr-xhost/tests/devtest/devtest_e3xx.py18
-rw-r--r--mpm/python/usrp_mpm/CMakeLists.txt2
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/neon.py4
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/rhodium.py1
-rw-r--r--mpm/python/usrp_mpm/ethdispatch.py102
-rw-r--r--mpm/python/usrp_mpm/ethtable.py187
-rw-r--r--mpm/python/usrp_mpm/periph_manager/base.py264
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e31x.py154
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e31x_periphs.py166
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e320.py188
-rw-r--r--mpm/python/usrp_mpm/periph_manager/e320_periphs.py164
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n3xx.py199
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py67
-rw-r--r--mpm/python/usrp_mpm/rpc_server.py2
-rw-r--r--mpm/python/usrp_mpm/xports/xportmgr_liberio.py69
-rw-r--r--mpm/python/usrp_mpm/xports/xportmgr_udp.py218
112 files changed, 12381 insertions, 9005 deletions
diff --git a/host/docs/configuration.dox b/host/docs/configuration.dox
index 4ebc2cf9e..8216a59d0 100644
--- a/host/docs/configuration.dox
+++ b/host/docs/configuration.dox
@@ -36,8 +36,6 @@ and possible more options.
ext_adc_self_test | Run an extended ADC self test (more than the usual) | X3x0 | ext_adc_self_test=1
recover_mb_eeprom | Disable version checks. Can damage hardware. Only recommended for recovering devices with corrupted EEPROMs. | X3x0 | recover_mb_eeprom=1
skip_dram | Ignore DRAM FIFO block. Connect TX streamers straight into DUC or radio. | X3x0, N3xx | skip_dram=1
- skip_ddc | Ignore DDC block. Connect Rx streamers straight into radio. | X3x0, N3xx, E3xx | skip_ddc=1
- skip_duc | Ignore DUC block. Connect Rx streamers or DRAM straight into radio. | X3x0, N3xx, E3xx | skip_duc=1
In addition, many of the streaming-related options can be set per-device at configuration time.
diff --git a/host/include/uhd/erfnoc/blocks/e310_bsp.yml b/host/include/uhd/erfnoc/blocks/e310_bsp.yml
new file mode 100644
index 000000000..8fabbb55a
--- /dev/null
+++ b/host/include/uhd/erfnoc/blocks/e310_bsp.yml
@@ -0,0 +1,24 @@
+type: e31x
+type_id: E310
+family: 7SERIES
+transports:
+- name: dma
+ type: dma
+ width: 64
+
+clocks:
+- name: radio
+
+io_ports:
+ ctrlport_radio:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1\2
+ time_keeper:
+ type: time_keeper
+ drive: broadcaster
+ x300_radio:
+ type: x300_radio
+ drive: master
diff --git a/host/include/uhd/erfnoc/core/e320_bsp.yml b/host/include/uhd/erfnoc/core/e320_bsp.yml
new file mode 100644
index 000000000..c8d5b7de0
--- /dev/null
+++ b/host/include/uhd/erfnoc/core/e320_bsp.yml
@@ -0,0 +1,27 @@
+type: e320
+type_id: E320
+family: 7SERIES
+transports:
+- name: eth
+ type: 10G
+ width: 64
+- name: dma
+ type: dma
+ width: 64
+
+clocks:
+- name: radio
+
+io_ports:
+ ctrl_port:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1\2
+ time_keeper:
+ type: time_keeper
+ drive: broadcaster
+ x300_radio:
+ type: x300_radio
+ drive: master
diff --git a/host/include/uhd/erfnoc/core/n300_bsp.yml b/host/include/uhd/erfnoc/core/n300_bsp.yml
new file mode 100644
index 000000000..f0c75df4e
--- /dev/null
+++ b/host/include/uhd/erfnoc/core/n300_bsp.yml
@@ -0,0 +1,36 @@
+type: n300
+type_id: 1300
+family: 7SERIES
+transports:
+- name: eth0
+ type: 1G
+ width: 64
+- name: eth1
+ type: 10G
+ width: 64
+- name: dma
+ type: dma
+ width: 64
+
+clocks:
+- name: radio
+
+io_ports:
+ ctrlport_radio0:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1radio0_\2
+ time_keeper:
+ type: time_keeper
+ drive: broadcaster
+ x300_radio0:
+ type: x300_radio
+ drive: master
+ rename:
+ pattern: (.*)
+ repl: \1_radio0
+ dram:
+ type: axi4_mm_4x64_4g
+ drive: master
diff --git a/host/include/uhd/erfnoc/core/n310_bsp.yml b/host/include/uhd/erfnoc/core/n310_bsp.yml
new file mode 100644
index 000000000..08690ed5a
--- /dev/null
+++ b/host/include/uhd/erfnoc/core/n310_bsp.yml
@@ -0,0 +1,48 @@
+type: n310
+type_id: 1300
+family: 7SERIES
+transports:
+- name: eth0
+ type: 1G
+ width: 64
+- name: eth1
+ type: 10G
+ width: 64
+- name: dma
+ type: dma
+ width: 64
+
+clocks:
+- name: radio
+
+io_ports:
+ ctrlport_radio0:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1radio0_\2
+ ctrlport_radio1:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1radio1_\2
+ time_keeper:
+ type: time_keeper
+ drive: broadcaster
+ x300_radio0:
+ type: x300_radio
+ drive: master
+ rename:
+ pattern: (.*)
+ repl: \1_radio0
+ x300_radio1:
+ type: x300_radio
+ drive: master
+ rename:
+ pattern: (.*)
+ repl: \1_radio1
+ dram:
+ type: axi4_mm_4x64_4g
+ drive: master
diff --git a/host/include/uhd/erfnoc/core/n320_bsp.yml b/host/include/uhd/erfnoc/core/n320_bsp.yml
new file mode 100644
index 000000000..5d31da947
--- /dev/null
+++ b/host/include/uhd/erfnoc/core/n320_bsp.yml
@@ -0,0 +1,48 @@
+type: n320
+type_id: 1320
+family: 7SERIES
+transports:
+- name: eth0
+ type: 1G
+ width: 64
+- name: eth1
+ type: 10G
+ width: 64
+- name: dma
+ type: dma
+ width: 64
+
+clocks:
+- name: radio
+
+io_ports:
+ ctrlport_radio0:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1radio0_\2
+ ctrlport_radio1:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1radio1_\2
+ time_keeper:
+ type: time_keeper
+ drive: broadcaster
+ radio_ch0:
+ type: radio_1x32
+ drive: master
+ rename:
+ pattern: (.*)
+ repl: \1_radio0
+ radio_ch1:
+ type: radio_1x32
+ drive: master
+ rename:
+ pattern: (.*)
+ repl: \1_radio1
+ dram:
+ type: axi4_mm_4x64_4g
+ drive: master
diff --git a/host/include/uhd/erfnoc/core/x300_bsp.yml b/host/include/uhd/erfnoc/core/x300_bsp.yml
new file mode 100644
index 000000000..20a78958f
--- /dev/null
+++ b/host/include/uhd/erfnoc/core/x300_bsp.yml
@@ -0,0 +1,49 @@
+type: x300
+type_id: A300
+family: 7SERIES
+transports:
+- name: eth0
+ type: 10G
+ width: 64
+- name: eth1
+ type: 1G
+ width: 64
+- name: pcie
+ type: PCIe
+ width: 64
+
+clocks:
+- name: radio
+- name: ce
+
+io_ports:
+ ctrlport_radio0:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1radio0_\2
+ ctrlport_radio1:
+ type: ctrl_port
+ drive: slave
+ rename:
+ pattern: (ctrlport_)(.*)
+ repl: m_\1radio1_\2
+ time_keeper:
+ type: time_keeper
+ drive: broadcaster
+ x300_radio0:
+ type: x300_radio
+ drive: master
+ rename:
+ pattern: (.*)
+ repl: \1_radio0
+ x300_radio1:
+ type: x300_radio
+ drive: master
+ rename:
+ pattern: (.*)
+ repl: \1_radio1
+ dram:
+ type: axi4_mm_2x64_4g
+ drive: master
diff --git a/host/include/uhd/erfnoc/core/x310_bsp.yml b/host/include/uhd/erfnoc/core/x310_bsp.yml
index e63d382d8..20a78958f 100644
--- a/host/include/uhd/erfnoc/core/x310_bsp.yml
+++ b/host/include/uhd/erfnoc/core/x310_bsp.yml
@@ -44,3 +44,6 @@ io_ports:
rename:
pattern: (.*)
repl: \1_radio1
+ dram:
+ type: axi4_mm_2x64_4g
+ drive: master
diff --git a/host/include/uhd/rfnoc/defaults.hpp b/host/include/uhd/rfnoc/defaults.hpp
index 9a3ce252f..0b95ddc2e 100644
--- a/host/include/uhd/rfnoc/defaults.hpp
+++ b/host/include/uhd/rfnoc/defaults.hpp
@@ -12,6 +12,9 @@
namespace uhd { namespace rfnoc {
+// FIXME come up with a better place for this
+static const size_t CHDR_MAX_LEN_HDR = 16;
+
static const std::string CLOCK_KEY_GRAPH("__graph__");
static const std::string PROP_KEY_DECIM("decim");
diff --git a/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
index 3b4669f9a..fca566493 100644
--- a/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
+++ b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
@@ -14,30 +14,26 @@
namespace uhd{ namespace usrp{
- void apply_tx_fe_corrections(
- property_tree::sptr sub_tree, //starts at mboards/x
- const fs_path db_path,
- const fs_path tx_fe_corr_path,
- const double tx_lo_freq //actual lo freq
- );
+void apply_tx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
+ const std::string& db_serial,
+ const fs_path tx_fe_corr_path,
+ const double tx_lo_freq // actual lo freq
+);
- void apply_tx_fe_corrections(
- property_tree::sptr sub_tree, //starts at mboards/x
- const std::string &slot, //name of dboard slot
- const double tx_lo_freq //actual lo freq
- );
- void apply_rx_fe_corrections(
- property_tree::sptr sub_tree, //starts at mboards/x
- const std::string &slot, //name of dboard slot
- const double rx_lo_freq //actual lo freq
- );
+void apply_tx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
+ const std::string& slot, // name of dboard slot
+ const double tx_lo_freq // actual lo freq
+);
+void apply_rx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
+ const std::string& slot, // name of dboard slot
+ const double rx_lo_freq // actual lo freq
+);
- void apply_rx_fe_corrections(
- property_tree::sptr sub_tree, //starts at mboards/x
- const fs_path db_path,
- const fs_path rx_fe_corr_path,
- const double rx_lo_freq //actual lo freq
- );
+void apply_rx_fe_corrections(property_tree::sptr sub_tree, // starts at mboards/x
+ const std::string& db_serial,
+ const fs_path rx_fe_corr_path,
+ const double rx_lo_freq // actual lo freq
+);
}} //namespace uhd::usrp
#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp b/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp
new file mode 100644
index 000000000..0c64ecbad
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/mpmd_mb_controller.hpp
@@ -0,0 +1,101 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_MPMD_MB_CONTROLLER_HPP
+#define INCLUDED_LIBUHD_MPMD_MB_CONTROLLER_HPP
+
+#include <uhd/rfnoc/mb_controller.hpp>
+#include <uhdlib/utils/rpc.hpp>
+#include <memory>
+
+namespace uhd { namespace rfnoc {
+
+/*! MPM-Specific version of the mb_controller
+ *
+ * Reminder: There is one of these per motherboard.
+ *
+ * This motherboard controller abstracts out a bunch of RPC calls.
+ */
+class mpmd_mb_controller : public mb_controller
+{
+public:
+ using sptr = std::shared_ptr<mpmd_mb_controller>;
+
+ mpmd_mb_controller(uhd::rpc_client::sptr rpcc, uhd::device_addr_t device_info);
+
+ //! Return reference to the RPC client
+ uhd::rpc_client::sptr get_rpc_client() { return _rpc; }
+
+ /**************************************************************************
+ * Timekeeper API
+ *************************************************************************/
+ //! MPM-specific version of the timekeeper controls
+ //
+ // MPM devices talk to MPM via RPC to control the timekeeper
+ class mpmd_timekeeper : public mb_controller::timekeeper
+ {
+ public:
+ using sptr = std::shared_ptr<mpmd_timekeeper>;
+
+ mpmd_timekeeper(const size_t tk_idx, uhd::rpc_client::sptr rpc_client)
+ : _tk_idx(tk_idx), _rpc(rpc_client)
+ {
+ // nop
+ }
+
+ uint64_t get_ticks_now();
+ uint64_t get_ticks_last_pps();
+ void set_ticks_now(const uint64_t ticks);
+ void set_ticks_next_pps(const uint64_t ticks);
+ void set_period(const uint64_t period_ns);
+
+ /*! Update the tick rate
+ * Note: This is separate from set_tick_rate because the latter is
+ * protected, and we need to implement mpmd-specific functionality here
+ */
+ void update_tick_rate(const double tick_rate);
+
+ private:
+ const size_t _tk_idx;
+ uhd::rpc_client::sptr _rpc;
+ };
+
+ /**************************************************************************
+ * Motherboard Control API (see mb_controller.hpp)
+ *************************************************************************/
+ std::string get_mboard_name() const;
+ void set_time_source(const std::string& source);
+ std::string get_time_source() const;
+ std::vector<std::string> get_time_sources() const;
+ void set_clock_source(const std::string& source);
+ std::string get_clock_source() const;
+ std::vector<std::string> get_clock_sources() const;
+ void set_sync_source(const std::string& clock_source, const std::string& time_source);
+ void set_sync_source(const uhd::device_addr_t& sync_source);
+ uhd::device_addr_t get_sync_source() const;
+ std::vector<uhd::device_addr_t> get_sync_sources();
+ void set_clock_source_out(const bool enb);
+ void set_time_source_out(const bool enb);
+ uhd::sensor_value_t get_sensor(const std::string& name);
+ std::vector<std::string> get_sensor_names();
+ uhd::usrp::mboard_eeprom_t get_eeprom();
+
+private:
+ /**************************************************************************
+ * Attributes
+ *************************************************************************/
+ //! Reference to RPC interface
+ mutable uhd::rpc_client::sptr _rpc;
+
+ uhd::device_addr_t _device_info;
+
+ //! List of MB sensor names
+ std::unordered_set<std::string> _sensor_names;
+};
+
+}} // namespace uhd::rfnoc
+
+#endif /* INCLUDED_LIBUHD_MPMD_MB_CONTROLLER_HPP */
diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp
index 5c606c338..b57ace71b 100644
--- a/host/lib/usrp/common/apply_corrections.cpp
+++ b/host/lib/usrp/common/apply_corrections.cpp
@@ -82,18 +82,15 @@ static std::complex<double> get_fe_correction(
);
}
-static void apply_fe_corrections(
- uhd::property_tree::sptr sub_tree,
- const uhd::fs_path &db_path,
- const uhd::fs_path &fe_path,
- const std::string &file_prefix,
- const double lo_freq
-){
- //extract eeprom serial
- const uhd::usrp::dboard_eeprom_t db_eeprom = sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();
-
+static void apply_fe_corrections(uhd::property_tree::sptr sub_tree,
+ const std::string& db_serial,
+ const uhd::fs_path& fe_path,
+ const std::string& file_prefix,
+ const double lo_freq)
+{
//make the calibration file path
- const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal" / (file_prefix + db_eeprom.serial + ".csv");
+ const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal"
+ / (file_prefix + db_serial + ".csv");
if (not fs::exists(cal_data_path)) return;
//parse csv file or get from cache
@@ -133,28 +130,26 @@ static void apply_fe_corrections(
/***********************************************************************
* Wrapper routines with nice try/catch + print
**********************************************************************/
-void uhd::usrp::apply_tx_fe_corrections( //overloading to work according to rfnoc tree struct
- property_tree::sptr sub_tree, //starts at mboards/x
- const uhd::fs_path db_path,
+void uhd::usrp::apply_tx_fe_corrections( // overloading to work according to rfnoc tree
+ // struct
+ property_tree::sptr sub_tree, // starts at mboards/x
+ const std::string& db_serial,
const uhd::fs_path tx_fe_corr_path,
- const double lo_freq //actual lo freq
-){
+ const double lo_freq // actual lo freq
+)
+{
boost::mutex::scoped_lock l(corrections_mutex);
try{
- apply_fe_corrections(
- sub_tree,
- db_path + "/tx_eeprom",
+ apply_fe_corrections(sub_tree,
+ db_serial,
tx_fe_corr_path + "/iq_balance/value",
"tx_iq_cal_v0.2_",
- lo_freq
- );
- apply_fe_corrections(
- sub_tree,
- db_path + "/tx_eeprom",
+ lo_freq);
+ apply_fe_corrections(sub_tree,
+ db_serial,
tx_fe_corr_path + "/dc_offset/value",
"tx_dc_cal_v0.2_",
- lo_freq
- );
+ lo_freq);
}
catch(const std::exception &e){
UHD_LOGGER_ERROR("CAL") << "Failure in apply_tx_fe_corrections: " << e.what();
@@ -167,42 +162,44 @@ void uhd::usrp::apply_tx_fe_corrections(
const double lo_freq //actual lo freq
){
boost::mutex::scoped_lock l(corrections_mutex);
+
+ // extract eeprom serial
+ const uhd::fs_path db_path = "dboards/" + slot + "/tx_eeprom";
+ const std::string db_serial =
+ sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get().serial;
+
try{
- apply_fe_corrections(
- sub_tree,
- "dboards/" + slot + "/tx_eeprom",
+ apply_fe_corrections(sub_tree,
+ db_serial,
"tx_frontends/" + slot + "/iq_balance/value",
"tx_iq_cal_v0.2_",
- lo_freq
- );
- apply_fe_corrections(
- sub_tree,
- "dboards/" + slot + "/tx_eeprom",
+ lo_freq);
+ apply_fe_corrections(sub_tree,
+ db_serial,
"tx_frontends/" + slot + "/dc_offset/value",
"tx_dc_cal_v0.2_",
- lo_freq
- );
+ lo_freq);
}
catch(const std::exception &e){
UHD_LOGGER_ERROR("CAL") << "Failure in apply_tx_fe_corrections: " << e.what();
}
}
-void uhd::usrp::apply_rx_fe_corrections( //overloading to work according to rfnoc tree struct
- property_tree::sptr sub_tree, //starts at mboards/x
- const uhd::fs_path db_path,
+void uhd::usrp::apply_rx_fe_corrections( // overloading to work according to rfnoc tree
+ // struct
+ property_tree::sptr sub_tree, // starts at mboards/x
+ const std::string& db_serial,
const uhd::fs_path rx_fe_corr_path,
- const double lo_freq //actual lo freq
-){
+ const double lo_freq // actual lo freq
+)
+{
boost::mutex::scoped_lock l(corrections_mutex);
try{
- apply_fe_corrections(
- sub_tree,
- db_path + "/rx_eeprom",
+ apply_fe_corrections(sub_tree,
+ db_serial,
rx_fe_corr_path + "/iq_balance/value",
"rx_iq_cal_v0.2_",
- lo_freq
- );
+ lo_freq);
}
catch(const std::exception &e){
UHD_LOGGER_ERROR("CAL") << "Failure in apply_tx_fe_corrections: " << e.what();
@@ -215,14 +212,15 @@ void uhd::usrp::apply_rx_fe_corrections(
const double lo_freq //actual lo freq
){
boost::mutex::scoped_lock l(corrections_mutex);
+ const uhd::fs_path db_path = "dboards/" + slot + "/rx_eeprom";
+ const std::string db_serial =
+ sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get().serial;
try{
- apply_fe_corrections(
- sub_tree,
- "dboards/" + slot + "/rx_eeprom",
+ apply_fe_corrections(sub_tree,
+ db_serial,
"rx_frontends/" + slot + "/iq_balance/value",
"rx_iq_cal_v0.2_",
- lo_freq
- );
+ lo_freq);
}
catch(const std::exception &e){
UHD_LOGGER_ERROR("CAL") << "Failure in apply_rx_fe_corrections: " << e.what();
diff --git a/host/lib/usrp/dboard/e3xx/CMakeLists.txt b/host/lib/usrp/dboard/e3xx/CMakeLists.txt
index 5d452fb53..6a14c0766 100644
--- a/host/lib/usrp/dboard/e3xx/CMakeLists.txt
+++ b/host/lib/usrp/dboard/e3xx/CMakeLists.txt
@@ -1,13 +1,14 @@
#
# Copyright 2018 Ettus Research, a National Instruments Company
+# Copyright 2019 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
IF(ENABLE_E300 OR ENABLE_E320)
LIST(APPEND E3XX_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_impl.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_init.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_control_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_control_init.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e3xx_ad9361_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e3xx_bands.cpp
)
@@ -16,14 +17,14 @@ ENDIF(ENABLE_E300 OR ENABLE_E320)
IF(ENABLE_E300)
LIST(APPEND E300_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/e31x_radio_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e31x_radio_control_impl.cpp
)
LIBUHD_APPEND_SOURCES(${E300_SOURCES})
ENDIF(ENABLE_E300)
IF(ENABLE_E320)
LIST(APPEND E320_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/e320_radio_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e320_radio_control_impl.cpp
)
LIBUHD_APPEND_SOURCES(${E320_SOURCES})
ENDIF(ENABLE_E320)
diff --git a/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.cpp b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.cpp
new file mode 100644
index 000000000..b7524e04c
--- /dev/null
+++ b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.cpp
@@ -0,0 +1,212 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "e31x_radio_control_impl.hpp"
+#include "e31x_regs.hpp"
+#include <uhd/rfnoc/registry.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+e31x_radio_control_impl::e31x_radio_control_impl(make_args_ptr make_args)
+ : e3xx_radio_control_impl(std::move(make_args))
+{
+ // Swap front ends for E310
+ _fe_swap = true;
+ _init_mpm();
+}
+
+e31x_radio_control_impl::~e31x_radio_control_impl()
+{
+ RFNOC_LOG_TRACE("e31x_radio_control_impl::dtor()");
+}
+
+/******************************************************************************
+ * API Calls
+ *****************************************************************************/
+uint32_t e31x_radio_control_impl::get_tx_switches(
+ const size_t chan,
+ const double freq
+) {
+ RFNOC_LOG_TRACE(
+ "Update all TX freq related switches. f=" << freq << " Hz, "
+ );
+
+ size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan;
+
+ auto tx_sw1 = TX_SW1_LB_2750; // SW1 = 0
+ auto vctxrx_sw = VCTXRX_SW_OFF;
+ auto tx_bias = (fe_chan == 0) ? TX1_BIAS_LB_ON: TX2_BIAS_LB_ON;
+
+ const auto band = e3xx_radio_control_impl::map_freq_to_tx_band(freq);
+ switch(band) {
+ case tx_band::LB_80:
+ tx_sw1 = TX_SW1_LB_80;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::LB_160:
+ tx_sw1 = TX_SW1_LB_160;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::LB_225:
+ tx_sw1 = TX_SW1_LB_225;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::LB_400:
+ tx_sw1 = TX_SW1_LB_400;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::LB_575:
+ tx_sw1 = TX_SW1_LB_575;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::LB_1000:
+ tx_sw1 = TX_SW1_LB_1000;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::LB_1700:
+ tx_sw1 = TX_SW1_LB_1700;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::LB_2750:
+ tx_sw1 = TX_SW1_LB_2750;
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
+ break;
+ case tx_band::HB:
+ tx_sw1 = TX_SW1_LB_80;
+ vctxrx_sw = VCTXRX_SW_TX_HB;
+ tx_bias = (fe_chan == 0) ? TX1_BIAS_HB_ON: TX2_BIAS_HB_ON;
+ break;
+ case tx_band::INVALID_BAND:
+ RFNOC_LOG_ERROR(
+ "Cannot map TX frequency to band: " << freq);
+ UHD_THROW_INVALID_CODE_PATH();
+ break;
+ }
+ auto tx_regs = 0 |
+ vctxrx_sw << VCTXRX_SW_SHIFT |
+ tx_bias << TX_BIAS_SHIFT |
+ tx_sw1 << TX_SW1_SHIFT;
+ return tx_regs;
+}
+
+uint32_t e31x_radio_control_impl::get_rx_switches(
+ const size_t chan,
+ const double freq,
+ const std::string &ant
+){
+ RFNOC_LOG_TRACE(
+ "Update all RX freq related switches. f=" << freq << " Hz, "
+ );
+
+ size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan;
+
+ // Default to OFF
+ auto rx_sw1 = RX_SW1_OFF;
+ auto rx_swc = RX_SWC_OFF;
+ auto rx_swb = RX_SWB_OFF;
+ auto vctxrx_sw = VCTXRX_SW_OFF;
+ auto vcrx_sw = VCRX_SW_LB;
+ if (ant == "TX/RX") {
+ vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_RX: VCTXRX2_SW_RX;
+ }
+
+ RFNOC_LOG_INFO("RX freq = " << freq);
+ const auto band = e3xx_radio_control_impl::map_freq_to_rx_band(freq);
+ RFNOC_LOG_INFO("RX band = " << int(band));
+
+ switch(band) {
+ case rx_band::LB_B2:
+ rx_sw1 = RX_SW1_LB_B2;
+ rx_swc = RX_SWC_LB_B2;
+ rx_swb = RX_SWB_OFF;
+ break;
+ case rx_band::LB_B3:
+ rx_sw1 = RX_SW1_LB_B3;
+ rx_swc = RX_SWC_LB_B3;
+ rx_swb = RX_SWB_OFF;
+ break;
+ case rx_band::LB_B4:
+ rx_sw1 = RX_SW1_LB_B4;
+ rx_swc = RX_SWC_LB_B4;
+ rx_swb = RX_SWB_OFF;
+ break;
+ case rx_band::LB_B5:
+ rx_sw1 = RX_SW1_LB_B5;
+ rx_swc = RX_SWC_OFF;
+ rx_swb = RX_SWB_LB_B5;
+ break;
+ case rx_band::LB_B6:
+ rx_sw1 = RX_SW1_LB_B6;
+ rx_swc = RX_SWC_OFF;
+ rx_swb = RX_SWB_LB_B6;
+ break;
+ case rx_band::LB_B7:
+ rx_sw1 = RX_SW1_LB_B7;
+ rx_swc = RX_SWC_OFF;
+ rx_swb = RX_SWB_LB_B7;
+ break;
+ case rx_band::HB:
+ rx_sw1 = RX_SW1_OFF;
+ rx_swc = RX_SWC_OFF;
+ rx_swb = RX_SWB_OFF;
+ vcrx_sw = VCRX_SW_HB;
+ break;
+ case rx_band::INVALID_BAND:
+ RFNOC_LOG_ERROR("Cannot map RX frequency to band: " << freq);
+ UHD_THROW_INVALID_CODE_PATH();
+ break;
+ }
+ RFNOC_LOG_INFO("RX SW1 = " << rx_sw1);
+ RFNOC_LOG_INFO("RX SWC = " << rx_swc);
+ RFNOC_LOG_INFO("RX SWB = " << rx_swb);
+ RFNOC_LOG_INFO("RX VCRX_SW = " << vcrx_sw);
+ RFNOC_LOG_INFO("RX VCTXRX_SW = " << vctxrx_sw);
+
+ auto rx_regs = 0 |
+ vcrx_sw << VCRX_SW_SHIFT |
+ vctxrx_sw << VCTXRX_SW_SHIFT |
+ rx_swc << RX_SWC_SHIFT |
+ rx_swb << RX_SWB_SHIFT |
+ rx_sw1 << RX_SW1_SHIFT;
+ return rx_regs;
+}
+
+uint32_t e31x_radio_control_impl::get_idle_switches()
+{
+ uint32_t idle_regs = VCRX_SW_OFF << VCRX_SW_SHIFT |
+ VCTXRX_SW_OFF << VCTXRX_SW_SHIFT |
+ TX_BIAS_OFF << TX_BIAS_SHIFT |
+ RX_SWC_OFF << RX_SWC_SHIFT |
+ RX_SWB_OFF << RX_SWB_SHIFT |
+ RX_SW1_OFF << RX_SW1_SHIFT |
+ TX_SW1_LB_2750 << TX_SW1_SHIFT;
+ return idle_regs;
+}
+
+uint32_t e31x_radio_control_impl::get_idle_led()
+{
+ return 0;
+}
+
+uint32_t e31x_radio_control_impl::get_rx_led()
+{
+ return 1 << LED_RX_RX_SHIFT;
+}
+
+uint32_t e31x_radio_control_impl::get_tx_led()
+{
+ return 1 << LED_TXRX_TX_SHIFT;
+}
+
+uint32_t e31x_radio_control_impl::get_txrx_led()
+{
+ return 1 << LED_TXRX_RX_SHIFT;
+}
+
+UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
+ e31x_radio_control, RADIO_BLOCK, E310, "Radio", true, "radio_clk", "bus_clk")
diff --git a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.hpp
index 581a90c8e..c51d74203 100644
--- a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.hpp
+++ b/host/lib/usrp/dboard/e3xx/e31x_radio_control_impl.hpp
@@ -1,5 +1,6 @@
//
// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
@@ -8,30 +9,37 @@
#define INCLUDED_LIBUHD_RFNOC_E31X_RADIO_CTRL_IMPL_HPP
#include "e3xx_constants.hpp"
-#include "e3xx_radio_ctrl_impl.hpp"
+#include "e3xx_radio_control_impl.hpp"
-namespace uhd {
- namespace rfnoc {
+namespace {
+static constexpr char E31x_GPIO_BANK[] = "INT0";
+}
-/*! \brief Provide access to an E31X radio.
+namespace uhd { namespace rfnoc {
+
+/*! Provide access to an E31X radio.
+ *
+ * This class only contains hardware-specific things that are different between
+ * E31X and E320.
*/
-class e31x_radio_ctrl_impl : public e3xx_radio_ctrl_impl
+class e31x_radio_control_impl : public e3xx_radio_control_impl
{
public:
/************************************************************************
- * Structors
+ * Structors and deinit
***********************************************************************/
- e31x_radio_ctrl_impl(
- const make_args_t &make_args
- );
- virtual ~e31x_radio_ctrl_impl();
+ e31x_radio_control_impl(make_args_ptr make_args);
+ virtual ~e31x_radio_control_impl();
-protected:
+ std::vector<std::string> get_gpio_banks() const
+ {
+ return {E31x_GPIO_BANK};
+ }
+private:
/**************************************************************************
* ATR/ Switches Types
*************************************************************************/
-
enum tx_sw1_t {
TX_SW1_LB_80 = 7,
TX_SW1_LB_160 = 6,
@@ -93,32 +101,13 @@ protected:
};
/************************************************************************
- * API calls
+ * E3XX API calls
***********************************************************************/
- virtual bool check_radio_config();
-
const std::string get_default_timing_mode()
{
return TIMING_MODE_1R1T;
};
- /*! Run a loopback self test.
- *
- * This will write data to the AD936x and read it back again.
- * If this test fails, it generally means the interface is broken,
- * so we assume it passes and throw otherwise. Running this requires
- * a core that we can peek and poke the loopback values into.
- *
- * \param iface An interface to the associated radio control core
- * \param iface The radio control core's address to write the loopback value
- * \param iface The radio control core's readback address to read back the returned
- * value
- *
- * \throws a uhd::runtime_error if the loopback value didn't match.
- */
- void loopback_self_test(std::function<void(uint32_t)> poker_functor,
- std::function<uint64_t()> peeker_functor);
-
uint32_t get_rx_switches(
const size_t chan,
const double freq,
@@ -136,7 +125,7 @@ protected:
uint32_t get_rx_led();
uint32_t get_txrx_led();
uint32_t get_idle_led();
-}; /* class radio_ctrl_impl */
+};
}} /* namespace uhd::rfnoc */
diff --git a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp
deleted file mode 100644
index 60df247a2..000000000
--- a/host/lib/usrp/dboard/e3xx/e31x_radio_ctrl_impl.cpp
+++ /dev/null
@@ -1,300 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "e31x_radio_ctrl_impl.hpp"
-#include "e31x_regs.hpp"
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-e31x_radio_ctrl_impl::e31x_radio_ctrl_impl(
- const make_args_t &make_args
-): block_ctrl_base(make_args)
-{
- // Swap front ends for E310
- _fe_swap = true;
-}
-
-e31x_radio_ctrl_impl::~e31x_radio_ctrl_impl()
-{
- UHD_LOG_TRACE(unique_id(), "e31x_radio_ctrl_impl::dtor() ");
-}
-
-/******************************************************************************
- * API Calls
- *****************************************************************************/
-bool e31x_radio_ctrl_impl::check_radio_config()
-{
- // mapping of frontend to radio perif index
- static const size_t FE0 = 1;
- static const size_t FE1 = 0;
- const size_t num_rx = _is_streamer_active(RX_DIRECTION, FE0) + _is_streamer_active(RX_DIRECTION, FE1);
- const size_t num_tx = _is_streamer_active(TX_DIRECTION, FE0) + _is_streamer_active(TX_DIRECTION, FE1);
-
- //setup the active chains in the codec
- if ((num_rx + num_tx) == 0) {
- // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock
- this->set_streaming_mode(true, false, true, false);
- } else {
- this->set_streaming_mode(
- _is_streamer_active(TX_DIRECTION, FE0),
- _is_streamer_active(TX_DIRECTION, FE1),
- _is_streamer_active(RX_DIRECTION, FE0),
- _is_streamer_active(RX_DIRECTION, FE1)
- );
- }
- return true;
-}
-
-/* loopback_self_test checks the integrity of the FPGA->AD936x->FPGA sample interface.
- The AD936x is put in loopback mode that sends the TX data unchanged to the RX side.
- A test value is written to the codec_idle register in the TX side of the radio.
- The readback register is then used to capture the values on the TX and RX sides
- simultaneously for comparison. It is a reasonably effective test for AC timing
- since I/Q Ch0/Ch1 alternate over the same wires. Note, however, that it uses
- whatever timing is configured at the time the test is called rather than select
- worst case conditions to stress the interface.
- Note: This currently only tests 2R2T mode
-*/
-void e31x_radio_ctrl_impl::loopback_self_test(
- std::function<void(uint32_t)> poker_functor, std::function<uint64_t()> peeker_functor)
-{
- // Save current rate before running this test
- const double current_rate = this->get_rate();
- // Set 2R2T mode, stream on all channels
- this->set_streaming_mode(true, false, true, false);
- // Set maximum rate for 2R2T mode
- this->set_rate(30.72e6);
- // Put AD936x in loopback mode
- _ad9361->data_port_loopback(true);
- UHD_LOG_INFO(unique_id(), "Performing CODEC loopback test... ");
- size_t hash = size_t(time(NULL));
- constexpr size_t loopback_count = 100;
-
- // Allow some time for AD936x to enter loopback mode.
- // There is no clear statement in the documentation of how long it takes,
- // but UG-570 does say to "allow six ADC_CLK/64 clock cycles of flush time"
- // when leaving the TX or RX states. That works out to ~75us at the
- // minimum clock rate of 5 MHz, which lines up with test results.
- // Sleeping 1ms is far more than enough.
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
-
- for (size_t i = 0; i < loopback_count; i++) {
- // Create test word
- boost::hash_combine(hash, i);
- const uint32_t word32 = uint32_t(hash) & 0xfff0fff0;
- // const uint32_t word32 = 0xCA00C100;
- // Write test word to codec_idle idle register (on TX side)
- poker_functor(word32);
-
- // Read back values - TX is lower 32-bits and RX is upper 32-bits
- const uint64_t rb_word64 = peeker_functor();
- const uint32_t rb_tx = uint32_t(rb_word64 >> 32);
- const uint32_t rb_rx = uint32_t(rb_word64 & 0xffffffff);
-
- // Compare TX and RX values to test word
- bool test_fail = word32 != rb_tx or word32 != rb_rx;
- if (test_fail) {
- UHD_LOG_WARNING(unique_id(),
- "CODEC loopback test failed! "
- << boost::format("Expected: 0x%08X Received (TX/RX): 0x%08X/0x%08X")
- % word32 % rb_tx % rb_rx);
- throw uhd::runtime_error("CODEC loopback test failed.");
- }
- }
- UHD_LOG_INFO(unique_id(), "CODEC loopback test passed");
-
- // Zero out the idle data.
- poker_functor(0);
-
- // Take AD936x out of loopback mode
- _ad9361->data_port_loopback(false);
- this->set_streaming_mode(true, false, true, false);
- // Switch back to current rate
- this->set_rate(current_rate);
-}
-
-
-uint32_t e31x_radio_ctrl_impl::get_tx_switches(
- const size_t chan,
- const double freq
-) {
- UHD_LOG_TRACE(unique_id(),
- "Update all TX freq related switches. f=" << freq << " Hz, "
- );
-
- size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan;
-
- auto tx_sw1 = TX_SW1_LB_2750; // SW1 = 0
- auto vctxrx_sw = VCTXRX_SW_OFF;
- auto tx_bias = (fe_chan == 0) ? TX1_BIAS_LB_ON: TX2_BIAS_LB_ON;
-
- const auto band = e3xx_radio_ctrl_impl::map_freq_to_tx_band(freq);
- switch(band) {
- case tx_band::LB_80:
- tx_sw1 = TX_SW1_LB_80;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::LB_160:
- tx_sw1 = TX_SW1_LB_160;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::LB_225:
- tx_sw1 = TX_SW1_LB_225;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::LB_400:
- tx_sw1 = TX_SW1_LB_400;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::LB_575:
- tx_sw1 = TX_SW1_LB_575;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::LB_1000:
- tx_sw1 = TX_SW1_LB_1000;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::LB_1700:
- tx_sw1 = TX_SW1_LB_1700;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::LB_2750:
- tx_sw1 = TX_SW1_LB_2750;
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_TX_LB: VCTXRX2_SW_TX_LB;
- break;
- case tx_band::HB:
- tx_sw1 = TX_SW1_LB_80;
- vctxrx_sw = VCTXRX_SW_TX_HB;
- tx_bias = (fe_chan == 0) ? TX1_BIAS_HB_ON: TX2_BIAS_HB_ON;
- break;
- case tx_band::INVALID_BAND:
- UHD_LOG_ERROR(unique_id(),
- "Cannot map TX frequency to band: " << freq);
- UHD_THROW_INVALID_CODE_PATH();
- break;
- }
- auto tx_regs = 0 |
- vctxrx_sw << VCTXRX_SW_SHIFT |
- tx_bias << TX_BIAS_SHIFT |
- tx_sw1 << TX_SW1_SHIFT;
- return tx_regs;
-}
-
-uint32_t e31x_radio_ctrl_impl::get_rx_switches(
- const size_t chan,
- const double freq,
- const std::string &ant
-){
- UHD_LOG_TRACE(unique_id(),
- "Update all RX freq related switches. f=" << freq << " Hz, "
- );
-
- size_t fe_chan = _fe_swap ? (chan ? 0 : 1): chan;
-
- // Default to OFF
- auto rx_sw1 = RX_SW1_OFF;
- auto rx_swc = RX_SWC_OFF;
- auto rx_swb = RX_SWB_OFF;
- auto vctxrx_sw = VCTXRX_SW_OFF;
- auto vcrx_sw = VCRX_SW_LB;
- if (ant == "TX/RX") {
- vctxrx_sw = (fe_chan == 0) ? VCTXRX1_SW_RX: VCTXRX2_SW_RX;
- }
-
- const auto band = e3xx_radio_ctrl_impl::map_freq_to_rx_band(freq);
-
- switch(band) {
- case rx_band::LB_B2:
- rx_sw1 = RX_SW1_LB_B2;
- rx_swc = RX_SWC_LB_B2;
- rx_swb = RX_SWB_OFF;
- break;
- case rx_band::LB_B3:
- rx_sw1 = RX_SW1_LB_B3;
- rx_swc = RX_SWC_LB_B3;
- rx_swb = RX_SWB_OFF;
- break;
- case rx_band::LB_B4:
- rx_sw1 = RX_SW1_LB_B4;
- rx_swc = RX_SWC_LB_B4;
- rx_swb = RX_SWB_OFF;
- break;
- case rx_band::LB_B5:
- rx_sw1 = RX_SW1_LB_B5;
- rx_swc = RX_SWC_OFF;
- rx_swb = RX_SWB_LB_B5;
- break;
- case rx_band::LB_B6:
- rx_sw1 = RX_SW1_LB_B6;
- rx_swc = RX_SWC_OFF;
- rx_swb = RX_SWB_LB_B6;
- break;
- case rx_band::LB_B7:
- rx_sw1 = RX_SW1_LB_B7;
- rx_swc = RX_SWC_OFF;
- rx_swb = RX_SWB_LB_B7;
- break;
- case rx_band::HB:
- rx_sw1 = RX_SW1_OFF;
- rx_swc = RX_SWC_OFF;
- rx_swb = RX_SWB_OFF;
- vcrx_sw = VCRX_SW_HB;
- break;
- case rx_band::INVALID_BAND:
- UHD_LOG_ERROR(unique_id(),
- "Cannot map RX frequency to band: " << freq);
- UHD_THROW_INVALID_CODE_PATH();
- break;
- }
-
- UHD_LOG_TRACE(unique_id(),
- "RX band = " << int(band) << "RX SW1 = " << rx_sw1 << "RX SWC = " << rx_swc
- << "RX SWB = " << rx_swb << "RX VCRX_SW = " << vcrx_sw
- << "RX VCTXRX_SW = " << vctxrx_sw);
-
- auto rx_regs = 0 |
- vcrx_sw << VCRX_SW_SHIFT |
- vctxrx_sw << VCTXRX_SW_SHIFT |
- rx_swc << RX_SWC_SHIFT |
- rx_swb << RX_SWB_SHIFT |
- rx_sw1 << RX_SW1_SHIFT;
- return rx_regs;
-}
-
-uint32_t e31x_radio_ctrl_impl::get_idle_switches()
-{
- uint32_t idle_regs = VCRX_SW_OFF << VCRX_SW_SHIFT |
- VCTXRX_SW_OFF << VCTXRX_SW_SHIFT |
- TX_BIAS_OFF << TX_BIAS_SHIFT |
- RX_SWC_OFF << RX_SWC_SHIFT |
- RX_SWB_OFF << RX_SWB_SHIFT |
- RX_SW1_OFF << RX_SW1_SHIFT |
- TX_SW1_LB_2750 << TX_SW1_SHIFT;
- return idle_regs;
-}
-
-uint32_t e31x_radio_ctrl_impl::get_idle_led()
-{
- return 0;
-}
-
-uint32_t e31x_radio_ctrl_impl::get_rx_led()
-{
- return 1 << LED_RX_RX_SHIFT;
-}
-
-uint32_t e31x_radio_ctrl_impl::get_tx_led()
-{
- return 1 << LED_TXRX_TX_SHIFT;
-}
-
-uint32_t e31x_radio_ctrl_impl::get_txrx_led()
-{
- return 1 << LED_TXRX_RX_SHIFT;
-}
-UHD_RFNOC_BLOCK_REGISTER(e31x_radio_ctrl, "E31XRadio");
diff --git a/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.cpp b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.cpp
new file mode 100644
index 000000000..df325bb75
--- /dev/null
+++ b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.cpp
@@ -0,0 +1,183 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "e320_radio_control_impl.hpp"
+#include "e320_regs.hpp"
+#include <uhd/rfnoc/registry.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+e320_radio_control_impl::e320_radio_control_impl(make_args_ptr make_args)
+ : e3xx_radio_control_impl(std::move(make_args))
+{
+ RFNOC_LOG_TRACE("e320_radio_control_impl::ctor()");
+ // Don't swap front ends for E320
+ _fe_swap = false;
+ _init_mpm();
+}
+
+e320_radio_control_impl::~e320_radio_control_impl()
+{
+ RFNOC_LOG_TRACE("e320_radio_control_impl::dtor() ");
+}
+
+/******************************************************************************
+ * E320 API Calls
+ *****************************************************************************/
+uint32_t e320_radio_control_impl::get_tx_switches(const size_t chan, const double freq)
+{
+ RFNOC_LOG_TRACE("Update all TX freq related switches. f=" << freq << " Hz, ");
+ auto tx_sw1 = TX_SW1_LB_160;
+ auto tx_sw2 = TX_SW2_LB_160;
+ auto trx_sw = (chan == 0) ? TRX1_SW_TX_LB : TRX2_SW_TX_LB;
+ auto tx_amp = TX_AMP_LF_ON;
+
+ const auto band = e3xx_radio_control_impl::map_freq_to_tx_band(freq);
+ switch (band) {
+ case tx_band::LB_80:
+ tx_sw1 = TX_SW1_LB_80;
+ tx_sw2 = TX_SW2_LB_80;
+ break;
+ case tx_band::LB_160:
+ tx_sw1 = TX_SW1_LB_160;
+ tx_sw2 = TX_SW2_LB_160;
+ break;
+ case tx_band::LB_225:
+ tx_sw1 = TX_SW1_LB_225;
+ tx_sw2 = TX_SW2_LB_225;
+ break;
+ case tx_band::LB_400:
+ tx_sw1 = TX_SW1_LB_400;
+ tx_sw2 = TX_SW2_LB_400;
+ break;
+ case tx_band::LB_575:
+ tx_sw1 = TX_SW1_LB_575;
+ tx_sw2 = TX_SW2_LB_575;
+ break;
+ case tx_band::LB_1000:
+ tx_sw1 = TX_SW1_LB_1000;
+ tx_sw2 = TX_SW2_LB_1000;
+ break;
+ case tx_band::LB_1700:
+ tx_sw1 = TX_SW1_LB_1700;
+ tx_sw2 = TX_SW2_LB_1700;
+ break;
+ case tx_band::LB_2750:
+ tx_sw1 = TX_SW1_LB_2750;
+ tx_sw2 = TX_SW2_LB_2750;
+ break;
+ case tx_band::HB:
+ trx_sw = (chan == 0) ? TRX1_SW_TX_HB : TRX2_SW_TX_HB;
+ tx_amp = TX_AMP_HF_ON;
+ break;
+ case tx_band::INVALID_BAND:
+ RFNOC_LOG_ERROR("Cannot map TX frequency to band: " << freq);
+ UHD_THROW_INVALID_CODE_PATH();
+ break;
+ }
+
+ auto tx_regs = tx_amp << TX_AMP_SHIFT | trx_sw << TRX_SW_SHIFT
+ | tx_sw2 << TX_SW2_SHIFT | tx_sw1 << TX_SW1_SHIFT;
+ return tx_regs;
+}
+
+uint32_t e320_radio_control_impl::get_rx_switches(
+ const size_t chan, const double freq, const std::string& ant)
+{
+ RFNOC_LOG_TRACE("Update all RX freq related switches. f=" << (freq / 1e6) << " MHz");
+ // Default to OFF
+ auto rx_sw1 = RX_SW1_OFF;
+ auto rx_sw2 = RX_SW2_OFF;
+ auto rx_sw3 = RX_SW3_OFF;
+ auto trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX;
+ if (ant == "TX/RX") {
+ rx_sw3 = RX_SW3_HBRX_LBTRX;
+ trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX;
+ } else if (ant == "RX2") {
+ rx_sw3 = RX_SW3_HBTRX_LBRX;
+ // Set TRX switch to TX when receiving on RX2
+ trx_sw = TRX1_SW_TX_HB;
+ }
+
+ const auto band = e3xx_radio_control_impl::map_freq_to_rx_band(freq);
+ switch (band) {
+ case rx_band::LB_B2:
+ rx_sw1 = RX_SW1_LB_B2;
+ rx_sw2 = RX_SW2_LB_B2;
+ break;
+ case rx_band::LB_B3:
+ rx_sw1 = RX_SW1_LB_B3;
+ rx_sw2 = RX_SW2_LB_B3;
+ break;
+ case rx_band::LB_B4:
+ rx_sw1 = RX_SW1_LB_B4;
+ rx_sw2 = RX_SW2_LB_B4;
+ break;
+ case rx_band::LB_B5:
+ rx_sw1 = RX_SW1_LB_B5;
+ rx_sw2 = RX_SW2_LB_B5;
+ break;
+ case rx_band::LB_B6:
+ rx_sw1 = RX_SW1_LB_B6;
+ rx_sw2 = RX_SW2_LB_B6;
+ break;
+ case rx_band::LB_B7:
+ rx_sw1 = RX_SW1_LB_B7;
+ rx_sw2 = RX_SW2_LB_B7;
+ break;
+ case rx_band::HB:
+ rx_sw1 = RX_SW1_OFF;
+ rx_sw2 = RX_SW2_OFF;
+ if (ant == "TX/RX") {
+ rx_sw3 = RX_SW3_HBTRX_LBRX;
+ } else if (ant == "RX2") {
+ rx_sw3 = RX_SW3_HBRX_LBTRX;
+ }
+ break;
+ case rx_band::INVALID_BAND:
+ RFNOC_LOG_ERROR("Cannot map RX frequency to band: " << freq);
+ UHD_THROW_INVALID_CODE_PATH();
+ break;
+ }
+
+ auto rx_regs = trx_sw << TRX_SW_SHIFT | rx_sw3 << RX_SW3_SHIFT
+ | rx_sw2 << RX_SW2_SHIFT | rx_sw1 << RX_SW1_SHIFT;
+ return rx_regs;
+}
+
+uint32_t e320_radio_control_impl::get_idle_switches()
+{
+ uint32_t idle_regs = TX_AMP_OFF << TX_AMP_SHIFT | TRX1_SW_TX_HB << TRX_SW_SHIFT
+ | TX_SW2_LB_80 << TX_SW2_SHIFT | TX_SW1_LB_80 << TX_SW1_SHIFT
+ | RX_SW3_OFF << RX_SW3_SHIFT | RX_SW2_OFF << RX_SW2_SHIFT
+ | RX_SW1_OFF << RX_SW1_SHIFT;
+ return idle_regs;
+}
+
+uint32_t e320_radio_control_impl::get_idle_led()
+{
+ return 0;
+}
+
+uint32_t e320_radio_control_impl::get_rx_led()
+{
+ return 1 << TRX_LED_GRN_SHIFT;
+}
+
+uint32_t e320_radio_control_impl::get_tx_led()
+{
+ return 1 << TX_LED_RED_SHIFT;
+}
+
+uint32_t e320_radio_control_impl::get_txrx_led()
+{
+ return 1 << RX_LED_GRN_SHIFT;
+}
+
+UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
+ e320_radio_control, RADIO_BLOCK, E320, "Radio", true, "radio_clk", "bus_clk")
diff --git a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.hpp
index 7f75cadc7..f781eb49d 100644
--- a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.hpp
+++ b/host/lib/usrp/dboard/e3xx/e320_radio_control_impl.hpp
@@ -8,20 +8,32 @@
# define INCLUDED_LIBUHD_RFNOC_E320_RADIO_CTRL_IMPL_HPP
# include "e3xx_constants.hpp"
-# include "e3xx_radio_ctrl_impl.hpp"
+# include "e3xx_radio_control_impl.hpp"
+
+namespace {
+static constexpr char E320_GPIO_BANK[] = "FP0";
+}
namespace uhd { namespace rfnoc {
/*! \brief Provide access to an E320 radio.
+ *
+ * This class only contains hardware-specific things that are different between
+ * E320 and E31X.
*/
-class e320_radio_ctrl_impl : public e3xx_radio_ctrl_impl
+class e320_radio_control_impl : public e3xx_radio_control_impl
{
public:
/************************************************************************
* Structors
***********************************************************************/
- e320_radio_ctrl_impl(const make_args_t& make_args);
- virtual ~e320_radio_ctrl_impl();
+ e320_radio_control_impl(make_args_ptr make_args);
+ virtual ~e320_radio_control_impl();
+
+ std::vector<std::string> get_gpio_banks() const
+ {
+ return {E320_GPIO_BANK};
+ }
protected:
/**************************************************************************
@@ -89,32 +101,13 @@ protected:
enum tx_amp_t { TX_AMP_HF_ON = 2, TX_AMP_LF_ON = 1, TX_AMP_OFF = 3 };
/************************************************************************
- * API calls
+ * E3XX API calls
***********************************************************************/
- virtual bool check_radio_config();
-
const std::string get_default_timing_mode()
{
return TIMING_MODE_2R2T;
};
- /*! Run a loopback self test.
- *
- * This will write data to the AD936x and read it back again.
- * If this test fails, it generally means the interface is broken,
- * so we assume it passes and throw otherwise. Running this requires
- * a core that we can peek and poke the loopback values into.
- *
- * \param iface An interface to the associated radio control core
- * \param iface The radio control core's address to write the loopback value
- * \param iface The radio control core's readback address to read back the returned
- * value
- *
- * \throws a uhd::runtime_error if the loopback value didn't match.
- */
- void loopback_self_test(std::function<void(uint32_t)> poker_functor,
- std::function<uint64_t()> peeker_functor);
-
uint32_t get_rx_switches(
const size_t chan, const double freq, const std::string& ant);
@@ -126,7 +119,7 @@ protected:
uint32_t get_rx_led();
uint32_t get_txrx_led();
uint32_t get_idle_led();
-}; /* class radio_ctrl_impl */
+};
}} /* namespace uhd::rfnoc */
diff --git a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp
deleted file mode 100644
index c48cabc9c..000000000
--- a/host/lib/usrp/dboard/e3xx/e320_radio_ctrl_impl.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "e320_radio_ctrl_impl.hpp"
-#include "e320_regs.hpp"
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-e320_radio_ctrl_impl::e320_radio_ctrl_impl(const make_args_t& make_args)
- : block_ctrl_base(make_args)
-{
- UHD_LOG_TRACE(unique_id(), "Entering e320_radio_ctrl_impl ctor...");
- // Don't swap front ends for E320
- _fe_swap = false;
-}
-
-e320_radio_ctrl_impl::~e320_radio_ctrl_impl()
-{
- UHD_LOG_TRACE(unique_id(), "e320_radio_ctrl_impl::dtor() ");
-}
-
-/******************************************************************************
- * API Calls
- *****************************************************************************/
-bool e320_radio_ctrl_impl::check_radio_config()
-{
- // mapping of frontend to radio perif index
- static const size_t FE0 = 0;
- static const size_t FE1 = 1;
- const size_t num_rx =
- _is_streamer_active(RX_DIRECTION, FE0) + _is_streamer_active(RX_DIRECTION, FE1);
- const size_t num_tx =
- _is_streamer_active(TX_DIRECTION, FE0) + _is_streamer_active(TX_DIRECTION, FE1);
-
- // setup the active chains in the codec
- if ((num_rx + num_tx) == 0) {
- // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock
- this->set_streaming_mode(true, false, true, false);
- } else {
- this->set_streaming_mode(_is_streamer_active(TX_DIRECTION, FE0),
- _is_streamer_active(TX_DIRECTION, FE1),
- _is_streamer_active(RX_DIRECTION, FE0),
- _is_streamer_active(RX_DIRECTION, FE1));
- }
- return true;
-}
-
-/* loopback_self_test checks the integrity of the FPGA->AD936x->FPGA sample interface.
- The AD936x is put in loopback mode that sends the TX data unchanged to the RX side.
- A test value is written to the codec_idle register in the TX side of the radio.
- The readback register is then used to capture the values on the TX and RX sides
- simultaneously for comparison. It is a reasonably effective test for AC timing
- since I/Q Ch0/Ch1 alternate over the same wires. Note, however, that it uses
- whatever timing is configured at the time the test is called rather than select
- worst case conditions to stress the interface.
- Note: This currently only tests 2R2T mode
-*/
-void e320_radio_ctrl_impl::loopback_self_test(
- std::function<void(uint32_t)> poker_functor, std::function<uint64_t()> peeker_functor)
-{
- // Save current rate before running this test
- const double current_rate = this->get_rate();
- // Set 2R2T mode, stream on all channels
- this->set_streaming_mode(true, true, true, true);
- // Set maximum rate for 2R2T mode
- this->set_rate(30.72e6);
- // Put AD936x in loopback mode
- _ad9361->data_port_loopback(true);
- UHD_LOG_INFO(unique_id(), "Performing CODEC loopback test... ");
- size_t hash = size_t(time(NULL));
- constexpr size_t loopback_count = 100;
-
- // Allow some time for AD936x to enter loopback mode.
- // There is no clear statement in the documentation of how long it takes,
- // but UG-570 does say to "allow six ADC_CLK/64 clock cycles of flush time"
- // when leaving the TX or RX states. That works out to ~75us at the
- // minimum clock rate of 5 MHz, which lines up with test results.
- // Sleeping 1ms is far more than enough.
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
-
- for (size_t i = 0; i < loopback_count; i++) {
- // Create test word
- boost::hash_combine(hash, i);
- const uint32_t word32 = uint32_t(hash) & 0xfff0fff0;
- // const uint32_t word32 = 0xCA00C100;
- // Write test word to codec_idle idle register (on TX side)
- poker_functor(word32);
-
- // Read back values - TX is lower 32-bits and RX is upper 32-bits
- const uint64_t rb_word64 = peeker_functor();
- const uint32_t rb_tx = uint32_t(rb_word64 >> 32);
- const uint32_t rb_rx = uint32_t(rb_word64 & 0xffffffff);
-
- // Compare TX and RX values to test word
- bool test_fail = word32 != rb_tx or word32 != rb_rx;
- if (test_fail) {
- UHD_LOG_WARNING(unique_id(),
- "CODEC loopback test failed! "
- << boost::format("Expected: 0x%08X Received (TX/RX): 0x%08X/0x%08X")
- % word32 % rb_tx % rb_rx);
- throw uhd::runtime_error("CODEC loopback test failed.");
- }
- }
- UHD_LOG_INFO(unique_id(), "CODEC loopback test passed");
-
- // Zero out the idle data.
- poker_functor(0);
-
- // Take AD936x out of loopback mode
- _ad9361->data_port_loopback(false);
- this->set_streaming_mode(true, false, true, false);
- // Switch back to current rate
- this->set_rate(current_rate);
-}
-
-uint32_t e320_radio_ctrl_impl::get_tx_switches(const size_t chan, const double freq)
-{
- UHD_LOG_TRACE(
- unique_id(), "Update all TX freq related switches. f=" << freq << " Hz, ");
- auto tx_sw1 = TX_SW1_LB_160;
- auto tx_sw2 = TX_SW2_LB_160;
- auto trx_sw = (chan == 0) ? TRX1_SW_TX_LB : TRX2_SW_TX_LB;
- auto tx_amp = TX_AMP_LF_ON;
-
- const auto band = e3xx_radio_ctrl_impl::map_freq_to_tx_band(freq);
- switch (band) {
- case tx_band::LB_80:
- tx_sw1 = TX_SW1_LB_80;
- tx_sw2 = TX_SW2_LB_80;
- break;
- case tx_band::LB_160:
- tx_sw1 = TX_SW1_LB_160;
- tx_sw2 = TX_SW2_LB_160;
- break;
- case tx_band::LB_225:
- tx_sw1 = TX_SW1_LB_225;
- tx_sw2 = TX_SW2_LB_225;
- break;
- case tx_band::LB_400:
- tx_sw1 = TX_SW1_LB_400;
- tx_sw2 = TX_SW2_LB_400;
- break;
- case tx_band::LB_575:
- tx_sw1 = TX_SW1_LB_575;
- tx_sw2 = TX_SW2_LB_575;
- break;
- case tx_band::LB_1000:
- tx_sw1 = TX_SW1_LB_1000;
- tx_sw2 = TX_SW2_LB_1000;
- break;
- case tx_band::LB_1700:
- tx_sw1 = TX_SW1_LB_1700;
- tx_sw2 = TX_SW2_LB_1700;
- break;
- case tx_band::LB_2750:
- tx_sw1 = TX_SW1_LB_2750;
- tx_sw2 = TX_SW2_LB_2750;
- break;
- case tx_band::HB:
- trx_sw = (chan == 0) ? TRX1_SW_TX_HB : TRX2_SW_TX_HB;
- tx_amp = TX_AMP_HF_ON;
- break;
- case tx_band::INVALID_BAND:
- UHD_LOG_ERROR(unique_id(), "Cannot map TX frequency to band: " << freq);
- UHD_THROW_INVALID_CODE_PATH();
- break;
- }
-
- auto tx_regs = tx_amp << TX_AMP_SHIFT | trx_sw << TRX_SW_SHIFT
- | tx_sw2 << TX_SW2_SHIFT | tx_sw1 << TX_SW1_SHIFT;
- return tx_regs;
-}
-
-uint32_t e320_radio_ctrl_impl::get_rx_switches(
- const size_t chan, const double freq, const std::string& ant)
-{
- UHD_LOG_TRACE(
- unique_id(), "Update all RX freq related switches. f=" << freq << " Hz, ");
- // Default to OFF
- auto rx_sw1 = RX_SW1_OFF;
- auto rx_sw2 = RX_SW2_OFF;
- auto rx_sw3 = RX_SW3_OFF;
- auto trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX;
- if (ant == "TX/RX") {
- rx_sw3 = RX_SW3_HBRX_LBTRX;
- trx_sw = (chan == 0) ? TRX1_SW_RX : TRX2_SW_RX;
- } else if (ant == "RX2") {
- rx_sw3 = RX_SW3_HBTRX_LBRX;
- // Set TRX switch to TX when receiving on RX2
- trx_sw = TRX1_SW_TX_HB;
- }
-
- const auto band = e3xx_radio_ctrl_impl::map_freq_to_rx_band(freq);
- switch (band) {
- case rx_band::LB_B2:
- rx_sw1 = RX_SW1_LB_B2;
- rx_sw2 = RX_SW2_LB_B2;
- break;
- case rx_band::LB_B3:
- rx_sw1 = RX_SW1_LB_B3;
- rx_sw2 = RX_SW2_LB_B3;
- break;
- case rx_band::LB_B4:
- rx_sw1 = RX_SW1_LB_B4;
- rx_sw2 = RX_SW2_LB_B4;
- break;
- case rx_band::LB_B5:
- rx_sw1 = RX_SW1_LB_B5;
- rx_sw2 = RX_SW2_LB_B5;
- break;
- case rx_band::LB_B6:
- rx_sw1 = RX_SW1_LB_B6;
- rx_sw2 = RX_SW2_LB_B6;
- break;
- case rx_band::LB_B7:
- rx_sw1 = RX_SW1_LB_B7;
- rx_sw2 = RX_SW2_LB_B7;
- break;
- case rx_band::HB:
- rx_sw1 = RX_SW1_OFF;
- rx_sw2 = RX_SW2_OFF;
- if (ant == "TX/RX") {
- rx_sw3 = RX_SW3_HBTRX_LBRX;
- } else if (ant == "RX2") {
- rx_sw3 = RX_SW3_HBRX_LBTRX;
- }
- break;
- case rx_band::INVALID_BAND:
- UHD_LOG_ERROR(unique_id(), "Cannot map RX frequency to band: " << freq);
- UHD_THROW_INVALID_CODE_PATH();
- break;
- }
-
- auto rx_regs = trx_sw << TRX_SW_SHIFT | rx_sw3 << RX_SW3_SHIFT
- | rx_sw2 << RX_SW2_SHIFT | rx_sw1 << RX_SW1_SHIFT;
- return rx_regs;
-}
-
-uint32_t e320_radio_ctrl_impl::get_idle_switches()
-{
- uint32_t idle_regs = TX_AMP_OFF << TX_AMP_SHIFT | TRX1_SW_TX_HB << TRX_SW_SHIFT
- | TX_SW2_LB_80 << TX_SW2_SHIFT | TX_SW1_LB_80 << TX_SW1_SHIFT
- | RX_SW3_OFF << RX_SW3_SHIFT | RX_SW2_OFF << RX_SW2_SHIFT
- | RX_SW1_OFF << RX_SW1_SHIFT;
- return idle_regs;
-}
-
-uint32_t e320_radio_ctrl_impl::get_idle_led()
-{
- return 0;
-}
-
-uint32_t e320_radio_ctrl_impl::get_rx_led()
-{
- return 1 << TRX_LED_GRN_SHIFT;
-}
-
-uint32_t e320_radio_ctrl_impl::get_tx_led()
-{
- return 1 << TX_LED_RED_SHIFT;
-}
-
-uint32_t e320_radio_ctrl_impl::get_txrx_led()
-{
- return 1 << RX_LED_GRN_SHIFT;
-}
-UHD_RFNOC_BLOCK_REGISTER(e320_radio_ctrl, "NeonRadio");
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp b/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp
index 001cf5d1b..83e96b3ec 100644
--- a/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp
+++ b/host/lib/usrp/dboard/e3xx/e3xx_bands.cpp
@@ -5,7 +5,7 @@
//
#include "e3xx_constants.hpp"
-#include "e3xx_radio_ctrl_impl.hpp"
+#include "e3xx_radio_control_impl.hpp"
#include <uhd/utils/math.hpp>
/*
@@ -131,9 +131,9 @@ constexpr double E3XX_TX_LB_2750_MIN_FREQ = 1842.6e6;
constexpr double E3XX_TX_HB_MIN_FREQ = 2940.0e6;
} // namespace
-e3xx_radio_ctrl_impl::rx_band e3xx_radio_ctrl_impl::map_freq_to_rx_band(const double freq)
+e3xx_radio_control_impl::rx_band e3xx_radio_control_impl::map_freq_to_rx_band(const double freq)
{
- e3xx_radio_ctrl_impl::rx_band band;
+ e3xx_radio_control_impl::rx_band band;
if (fp_compare_epsilon<double>(freq) < AD9361_RX_MIN_FREQ) {
band = rx_band::INVALID_BAND;
@@ -158,9 +158,9 @@ e3xx_radio_ctrl_impl::rx_band e3xx_radio_ctrl_impl::map_freq_to_rx_band(const do
return band;
}
-e3xx_radio_ctrl_impl::tx_band e3xx_radio_ctrl_impl::map_freq_to_tx_band(const double freq)
+e3xx_radio_control_impl::tx_band e3xx_radio_control_impl::map_freq_to_tx_band(const double freq)
{
- e3xx_radio_ctrl_impl::tx_band band;
+ e3xx_radio_control_impl::tx_band band;
if (fp_compare_epsilon<double>(freq) < AD9361_TX_MIN_FREQ) {
band = tx_band::INVALID_BAND;
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp b/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp
index 53f64d837..f883a7d72 100644
--- a/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp
+++ b/host/lib/usrp/dboard/e3xx/e3xx_constants.hpp
@@ -12,12 +12,11 @@
#include <vector>
static constexpr size_t FPGPIO_MASTER_RADIO = 0;
-static constexpr size_t TOTAL_RADIO_PORTS = 2;
-static constexpr double AD9361_RX_MIN_BANDWIDTH = 20.0e6; // HZ
-static constexpr double AD9361_RX_MAX_BANDWIDTH = 40.0e6; // HZ
+static constexpr double AD9361_RX_MIN_BANDWIDTH = 20.0e6; // Hz
+static constexpr double AD9361_RX_MAX_BANDWIDTH = 40.0e6; // Hz
-static constexpr double AD9361_TX_MIN_BANDWIDTH = 20.0e6; // HZ
-static constexpr double AD9361_TX_MAX_BANDWIDTH = 40.0e6; // HZ
+static constexpr double AD9361_TX_MIN_BANDWIDTH = 20.0e6; // Hz
+static constexpr double AD9361_TX_MAX_BANDWIDTH = 40.0e6; // Hz
static constexpr double AD9361_TX_MIN_FREQ = 47.0e6; // Hz
static constexpr double AD9361_TX_MAX_FREQ = 6.0e9; // Hz
@@ -44,7 +43,10 @@ static constexpr double E3XX_DEFAULT_BANDWIDTH = 40e6; // Hz
static constexpr char E3XX_DEFAULT_RX_ANTENNA[] = "RX2";
static constexpr char E3XX_DEFAULT_TX_ANTENNA[] = "TX/RX";
-static const std::vector<std::string> E3XX_RX_ANTENNAS = {"RX2", "TX/RX"};
+static const std::vector<std::string> E3XX_RX_ANTENNAS = {
+ E3XX_DEFAULT_RX_ANTENNA, E3XX_DEFAULT_TX_ANTENNA};
+
+static constexpr char E3XX_GPIO_BANK[] = "INT0";
static constexpr size_t E3XX_NUM_CHANS = 2;
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp
new file mode 100644
index 000000000..29381a53c
--- /dev/null
+++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.cpp
@@ -0,0 +1,621 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "e3xx_radio_control_impl.hpp"
+#include "e3xx_constants.hpp"
+#include <uhd/transport/chdr.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/math.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/format.hpp>
+#include <cmath>
+#include <cstdlib>
+#include <sstream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+using namespace uhd::math::fp_compare;
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+e3xx_radio_control_impl::e3xx_radio_control_impl(make_args_ptr make_args)
+ : radio_control_impl(std::move(make_args))
+{
+ RFNOC_LOG_TRACE("Entering e3xx_radio_control_impl ctor...");
+ UHD_ASSERT_THROW(get_block_id().get_block_count() == 0);
+ UHD_ASSERT_THROW(
+ std::max(get_num_output_ports(), get_num_input_ports()) == E3XX_NUM_CHANS);
+ UHD_ASSERT_THROW(get_mb_controller());
+ _e3xx_mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller());
+ UHD_ASSERT_THROW(_e3xx_mb_control);
+ _e3xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>(
+ _e3xx_mb_control->get_timekeeper(0));
+ UHD_ASSERT_THROW(_e3xx_timekeeper);
+ _rpcc = _e3xx_mb_control->get_rpc_client();
+ UHD_ASSERT_THROW(_rpcc);
+ RFNOC_LOG_TRACE("Instantiating AD9361 control object...");
+ _ad9361 = make_rpc(_rpcc);
+
+ _init_defaults();
+ _init_peripherals();
+ _init_prop_tree();
+
+ // Properties
+ for (auto& samp_rate_prop : _samp_rate_in) {
+ samp_rate_prop.set(_master_clock_rate);
+ }
+ for (auto& samp_rate_prop : _samp_rate_out) {
+ samp_rate_prop.set(_master_clock_rate);
+ }
+}
+
+e3xx_radio_control_impl::~e3xx_radio_control_impl()
+{
+ RFNOC_LOG_TRACE("e3xx_radio_control_impl::dtor() ");
+}
+
+void e3xx_radio_control_impl::deinit()
+{
+ _db_gpio.clear();
+ _leds_gpio.clear();
+ _fp_gpio.reset();
+ _wb_ifaces.clear();
+}
+
+
+/******************************************************************************
+ * API Calls
+ *****************************************************************************/
+bool e3xx_radio_control_impl::check_topology(const std::vector<size_t>& connected_inputs,
+ const std::vector<size_t>& connected_outputs)
+{
+ if (!node_t::check_topology(connected_inputs, connected_outputs)) {
+ return false;
+ }
+ // Now we know that the connected ports are either 0 or 1
+
+ // Check if we're running a 2x1 or 1x2 configuration -- the device does not
+ // support this!
+ if ((connected_outputs.size() == 1 && connected_inputs.size() == 2)
+ || (connected_outputs.size() == 2 && connected_inputs.size() == 1)) {
+ const std::string err_msg("Invalid channel configuration: This device does not "
+ "support 1 TX x 2 RX or 2 TX x 1 RX configurations!");
+ RFNOC_LOG_ERROR(err_msg);
+ throw uhd::runtime_error(err_msg);
+ }
+ // mapping of frontend to radio perif index
+ const size_t FE0 = _fe_swap ? 1 : 0;
+ const size_t FE1 = _fe_swap ? 0 : 1;
+
+ const bool tx_fe0_active = std::any_of(connected_inputs.begin(),
+ connected_inputs.end(),
+ [FE0](const size_t port) { return port == FE0; });
+ const bool tx_fe1_active = std::any_of(connected_inputs.begin(),
+ connected_inputs.end(),
+ [FE1](const size_t port) { return port == FE1; });
+ const bool rx_fe0_active = std::any_of(connected_outputs.begin(),
+ connected_outputs.end(),
+ [FE0](const size_t port) { return port == FE0; });
+ const bool rx_fe1_active = std::any_of(connected_outputs.begin(),
+ connected_outputs.end(),
+ [FE1](const size_t port) { return port == FE1; });
+ RFNOC_LOG_TRACE("TX FE0 Active: " << tx_fe0_active);
+ RFNOC_LOG_TRACE("TX FE1 Active: " << tx_fe1_active);
+ RFNOC_LOG_TRACE("RX FE0 Active: " << rx_fe0_active);
+ RFNOC_LOG_TRACE("RX FE1 Active: " << rx_fe1_active);
+
+ //setup the active chains in the codec
+ if (connected_inputs.size() + connected_outputs.size() == 0) {
+ // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock
+ this->set_streaming_mode(true, false, true, false);
+ } else {
+ this->set_streaming_mode(
+ tx_fe0_active, tx_fe1_active, rx_fe0_active, rx_fe1_active);
+ }
+ return true;
+}
+
+
+void e3xx_radio_control_impl::set_streaming_mode(
+ const bool tx1, const bool tx2, const bool rx1, const bool rx2)
+{
+ RFNOC_LOG_TRACE("Setting streaming mode...")
+ const size_t num_rx = rx1 + rx2;
+ const size_t num_tx = tx1 + tx2;
+
+ // setup the active chains in the codec
+ if ((num_rx + num_tx) == 0) {
+ // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock
+ _ad9361->set_active_chains(true, false, true, false);
+ } else {
+ // setup the active chains in the codec
+ _ad9361->set_active_chains(tx1, tx2, rx1, rx2);
+ }
+
+ // setup 1R1T/2R2T mode in catalina and fpga
+ // The Catalina interface in the fpga needs to know which TX channel to use for
+ // the data on the LVDS lines.
+ if ((num_rx == 2) or (num_tx == 2)) {
+ // AD9361 is in 1R1T mode
+ _ad9361->set_timing_mode(this->get_default_timing_mode());
+ this->set_channel_mode(MIMO);
+ } else {
+ // AD9361 is in 1R1T mode
+ _ad9361->set_timing_mode(TIMING_MODE_1R1T);
+
+ // Set to SIS0_TX1 if we're using the second TX antenna, otherwise
+ // default to SISO_TX0
+ this->set_channel_mode(tx2 ? SISO_TX1 : SISO_TX0);
+ }
+}
+
+void e3xx_radio_control_impl::set_channel_mode(const std::string& channel_mode)
+{
+ // MIMO for 2R2T mode for 2 channels
+ // SISO_TX1 for 1R1T mode for 1 channel - TX1
+ // SISO_TX0 for 1R1T mode for 1 channel - TX0
+ _rpcc->request_with_token<void>("set_channel_mode", channel_mode);
+}
+
+double e3xx_radio_control_impl::set_rate(const double rate)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_DEBUG("Asking for clock rate " << rate / 1e6 << " MHz\n");
+ // On E3XX, tick rate and samp rate are always the same
+ double actual_tick_rate = _ad9361->set_clock_rate(rate);
+ RFNOC_LOG_DEBUG("Actual clock rate " << actual_tick_rate / 1e6 << " MHz\n");
+ set_tick_rate(actual_tick_rate);
+ radio_control_impl::set_rate(actual_tick_rate);
+ _e3xx_timekeeper->update_tick_rate(rate);
+ return rate;
+}
+
+uhd::meta_range_t e3xx_radio_control_impl::get_rate_range() const
+{
+ return _ad9361->get_clock_rate_range();
+}
+
+/******************************************************************************
+ * RF API calls
+ *****************************************************************************/
+void e3xx_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan)
+{
+ if (ant != get_tx_antenna(chan)) {
+ throw uhd::value_error(
+ str(boost::format("[%s] Requesting invalid TX antenna value: %s")
+ % get_unique_id() % ant));
+ }
+ radio_control_impl::set_tx_antenna(ant, chan);
+ // We can't actually set the TX antenna, so let's stop here.
+}
+
+void e3xx_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan <= E3XX_NUM_CHANS);
+ if (std::find(E3XX_RX_ANTENNAS.begin(), E3XX_RX_ANTENNAS.end(), ant)
+ == E3XX_RX_ANTENNAS.end()) {
+ throw uhd::value_error(
+ str(boost::format("[%s] Requesting invalid RX antenna value: %s")
+ % get_unique_id() % ant));
+ }
+ RFNOC_LOG_TRACE("Setting RX antenna to " << ant << " for chan " << chan);
+
+ radio_control_impl::set_rx_antenna(ant, chan);
+ _set_atr_bits(chan);
+}
+
+double e3xx_radio_control_impl::set_tx_frequency(const double freq, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
+ std::lock_guard<std::mutex> l(_set_lock);
+
+ double clipped_freq = uhd::clip(freq, AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ);
+
+ double coerced_freq =
+ _ad9361->tune(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clipped_freq);
+ radio_control_impl::set_tx_frequency(coerced_freq, chan);
+ // Front-end switching
+ _set_atr_bits(chan);
+
+ return coerced_freq;
+}
+
+double e3xx_radio_control_impl::set_rx_frequency(const double freq, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
+ std::lock_guard<std::mutex> l(_set_lock);
+
+ double clipped_freq = uhd::clip(freq, AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ);
+
+ double coerced_freq =
+ _ad9361->tune(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clipped_freq);
+ radio_control_impl::set_rx_frequency(coerced_freq, chan);
+ // Front-end switching
+ _set_atr_bits(chan);
+
+ return coerced_freq;
+}
+
+void e3xx_radio_control_impl::set_rx_agc(const bool enb, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("set_rx_agc(enb=" << enb << ", chan=" << chan << ")");
+ const std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan);
+ _ad9361->set_agc(rx_fe, enb);
+}
+
+double e3xx_radio_control_impl::set_rx_bandwidth(const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ double clipped_bw =
+ _ad9361->set_bw_filter(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), bandwidth);
+ return radio_control_impl::set_rx_bandwidth(clipped_bw, chan);
+}
+
+double e3xx_radio_control_impl::set_tx_bandwidth(const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ double clipped_bw =
+ _ad9361->set_bw_filter(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), bandwidth);
+ return radio_control_impl::set_tx_bandwidth(clipped_bw, chan);
+}
+
+double e3xx_radio_control_impl::set_tx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
+ double clip_gain = uhd::clip(gain, AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN);
+ _ad9361->set_gain(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clip_gain);
+ radio_control_impl::set_tx_gain(clip_gain, chan);
+ return clip_gain;
+}
+
+double e3xx_radio_control_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ UHD_ASSERT_THROW(chan < get_num_output_ports());
+ RFNOC_LOG_TRACE("set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
+ double clip_gain = uhd::clip(gain, AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN);
+ _ad9361->set_gain(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clip_gain);
+ radio_control_impl::set_rx_gain(clip_gain, chan);
+ return clip_gain;
+}
+
+std::vector<std::string> e3xx_radio_control_impl::get_tx_antennas(const size_t) const
+{
+ return {E3XX_DEFAULT_TX_ANTENNA};
+}
+
+std::vector<std::string> e3xx_radio_control_impl::get_rx_antennas(const size_t) const
+{
+ return E3XX_RX_ANTENNAS;
+}
+
+freq_range_t e3xx_radio_control_impl::get_tx_frequency_range(const size_t) const
+{
+ return freq_range_t(AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ, 1.0);
+}
+
+freq_range_t e3xx_radio_control_impl::get_rx_frequency_range(const size_t) const
+{
+ return freq_range_t(AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ, 1.0);
+}
+
+uhd::gain_range_t e3xx_radio_control_impl::get_tx_gain_range(const size_t) const
+{
+ return meta_range_t(AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN, AD9361_TX_GAIN_STEP);
+}
+
+uhd::gain_range_t e3xx_radio_control_impl::get_rx_gain_range(const size_t) const
+{
+ return meta_range_t(AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN, AD9361_RX_GAIN_STEP);
+}
+
+meta_range_t e3xx_radio_control_impl::get_tx_bandwidth_range(size_t) const
+{
+ return meta_range_t(AD9361_TX_MIN_BANDWIDTH, AD9361_TX_MAX_BANDWIDTH);
+}
+
+meta_range_t e3xx_radio_control_impl::get_rx_bandwidth_range(size_t) const
+{
+ return meta_range_t(AD9361_RX_MIN_BANDWIDTH, AD9361_RX_MAX_BANDWIDTH);
+}
+
+/**************************************************************************
+ * Calibration-Related API Calls
+ *************************************************************************/
+void e3xx_radio_control_impl::set_rx_dc_offset(const bool enb, size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("set_rx_dc_offset(enb=" << enb << ", chan=" << chan << ")");
+ const std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan);
+ _ad9361->set_dc_offset_auto(rx_fe, enb);
+}
+
+void e3xx_radio_control_impl::set_rx_iq_balance(const bool enb, size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("set_rx_iq_balance(enb=" << enb << ", chan=" << chan << ")");
+ const std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan);
+ _ad9361->set_iq_balance_auto(rx_fe, enb);
+}
+
+/**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+void e3xx_radio_control_impl::set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value)
+{
+ if (bank != get_gpio_banks().front()) {
+ RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank);
+ throw uhd::key_error("Invalid GPIO bank!");
+ }
+ if (!gpio_atr::gpio_attr_rev_map.count(attr)) {
+ RFNOC_LOG_ERROR("Invalid GPIO attr: " << attr);
+ throw uhd::key_error("Invalid GPIO attr!");
+ }
+
+ const gpio_atr::gpio_attr_t gpio_attr = gpio_atr::gpio_attr_rev_map.at(attr);
+
+ if (gpio_attr == gpio_atr::GPIO_READBACK) {
+ RFNOC_LOG_WARNING("Cannot set READBACK attr.");
+ return;
+ }
+
+ _fp_gpio->set_gpio_attr(gpio_attr, value);
+}
+
+uint32_t e3xx_radio_control_impl::get_gpio_attr(
+ const std::string& bank, const std::string& attr)
+{
+ if (bank != get_gpio_banks().front()) {
+ RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank);
+ throw uhd::key_error("Invalid GPIO bank!");
+ }
+
+ const gpio_atr::gpio_attr_t gpio_attr = gpio_atr::gpio_attr_rev_map.at(attr);
+ return _fp_gpio->get_attr_reg(gpio_attr);
+}
+
+/******************************************************************************
+ * Sensor API
+ *****************************************************************************/
+std::vector<std::string> e3xx_radio_control_impl::get_rx_sensor_names(const size_t) const
+{
+ return _rx_sensor_names;
+}
+
+uhd::sensor_value_t e3xx_radio_control_impl::get_rx_sensor(
+ const std::string& sensor_name, const size_t chan)
+{
+ return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
+ _rpc_prefix + "get_sensor", "RX", sensor_name, chan));
+}
+
+std::vector<std::string> e3xx_radio_control_impl::get_tx_sensor_names(const size_t) const
+{
+ return _tx_sensor_names;
+}
+
+uhd::sensor_value_t e3xx_radio_control_impl::get_tx_sensor(
+ const std::string& sensor_name, const size_t chan)
+{
+ return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
+ _rpc_prefix + "get_sensor", "TX", sensor_name, chan));
+}
+
+/* loopback_self_test checks the integrity of the FPGA->AD936x->FPGA sample interface.
+ The AD936x is put in loopback mode that sends the TX data unchanged to the RX side.
+ A test value is written to the codec_idle register in the TX side of the radio.
+ The readback register is then used to capture the values on the TX and RX sides
+ simultaneously for comparison. It is a reasonably effective test for AC timing
+ since I/Q Ch0/Ch1 alternate over the same wires. Note, however, that it uses
+ whatever timing is configured at the time the test is called rather than select
+ worst case conditions to stress the interface.
+ Note: This currently only tests 2R2T mode
+*/
+void e3xx_radio_control_impl::loopback_self_test(const size_t chan)
+{
+ // Save current rate before running this test
+ const double current_rate = this->get_rate();
+ // Set 2R2T mode, stream on all channels
+ this->set_streaming_mode(true, true, true, true);
+ // This was in there in the E320 code, but the comments didn't make sense:
+ //this->set_streaming_mode(true, true, true, true);
+ // Set maximum rate for 2R2T mode
+ /* FIXME
+ * We're directly setting the master clock rate here because we want to
+ * avoid property propagation, etc, and we know that we're going to set it
+ * back once we're done
+ * this->set_rate(30.72e6);
+ */
+ _ad9361->set_clock_rate(30.72e6);
+ // Put AD936x in loopback mode
+ _ad9361->data_port_loopback(true);
+ RFNOC_LOG_INFO("Performing CODEC loopback test... ");
+ size_t hash = size_t(time(NULL));
+ constexpr size_t loopback_count = 100;
+
+ // Allow some time for AD936x to enter loopback mode.
+ // There is no clear statement in the documentation of how long it takes,
+ // but UG-570 does say to "allow six ADC_CLK/64 clock cycles of flush time"
+ // when leaving the TX or RX states. That works out to ~75us at the
+ // minimum clock rate of 5 MHz, which lines up with test results.
+ // Sleeping 1ms is far more than enough.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ for (size_t i = 0; i < loopback_count; i++) {
+ // Create test word
+ boost::hash_combine(hash, i);
+ const uint32_t word32 = uint32_t(hash) & 0xfff0fff0;
+ // Write test word to codec_idle idle register (on TX side)
+ regs().poke32(
+ regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_TX_IDLE_VALUE, word32);
+
+ // Read back values - TX is lower 32-bits and RX is upper 32-bits
+ const uint32_t rb_tx =
+ regs().peek32(regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_TX_IDLE_VALUE);
+ const uint32_t rb_rx =
+ regs().peek32(regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_RX_DATA);
+
+ // Compare TX and RX values to test word
+ bool test_fail = word32 != rb_tx or word32 != rb_rx;
+ if (test_fail) {
+ RFNOC_LOG_WARNING(
+ "CODEC loopback test failed! "
+ << boost::format("Expected: 0x%08X Received (TX/RX): 0x%08X/0x%08X")
+ % word32 % rb_tx % rb_rx);
+ throw uhd::runtime_error("CODEC loopback test failed.");
+ }
+ }
+ RFNOC_LOG_INFO("CODEC loopback test passed");
+
+ // Zero out the idle data.
+ regs().poke32(regmap::RADIO_BASE_ADDR + chan * regmap::REG_CHAN_OFFSET + regmap::REG_TX_IDLE_VALUE, 0);
+
+ // Take AD936x out of loopback mode
+ _ad9361->data_port_loopback(false);
+ this->set_streaming_mode(true, false, true, false);
+ // Switch back to current rate
+ // FIXME along with the other comment above
+ // this->set_rate(current_rate);
+ _ad9361->set_clock_rate(current_rate);
+}
+
+void e3xx_radio_control_impl::_identify_with_leds(const int identify_duration)
+{
+ RFNOC_LOG_INFO(
+ "Running LED identification process for " << identify_duration << " seconds.");
+ auto end_time =
+ std::chrono::steady_clock::now() + std::chrono::seconds(identify_duration);
+ bool led_state = true;
+ while (std::chrono::steady_clock::now() < end_time) {
+ // Add update_leds
+ led_state = !led_state;
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ }
+}
+
+void e3xx_radio_control_impl::_set_atr_bits(const size_t chan)
+{
+ const auto rx_freq = radio_control_impl::get_rx_frequency(chan);
+ const auto tx_freq = radio_control_impl::get_tx_frequency(chan);
+ const auto rx_ant = radio_control_impl::get_rx_antenna(chan);
+ const uint32_t rx_regs = this->get_rx_switches(chan, rx_freq, rx_ant);
+ const uint32_t tx_regs = this->get_tx_switches(chan, tx_freq);
+ const uint32_t idle_regs = this->get_idle_switches();
+
+ _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_regs);
+ _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_RX_ONLY, rx_regs);
+ _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_regs);
+ _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_regs | tx_regs);
+
+ // The LED signal names are reversed, but are consistent with the schematic
+ const bool is_txrx = rx_ant == "TX/RX";
+ const int idle_led = 0;
+ const int rx_led = this->get_rx_led();
+ const int tx_led = this->get_tx_led();
+ const int txrx_led = this->get_txrx_led();
+
+ _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_led);
+ _leds_gpio[chan]->set_atr_reg(
+ usrp::gpio_atr::ATR_REG_RX_ONLY, is_txrx ? txrx_led : rx_led);
+ _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_led);
+ _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_led | tx_led);
+}
+
+void e3xx_radio_control_impl::set_db_eeprom(const eeprom_map_t& db_eeprom)
+{
+ _rpcc->notify_with_token("set_db_eeprom", 0, db_eeprom);
+}
+
+eeprom_map_t e3xx_radio_control_impl::get_db_eeprom()
+{
+ return _rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", 0);
+}
+
+/**************************************************************************
+ * Filter API
+ *************************************************************************/
+std::vector<std::string> e3xx_radio_control_impl::get_rx_filter_names(const size_t) const
+{
+ return _rx_filter_names;
+}
+
+uhd::filter_info_base::sptr e3xx_radio_control_impl::get_rx_filter(
+ const std::string& name, const size_t chan)
+{
+ return _ad9361->get_filter(
+ get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), name);
+}
+
+void e3xx_radio_control_impl::set_rx_filter(
+ const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan)
+{
+ _ad9361->set_filter(
+ get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), name, filter);
+}
+
+std::vector<std::string> e3xx_radio_control_impl::get_tx_filter_names(const size_t) const
+{
+ return _tx_filter_names;
+}
+
+uhd::filter_info_base::sptr e3xx_radio_control_impl::get_tx_filter(
+ const std::string& name, const size_t chan)
+{
+ return _ad9361->get_filter(
+ get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), name);
+}
+
+void e3xx_radio_control_impl::set_tx_filter(
+ const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan)
+{
+ _ad9361->set_filter(
+ get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), name, filter);
+}
+
+
+/**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+size_t e3xx_radio_control_impl::get_chan_from_dboard_fe(
+ const std::string& fe, const uhd::direction_t) const
+{
+ // A and B are available here for backward compat
+ if (fe == "A" || fe == "0") {
+ return 0;
+ }
+ if (fe == "B" || fe == "1") {
+ return 1;
+ }
+ throw uhd::key_error(std::string("[E3xx] Invalid frontend: ") + fe);
+}
+
+std::string e3xx_radio_control_impl::get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t) const
+{
+ if (chan == 0) {
+ return "0";
+ }
+ if (chan == 1) {
+ return "1";
+ }
+ throw uhd::lookup_error(
+ std::string("[E3xx] Invalid channel: ") + std::to_string(chan));
+}
+
+std::string e3xx_radio_control_impl::get_fe_name(
+ const size_t, const uhd::direction_t) const
+{
+ return "E3xx";
+}
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.hpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.hpp
new file mode 100644
index 000000000..f49cde64a
--- /dev/null
+++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_impl.hpp
@@ -0,0 +1,293 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP
+#define INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP
+
+#include "e3xx_ad9361_iface.hpp"
+#include <uhd/types/eeprom.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+#include <uhd/rfnoc/filter_node.hpp>
+#include <uhdlib/rfnoc/radio_control_impl.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
+#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
+#include <mutex>
+
+namespace uhd { namespace rfnoc {
+
+namespace e3xx_regs {
+ constexpr uint32_t PERIPH_BASE = 0x80000;
+ constexpr uint32_t PERIPH_REG_OFFSET = 8;
+ constexpr uint32_t PERIPH_REG_CHAN_OFFSET = 0x800;
+ constexpr uint32_t SR_LEDS = PERIPH_BASE + 176 * PERIPH_REG_OFFSET;
+ constexpr uint32_t SR_FP_GPIO = PERIPH_BASE + 184 * PERIPH_REG_OFFSET;
+ constexpr uint32_t SR_DB_GPIO = PERIPH_BASE + 192 * PERIPH_REG_OFFSET;
+
+ constexpr uint32_t RB_DB_GPIO = PERIPH_BASE + 19 * PERIPH_REG_OFFSET;
+ constexpr uint32_t RB_FP_GPIO = PERIPH_BASE + 20 * PERIPH_REG_OFFSET;
+
+
+}
+
+/*! \brief Provide access to an E3xx radio.
+ */
+class e3xx_radio_control_impl : public radio_control_impl, public uhd::rfnoc::detail::filter_node
+{
+public:
+ //! Frequency bands for RX. Bands are a function of the analog filter banks
+ enum class rx_band { INVALID_BAND, LB_B2, LB_B3, LB_B4, LB_B5, LB_B6, LB_B7, HB };
+
+ //! Frequency bands for TX. Bands are a function of the analog filter banks
+ enum class tx_band {
+ INVALID_BAND,
+ LB_80,
+ LB_160,
+ LB_225,
+ LB_400,
+ LB_575,
+ LB_1000,
+ LB_1700,
+ LB_2750,
+ HB
+ };
+
+ /**************************************************************************
+ * ATR/ Switches Types
+ *************************************************************************/
+ //! ATR state
+ enum atr_state_t { IDLE, RX_ONLY, TX_ONLY, FULL_DUPLEX };
+
+ //! Channel select:
+ enum chan_sel_t { CHAN1, CHAN2, BOTH };
+
+ /************************************************************************
+ * Structors
+ ***********************************************************************/
+ e3xx_radio_control_impl(make_args_ptr make_args);
+ virtual ~e3xx_radio_control_impl();
+
+ /************************************************************************
+ * node_t && noc_block_base API calls
+ ***********************************************************************/
+ void deinit();
+
+ bool check_topology(const std::vector<size_t>& connected_inputs,
+ const std::vector<size_t>& connected_outputs);
+
+ /************************************************************************
+ * radio_control API calls
+ ***********************************************************************/
+ double set_rate(const double rate);
+ uhd::meta_range_t get_rate_range() const;
+
+ // Setters
+ void set_tx_antenna(const std::string &ant, const size_t chan);
+ void set_rx_antenna(const std::string &ant, const size_t chan);
+ double set_tx_frequency(const double freq, const size_t chan);
+ double set_rx_frequency(const double freq, const size_t chan);
+ double set_tx_gain(const double gain, const size_t chan);
+ double set_rx_gain(const double gain, const size_t chan);
+ void set_rx_agc(const bool enable, const size_t chan);
+ double set_tx_bandwidth(const double bandwidth, const size_t chan);
+ double set_rx_bandwidth(const double bandwidth, const size_t chan);
+
+ // Getters
+ std::vector<std::string> get_tx_antennas(const size_t chan) const;
+ std::vector<std::string> get_rx_antennas(const size_t chan) const;
+ uhd::freq_range_t get_tx_frequency_range(const size_t chan) const;
+ uhd::freq_range_t get_rx_frequency_range(const size_t chan) const;
+ uhd::gain_range_t get_tx_gain_range(const size_t) const;
+ uhd::gain_range_t get_rx_gain_range(const size_t) const;
+ meta_range_t get_tx_bandwidth_range(size_t chan) const;
+ meta_range_t get_rx_bandwidth_range(size_t chan) const;
+
+ /**************************************************************************
+ * Calibration-Related API Calls
+ *************************************************************************/
+ virtual void set_rx_dc_offset(const bool enb, size_t chan = ALL_CHANS);
+ virtual void set_rx_iq_balance(const bool enb, size_t chan);
+
+ /**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+ virtual void set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value);
+ virtual uint32_t get_gpio_attr(const std::string& bank, const std::string& attr);
+
+ /**************************************************************************
+ * Sensor API
+ *************************************************************************/
+ std::vector<std::string> get_rx_sensor_names(size_t chan) const;
+ uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan);
+ std::vector<std::string> get_tx_sensor_names(size_t chan) const;
+ uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan);
+
+ /**************************************************************************
+ * Filter API
+ *************************************************************************/
+ std::vector<std::string> get_rx_filter_names(const size_t chan) const;
+ uhd::filter_info_base::sptr get_rx_filter(const std::string& name, const size_t chan);
+ void set_rx_filter(
+ const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan);
+
+ std::vector<std::string> get_tx_filter_names(const size_t chan) const;
+ uhd::filter_info_base::sptr get_tx_filter(const std::string& name, const size_t chan);
+ void set_tx_filter(
+ const std::string& name, uhd::filter_info_base::sptr filter, const size_t chan);
+
+ /**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+ std::string get_slot_name() const { return "A"; }
+ virtual size_t get_chan_from_dboard_fe(
+ const std::string& fe, const uhd::direction_t direction) const;
+ virtual std::string get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t direction) const;
+ virtual std::string get_fe_name(
+ const size_t chan, const uhd::direction_t direction) const;
+
+protected:
+ //! Map a frequency in Hz to an rx_band value. Will return
+ // rx_band::INVALID_BAND if the frequency is out of range.
+ rx_band map_freq_to_rx_band(const double freq);
+ //! Map a frequency in Hz to an tx_band value. Will return
+ // tx_band::INVALID_BAND if the frequency is out of range.
+ tx_band map_freq_to_tx_band(const double freq);
+
+ virtual const std::string get_default_timing_mode() = 0;
+
+ /*! Run a loopback self test.
+ *
+ * This will write data to the AD936x and read it back again.
+ * If this test fails, it generally means the interface is broken,
+ * so we assume it passes and throw otherwise. Running this requires
+ * a core that we can peek and poke the loopback values into.
+ *
+ * \param iface An interface to the associated radio control core
+ * \param iface The radio control core's address to write the loopback value
+ * \param iface The radio control core's readback address to read back the returned
+ * value
+ *
+ * \throws a uhd::runtime_error if the loopback value didn't match.
+ */
+ void loopback_self_test(const size_t chan);
+
+ virtual uint32_t get_rx_switches(
+ const size_t chan, const double freq, const std::string& ant) = 0;
+
+ virtual uint32_t get_tx_switches(const size_t chan, const double freq) = 0;
+
+ virtual uint32_t get_idle_switches() = 0;
+
+ virtual uint32_t get_tx_led() = 0;
+ virtual uint32_t get_rx_led() = 0;
+ virtual uint32_t get_txrx_led() = 0;
+ virtual uint32_t get_idle_led() = 0;
+
+ //! Reference to the AD9361 controls
+ // e3xx_ad9361_iface::uptr _ad9361;
+ ad9361_ctrl::sptr _ad9361;
+
+ //! Swap RFA and RFB for catalina
+ bool _fe_swap;
+
+ //! Init RPC-related items
+ void _init_mpm();
+
+private:
+ /**************************************************************************
+ * Helpers
+ *************************************************************************/
+ //! Initialize all the peripherals connected to this block
+ void _init_peripherals();
+
+ //! Set state of this class to sensible defaults
+ void _init_defaults();
+
+ //! Init a subtree for the RF frontends
+ void _init_frontend_subtree(uhd::property_tree::sptr subtree, const size_t chan_idx);
+
+ //! Initialize Catalina defaults
+ void _init_codec();
+
+ //! Initialize property tree
+ void _init_prop_tree();
+
+ //! Set streaming mode - active chains, channel_mode, timing_mode
+ void set_streaming_mode(
+ const bool tx1, const bool tx2, const bool rx1, const bool rx2);
+
+ //! Set which channel mode is used
+ void set_channel_mode(const std::string& channel_mode);
+
+ /**************************************************************************
+ * Misc Controls
+ *************************************************************************/
+ //! Blink the front-panel LEDs for \p identify_duration,
+ // and resume normal operation.
+ void _identify_with_leds(const int identify_duration);
+
+ void _set_atr_bits(const size_t chan);
+
+ void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom);
+
+ uhd::eeprom_map_t get_db_eeprom();
+
+ /**************************************************************************
+ * Private attributes
+ *************************************************************************/
+ //! Locks access to setter APIs
+ mutable std::mutex _set_lock;
+
+ //! Prepended for all dboard RPC calls
+ std::string _rpc_prefix = "db_0_";
+
+ //! Reference to the MB controller
+ uhd::rfnoc::mpmd_mb_controller::sptr _e3xx_mb_control;
+
+ //! Reference to the MB timekeeper
+ uhd::rfnoc::mpmd_mb_controller::mpmd_timekeeper::sptr _e3xx_timekeeper;
+
+ //! Reference to wb_iface adapters
+ std::vector<uhd::timed_wb_iface::sptr> _wb_ifaces;
+
+ //! Reference to the RPC client
+ uhd::rpc_client::sptr _rpcc;
+
+ //! ATR controls. These control the AD9361 gain up/down bits.
+ // Every radio channel gets its own ATR state register.
+ std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _db_gpio;
+
+ // ATR controls for LEDs
+ // Every radio channel gets its own ATR state register.
+ std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _leds_gpio;
+
+ //! Front panel GPIO controller. Note that only one radio block per
+ // module can be the FP-GPIO master.
+ usrp::gpio_atr::gpio_atr_3000::sptr _fp_gpio;
+
+ //! Sampling rate
+ double _master_clock_rate = 1.0;
+
+ //! RX sensor names (they get cached)
+ std::vector<std::string> _rx_sensor_names;
+
+ //! TX sensor names (they get cached)
+ std::vector<std::string> _tx_sensor_names;
+
+ //! RX filter names (they get cached)
+ std::vector<std::string> _rx_filter_names;
+
+ //! TX filter names (they get cached)
+ std::vector<std::string> _tx_filter_names;
+};
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP */
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp
new file mode 100644
index 000000000..f97feeb68
--- /dev/null
+++ b/host/lib/usrp/dboard/e3xx/e3xx_radio_control_init.cpp
@@ -0,0 +1,305 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "e3xx_constants.hpp"
+#include "e3xx_radio_control_impl.hpp"
+#include <uhd/transport/chdr.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/rfnoc/reg_iface_adapter.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <string>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::rfnoc;
+
+void e3xx_radio_control_impl::_init_defaults()
+{
+ RFNOC_LOG_TRACE("Initializing defaults...");
+ const size_t num_rx_chans = get_num_output_ports();
+ const size_t num_tx_chans = get_num_input_ports();
+
+ RFNOC_LOG_TRACE(
+ "Num TX chans: " << num_tx_chans << " Num RX chans: " << num_rx_chans);
+
+
+ // Note: MCR gets set during the init() call (prior to this), which takes
+ // in arguments from the device args. So if block_args contains a
+ // master_clock_rate key, then it should better be whatever the device is
+ // configured to do.
+ auto block_args = get_block_args();
+ _master_clock_rate =
+ _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate");
+ const double block_args_mcr =
+ block_args.cast<double>("master_clock_rate", _master_clock_rate);
+ if (block_args_mcr != _master_clock_rate) {
+ throw uhd::runtime_error(
+ str(boost::format("Master clock rate mismatch. Device returns %f MHz, "
+ "but should have been %f MHz.")
+ % (_master_clock_rate / 1e6) % (block_args_mcr / 1e6)));
+ }
+ RFNOC_LOG_DEBUG("Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz.");
+ set_tick_rate(_master_clock_rate);
+ _e3xx_timekeeper->update_tick_rate(_master_clock_rate);
+ radio_control_impl::set_rate(_master_clock_rate);
+ for (size_t chan = 0; chan < num_rx_chans; chan++) {
+ radio_control_impl::set_rx_frequency(E3XX_DEFAULT_FREQ, chan);
+ radio_control_impl::set_rx_gain(E3XX_DEFAULT_GAIN, chan);
+ radio_control_impl::set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan);
+ radio_control_impl::set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
+ }
+
+ for (size_t chan = 0; chan < num_tx_chans; chan++) {
+ radio_control_impl::set_tx_frequency(E3XX_DEFAULT_FREQ, chan);
+ radio_control_impl::set_tx_gain(E3XX_DEFAULT_GAIN, chan);
+ radio_control_impl::set_tx_antenna(E3XX_DEFAULT_TX_ANTENNA, chan);
+ radio_control_impl::set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
+ }
+
+ _rx_sensor_names = _rpcc->request_with_token<std::vector<std::string>>(
+ this->_rpc_prefix + "get_sensors", "RX");
+ _tx_sensor_names = _rpcc->request_with_token<std::vector<std::string>>(
+ this->_rpc_prefix + "get_sensors", "TX");
+
+ // Cache the filter names
+ // FIXME: Uncomment this
+ //_rx_filter_names = _ad9361->get_filter_names(
+ // get_which_ad9361_chain(RX_DIRECTION, 0, _fe_swap));
+ //_tx_filter_names = _ad9361->get_filter_names(
+ // get_which_ad9361_chain(TX_DIRECTION, 0, _fe_swap));
+}
+
+void e3xx_radio_control_impl::_init_peripherals()
+{
+ RFNOC_LOG_TRACE("Initializing peripherals...");
+ for (size_t radio_idx = 0; radio_idx < E3XX_NUM_CHANS; radio_idx++) {
+ _wb_ifaces.push_back(RFNOC_MAKE_WB_IFACE(0, radio_idx));
+ }
+ _db_gpio.clear(); // Following the as-if rule, this can get optimized out
+ for (size_t radio_idx = 0; radio_idx < E3XX_NUM_CHANS; radio_idx++) {
+ RFNOC_LOG_TRACE("Initializing DB GPIOs for channel " << radio_idx);
+ // Note: The register offset is baked into the different _wb_iface
+ // objects!
+ _db_gpio.emplace_back(
+ usrp::gpio_atr::gpio_atr_3000::make_write_only(_wb_ifaces.at(radio_idx),
+ e3xx_regs::SR_DB_GPIO + (radio_idx * e3xx_regs::PERIPH_REG_CHAN_OFFSET),
+ e3xx_regs::PERIPH_REG_OFFSET));
+ _db_gpio[radio_idx]->set_atr_mode(
+ usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ }
+ _leds_gpio.clear(); // Following the as-if rule, this can get optimized out
+ for (size_t radio_idx = 0; radio_idx < E3XX_NUM_CHANS; radio_idx++) {
+ RFNOC_LOG_TRACE("Initializing LED GPIOs for channel " << radio_idx);
+ _leds_gpio.emplace_back(
+ usrp::gpio_atr::gpio_atr_3000::make_write_only(_wb_ifaces.at(radio_idx),
+ e3xx_regs::SR_LEDS + (radio_idx * e3xx_regs::PERIPH_REG_CHAN_OFFSET),
+ e3xx_regs::PERIPH_REG_OFFSET));
+ _leds_gpio[radio_idx]->set_atr_mode(
+ usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ }
+ RFNOC_LOG_TRACE("Initializing front-panel GPIO control...")
+ _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make(
+ _wb_ifaces.at(0), e3xx_regs::SR_FP_GPIO, e3xx_regs::RB_FP_GPIO, e3xx_regs::PERIPH_REG_OFFSET);
+
+
+ auto block_args = get_block_args();
+ if (block_args.has_key("identify")) {
+ const std::string identify_val = block_args.get("identify");
+ int identify_duration = std::atoi(identify_val.c_str());
+ if (identify_duration == 0) {
+ identify_duration = 5;
+ }
+ _identify_with_leds(identify_duration);
+ }
+}
+
+void e3xx_radio_control_impl::_init_frontend_subtree(
+ uhd::property_tree::sptr subtree, const size_t chan_idx)
+{
+ const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx;
+ const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx;
+ RFNOC_LOG_TRACE(
+ "Adding non-RFNoC block properties for channel "
+ << chan_idx << " to prop tree path " << tx_fe_path << " and " << rx_fe_path);
+ // TX Standard attributes
+ subtree->create<std::string>(tx_fe_path / "name").set("E3xx");
+ subtree->create<std::string>(tx_fe_path / "connection").set("IQ");
+ // RX Standard attributes
+ subtree->create<std::string>(rx_fe_path / "name").set("E3xx");
+ subtree->create<std::string>(rx_fe_path / "connection").set("IQ");
+ // TX Antenna
+ subtree->create<std::string>(tx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
+ this->set_tx_antenna(ant, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); });
+ subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
+ .set({E3XX_DEFAULT_TX_ANTENNA})
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ });
+ // RX Antenna
+ subtree->create<std::string>(rx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
+ this->set_rx_antenna(ant, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); });
+ subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
+ .set(E3XX_RX_ANTENNAS)
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ });
+ // TX frequency
+ subtree->create<double>(tx_fe_path / "freq" / "value")
+ .set_coercer([this, chan_idx](const double freq) {
+ return this->set_tx_frequency(freq, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); });
+ subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
+ .set_publisher([this]() { return get_tx_frequency_range(0); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ });
+ // RX frequency
+ subtree->create<double>(rx_fe_path / "freq" / "value")
+ .set_coercer([this, chan_idx](const double freq) {
+ return this->set_rx_frequency(freq, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); });
+ subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
+ .set_publisher([this]() { return get_rx_frequency_range(0); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ });
+ // TX bandwidth
+ subtree->create<double>(tx_fe_path / "bandwidth" / "value")
+ .set_publisher([this, chan_idx]() { return get_tx_bandwidth(chan_idx); })
+ .set_coercer([this, chan_idx](const double bw) {
+ return this->set_tx_bandwidth(bw, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); });
+ subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
+ .set_publisher([this]() { return get_tx_bandwidth_range(0); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ });
+ // RX bandwidth
+ subtree->create<double>(rx_fe_path / "bandwidth" / "value")
+ .set_publisher([this, chan_idx]() { return get_rx_bandwidth(chan_idx); })
+ .set_coercer([this, chan_idx](const double bw) {
+ return this->set_rx_bandwidth(bw, chan_idx);
+ });
+ subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
+ .set_publisher([this]() { return get_rx_bandwidth_range(0); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ });
+
+ // TX gains
+ const std::vector<std::string> tx_gain_names = ad9361_ctrl::get_gain_names("TX1");
+ for (auto tx_gain_name : tx_gain_names) {
+ subtree->create<double>(tx_fe_path / "gains" / tx_gain_name / "value")
+ .set_coercer([this, chan_idx](const double gain) {
+ return this->set_tx_gain(gain, chan_idx);
+ })
+ .set_publisher(
+ [this, chan_idx]() { return radio_control_impl::get_tx_gain(chan_idx); });
+ subtree->create<meta_range_t>(tx_fe_path / "gains" / tx_gain_name / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update gain range!");
+ })
+ .set_publisher([this]() {
+ return meta_range_t(
+ AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN, AD9361_TX_GAIN_STEP);
+ });
+ }
+
+ // RX gains
+ const std::vector<std::string> rx_gain_names = ad9361_ctrl::get_gain_names("RX1");
+ for (auto rx_gain_name : rx_gain_names) {
+ subtree->create<double>(rx_fe_path / "gains" / rx_gain_name / "value")
+ .set_coercer([this, chan_idx](const double gain) {
+ return this->set_rx_gain(gain, chan_idx);
+ })
+ .set_publisher(
+ [this, chan_idx]() { return radio_control_impl::get_rx_gain(chan_idx); });
+
+ subtree->create<meta_range_t>(rx_fe_path / "gains" / rx_gain_name / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update gain range!");
+ })
+ .set_publisher([this]() {
+ return meta_range_t(
+ AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN, AD9361_RX_GAIN_STEP);
+ });
+ }
+
+ auto rx_sensor_names = get_rx_sensor_names(chan_idx);
+ for (const auto& rx_sensor_name : rx_sensor_names) {
+ RFNOC_LOG_TRACE("Adding RX sensor " << rx_sensor_name);
+ get_tree()->create<sensor_value_t>(rx_fe_path / "sensors" / rx_sensor_name)
+ .add_coerced_subscriber([](const sensor_value_t&) {
+ throw uhd::runtime_error("Attempting to write to sensor!");
+ })
+ .set_publisher([this, rx_sensor_name, chan_idx]() {
+ return get_rx_sensor(rx_sensor_name, chan_idx);
+ });
+ }
+ auto tx_sensor_names = get_tx_sensor_names(chan_idx);
+ for (const auto& tx_sensor_name : tx_sensor_names) {
+ RFNOC_LOG_TRACE("Adding TX sensor " << tx_sensor_name);
+ get_tree()->create<sensor_value_t>(tx_fe_path / "sensors" / tx_sensor_name)
+ .add_coerced_subscriber([](const sensor_value_t&) {
+ throw uhd::runtime_error("Attempting to write to sensor!");
+ })
+ .set_publisher([this, tx_sensor_name, chan_idx]() {
+ return get_tx_sensor(tx_sensor_name, chan_idx);
+ });
+ }
+}
+
+void e3xx_radio_control_impl::_init_prop_tree()
+{
+ for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; chan_idx++) {
+ this->_init_frontend_subtree(get_tree()->subtree(DB_PATH), chan_idx);
+ }
+ get_tree()->create<std::string>("rx_codec/name").set("AD9361 Dual ADC");
+ get_tree()->create<std::string>("tx_codec/name").set("AD9361 Dual DAC");
+}
+
+void e3xx_radio_control_impl::_init_mpm()
+{
+ // Initialize catalina
+ _init_codec();
+
+ // Loopback test
+ for (size_t chan = 0; chan < E3XX_NUM_CHANS; chan++) {
+ loopback_self_test(chan);
+ }
+}
+
+void e3xx_radio_control_impl::_init_codec()
+{
+ RFNOC_LOG_TRACE("Setting Catalina Defaults... ");
+ for (size_t chan = 0; chan < E3XX_NUM_CHANS; chan++) {
+ std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan);
+ this->set_rx_gain(E3XX_DEFAULT_GAIN, chan);
+ this->set_rx_frequency(E3XX_DEFAULT_FREQ, chan);
+ this->set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan);
+ this->set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
+ _ad9361->set_dc_offset_auto(rx_fe, E3XX_DEFAULT_AUTO_DC_OFFSET);
+ _ad9361->set_iq_balance_auto(rx_fe, E3XX_DEFAULT_AUTO_IQ_BALANCE);
+ _ad9361->set_agc(rx_fe, E3XX_DEFAULT_AGC_ENABLE);
+ std::string tx_fe = get_which_ad9361_chain(TX_DIRECTION, chan);
+ this->set_tx_gain(E3XX_DEFAULT_GAIN, chan);
+ this->set_tx_frequency(E3XX_DEFAULT_FREQ, chan);
+ this->set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
+ }
+}
+
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp
deleted file mode 100644
index 989b73b82..000000000
--- a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "e3xx_radio_ctrl_impl.hpp"
-#include "e3xx_constants.hpp"
-#include <uhd/rfnoc/node_ctrl_base.hpp>
-#include <uhd/transport/chdr.hpp>
-#include <uhd/types/direction.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/utils/math.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/format.hpp>
-#include <boost/make_shared.hpp>
-#include <cmath>
-#include <cstdlib>
-#include <sstream>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-using namespace uhd::math::fp_compare;
-
-/******************************************************************************
- * Structors
- *****************************************************************************/
-e3xx_radio_ctrl_impl::e3xx_radio_ctrl_impl()
-{
- UHD_LOG_TRACE(unique_id(), "Entering e3xx_radio_ctrl_impl ctor...");
- const char radio_slot_name[1] = {'A'};
- _radio_slot = radio_slot_name[get_block_id().get_block_count()];
- UHD_LOG_TRACE(unique_id(), "Radio slot: " << _radio_slot);
- _rpc_prefix = "db_0_";
-
- _init_defaults();
- _init_peripherals();
- _init_prop_tree();
-}
-
-e3xx_radio_ctrl_impl::~e3xx_radio_ctrl_impl()
-{
- UHD_LOG_TRACE(unique_id(), "e3xx_radio_ctrl_impl::dtor() ");
-}
-
-
-/******************************************************************************
- * API Calls
- *****************************************************************************/
-
-void e3xx_radio_ctrl_impl::set_streaming_mode(
- const bool tx1, const bool tx2, const bool rx1, const bool rx2)
-{
- UHD_LOG_TRACE(unique_id(), "Setting up streaming ...")
- const size_t num_rx = rx1 + rx2;
- const size_t num_tx = tx1 + tx2;
-
- // setup the active chains in the codec
- if ((num_rx + num_tx) == 0) {
- // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock
- _ad9361->set_active_chains(true, false, true, false);
- } else {
- // setup the active chains in the codec
- _ad9361->set_active_chains(tx1, tx2, rx1, rx2);
- }
-
- // setup 1R1T/2R2T mode in catalina and fpga
- // The Catalina interface in the fpga needs to know which TX channel to use for
- // the data on the LVDS lines.
- if ((num_rx == 2) or (num_tx == 2)) {
- // AD9361 is in 1R1T mode
- _ad9361->set_timing_mode(this->get_default_timing_mode());
- this->set_channel_mode(MIMO);
- } else {
- // AD9361 is in 1R1T mode
- _ad9361->set_timing_mode(TIMING_MODE_1R1T);
-
- // Set to SIS0_TX1 if we're using the second TX antenna, otherwise
- // default to SISO_TX0
- this->set_channel_mode(tx2 ? SISO_TX1 : SISO_TX0);
- }
-}
-
-void e3xx_radio_ctrl_impl::set_channel_mode(const std::string& channel_mode)
-{
- // MIMO for 2R2T mode for 2 channels
- // SISO_TX1 for 1R1T mode for 1 channel - TX1
- // SISO_TX0 for 1R1T mode for 1 channel - TX0
-
- _rpcc->request_with_token<void>("set_channel_mode", channel_mode);
-}
-
-double e3xx_radio_ctrl_impl::set_rate(const double rate)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_DEBUG(unique_id(), "Asking for clock rate " << rate / 1e6 << " MHz\n");
- double actual_tick_rate = _ad9361->set_clock_rate(rate);
- UHD_LOG_DEBUG(
- unique_id(), "Actual clock rate " << actual_tick_rate / 1e6 << " MHz\n");
-
- radio_ctrl_impl::set_rate(rate);
- return rate;
-}
-
-void e3xx_radio_ctrl_impl::set_tx_antenna(const std::string& ant, const size_t chan)
-{
- if (ant != get_tx_antenna(chan)) {
- throw uhd::value_error(
- str(boost::format("[%s] Requesting invalid TX antenna value: %s")
- % unique_id() % ant));
- }
- radio_ctrl_impl::set_tx_antenna(ant, chan);
- // We can't actually set the TX antenna, so let's stop here.
-}
-
-void e3xx_radio_ctrl_impl::set_rx_antenna(const std::string& ant, const size_t chan)
-{
- UHD_ASSERT_THROW(chan <= E3XX_NUM_CHANS);
- if (std::find(E3XX_RX_ANTENNAS.begin(), E3XX_RX_ANTENNAS.end(), ant)
- == E3XX_RX_ANTENNAS.end()) {
- throw uhd::value_error(
- str(boost::format("[%s] Requesting invalid RX antenna value: %s")
- % unique_id() % ant));
- }
- UHD_LOG_TRACE(unique_id(), "Setting RX antenna to " << ant << " for chan " << chan);
-
- radio_ctrl_impl::set_rx_antenna(ant, chan);
- _set_atr_bits(chan);
-}
-
-double e3xx_radio_ctrl_impl::set_tx_frequency(const double freq, const size_t chan)
-{
- UHD_LOG_TRACE(unique_id(), "set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
- std::lock_guard<std::mutex> l(_set_lock);
-
- double clipped_freq = uhd::clip(freq, AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ);
-
- double coerced_freq =
- _ad9361->tune(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clipped_freq);
- radio_ctrl_impl::set_tx_frequency(coerced_freq, chan);
- // Front-end switching
- _set_atr_bits(chan);
-
- return coerced_freq;
-}
-
-double e3xx_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan)
-{
- UHD_LOG_TRACE(unique_id(), "set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
- std::lock_guard<std::mutex> l(_set_lock);
-
- double clipped_freq = uhd::clip(freq, AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ);
-
- double coerced_freq =
- _ad9361->tune(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clipped_freq);
- radio_ctrl_impl::set_rx_frequency(coerced_freq, chan);
- // Front-end switching
- _set_atr_bits(chan);
-
- return coerced_freq;
-}
-
-double e3xx_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- double clipped_bw =
- _ad9361->set_bw_filter(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), bandwidth);
- return radio_ctrl_impl::set_rx_bandwidth(clipped_bw, chan);
-}
-
-double e3xx_radio_ctrl_impl::set_tx_bandwidth(const double bandwidth, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- double clipped_bw =
- _ad9361->set_bw_filter(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), bandwidth);
- return radio_ctrl_impl::set_tx_bandwidth(clipped_bw, chan);
-}
-
-double e3xx_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(), "set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
- double clip_gain = uhd::clip(gain, AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN);
- _ad9361->set_gain(get_which_ad9361_chain(TX_DIRECTION, chan, _fe_swap), clip_gain);
- radio_ctrl_impl::set_tx_gain(clip_gain, chan);
- return clip_gain;
-}
-
-double e3xx_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(), "set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
- double clip_gain = uhd::clip(gain, AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN);
- _ad9361->set_gain(get_which_ad9361_chain(RX_DIRECTION, chan, _fe_swap), clip_gain);
- radio_ctrl_impl::set_rx_gain(clip_gain, chan);
- return clip_gain;
-}
-
-size_t e3xx_radio_ctrl_impl::get_chan_from_dboard_fe(
- const std::string& fe, const direction_t /* dir */
-)
-{
- const size_t chan = boost::lexical_cast<size_t>(fe);
- if (chan > _get_num_radios() - 1) {
- UHD_LOG_WARNING(unique_id(),
- boost::format("Invalid channel determined from dboard frontend %s.") % fe);
- }
- return chan;
-}
-
-std::string e3xx_radio_ctrl_impl::get_dboard_fe_from_chan(
- const size_t chan, const direction_t /* dir */
-)
-{
- return std::to_string(chan);
-}
-
-void e3xx_radio_ctrl_impl::set_rpc_client(
- uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args)
-{
- _rpcc = rpcc;
- _block_args = block_args;
- UHD_LOG_TRACE(unique_id(), "Instantiating AD9361 control object...");
- _ad9361 = make_rpc(_rpcc);
-
- UHD_LOG_TRACE(unique_id(), "Setting Catalina Defaults... ");
- // Initialize catalina
- this->_init_codec();
-
- if (block_args.has_key("identify")) {
- const std::string identify_val = block_args.get("identify");
- int identify_duration = std::atoi(identify_val.c_str());
- if (identify_duration == 0) {
- identify_duration = 5;
- }
- UHD_LOG_INFO(unique_id(),
- "Running LED identification process for " << identify_duration
- << " seconds.");
- _identify_with_leds(identify_duration);
- }
- // Note: MCR gets set during the init() call (prior to this), which takes
- // in arguments from the device args. So if block_args contains a
- // master_clock_rate key, then it should better be whatever the device is
- // configured to do.
- _master_clock_rate =
- _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate");
- if (block_args.cast<double>("master_clock_rate", _master_clock_rate)
- != _master_clock_rate) {
- throw uhd::runtime_error(str(
- boost::format("Master clock rate mismatch. Device returns %f MHz, "
- "but should have been %f MHz.")
- % (_master_clock_rate / 1e6)
- % (block_args.cast<double>("master_clock_rate", _master_clock_rate) / 1e6)));
- }
- UHD_LOG_DEBUG(
- unique_id(), "Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz.");
- this->set_rate(_master_clock_rate);
-
- // Loopback test
- for (size_t chan = 0; chan < _get_num_radios(); chan++) {
- loopback_self_test(
- [this, chan](
- const uint32_t value) { this->sr_write(regs::CODEC_IDLE, value, chan); },
- [this, chan]() {
- return this->user_reg_read64(regs::RB_CODEC_READBACK, chan);
- });
- }
-
- const size_t db_idx = get_block_id().get_block_count();
- _tree->access<eeprom_map_t>(_root_path / "eeprom")
- .add_coerced_subscriber([this, db_idx](const eeprom_map_t& db_eeprom) {
- this->_rpcc->notify_with_token("set_db_eeprom", db_idx, db_eeprom);
- })
- .set_publisher([this, db_idx]() {
- return this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx);
- });
-
- // Init sensors
- for (const auto& dir : std::vector<direction_t>{RX_DIRECTION, TX_DIRECTION}) {
- for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; chan_idx++) {
- _init_mpm_sensors(dir, chan_idx);
- }
- }
-}
-
-bool e3xx_radio_ctrl_impl::get_lo_lock_status(const direction_t dir)
-{
- if (not(bool(_rpcc))) {
- UHD_LOG_DEBUG(unique_id(), "Reported no LO lock due to lack of RPC connection.");
- return false;
- }
-
- const std::string trx = (dir == RX_DIRECTION) ? "rx" : "tx";
- bool lo_lock =
- _rpcc->request_with_token<bool>(_rpc_prefix + "get_ad9361_lo_lock", trx);
- UHD_LOG_TRACE(unique_id(),
- "AD9361 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No"));
-
- return lo_lock;
-}
-
-void e3xx_radio_ctrl_impl::_set_atr_bits(const size_t chan)
-{
- const auto rx_freq = radio_ctrl_impl::get_rx_frequency(chan);
- const auto tx_freq = radio_ctrl_impl::get_tx_frequency(chan);
- const auto rx_ant = radio_ctrl_impl::get_rx_antenna(chan);
- const uint32_t rx_regs = this->get_rx_switches(chan, rx_freq, rx_ant);
- const uint32_t tx_regs = this->get_tx_switches(chan, tx_freq);
- const uint32_t idle_regs = this->get_idle_switches();
-
- _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_regs);
- _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_RX_ONLY, rx_regs);
- _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_regs);
- _db_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_regs | tx_regs);
-
- // The LED signal names are reversed, but are consistent with the schematic
- const bool is_txrx = rx_ant == "TX/RX";
- const int idle_led = 0;
- const int rx_led = this->get_rx_led();
- const int tx_led = this->get_tx_led();
- const int txrx_led = this->get_txrx_led();
-
- _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, idle_led);
- _leds_gpio[chan]->set_atr_reg(
- usrp::gpio_atr::ATR_REG_RX_ONLY, is_txrx ? txrx_led : rx_led);
- _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_led);
- _leds_gpio[chan]->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, rx_led | tx_led);
-}
-
-void e3xx_radio_ctrl_impl::_identify_with_leds(const int identify_duration)
-{
- auto end_time =
- std::chrono::steady_clock::now() + std::chrono::seconds(identify_duration);
- bool led_state = true;
- while (std::chrono::steady_clock::now() < end_time) {
- // Add update_leds
- led_state = !led_state;
- std::this_thread::sleep_for(std::chrono::milliseconds(500));
- }
-}
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp
deleted file mode 100644
index 41c2c4594..000000000
--- a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_impl.hpp
+++ /dev/null
@@ -1,215 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#ifndef INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP
-# define INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP
-
-# include "e3xx_ad9361_iface.hpp"
-# include <uhd/types/serial.hpp>
-# include <uhd/usrp/dboard_manager.hpp>
-# include <uhd/usrp/gpio_defs.hpp>
-# include <uhdlib/rfnoc/radio_ctrl_impl.hpp>
-# include <uhdlib/rfnoc/rpc_block_ctrl.hpp>
-# include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
-# include <mutex>
-
-namespace uhd { namespace rfnoc {
-
-/*! \brief Provide access to an E3xx radio.
- */
-class e3xx_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl
-{
-public:
- typedef boost::shared_ptr<e3xx_radio_ctrl_impl> sptr;
-
- //! Frequency bands for RX. Bands are a function of the analog filter banks
- enum class rx_band { INVALID_BAND, LB_B2, LB_B3, LB_B4, LB_B5, LB_B6, LB_B7, HB };
-
- //! Frequency bands for TX. Bands are a function of the analog filter banks
- enum class tx_band {
- INVALID_BAND,
- LB_80,
- LB_160,
- LB_225,
- LB_400,
- LB_575,
- LB_1000,
- LB_1700,
- LB_2750,
- HB
- };
-
- /**************************************************************************
- * ATR/ Switches Types
- *************************************************************************/
- //! ATR state
- enum atr_state_t { IDLE, RX_ONLY, TX_ONLY, FULL_DUPLEX };
-
- //! Channel select:
- enum chan_sel_t { CHAN1, CHAN2, BOTH };
-
- /************************************************************************
- * Structors
- ***********************************************************************/
- e3xx_radio_ctrl_impl();
- virtual ~e3xx_radio_ctrl_impl();
-
- /************************************************************************
- * API calls
- ***********************************************************************/
-
- // Note: We use the cached values in radio_ctrl_impl, so most getters are
- // not reimplemented here
- //! Set streaming mode - active chains, channel_mode, timing_mode
- void set_streaming_mode(
- const bool tx1, const bool tx2, const bool rx1, const bool rx2);
-
- //! Set which channel mode is used
- void set_channel_mode(const std::string& channel_mode);
-
- double set_rate(const double rate);
-
- void set_tx_antenna(const std::string& ant, const size_t chan);
- void set_rx_antenna(const std::string& ant, const size_t chan);
-
- double set_tx_frequency(const double freq, const size_t chan);
- double set_rx_frequency(const double freq, const size_t chan);
- double set_tx_bandwidth(const double bandwidth, const size_t chan);
- double set_rx_bandwidth(const double bandwidth, const size_t chan);
-
- // gain
- double set_tx_gain(const double gain, const size_t chan);
- double set_rx_gain(const double gain, const size_t chan);
-
- size_t get_chan_from_dboard_fe(const std::string& fe, const direction_t dir);
- std::string get_dboard_fe_from_chan(const size_t chan, const direction_t dir);
-
- void set_rpc_client(uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args);
-
-protected:
- //! Map a frequency in Hz to an rx_band value. Will return
- // rx_band::INVALID_BAND if the frequency is out of range.
- rx_band map_freq_to_rx_band(const double freq);
- //! Map a frequency in Hz to an tx_band value. Will return
- // tx_band::INVALID_BAND if the frequency is out of range.
- tx_band map_freq_to_tx_band(const double freq);
-
- virtual const std::string get_default_timing_mode() = 0;
-
- /*! Run a loopback self test.
- *
- * This will write data to the AD936x and read it back again.
- * If this test fails, it generally means the interface is broken,
- * so we assume it passes and throw otherwise. Running this requires
- * a core that we can peek and poke the loopback values into.
- *
- * \param iface An interface to the associated radio control core
- * \param iface The radio control core's address to write the loopback value
- * \param iface The radio control core's readback address to read back the returned
- * value
- *
- * \throws a uhd::runtime_error if the loopback value didn't match.
- */
- virtual void loopback_self_test(std::function<void(uint32_t)> poker_functor,
- std::function<uint64_t()> peeker_functor) = 0;
-
- virtual uint32_t get_rx_switches(
- const size_t chan, const double freq, const std::string& ant) = 0;
-
- virtual uint32_t get_tx_switches(const size_t chan, const double freq) = 0;
-
- virtual uint32_t get_idle_switches() = 0;
-
- virtual uint32_t get_tx_led() = 0;
- virtual uint32_t get_rx_led() = 0;
- virtual uint32_t get_txrx_led() = 0;
- virtual uint32_t get_idle_led() = 0;
-
- //! Reference to the AD9361 controls
- // e3xx_ad9361_iface::uptr _ad9361;
- ad9361_ctrl::sptr _ad9361;
-
- //! Swap RFA and RFB for catalina
- bool _fe_swap;
-
-private:
- /**************************************************************************
- * Helpers
- *************************************************************************/
- //! Initialize all the peripherals connected to this block
- void _init_peripherals();
-
- //! Set state of this class to sensible defaults
- void _init_defaults();
-
- //! Init a subtree for the RF frontends
- void _init_frontend_subtree(uhd::property_tree::sptr subtree, const size_t chan_idx);
-
- //! Initialize Catalina defaults
- void _init_codec();
-
- //! Initialize property tree
- void _init_prop_tree();
-
- void _init_mpm_sensors(const direction_t dir, const size_t chan_idx);
-
- /*************************************************************************
- * Sensors
- *************************************************************************/
- //! Return LO lock status. Factors in current band (low/high) and
- // direction (TX/RX)
- bool get_lo_lock_status(const direction_t dir);
-
- /**************************************************************************
- * Misc Controls
- *************************************************************************/
- //! Blink the front-panel LEDs for \p identify_duration,
- // and resume normal operation.
- void _identify_with_leds(const int identify_duration);
-
- void _set_atr_bits(const size_t chan);
-
- /**************************************************************************
- * Private attributes
- *************************************************************************/
- //! Locks access to setter APIs
- std::mutex _set_lock;
-
- //! Letter representation of the radio we're currently running
- std::string _radio_slot;
-
- //! Prepended for all dboard RPC calls
- std::string _rpc_prefix;
-
- //! Additional block args; gets set during set_rpc_client()
- uhd::device_addr_t _block_args;
-
- //! Reference to the RPC client
- uhd::rpc_client::sptr _rpcc;
-
- //! Reference to the SPI core
- uhd::spi_iface::sptr _spi;
-
- //! ATR controls. These control the AD9361 gain
- // up/down bits.
- // Every radio channel gets its own ATR state register.
- std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _db_gpio;
-
- // ATR controls for LEDs
- std::vector<usrp::gpio_atr::gpio_atr_3000::sptr> _leds_gpio;
-
- //! Front panel GPIO controller. Note that only one radio block per
- // module can be the FP-GPIO master.
- usrp::gpio_atr::gpio_atr_3000::sptr _fp_gpio;
-
- //! Sampling rate
- double _master_clock_rate = 1.0;
-}; /* class radio_ctrl_impl */
-
-}} /* namespace uhd::rfnoc */
-
-#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP */
-// vim: sw=4 et:
diff --git a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp b/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp
deleted file mode 100644
index 5b33b33e7..000000000
--- a/host/lib/usrp/dboard/e3xx/e3xx_radio_ctrl_init.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "e3xx_constants.hpp"
-#include "e3xx_radio_ctrl_impl.hpp"
-#include <uhd/transport/chdr.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/types/sensors.hpp>
-#include <uhd/utils/log.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/case_conv.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <string>
-#include <vector>
-
-using namespace uhd;
-using namespace uhd::rfnoc;
-
-//! Helper function to extract single value of port number.
-//
-// Each GPIO pins can be controlled by each radio output ports.
-// This function convert the format of attribute "Radio_N_M"
-// to a single value port number = N*number_of_port_per_radio + M
-
-uint32_t _extract_port_number(
- std::string radio_src_string, uhd::property_tree::sptr ptree)
-{
- std::string s_val = "0";
- std::vector<std::string> radio_strings;
- boost::algorithm::split(radio_strings,
- radio_src_string,
- boost::is_any_of("_/"),
- boost::token_compress_on);
- boost::to_lower(radio_strings[0]);
- if (radio_strings.size() < 3) {
- throw uhd::runtime_error(str(
- boost::format("%s is an invalid GPIO source string.") % radio_src_string));
- }
- size_t radio_num = std::stoi(radio_strings[1]);
- size_t port_num = std::stoi(radio_strings[2]);
- if (radio_strings[0] != "radio") {
- throw uhd::runtime_error(
- "Front panel GPIO bank can only accept a radio block as its driver.");
- }
- std::string radio_port_out = "Radio_" + radio_strings[1] + "/ports/out";
- std::string radio_port_path = radio_port_out + "/" + radio_strings[2];
- auto found = ptree->exists(fs_path("xbar") / radio_port_path);
- if (not found) {
- throw uhd::runtime_error(
- str(boost::format("Could not find radio port %s.\n") % radio_port_path));
- }
- size_t port_size = ptree->list(fs_path("xbar") / radio_port_out).size();
- return radio_num * port_size + port_num;
-}
-
-void e3xx_radio_ctrl_impl::_init_defaults()
-{
- UHD_LOG_TRACE(unique_id(), "Initializing defaults...");
- const size_t num_rx_chans = get_output_ports().size();
- const size_t num_tx_chans = get_input_ports().size();
-
- UHD_LOG_TRACE(unique_id(),
- "Num TX chans: " << num_tx_chans << " Num RX chans: " << num_rx_chans);
-
- for (size_t chan = 0; chan < num_rx_chans; chan++) {
- radio_ctrl_impl::set_rx_frequency(E3XX_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_rx_gain(E3XX_DEFAULT_GAIN, chan);
- radio_ctrl_impl::set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan);
- radio_ctrl_impl::set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
- }
-
- for (size_t chan = 0; chan < num_tx_chans; chan++) {
- radio_ctrl_impl::set_tx_frequency(E3XX_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_tx_gain(E3XX_DEFAULT_GAIN, chan);
- radio_ctrl_impl::set_tx_antenna(E3XX_DEFAULT_TX_ANTENNA, chan);
- radio_ctrl_impl::set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
- }
-
- /** Update default SPP (overwrites the default value from the XML file) **/
- const size_t max_bytes_header =
- uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t);
- const size_t default_spp =
- (_tree->access<size_t>("mtu/recv").get() - max_bytes_header)
- / (2 * sizeof(int16_t));
- UHD_LOG_DEBUG(unique_id(), "Setting default spp to " << default_spp);
- _tree->access<int>(get_arg_path("spp") / "value").set(default_spp);
-}
-
-void e3xx_radio_ctrl_impl::_init_peripherals()
-{
- UHD_LOG_TRACE(unique_id(), "Initializing peripherals...");
-
- _db_gpio.clear(); // Following the as-if rule, this can get optimized out
- for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) {
- UHD_LOG_TRACE(unique_id(), "Initializing GPIOs for channel " << radio_idx);
- _db_gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make_write_only(
- _get_ctrl(radio_idx), regs::sr_addr(regs::GPIO)));
- _db_gpio[radio_idx]->set_atr_mode(
- usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
- }
- _leds_gpio.clear(); // Following the as-if rule, this can get optimized out
- for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) {
- UHD_LOG_TRACE(unique_id(), "Initializing GPIOs for channel " << radio_idx);
- _leds_gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make_write_only(
- _get_ctrl(radio_idx), regs::sr_addr(regs::LEDS)));
-
- _leds_gpio[radio_idx]->set_atr_mode(
- usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
- }
- UHD_LOG_TRACE(unique_id(), "Initializing front-panel GPIO control...")
- _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make(
- _get_ctrl(0), regs::sr_addr(regs::FP_GPIO), regs::rb_addr(regs::RB_FP_GPIO));
-}
-
-void e3xx_radio_ctrl_impl::_init_frontend_subtree(
- uhd::property_tree::sptr subtree, const size_t chan_idx)
-{
- const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx;
- const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx;
- UHD_LOG_TRACE(unique_id(),
- "Adding non-RFNoC block properties for channel "
- << chan_idx << " to prop tree path " << tx_fe_path << " and " << rx_fe_path);
- // TX Standard attributes
- subtree->create<std::string>(tx_fe_path / "name").set(str(boost::format("E3xx")));
- subtree->create<std::string>(tx_fe_path / "connection").set("IQ");
- // RX Standard attributes
- subtree->create<std::string>(rx_fe_path / "name").set(str(boost::format("E3xx")));
- subtree->create<std::string>(rx_fe_path / "connection").set("IQ");
- // TX Antenna
- subtree->create<std::string>(tx_fe_path / "antenna" / "value")
- .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
- this->set_tx_antenna(ant, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); });
- subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
- .set({E3XX_DEFAULT_TX_ANTENNA})
- .add_coerced_subscriber([](const std::vector<std::string>&) {
- throw uhd::runtime_error("Attempting to update antenna options!");
- });
- // RX Antenna
- subtree->create<std::string>(rx_fe_path / "antenna" / "value")
- .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
- this->set_rx_antenna(ant, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); });
- subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
- .set(E3XX_RX_ANTENNAS)
- .add_coerced_subscriber([](const std::vector<std::string>&) {
- throw uhd::runtime_error("Attempting to update antenna options!");
- });
- // TX frequency
- subtree->create<double>(tx_fe_path / "freq" / "value")
- .set_coercer([this, chan_idx](const double freq) {
- return this->set_tx_frequency(freq, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); });
- subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
- .set(meta_range_t(AD9361_TX_MIN_FREQ, AD9361_TX_MAX_FREQ, 1.0))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update freq range!");
- });
- // RX frequency
- subtree->create<double>(rx_fe_path / "freq" / "value")
- .set_coercer([this, chan_idx](const double freq) {
- return this->set_rx_frequency(freq, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); });
- subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
- .set(meta_range_t(AD9361_RX_MIN_FREQ, AD9361_RX_MAX_FREQ, 1.0))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update freq range!");
- });
- // TX bandwidth
- subtree->create<double>(tx_fe_path / "bandwidth" / "value")
- .set(AD9361_TX_MAX_BANDWIDTH)
- .set_coercer([this, chan_idx](const double bw) {
- return this->set_tx_bandwidth(bw, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); });
- subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
- .set(meta_range_t(AD9361_TX_MIN_BANDWIDTH, AD9361_TX_MAX_BANDWIDTH))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update bandwidth range!");
- });
- // RX bandwidth
- subtree->create<double>(rx_fe_path / "bandwidth" / "value")
- .set(AD9361_RX_MAX_BANDWIDTH)
- .set_coercer([this, chan_idx](const double bw) {
- return this->set_rx_bandwidth(bw, chan_idx);
- });
- subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
- .set(meta_range_t(AD9361_RX_MIN_BANDWIDTH, AD9361_RX_MAX_BANDWIDTH))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update bandwidth range!");
- });
-
- // TX gains
- const std::vector<std::string> tx_gain_names = ad9361_ctrl::get_gain_names("TX1");
- for (auto tx_gain_name : tx_gain_names) {
- subtree->create<double>(tx_fe_path / "gains" / tx_gain_name / "value")
- .set_coercer([this, chan_idx](const double gain) {
- return this->set_tx_gain(gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return radio_ctrl_impl::get_tx_gain(chan_idx); });
- subtree->create<meta_range_t>(tx_fe_path / "gains" / tx_gain_name / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- return meta_range_t(
- AD9361_MIN_TX_GAIN, AD9361_MAX_TX_GAIN, AD9361_TX_GAIN_STEP);
- });
- }
-
- // RX gains
- const std::vector<std::string> rx_gain_names = ad9361_ctrl::get_gain_names("RX1");
- for (auto rx_gain_name : rx_gain_names) {
- subtree->create<double>(rx_fe_path / "gains" / rx_gain_name / "value")
- .set_coercer([this, chan_idx](const double gain) {
- return this->set_rx_gain(gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return radio_ctrl_impl::get_rx_gain(chan_idx); });
-
- subtree->create<meta_range_t>(rx_fe_path / "gains" / rx_gain_name / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- return meta_range_t(
- AD9361_MIN_RX_GAIN, AD9361_MAX_RX_GAIN, AD9361_RX_GAIN_STEP);
- });
- }
-
- // TX LO lock sensor //////////////////////////////////////////////////////
- // Note: The AD9361 LO lock sensors are generated programmatically in
- // set_rpc_client(). The actual lo_locked publisher is also set there.
- subtree->create<sensor_value_t>(tx_fe_path / "sensors" / "lo_locked")
- .set(sensor_value_t("all_los", false, "locked", "unlocked"))
- .add_coerced_subscriber([](const sensor_value_t&) {
- throw uhd::runtime_error("Attempting to write to sensor!");
- })
- .set_publisher([this]() {
- return sensor_value_t(
- "all_los", this->get_lo_lock_status(TX_DIRECTION), "locked", "unlocked");
- });
- // RX LO lock sensor (see not on TX LO lock sensor)
- subtree->create<sensor_value_t>(rx_fe_path / "sensors" / "lo_locked")
- .set(sensor_value_t("all_los", false, "locked", "unlocked"))
- .add_coerced_subscriber([](const sensor_value_t&) {
- throw uhd::runtime_error("Attempting to write to sensor!");
- })
- .set_publisher([this]() {
- return sensor_value_t(
- "all_los", this->get_lo_lock_status(RX_DIRECTION), "locked", "unlocked");
- });
-}
-
-void e3xx_radio_ctrl_impl::_init_prop_tree()
-{
- const fs_path fe_base = fs_path("dboards") / _radio_slot;
- for (size_t chan_idx = 0; chan_idx < E3XX_NUM_CHANS; chan_idx++) {
- this->_init_frontend_subtree(_tree->subtree(fe_base), chan_idx);
- }
-
- _tree->create<eeprom_map_t>(_root_path / "eeprom").set(eeprom_map_t());
-
- _tree->create<int>("rx_codecs" / _radio_slot / "gains");
- _tree->create<int>("tx_codecs" / _radio_slot / "gains");
- _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("AD9361 Dual ADC");
- _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("AD9361 Dual DAC");
-
- if (not _tree->exists("tick_rate")) {
- _tree->create<double>("tick_rate")
- .set_coercer([this](double tick_rate) { return this->set_rate(tick_rate); })
- .set_publisher([this]() { return this->get_rate(); });
- } else {
- UHD_LOG_WARNING(unique_id(), "Cannot set tick_rate again");
- }
-
- // *****FP_GPIO************************
- for (const auto& attr : usrp::gpio_atr::gpio_attr_map) {
- if (not _tree->exists(fs_path("gpio") / "FP0" / attr.second)) {
- switch (attr.first) {
- case usrp::gpio_atr::GPIO_SRC:
- // FIXME: move this creation of this branch of ptree out side of
- // radio impl;
- // since there's no data dependency between radio and SRC setting for
- // FP0
- _tree
- ->create<std::vector<std::string>>(
- fs_path("gpio") / "FP0" / attr.second)
- .set(std::vector<std::string>(
- 32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber(
- [this, attr](const std::vector<std::string> str_val) {
- uint32_t radio_src_value = 0;
- uint32_t master_value = 0;
- for (size_t i = 0; i < str_val.size(); i++) {
- if (str_val[i] == "PS") {
- master_value += 1 << i;
- ;
- } else {
- auto port_num =
- _extract_port_number(str_val[i], _tree);
- radio_src_value =
- (1 << (2 * i)) * port_num + radio_src_value;
- }
- }
- _rpcc->notify_with_token(
- "set_fp_gpio_master", master_value);
- _rpcc->notify_with_token(
- "set_fp_gpio_radio_src", radio_src_value);
- });
- break;
- case usrp::gpio_atr::GPIO_CTRL:
- case usrp::gpio_atr::GPIO_DDR:
- _tree
- ->create<std::vector<std::string>>(
- fs_path("gpio") / "FP0" / attr.second)
- .set(std::vector<std::string>(
- 32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber(
- [this, attr](const std::vector<std::string> str_val) {
- uint32_t val = 0;
- for (size_t i = 0; i < str_val.size(); i++) {
- val += usrp::gpio_atr::gpio_attr_value_pair
- .at(attr.second)
- .at(str_val[i])
- << i;
- }
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- break;
- case usrp::gpio_atr::GPIO_READBACK: {
- _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second)
- .set_publisher([this]() { return _fp_gpio->read_gpio(); });
- } break;
- default:
- _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second)
- .set(0)
- .add_coerced_subscriber([this, attr](const uint32_t val) {
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- }
- } else {
- switch (attr.first) {
- case usrp::gpio_atr::GPIO_SRC:
- break;
- case usrp::gpio_atr::GPIO_CTRL:
- case usrp::gpio_atr::GPIO_DDR:
- _tree
- ->access<std::vector<std::string>>(
- fs_path("gpio") / "FP0" / attr.second)
- .set(std::vector<std::string>(
- 32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber(
- [this, attr](const std::vector<std::string> str_val) {
- uint32_t val = 0;
- for (size_t i = 0; i < str_val.size(); i++) {
- val += usrp::gpio_atr::gpio_attr_value_pair
- .at(attr.second)
- .at(str_val[i])
- << i;
- }
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- break;
- case usrp::gpio_atr::GPIO_READBACK:
- break;
- default:
- _tree->access<uint32_t>(fs_path("gpio") / "FP0" / attr.second)
- .set(0)
- .add_coerced_subscriber([this, attr](const uint32_t val) {
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- }
- }
- }
-}
-
-void e3xx_radio_ctrl_impl::_init_codec()
-{
- for (size_t chan = 0; chan < _get_num_radios(); chan++) {
- std::string rx_fe = get_which_ad9361_chain(RX_DIRECTION, chan);
- this->set_rx_gain(E3XX_DEFAULT_GAIN, chan);
- this->set_rx_frequency(E3XX_DEFAULT_FREQ, chan);
- this->set_rx_antenna(E3XX_DEFAULT_RX_ANTENNA, chan);
- this->set_rx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
- _ad9361->set_dc_offset_auto(rx_fe, E3XX_DEFAULT_AUTO_DC_OFFSET);
- _ad9361->set_iq_balance_auto(rx_fe, E3XX_DEFAULT_AUTO_IQ_BALANCE);
- _ad9361->set_agc(rx_fe, E3XX_DEFAULT_AGC_ENABLE);
- std::string tx_fe = get_which_ad9361_chain(TX_DIRECTION, chan);
- this->set_tx_gain(E3XX_DEFAULT_GAIN, chan);
- this->set_tx_frequency(E3XX_DEFAULT_FREQ, chan);
- this->set_tx_bandwidth(E3XX_DEFAULT_BANDWIDTH, chan);
- }
-}
-
-void e3xx_radio_ctrl_impl::_init_mpm_sensors(const direction_t dir, const size_t chan_idx)
-{
- const std::string trx = (dir == RX_DIRECTION) ? "RX" : "TX";
- const fs_path fe_path = fs_path("dboards") / _radio_slot
- / (dir == RX_DIRECTION ? "rx_frontends" : "tx_frontends")
- / chan_idx;
- auto sensor_list = _rpcc->request_with_token<std::vector<std::string>>(
- this->_rpc_prefix + "get_sensors", trx);
- UHD_LOG_TRACE(unique_id(),
- "Chan " << chan_idx << ": Found " << sensor_list.size() << " " << trx
- << " sensors.");
- for (const auto& sensor_name : sensor_list) {
- UHD_LOG_TRACE(unique_id(), "Adding " << trx << " sensor " << sensor_name);
- _tree->create<sensor_value_t>(fe_path / "sensors" / sensor_name)
- .add_coerced_subscriber([](const sensor_value_t&) {
- throw uhd::runtime_error("Attempting to write to sensor!");
- })
- .set_publisher([this, trx, sensor_name, chan_idx]() {
- return sensor_value_t(
- this->_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
- this->_rpc_prefix + "get_sensor", trx, sensor_name, chan_idx));
- });
- }
-}
diff --git a/host/lib/usrp/dboard/magnesium/CMakeLists.txt b/host/lib/usrp/dboard/magnesium/CMakeLists.txt
index 1029bfacd..df5fe765a 100644
--- a/host/lib/usrp/dboard/magnesium/CMakeLists.txt
+++ b/host/lib/usrp/dboard/magnesium/CMakeLists.txt
@@ -8,10 +8,10 @@
# set to true.
list(APPEND MAGNESIUM_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_impl.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_init.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_cpld.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_gain.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control_init.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control_cpld.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control_gain.cpp
${CMAKE_CURRENT_SOURCE_DIR}/magnesium_ad9371_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/magnesium_bands.cpp
${CMAKE_CURRENT_SOURCE_DIR}/magnesium_cpld_ctrl.cpp
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp b/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp
index 13cc52d49..ef72aee95 100644
--- a/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp
+++ b/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp
@@ -7,7 +7,7 @@
// The band plan
#include "magnesium_constants.hpp"
-#include "magnesium_radio_ctrl_impl.hpp"
+#include "magnesium_radio_control.hpp"
#include <uhd/utils/math.hpp>
/*
@@ -93,11 +93,10 @@ namespace {
} // namespace
-
-magnesium_radio_ctrl_impl::rx_band magnesium_radio_ctrl_impl::_map_freq_to_rx_band(
+magnesium_radio_control_impl::rx_band magnesium_radio_control_impl::_map_freq_to_rx_band(
const band_map_t band_map, const double freq)
{
- magnesium_radio_ctrl_impl::rx_band band;
+ magnesium_radio_control_impl::rx_band band;
if (fp_compare_epsilon<double>(freq) < MAGNESIUM_MIN_FREQ) {
band = rx_band::INVALID_BAND;
@@ -124,10 +123,10 @@ magnesium_radio_ctrl_impl::rx_band magnesium_radio_ctrl_impl::_map_freq_to_rx_ba
return band;
}
-magnesium_radio_ctrl_impl::tx_band magnesium_radio_ctrl_impl::_map_freq_to_tx_band(
+magnesium_radio_control_impl::tx_band magnesium_radio_control_impl::_map_freq_to_tx_band(
const band_map_t band_map, const double freq)
{
- magnesium_radio_ctrl_impl::tx_band band;
+ magnesium_radio_control_impl::tx_band band;
if (fp_compare_epsilon<double>(freq) < MAGNESIUM_MIN_FREQ) {
band = tx_band::INVALID_BAND;
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp b/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp
index 9b3bdf800..7d98bca91 100644
--- a/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp
+++ b/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp
@@ -69,6 +69,13 @@ static constexpr char MAGNESIUM_GAIN2[] = "dsa";
//! Amplifier gain
static constexpr char MAGNESIUM_AMP[] = "amp";
+static constexpr char MAGNESIUM_FE_NAME[] = "Magnesium";
+
+static constexpr char MAGNESIUM_DEFAULT_RX_ANTENNA[] = "RX2";
+static constexpr char MAGNESIUM_DEFAULT_TX_ANTENNA[] = "TX/RX";
+
+static constexpr char MAGNESIUM_FPGPIO_BANK[] = "FP0";
+
// Note: MAGNESIUM_NUM_CHANS is independent of the number of chans per
// RFNoC block. TODO: When we go to one radio per dboard, this comment can
// be deleted.
@@ -79,4 +86,20 @@ static constexpr double MAGNESIUM_TX_IF_FREQ = 1.95e9;
//! Max time we allow for a call to set_freq() to take
static constexpr size_t MAGNESIUM_TUNE_TIMEOUT = 15000; // milliseconds
+//! Magnesium gain profile options
+static const std::vector<std::string> MAGNESIUM_GP_OPTIONS = {"manual",
+ "default",
+ "default_rf_filter_bypass_always_on",
+ "default_rf_filter_bypass_always_off"};
+
+namespace n310_regs {
+
+constexpr uint32_t DB_GPIO_BASE = 0x80000; // FIXME
+constexpr uint32_t DB_GPIO_RB = 0x80000; // FIXME
+constexpr uint32_t DB_GPIO_OFFSET = 0x100; // FIXME
+constexpr uint32_t FP_GPIO = 0x80000; // FIXME
+constexpr uint32_t RB_FP_GPIO = 0x80000; // FIXME
+
+}
+
#endif /* INCLUDED_LIBUHD_MAGNESIUM_CONSTANTS_HPP */
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp
index 67b20f5fa..3e513218a 100644
--- a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp
+++ b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp
@@ -15,8 +15,8 @@ using namespace uhd::rfnoc;
using namespace magnesium;
namespace {
-typedef magnesium_radio_ctrl_impl::rx_band rx_band;
-typedef magnesium_radio_ctrl_impl::tx_band tx_band;
+typedef magnesium_radio_control_impl::rx_band rx_band;
+typedef magnesium_radio_control_impl::tx_band tx_band;
const size_t TX_LOWBAND = 0;
const size_t TX_HIGHBAND = 1;
@@ -454,7 +454,7 @@ gain_tuple_t fine_tune_ad9371_att(const gain_tuple_t gain_tuple, const double ga
gain_tuple_t magnesium::get_rx_gain_tuple(
- const double gain_index, const magnesium_radio_ctrl_impl::rx_band band)
+ const double gain_index, const magnesium_radio_control_impl::rx_band band)
{
UHD_ASSERT_THROW(gain_index <= ALL_RX_MAX_GAIN and gain_index >= ALL_RX_MIN_GAIN);
auto& gain_table = rx_gain_tables.at(map_rx_band(band));
@@ -463,7 +463,7 @@ gain_tuple_t magnesium::get_rx_gain_tuple(
}
gain_tuple_t magnesium::get_tx_gain_tuple(
- const double gain_index, const magnesium_radio_ctrl_impl::tx_band band)
+ const double gain_index, const magnesium_radio_control_impl::tx_band band)
{
UHD_ASSERT_THROW(gain_index <= ALL_TX_MAX_GAIN and gain_index >= ALL_TX_MIN_GAIN);
auto& gain_table = tx_gain_tables.at(map_tx_band(band));
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp
index 8769b44ac..6ba91a248 100644
--- a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp
+++ b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp
@@ -7,7 +7,7 @@
#ifndef INCLUDED_LIBUHD_MAGNESIUM_GAIN_TABLE_HPP
#define INCLUDED_LIBUHD_MAGNESIUM_GAIN_TABLE_HPP
-#include "magnesium_radio_ctrl_impl.hpp"
+#include "magnesium_radio_control.hpp"
#include <uhd/types/direction.hpp>
namespace magnesium {
@@ -31,12 +31,12 @@ struct gain_tuple_t
/*! Given a gain index, return a tuple of gain-related settings (Rx)
*/
gain_tuple_t get_rx_gain_tuple(
- const double gain_index, const uhd::rfnoc::magnesium_radio_ctrl_impl::rx_band band_);
+ const double gain_index, const uhd::rfnoc::magnesium_radio_control_impl::rx_band band_);
/*! Given a gain index, return a tuple of gain-related settings (Tx)
*/
gain_tuple_t get_tx_gain_tuple(
- const double gain_index, const uhd::rfnoc::magnesium_radio_ctrl_impl::tx_band band_);
+ const double gain_index, const uhd::rfnoc::magnesium_radio_control_impl::tx_band band_);
} /* namespace magnesium */
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp
new file mode 100644
index 000000000..dc78cee7d
--- /dev/null
+++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp
@@ -0,0 +1,1151 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "magnesium_radio_control.hpp"
+#include "magnesium_constants.hpp"
+#include "magnesium_gain_table.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/rfnoc/node_ctrl_base.hpp>
+#include <uhd/rfnoc/registry.hpp>
+#include <uhd/transport/chdr.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhd/rfnoc/registry.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/format.hpp>
+#include <boost/make_shared.hpp>
+#include <cmath>
+#include <cstdlib>
+#include <sstream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+using namespace uhd::math::fp_compare;
+
+namespace {
+
+/**************************************************************************
+ * ADF4351 Controls
+ *************************************************************************/
+/*!
+ * \param lo_iface Reference to the LO object
+ * \param freq Frequency (in Hz) of the tone to be generated from the LO
+ * \param ref_clock_freq Frequency (in Hz) of the reference clock at the
+ * PLL input of the LO
+ * \param int_n_mode Integer-N mode on or off
+ */
+double _lo_set_frequency(adf435x_iface::sptr lo_iface,
+ const double freq,
+ const double ref_clock_freq,
+ const bool int_n_mode)
+{
+ UHD_LOG_TRACE("MG/ADF4351",
+ "Attempting to tune low band LO to " << freq << " Hz with ref clock freq "
+ << ref_clock_freq);
+ lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED);
+ lo_iface->set_reference_freq(ref_clock_freq);
+ lo_iface->set_prescaler(adf435x_iface::PRESCALER_4_5);
+ const double actual_freq = lo_iface->set_frequency(freq, int_n_mode);
+ lo_iface->set_output_power(
+ adf435x_iface::RF_OUTPUT_A, adf435x_iface::OUTPUT_POWER_2DBM);
+ lo_iface->set_output_power(
+ adf435x_iface::RF_OUTPUT_B, adf435x_iface::OUTPUT_POWER_2DBM);
+ lo_iface->set_charge_pump_current(adf435x_iface::CHARGE_PUMP_CURRENT_0_31MA);
+ return actual_freq;
+}
+
+/*! Configure and enable LO
+ *
+ * Will tune it to requested frequency and enable outputs.
+ *
+ * \param lo_iface Reference to the LO object
+ * \param lo_freq Frequency (in Hz) of the tone to be generated from the LO
+ * \param ref_clock_freq Frequency (in Hz) of the reference clock at the
+ * PLL input of the LO
+ * \param int_n_mode Integer-N mode on or off
+ * \returns the actual frequency the LO is running at
+ */
+double _lo_enable(adf435x_iface::sptr lo_iface,
+ const double lo_freq,
+ const double ref_clock_freq,
+ const bool int_n_mode)
+{
+ const double actual_lo_freq =
+ _lo_set_frequency(lo_iface, lo_freq, ref_clock_freq, int_n_mode);
+ lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, true);
+ lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, true);
+ lo_iface->commit();
+ return actual_lo_freq;
+}
+
+/*! Disable LO
+ */
+void _lo_disable(adf435x_iface::sptr lo_iface)
+{
+ lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, false);
+ lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, false);
+ lo_iface->commit();
+}
+} // namespace
+
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+magnesium_radio_control_impl::magnesium_radio_control_impl(make_args_ptr make_args)
+ : radio_control_impl(std::move(make_args))
+{
+ RFNOC_LOG_TRACE("Entering magnesium_radio_control_impl ctor...");
+ UHD_ASSERT_THROW(get_block_id().get_block_count() < 2);
+ const char radio_slot_name[2] = {'A', 'B'};
+ _radio_slot = radio_slot_name[get_block_id().get_block_count()];
+ RFNOC_LOG_TRACE("Radio slot: " << _radio_slot);
+ _rpc_prefix = (_radio_slot == "A") ? "db_0_" : "db_1_";
+ UHD_ASSERT_THROW(get_num_input_ports() == MAGNESIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(get_num_output_ports() == MAGNESIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(get_mb_controller());
+ _n310_mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller());
+ UHD_ASSERT_THROW(_n310_mb_control);
+ _n3xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>(
+ _n310_mb_control->get_timekeeper(0));
+ UHD_ASSERT_THROW(_n3xx_timekeeper);
+ _rpcc = _n310_mb_control->get_rpc_client();
+ UHD_ASSERT_THROW(_rpcc);
+
+ _init_defaults();
+ _init_mpm();
+ _init_peripherals();
+ _init_prop_tree();
+}
+
+magnesium_radio_control_impl::~magnesium_radio_control_impl()
+{
+ RFNOC_LOG_TRACE("magnesium_radio_control_impl::dtor() ");
+}
+
+
+/******************************************************************************
+ * API Calls
+ *****************************************************************************/
+double magnesium_radio_control_impl::set_rate(double requested_rate)
+{
+ meta_range_t rates;
+ for (const double rate : MAGNESIUM_RADIO_RATES) {
+ rates.push_back(range_t(rate));
+ }
+
+ const double rate = rates.clip(requested_rate);
+ if (!math::frequencies_are_equal(requested_rate, rate)) {
+ RFNOC_LOG_WARNING("Coercing requested sample rate from "
+ << (requested_rate / 1e6) << " to " << (rate / 1e6));
+ }
+
+ const double current_rate = get_tick_rate();
+ if (math::frequencies_are_equal(current_rate, rate)) {
+ RFNOC_LOG_DEBUG("Rate is already at " << rate << " MHz. Skipping set_rate()");
+ return current_rate;
+ }
+
+ std::lock_guard<std::mutex> l(_set_lock);
+ // Now commit to device. First, disable LOs.
+ _lo_disable(_tx_lo);
+ _lo_disable(_rx_lo);
+ _master_clock_rate = _ad9371->set_master_clock_rate(rate);
+ _n3xx_timekeeper->update_tick_rate(_master_clock_rate);
+ radio_control_impl::set_rate(_master_clock_rate);
+ // Frequency settings apply to both channels, no loop needed. Will also
+ // re-enable the lowband LOs if they were used.
+ set_rx_frequency(get_rx_frequency(0), 0);
+ set_tx_frequency(get_tx_frequency(0), 0);
+ // Gain and bandwidth need to be looped:
+ for (size_t radio_idx = 0; radio_idx < MAGNESIUM_NUM_CHANS; radio_idx++) {
+ set_rx_gain(radio_control_impl::get_rx_gain(radio_idx), radio_idx);
+ set_tx_gain(radio_control_impl::get_rx_gain(radio_idx), radio_idx);
+ set_rx_bandwidth(get_rx_bandwidth(radio_idx), radio_idx);
+ set_tx_bandwidth(get_tx_bandwidth(radio_idx), radio_idx);
+ }
+ set_tick_rate(_master_clock_rate);
+ return _master_clock_rate;
+}
+
+void magnesium_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan)
+{
+ if (ant != get_tx_antenna(chan)) {
+ throw uhd::value_error(
+ str(boost::format("[%s] Requesting invalid TX antenna value: %s")
+ % get_unique_id() % ant));
+ }
+ // We can't actually set the TX antenna, so let's stop here.
+}
+
+void magnesium_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan <= MAGNESIUM_NUM_CHANS);
+ if (std::find(MAGNESIUM_RX_ANTENNAS.begin(), MAGNESIUM_RX_ANTENNAS.end(), ant)
+ == MAGNESIUM_RX_ANTENNAS.end()) {
+ throw uhd::value_error(
+ str(boost::format("[%s] Requesting invalid RX antenna value: %s")
+ % get_unique_id() % ant));
+ }
+ RFNOC_LOG_TRACE("Setting RX antenna to " << ant << " for chan " << chan);
+ magnesium_cpld_ctrl::chan_sel_t chan_sel = chan == 0 ? magnesium_cpld_ctrl::CHAN1
+ : magnesium_cpld_ctrl::CHAN2;
+ _update_atr_switches(chan_sel, RX_DIRECTION, ant);
+
+ radio_control_impl::set_rx_antenna(ant, chan);
+}
+
+double magnesium_radio_control_impl::set_tx_frequency(const double req_freq, const size_t chan)
+{
+ const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq);
+ RFNOC_LOG_TRACE("set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
+ _desired_rf_freq[TX_DIRECTION] = freq;
+ std::lock_guard<std::mutex> l(_set_lock);
+ // We need to set the switches on both channels, because they share an LO.
+ // This way, if we tune channel 0 it will not put channel 1 into a bad
+ // state.
+ _update_tx_freq_switches(freq, _tx_bypass_amp, magnesium_cpld_ctrl::BOTH);
+ const std::string ad9371_source = this->get_tx_lo_source(MAGNESIUM_LO1, chan);
+ const std::string adf4351_source = this->get_tx_lo_source(MAGNESIUM_LO2, chan);
+ UHD_ASSERT_THROW(adf4351_source == "internal");
+ double coerced_if_freq = freq;
+
+ if (_map_freq_to_tx_band(_tx_band_map, freq) == tx_band::LOWBAND) {
+ _is_low_band[TX_DIRECTION] = true;
+ const double desired_low_freq = MAGNESIUM_TX_IF_FREQ - freq;
+ coerced_if_freq =
+ this->_set_tx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan)
+ + freq;
+ RFNOC_LOG_TRACE("coerced_if_freq = " << coerced_if_freq);
+ } else {
+ _is_low_band[TX_DIRECTION] = false;
+ _lo_disable(_tx_lo);
+ }
+ // external LO required to tune at 2xdesired_frequency.
+ const double desired_if_freq = (ad9371_source == "internal") ? coerced_if_freq
+ : 2 * coerced_if_freq;
+
+ this->_set_tx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan);
+ this->_update_freq(chan, TX_DIRECTION);
+ this->_update_gain(chan, TX_DIRECTION);
+ return radio_control_impl::get_tx_frequency(chan);
+}
+
+void magnesium_radio_control_impl::_update_gain(const size_t chan, const uhd::direction_t dir)
+{
+ const std::string fe = (dir == TX_DIRECTION) ? "tx_frontends" : "rx_frontends";
+ const double freq = (dir == TX_DIRECTION) ? this->get_tx_frequency(chan)
+ : this->get_rx_frequency(chan);
+ this->_set_all_gain(this->_get_all_gain(chan, dir), freq, chan, dir);
+}
+
+void magnesium_radio_control_impl::_update_freq(const size_t chan, const uhd::direction_t dir)
+{
+ const std::string ad9371_source = dir == TX_DIRECTION
+ ? this->get_tx_lo_source(MAGNESIUM_LO1, chan)
+ : this->get_rx_lo_source(MAGNESIUM_LO1, chan);
+
+ const double ad9371_freq = ad9371_source == "external" ? _ad9371_freq[dir] / 2
+ : _ad9371_freq[dir];
+ const double rf_freq = _is_low_band[dir] ? ad9371_freq - _adf4351_freq[dir]
+ : ad9371_freq;
+
+ RFNOC_LOG_TRACE("RF freq = " << rf_freq);
+ UHD_ASSERT_THROW(fp_compare_epsilon<double>(rf_freq) >= 0);
+ UHD_ASSERT_THROW(fp_compare_epsilon<double>(std::abs(rf_freq - _desired_rf_freq[dir]))
+ <= _master_clock_rate / 2);
+ if (dir == RX_DIRECTION) {
+ radio_control_impl::set_rx_frequency(rf_freq, chan);
+ } else if (dir == TX_DIRECTION) {
+ radio_control_impl::set_tx_frequency(rf_freq, chan);
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+double magnesium_radio_control_impl::set_rx_frequency(const double req_freq, const size_t chan)
+{
+ const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq);
+ RFNOC_LOG_TRACE("set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
+ _desired_rf_freq[RX_DIRECTION] = freq;
+ std::lock_guard<std::mutex> l(_set_lock);
+ // We need to set the switches on both channels, because they share an LO.
+ // This way, if we tune channel 0 it will not put channel 1 into a bad
+ // state.
+ _update_rx_freq_switches(freq, _rx_bypass_lnas, magnesium_cpld_ctrl::BOTH);
+ const std::string ad9371_source = this->get_rx_lo_source(MAGNESIUM_LO1, chan);
+ const std::string adf4351_source = this->get_rx_lo_source(MAGNESIUM_LO2, chan);
+ UHD_ASSERT_THROW(adf4351_source == "internal");
+ double coerced_if_freq = freq;
+
+ if (_map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) {
+ _is_low_band[RX_DIRECTION] = true;
+ const double desired_low_freq = MAGNESIUM_RX_IF_FREQ - freq;
+ coerced_if_freq =
+ this->_set_rx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan)
+ + freq;
+ RFNOC_LOG_TRACE("coerced_if_freq = " << coerced_if_freq);
+ } else {
+ _is_low_band[RX_DIRECTION] = false;
+ _lo_disable(_rx_lo);
+ }
+ // external LO required to tune at 2xdesired_frequency.
+ const double desired_if_freq = ad9371_source == "internal" ? coerced_if_freq
+ : 2 * coerced_if_freq;
+
+ this->_set_rx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan);
+
+ this->_update_freq(chan, RX_DIRECTION);
+ this->_update_gain(chan, RX_DIRECTION);
+
+ return radio_control_impl::get_rx_frequency(chan);
+}
+
+double magnesium_radio_control_impl::set_rx_bandwidth(
+ const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ _ad9371->set_bandwidth(bandwidth, chan, RX_DIRECTION);
+ // FIXME: setting analog bandwidth on AD9371 take no effect.
+ // Remove this warning when ADI can confirm that it works.
+ RFNOC_LOG_WARNING("set_rx_bandwidth take no effect on AD9371. "
+ "Default analog bandwidth is 100MHz");
+ return AD9371_RX_MAX_BANDWIDTH;
+}
+
+double magnesium_radio_control_impl::set_tx_bandwidth(
+ const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ _ad9371->set_bandwidth(bandwidth, chan, TX_DIRECTION);
+ // FIXME: setting analog bandwidth on AD9371 take no effect.
+ // Remove this warning when ADI can confirm that it works.
+ RFNOC_LOG_WARNING("set_tx_bandwidth take no effect on AD9371. "
+ "Default analog bandwidth is 100MHz");
+ return AD9371_TX_MAX_BANDWIDTH;
+}
+
+void magnesium_radio_control_impl::set_tx_gain_profile(
+ const std::string& profile, const size_t)
+{
+ if (std::find(
+ MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile)
+ == MAGNESIUM_GP_OPTIONS.end()) {
+ RFNOC_LOG_ERROR("Invalid TX gain profile: " << profile);
+ throw uhd::key_error("Invalid TX gain profile!");
+ }
+ _gain_profile[TX_DIRECTION] = profile;
+}
+
+void magnesium_radio_control_impl::set_rx_gain_profile(
+ const std::string& profile, const size_t)
+{
+ if (std::find(
+ MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile)
+ == MAGNESIUM_GP_OPTIONS.end()) {
+ RFNOC_LOG_ERROR("Invalid RX gain profile: " << profile);
+ throw uhd::key_error("Invalid RX gain profile!");
+ }
+ _gain_profile[RX_DIRECTION] = profile;
+}
+
+double magnesium_radio_control_impl::set_tx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
+ const double coerced_gain =
+ _set_all_gain(gain, this->get_tx_frequency(chan), chan, TX_DIRECTION);
+ radio_control_impl::set_tx_gain(coerced_gain, chan);
+ return coerced_gain;
+}
+
+double magnesium_radio_control_impl::_set_tx_gain(
+ const std::string& name, const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE(
+ "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")");
+ RFNOC_LOG_TRACE(
+ "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")");
+ double clip_gain = 0;
+ if (name == MAGNESIUM_GAIN1) {
+ clip_gain = uhd::clip(gain, AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN);
+ _ad9371_att[TX_DIRECTION] = clip_gain;
+ } else if (name == MAGNESIUM_GAIN2) {
+ clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN);
+ _dsa_att[TX_DIRECTION] = clip_gain;
+ } else if (name == MAGNESIUM_AMP) {
+ clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN;
+ _amp_bypass[TX_DIRECTION] = clip_gain == 0.0;
+ } else {
+ throw uhd::value_error("Could not find gain element " + name);
+ }
+ RFNOC_LOG_TRACE("_set_tx_gain calling update gain");
+ this->_set_all_gain(this->_get_all_gain(chan, TX_DIRECTION),
+ this->get_tx_frequency(chan),
+ chan,
+ TX_DIRECTION);
+ return clip_gain;
+}
+
+double magnesium_radio_control_impl::_get_tx_gain(
+ const std::string& name, const size_t /*chan*/
+)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ if (name == MAGNESIUM_GAIN1) {
+ return _ad9371_att[TX_DIRECTION];
+ } else if (name == MAGNESIUM_GAIN2) {
+ return _dsa_att[TX_DIRECTION];
+ } else if (name == MAGNESIUM_AMP) {
+ return _amp_bypass[TX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN;
+ } else {
+ throw uhd::value_error("Could not find gain element " + name);
+ }
+}
+
+double magnesium_radio_control_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
+ const double coerced_gain =
+ _set_all_gain(gain, this->get_rx_frequency(chan), chan, RX_DIRECTION);
+ radio_control_impl::set_rx_gain(coerced_gain, chan);
+ return coerced_gain;
+}
+
+double magnesium_radio_control_impl::_set_rx_gain(
+ const std::string& name, const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE(
+ "_set_rx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")");
+ double clip_gain = 0;
+ if (name == MAGNESIUM_GAIN1) {
+ clip_gain = uhd::clip(gain, AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN);
+ _ad9371_att[RX_DIRECTION] = clip_gain;
+ } else if (name == MAGNESIUM_GAIN2) {
+ clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN);
+ _dsa_att[RX_DIRECTION] = clip_gain;
+ } else if (name == MAGNESIUM_AMP) {
+ clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN;
+ _amp_bypass[RX_DIRECTION] = clip_gain == 0.0;
+ } else {
+ throw uhd::value_error("Could not find gain element " + name);
+ }
+ RFNOC_LOG_TRACE("_set_rx_gain calling update gain");
+ this->_set_all_gain(this->_get_all_gain(chan, RX_DIRECTION),
+ this->get_rx_frequency(chan),
+ chan,
+ RX_DIRECTION);
+ return clip_gain; // not really any coerced here (only clip) for individual gain
+}
+
+double magnesium_radio_control_impl::_get_rx_gain(
+ const std::string& name, const size_t /*chan*/
+)
+{
+ std::lock_guard<std::mutex> l(_set_lock);
+
+ if (name == MAGNESIUM_GAIN1) {
+ return _ad9371_att[RX_DIRECTION];
+ } else if (name == MAGNESIUM_GAIN2) {
+ return _dsa_att[RX_DIRECTION];
+ } else if (name == MAGNESIUM_AMP) {
+ return _amp_bypass[RX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN;
+ } else {
+ throw uhd::value_error("Could not find gain element " + name);
+ }
+}
+
+double magnesium_radio_control_impl::set_tx_gain(
+ const double gain, const std::string& name, const size_t chan)
+{
+ if (get_tx_gain_profile(chan) == "manual") {
+ if (name == "all" || name == ALL_GAINS) {
+ RFNOC_LOG_ERROR("Setting overall gain is not supported in manual gain mode!");
+ throw uhd::key_error(
+ "Setting overall gain is not supported in manual gain mode!");
+ }
+ if (name != MAGNESIUM_GAIN1 && name != MAGNESIUM_GAIN2 && name != MAGNESIUM_AMP) {
+ RFNOC_LOG_ERROR("Invalid TX gain name: " << name);
+ throw uhd::key_error("Invalid TX gain name!");
+ }
+ const double coerced_gain = get_tx_gain_range(name, chan).clip(gain, true);
+ if (name == MAGNESIUM_GAIN1) {
+ _ad9371_att[TX_DIRECTION] = AD9371_MAX_TX_GAIN - coerced_gain;
+ } else if (name == MAGNESIUM_GAIN2) {
+ _dsa_set_att(AD9371_MAX_TX_GAIN - coerced_gain, chan, TX_DIRECTION);
+ } else if (name == MAGNESIUM_AMP) {
+ _amp_bypass[TX_DIRECTION] = (coerced_gain == AMP_MIN_GAIN);
+ } else {
+ throw uhd::value_error("Could not find gain element " + name);
+ }
+ _set_all_gain(coerced_gain /* this value doesn't actuall matter */,
+ get_tx_frequency(chan),
+ chan,
+ TX_DIRECTION);
+ return coerced_gain;
+ }
+
+ if (name == "all" || name == ALL_GAINS) {
+ return set_tx_gain(gain, chan);
+ }
+ RFNOC_LOG_ERROR("Setting individual TX gains is only supported in manual gain mode!");
+ throw uhd::key_error(
+ "Setting individual TX gains is only supported in manual gain mode!");
+}
+
+double magnesium_radio_control_impl::set_rx_gain(
+ const double gain, const std::string& name, const size_t chan)
+{
+ if (get_rx_gain_profile(chan) == "manual") {
+ if (name == "all" || name == ALL_GAINS) {
+ RFNOC_LOG_ERROR("Setting overall gain is not supported in manual gain mode!");
+ throw uhd::key_error(
+ "Setting overall gain is not supported in manual gain mode!");
+ }
+ if (name != MAGNESIUM_GAIN1 && name != MAGNESIUM_GAIN2 && name != MAGNESIUM_AMP) {
+ RFNOC_LOG_ERROR("Invalid RX gain name: " << name);
+ throw uhd::key_error("Invalid RX gain name!");
+ }
+ const double coerced_gain = get_rx_gain_range(name, chan).clip(gain, true);
+ if (name == MAGNESIUM_GAIN1) {
+ _ad9371_att[RX_DIRECTION] = AD9371_MAX_RX_GAIN - coerced_gain;
+ } else if (name == MAGNESIUM_GAIN2) {
+ _dsa_set_att(AD9371_MAX_RX_GAIN - coerced_gain, chan, RX_DIRECTION);
+ } else if (name == MAGNESIUM_AMP) {
+ _amp_bypass[RX_DIRECTION] = (coerced_gain == AMP_MIN_GAIN);
+ } else {
+ throw uhd::value_error("Could not find gain element " + name);
+ }
+ _set_all_gain(coerced_gain /* this value doesn't actuall matter */,
+ get_rx_frequency(chan),
+ chan,
+ RX_DIRECTION);
+ return coerced_gain;
+ }
+
+ if (name == "all" || name == ALL_GAINS) {
+ return set_rx_gain(gain, chan);
+ }
+ RFNOC_LOG_ERROR("Setting individual RX gains is only supported in manual gain mode!");
+ throw uhd::key_error(
+ "Setting individual RX gains is only supported in manual gain mode!");
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_tx_antennas(const size_t) const
+{
+ return {"TX/RX"};
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_rx_antennas(const size_t) const
+{
+ return MAGNESIUM_RX_ANTENNAS;
+}
+
+uhd::freq_range_t magnesium_radio_control_impl::get_tx_frequency_range(const size_t) const
+{
+ return meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0);
+}
+
+uhd::freq_range_t magnesium_radio_control_impl::get_rx_frequency_range(const size_t) const
+{
+ return meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0);
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_tx_gain_names(const size_t) const
+{
+ return {MAGNESIUM_GAIN1, MAGNESIUM_GAIN2, MAGNESIUM_AMP};
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_rx_gain_names(const size_t) const
+{
+ return {MAGNESIUM_GAIN1, MAGNESIUM_GAIN2, MAGNESIUM_AMP};
+}
+
+double magnesium_radio_control_impl::get_tx_gain(
+ const std::string& name, const size_t chan)
+{
+ if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) {
+ return _get_tx_gain(name, chan);
+ }
+ if (name == "all" || name == ALL_GAINS) {
+ return radio_control_impl::get_tx_gain(chan);
+ }
+ RFNOC_LOG_ERROR("Invalid TX gain name: " << name);
+ throw uhd::key_error("Invalid TX gain name!");
+}
+
+double magnesium_radio_control_impl::get_rx_gain(
+ const std::string& name, const size_t chan)
+{
+ if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) {
+ return _get_rx_gain(name, chan);
+ }
+ if (name == "all" || name == ALL_GAINS) {
+ return radio_control_impl::get_rx_gain(chan);
+ }
+ RFNOC_LOG_ERROR("Invalid RX gain name: " << name);
+ throw uhd::key_error("Invalid RX gain name!");
+}
+
+uhd::gain_range_t magnesium_radio_control_impl::get_tx_gain_range(const size_t chan) const
+{
+ if (get_tx_gain_profile(chan) == "manual") {
+ return meta_range_t(0.0, 0.0, 0.0);
+ }
+ return meta_range_t(ALL_TX_MIN_GAIN, ALL_TX_MAX_GAIN, ALL_TX_GAIN_STEP);
+}
+
+uhd::gain_range_t magnesium_radio_control_impl::get_tx_gain_range(
+ const std::string& name, const size_t chan) const
+{
+ if (get_tx_gain_profile(chan) == "manual") {
+ if (name == "all" || name == ALL_GAINS) {
+ return meta_range_t(0.0, 0.0, 0.0);
+ }
+ if (name == MAGNESIUM_GAIN1) {
+ return meta_range_t(
+ AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN, AD9371_TX_GAIN_STEP);
+ }
+ if (name == MAGNESIUM_GAIN2) {
+ return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_GAIN_STEP);
+ }
+ if (name == MAGNESIUM_AMP) {
+ return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP);
+ }
+ RFNOC_LOG_ERROR("Invalid TX gain name: " << name);
+ throw uhd::key_error("Invalid TX gain name!");
+ }
+ if (name == "all" || name == ALL_GAINS) {
+ return get_tx_gain_range(chan);
+ }
+ if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) {
+ return meta_range_t(0.0, 0.0, 0.0);
+ }
+ RFNOC_LOG_ERROR("Invalid TX gain name: " << name);
+ throw uhd::key_error("Invalid TX gain name!");
+}
+
+uhd::gain_range_t magnesium_radio_control_impl::get_rx_gain_range(const size_t chan) const
+{
+ if (get_rx_gain_profile(chan) == "manual") {
+ return meta_range_t(0.0, 0.0, 0.0);
+ }
+ return meta_range_t(ALL_RX_MIN_GAIN, ALL_RX_MAX_GAIN, ALL_RX_GAIN_STEP);
+}
+
+uhd::gain_range_t magnesium_radio_control_impl::get_rx_gain_range(
+ const std::string& name, const size_t chan) const
+{
+ if (get_rx_gain_profile(chan) == "manual") {
+ if (name == "all" || name == ALL_GAINS) {
+ return meta_range_t(0.0, 0.0, 0.0);
+ }
+ if (name == MAGNESIUM_GAIN1) {
+ return meta_range_t(
+ AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN, AD9371_RX_GAIN_STEP);
+ }
+ if (name == MAGNESIUM_GAIN2) {
+ return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_GAIN_STEP);
+ }
+ if (name == MAGNESIUM_AMP) {
+ return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP);
+ }
+ RFNOC_LOG_ERROR("Invalid RX gain name: " << name);
+ throw uhd::key_error("Invalid RX gain name!");
+ }
+ if (name == "all" || name == ALL_GAINS) {
+ return get_rx_gain_range(chan);
+ }
+ if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) {
+ return meta_range_t(0.0, 0.0, 0.0);
+ }
+ RFNOC_LOG_ERROR("Invalid RX gain name: " << name);
+ throw uhd::key_error("Invalid RX gain name!");
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_tx_gain_profile_names(
+ const size_t) const
+{
+ return MAGNESIUM_GP_OPTIONS;
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_rx_gain_profile_names(const size_t ) const
+{
+ return MAGNESIUM_GP_OPTIONS;
+}
+
+std::string magnesium_radio_control_impl::get_tx_gain_profile(const size_t) const
+{
+ return _gain_profile.at(TX_DIRECTION);
+}
+
+std::string magnesium_radio_control_impl::get_rx_gain_profile(const size_t) const
+{
+ return _gain_profile.at(RX_DIRECTION);
+}
+
+meta_range_t magnesium_radio_control_impl::get_tx_bandwidth_range(size_t) const
+{
+ return meta_range_t(AD9371_TX_MIN_BANDWIDTH, AD9371_TX_MAX_BANDWIDTH);
+}
+
+meta_range_t magnesium_radio_control_impl::get_rx_bandwidth_range(size_t) const
+{
+ return meta_range_t(AD9371_TX_MIN_BANDWIDTH, AD9371_TX_MAX_BANDWIDTH);
+}
+
+
+/******************************************************************************
+ * LO Controls
+ *****************************************************************************/
+std::vector<std::string> magnesium_radio_control_impl::get_rx_lo_names(
+ const size_t /*chan*/
+ ) const
+{
+ return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2};
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_rx_lo_sources(
+ const std::string& name, const size_t /*chan*/
+ ) const
+{
+ if (name == MAGNESIUM_LO2) {
+ return std::vector<std::string>{"internal"};
+ } else if (name == MAGNESIUM_LO1) {
+ return std::vector<std::string>{"internal", "external"};
+ } else {
+ throw uhd::value_error("Could not find LO stage " + name);
+ }
+}
+
+freq_range_t magnesium_radio_control_impl::get_rx_lo_freq_range(
+ const std::string& name, const size_t /*chan*/
+) const
+{
+ if (name == MAGNESIUM_LO1) {
+ return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ};
+ } else if (name == MAGNESIUM_LO2) {
+ return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ};
+ } else {
+ throw uhd::value_error("Could not find LO stage " + name);
+ }
+}
+
+void magnesium_radio_control_impl::set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t /*chan*/
+)
+{
+ // TODO: checking what options are there
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("Setting RX LO " << name << " to " << src);
+
+ if (name == MAGNESIUM_LO1) {
+ _ad9371->set_lo_source(src, RX_DIRECTION);
+ } else {
+ RFNOC_LOG_ERROR(
+ "RX LO " << name << " does not support setting source to " << src);
+ }
+}
+
+const std::string magnesium_radio_control_impl::get_rx_lo_source(
+ const std::string& name, const size_t /*chan*/
+) const
+{
+ if (name == MAGNESIUM_LO1) {
+ // TODO: should we use this from cache?
+ return _ad9371->get_lo_source(RX_DIRECTION);
+ }
+ return "internal";
+}
+
+double magnesium_radio_control_impl::_set_rx_lo_freq(const std::string source,
+ const std::string name,
+ const double freq,
+ const size_t chan)
+{
+ double coerced_lo_freq = freq;
+ if (source != "internal") {
+ RFNOC_LOG_WARNING(
+ "LO source is not internal. This set frequency will be ignored");
+ if (name == MAGNESIUM_LO1) {
+ // handle ad9371 external LO case
+ coerced_lo_freq = freq;
+ _ad9371_freq[RX_DIRECTION] = coerced_lo_freq;
+ }
+ } else {
+ if (name == MAGNESIUM_LO1) {
+ coerced_lo_freq = _ad9371->set_frequency(freq, chan, RX_DIRECTION);
+ _ad9371_freq[RX_DIRECTION] = coerced_lo_freq;
+ } else if (name == MAGNESIUM_LO2) {
+ // TODO: no hardcode the init_n_mode
+ coerced_lo_freq = _lo_enable(_rx_lo, freq, _master_clock_rate, false);
+ _adf4351_freq[RX_DIRECTION] = coerced_lo_freq;
+ } else {
+ RFNOC_LOG_WARNING("There's no LO with this name of "
+ << name
+ << " in the system. This set rx lo freq will be ignored");
+ };
+ }
+ return coerced_lo_freq;
+}
+
+double magnesium_radio_control_impl::set_rx_lo_freq(
+ double freq, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_lo_freq(freq=" << freq << ", name=" << name << ")");
+ std::lock_guard<std::mutex> l(_set_lock);
+ std::string source = this->get_rx_lo_source(name, chan);
+ const double coerced_lo_freq = this->_set_rx_lo_freq(source, name, freq, chan);
+ this->_update_freq(chan, RX_DIRECTION);
+ this->_update_gain(chan, RX_DIRECTION);
+ return coerced_lo_freq;
+}
+
+double magnesium_radio_control_impl::get_rx_lo_freq(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_freq(name=" << name << ")");
+ std::string source = this->get_rx_lo_source(name, chan);
+ if (name == MAGNESIUM_LO1) {
+ return _ad9371_freq.at(RX_DIRECTION);
+ } else if (name == "adf4531") {
+ return _adf4351_freq.at(RX_DIRECTION);
+ } else {
+ RFNOC_LOG_ERROR("get_rx_lo_freq(): No such LO: " << name);
+ }
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+// TX LO
+std::vector<std::string> magnesium_radio_control_impl::get_tx_lo_names(
+ const size_t /*chan*/
+) const
+{
+ return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2};
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_tx_lo_sources(
+ const std::string& name, const size_t /*chan*/
+) const
+{
+ if (name == MAGNESIUM_LO2) {
+ return std::vector<std::string>{"internal"};
+ } else if (name == MAGNESIUM_LO1) {
+ return std::vector<std::string>{"internal", "external"};
+ } else {
+ throw uhd::value_error("Could not find LO stage " + name);
+ }
+}
+
+freq_range_t magnesium_radio_control_impl::get_tx_lo_freq_range(
+ const std::string& name, const size_t /*chan*/
+)
+{
+ if (name == MAGNESIUM_LO2) {
+ return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ};
+ } else if (name == MAGNESIUM_LO1) {
+ return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ};
+ } else {
+ throw uhd::value_error("Could not find LO stage " + name);
+ }
+}
+
+void magnesium_radio_control_impl::set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t /*chan*/
+)
+{
+ // TODO: checking what options are there
+ std::lock_guard<std::mutex> l(_set_lock);
+ RFNOC_LOG_TRACE("set_tx_lo_source(name=" << name << ", src=" << src << ")");
+ if (name == MAGNESIUM_LO1) {
+ _ad9371->set_lo_source(src, TX_DIRECTION);
+ } else {
+ RFNOC_LOG_ERROR(
+ "TX LO " << name << " does not support setting source to " << src);
+ }
+}
+
+const std::string magnesium_radio_control_impl::get_tx_lo_source(
+ const std::string& name, const size_t /*chan*/
+)
+{
+ if (name == MAGNESIUM_LO1) {
+ // TODO: should we use this from cache?
+ return _ad9371->get_lo_source(TX_DIRECTION);
+ }
+ return "internal";
+}
+
+double magnesium_radio_control_impl::_set_tx_lo_freq(const std::string source,
+ const std::string name,
+ const double freq,
+ const size_t chan)
+{
+ double coerced_lo_freq = freq;
+ if (source != "internal") {
+ RFNOC_LOG_WARNING(
+ "LO source is not internal. This set frequency will be ignored");
+ if (name == MAGNESIUM_LO1) {
+ // handle ad9371 external LO case
+ coerced_lo_freq = freq;
+ _ad9371_freq[TX_DIRECTION] = coerced_lo_freq;
+ }
+ } else {
+ if (name == MAGNESIUM_LO1) {
+ coerced_lo_freq = _ad9371->set_frequency(freq, chan, TX_DIRECTION);
+ _ad9371_freq[TX_DIRECTION] = coerced_lo_freq;
+ } else if (name == MAGNESIUM_LO2) {
+ // TODO: no hardcode the int_n_mode
+ const bool int_n_mode = false;
+ coerced_lo_freq = _lo_enable(_tx_lo, freq, _master_clock_rate, int_n_mode);
+ _adf4351_freq[TX_DIRECTION] = coerced_lo_freq;
+ } else {
+ RFNOC_LOG_WARNING("There's no LO with this name of "
+ << name
+ << " in the system. This set tx lo freq will be ignored");
+ };
+ }
+ return coerced_lo_freq;
+}
+
+double magnesium_radio_control_impl::set_tx_lo_freq(
+ double freq, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_lo_freq(freq=" << freq << ", name=" << name << ")");
+ std::string source = this->get_tx_lo_source(name, chan);
+ const double return_freq = this->_set_tx_lo_freq(source, name, freq, chan);
+ this->_update_freq(chan, TX_DIRECTION);
+ this->_update_gain(chan, TX_DIRECTION);
+ return return_freq;
+}
+
+double magnesium_radio_control_impl::get_tx_lo_freq(const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_freq(name=" << name << ")");
+ std::string source = this->get_tx_lo_source(name, chan);
+ if (name == MAGNESIUM_LO1) {
+ return _ad9371_freq[TX_DIRECTION];
+ } else if (name == MAGNESIUM_LO2) {
+ return _adf4351_freq[TX_DIRECTION];
+ } else {
+ RFNOC_LOG_ERROR("get_tx_lo_freq(): No such LO: " << name);
+ };
+
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+void magnesium_radio_control_impl::_remap_band_limits(
+ const std::string band_map, const uhd::direction_t dir)
+{
+ const size_t dflt_band_size = (dir == RX_DIRECTION) ? _rx_band_map.size()
+ : _tx_band_map.size();
+
+ std::vector<std::string> band_map_split;
+ double band_lim;
+
+ RFNOC_LOG_DEBUG("Using user specified frequency band limits");
+ boost::split(band_map_split, band_map, boost::is_any_of(";"));
+ if (band_map_split.size() != dflt_band_size) {
+ throw uhd::runtime_error((
+ boost::format(
+ "size %s of given frequency band map doesn't match the required size: %s")
+ % band_map_split.size() % dflt_band_size)
+ .str());
+ }
+ RFNOC_LOG_DEBUG("newly used band limits: ");
+ for (size_t i = 0; i < band_map_split.size(); i++) {
+ try {
+ band_lim = std::stod(band_map_split.at(i));
+ } catch (...) {
+ throw uhd::value_error(
+ (boost::format("error while converting given frequency string %s "
+ "to a double value")
+ % band_map_split.at(i))
+ .str());
+ }
+ RFNOC_LOG_DEBUG("band " << i << " limit: " << band_lim << "Hz");
+ if (dir == RX_DIRECTION)
+ _rx_band_map.at(i) = band_lim;
+ else
+ _tx_band_map.at(i) = band_lim;
+ }
+}
+
+
+bool magnesium_radio_control_impl::get_lo_lock_status(const direction_t dir)
+{
+ if (not(bool(_rpcc))) {
+ RFNOC_LOG_WARNING("Reported no LO lock due to lack of RPC connection.");
+ return false;
+ }
+
+ const std::string trx = (dir == RX_DIRECTION) ? "rx" : "tx";
+ const size_t chan = 0; // They're the same after all
+ const double freq = (dir == RX_DIRECTION) ? get_rx_frequency(chan)
+ : get_tx_frequency(chan);
+
+ bool lo_lock =
+ _rpcc->request_with_token<bool>(_rpc_prefix + "get_ad9371_lo_lock", trx);
+ RFNOC_LOG_TRACE("AD9371 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No"));
+ if (lo_lock and _map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) {
+ lo_lock =
+ lo_lock
+ && _rpcc->request_with_token<bool>(_rpc_prefix + "get_lowband_lo_lock", trx);
+ RFNOC_LOG_TRACE(
+ "ADF4351 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No"));
+ }
+
+ return lo_lock;
+}
+
+/**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+std::vector<std::string> magnesium_radio_control_impl::get_gpio_banks() const
+{
+ return {MAGNESIUM_FPGPIO_BANK};
+}
+
+void magnesium_radio_control_impl::set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value)
+{
+ if (bank != MAGNESIUM_FPGPIO_BANK) {
+ RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank);
+ throw uhd::key_error("Invalid GPIO bank!");
+ }
+ if (!gpio_atr::gpio_attr_rev_map.count(attr)) {
+ RFNOC_LOG_ERROR("Invalid GPIO attr: " << attr);
+ throw uhd::key_error("Invalid GPIO attr!");
+ }
+
+ const gpio_atr::gpio_attr_t gpio_attr = gpio_atr::gpio_attr_rev_map.at(attr);
+
+ if (gpio_attr == gpio_atr::GPIO_READBACK) {
+ RFNOC_LOG_WARNING("Cannot set READBACK attr.");
+ return;
+ }
+
+ _fp_gpio->set_gpio_attr(gpio_attr, value);
+}
+
+uint32_t magnesium_radio_control_impl::get_gpio_attr(
+ const std::string& bank, const std::string& attr)
+{
+ if (bank != MAGNESIUM_FPGPIO_BANK) {
+ RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank);
+ throw uhd::key_error("Invalid GPIO bank!");
+ }
+
+ return _fp_gpio->get_attr_reg(usrp::gpio_atr::gpio_attr_rev_map.at(attr));
+}
+
+/******************************************************************************
+ * EEPROM API
+ *****************************************************************************/
+void magnesium_radio_control_impl::set_db_eeprom(const eeprom_map_t& db_eeprom)
+{
+ const size_t db_idx = get_block_id().get_block_count();
+ _rpcc->notify_with_token("set_db_eeprom", db_idx, db_eeprom);
+}
+
+eeprom_map_t magnesium_radio_control_impl::get_db_eeprom()
+{
+ const size_t db_idx = get_block_id().get_block_count();
+ return this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx);
+}
+
+/**************************************************************************
+ * Sensor API
+ *************************************************************************/
+std::vector<std::string> magnesium_radio_control_impl::get_rx_sensor_names(size_t)
+{
+ auto sensor_names = _rpcc->request_with_token<std::vector<std::string>>(
+ this->_rpc_prefix + "get_sensors", "RX");
+ sensor_names.push_back("lo_locked");
+ return sensor_names;
+}
+
+sensor_value_t magnesium_radio_control_impl::get_rx_sensor(const std::string& name, size_t chan)
+{
+ if (name == "lo_locked") {
+ return sensor_value_t(
+ "all_los", this->get_lo_lock_status(RX_DIRECTION), "locked", "unlocked");
+ }
+ return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
+ _rpc_prefix + "get_sensor", "RX", name, chan));
+}
+
+std::vector<std::string> magnesium_radio_control_impl::get_tx_sensor_names(size_t)
+{
+ auto sensor_names = _rpcc->request_with_token<std::vector<std::string>>(
+ this->_rpc_prefix + "get_sensors", "TX");
+ sensor_names.push_back("lo_locked");
+ return sensor_names;
+}
+
+sensor_value_t magnesium_radio_control_impl::get_tx_sensor(const std::string& name, size_t chan)
+{
+ if (name == "lo_locked") {
+ return sensor_value_t(
+ "all_los", this->get_lo_lock_status(TX_DIRECTION), "locked", "unlocked");
+ }
+ return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
+ _rpc_prefix + "get_sensor", "TX", name, chan));
+}
+
+/**************************************************************************
+ * node_t API Calls
+ *************************************************************************/
+void magnesium_radio_control_impl::set_command_time(uhd::time_spec_t time, const size_t chan)
+{
+ node_t::set_command_time(time, chan);
+ _wb_ifaces.at(chan)->set_time(time);
+}
+
+/**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+size_t magnesium_radio_control_impl::get_chan_from_dboard_fe(
+ const std::string& fe, const uhd::direction_t) const
+{
+ if (fe == "0") {
+ return 0;
+ }
+ if (fe == "1") {
+ return 1;
+ }
+ throw uhd::key_error(std::string("[N300] Invalid frontend: ") + fe);
+}
+
+std::string magnesium_radio_control_impl::get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t) const
+{
+ if (chan == 0) {
+ return "0";
+ }
+ if (chan == 1) {
+ return "1";
+ }
+ throw uhd::lookup_error(
+ std::string("[N300] Invalid channel: ") + std::to_string(chan));
+}
+
+std::string magnesium_radio_control_impl::get_fe_name(
+ const size_t, const uhd::direction_t) const
+{
+ return MAGNESIUM_FE_NAME;
+}
+
+// Register the block
+UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
+ magnesium_radio_control, RADIO_BLOCK, N300, "Radio", true, "radio_clk", "bus_clk");
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp
index 165e3c996..d7c721c3b 100644
--- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp
+++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp
@@ -14,12 +14,14 @@
#include "magnesium_ad9371_iface.hpp"
#include "magnesium_cpld_ctrl.hpp"
#include "magnesium_cpld_regs.hpp"
+#include <iostream>
#include <uhd/types/serial.hpp>
#include <uhd/usrp/dboard_manager.hpp>
#include <uhd/usrp/gpio_defs.hpp>
-#include <uhdlib/rfnoc/radio_ctrl_impl.hpp>
-#include <uhdlib/rfnoc/rpc_block_ctrl.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhdlib/rfnoc/radio_control_impl.hpp>
#include <uhdlib/usrp/common/adf435x.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
#include <mutex>
@@ -29,13 +31,9 @@ namespace uhd { namespace rfnoc {
*
* This daughterboard is used on the USRP N310 and N300.
*/
-
-
-class magnesium_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl
+class magnesium_radio_control_impl : public radio_control_impl
{
public:
- typedef boost::shared_ptr<magnesium_radio_ctrl_impl> sptr;
-
//! Frequency bands for RX. Bands are a function of the analog filter banks
enum class rx_band {
INVALID_BAND,
@@ -68,66 +66,109 @@ public:
/************************************************************************
* Structors
***********************************************************************/
- UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(magnesium_radio_ctrl)
- virtual ~magnesium_radio_ctrl_impl();
+ magnesium_radio_control_impl(make_args_ptr make_args);
+ virtual ~magnesium_radio_control_impl();
/************************************************************************
- * API calls
+ * RF API calls
***********************************************************************/
// Note: We use the cached values in radio_ctrl_impl, so most getters are
// not reimplemented here
double set_rate(double rate);
- void set_tx_antenna(const std::string& ant, const size_t chan);
- void set_rx_antenna(const std::string& ant, const size_t chan);
-
+ // Setters
+ void set_tx_antenna(const std::string &ant, const size_t chan);
+ void set_rx_antenna(const std::string &ant, const size_t chan);
double set_tx_frequency(const double freq, const size_t chan);
double set_rx_frequency(const double freq, const size_t chan);
- double get_rx_frequency(const size_t chan);
- double get_tx_frequency(const size_t chan);
+ double set_tx_gain(const double gain, const size_t chan);
+ double set_tx_gain(const double gain, const std::string& name, const size_t chan);
+ double set_rx_gain(const double gain, const size_t chan);
+ double set_rx_gain(const double gain, const std::string& name, const size_t chan);
double set_tx_bandwidth(const double bandwidth, const size_t chan);
double set_rx_bandwidth(const double bandwidth, const size_t chan);
+ void set_tx_gain_profile(const std::string& profile, const size_t chan);
+ void set_rx_gain_profile(const std::string& profile, const size_t chan);
+
+ // Getters
+ std::vector<std::string> get_tx_antennas(const size_t chan) const;
+ std::vector<std::string> get_rx_antennas(const size_t chan) const;
+ uhd::freq_range_t get_tx_frequency_range(const size_t chan) const;
+ uhd::freq_range_t get_rx_frequency_range(const size_t chan) const;
+ std::vector<std::string> get_tx_gain_names(const size_t) const;
+ std::vector<std::string> get_rx_gain_names(const size_t) const;
+ double get_tx_gain(const std::string&, size_t);
+ double get_rx_gain(const std::string&, size_t);
+ uhd::gain_range_t get_tx_gain_range(const size_t) const;
+ uhd::gain_range_t get_tx_gain_range(const std::string&, const size_t) const;
+ uhd::gain_range_t get_rx_gain_range(const size_t) const;
+ uhd::gain_range_t get_rx_gain_range(const std::string&, const size_t) const;
+ std::vector<std::string> get_tx_gain_profile_names(const size_t chan) const;
+ std::vector<std::string> get_rx_gain_profile_names(const size_t chan) const;
+ std::string get_tx_gain_profile(const size_t chan) const;
+ std::string get_rx_gain_profile(const size_t chan) const;
+ uhd::meta_range_t get_tx_bandwidth_range(size_t chan) const;
+ uhd::meta_range_t get_rx_bandwidth_range(size_t chan) const;
- // RX LO
- std::vector<std::string> get_rx_lo_names(const size_t chan);
+ /**************************************************************************
+ * LO Controls
+ *************************************************************************/
+ std::vector<std::string> get_rx_lo_names(const size_t chan) const;
std::vector<std::string> get_rx_lo_sources(
- const std::string& name, const size_t chan);
- freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan);
-
+ const std::string& name, const size_t chan) const;
+ freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan) const;
void set_rx_lo_source(
const std::string& src, const std::string& name, const size_t chan);
- const std::string get_rx_lo_source(const std::string& name, const size_t chan);
-
+ const std::string get_rx_lo_source(const std::string& name, const size_t chan) const;
double set_rx_lo_freq(double freq, const std::string& name, const size_t chan);
double get_rx_lo_freq(const std::string& name, const size_t chan);
-
- // TX LO
- std::vector<std::string> get_tx_lo_names(const size_t chan);
+ std::vector<std::string> get_tx_lo_names(const size_t chan) const;
std::vector<std::string> get_tx_lo_sources(
- const std::string& name, const size_t chan);
+ const std::string& name, const size_t chan) const;
freq_range_t get_tx_lo_freq_range(const std::string& name, const size_t chan);
-
void set_tx_lo_source(
const std::string& src, const std::string& name, const size_t chan);
const std::string get_tx_lo_source(const std::string& name, const size_t chan);
-
- double set_tx_lo_freq(double freq, const std::string& name, const size_t chan);
+ double set_tx_lo_freq(const double freq, const std::string& name, const size_t chan);
double get_tx_lo_freq(const std::string& name, const size_t chan);
- // gain
- double set_tx_gain(const double gain, const size_t chan);
- double set_rx_gain(const double gain, const size_t chan);
- void set_tx_gain_source(
- const std::string& src, const std::string& name, const size_t chan);
- std::string get_tx_gain_source(const std::string& name, const size_t chan);
- void set_rx_gain_source(
- const std::string& src, const std::string& name, const size_t chan);
- std::string get_rx_gain_source(const std::string& name, const size_t chan);
+ /**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+ std::vector<std::string> get_gpio_banks() const;
+ void set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value);
+ uint32_t get_gpio_attr(const std::string& bank, const std::string& attr);
+
+ /**************************************************************************
+ * EEPROM API
+ *************************************************************************/
+ void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom);
+ uhd::eeprom_map_t get_db_eeprom();
+
+ /**************************************************************************
+ * Sensor API
+ *************************************************************************/
+ std::vector<std::string> get_rx_sensor_names(size_t chan);
+ uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan);
+ std::vector<std::string> get_tx_sensor_names(size_t chan);
+ uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan);
- size_t get_chan_from_dboard_fe(const std::string& fe, const direction_t dir);
- std::string get_dboard_fe_from_chan(const size_t chan, const direction_t dir);
+ /**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+ std::string get_slot_name() const { return _radio_slot; }
+ size_t get_chan_from_dboard_fe(
+ const std::string& fe, const uhd::direction_t direction) const;
+ std::string get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t direction) const;
+ std::string get_fe_name(
+ const size_t chan, const uhd::direction_t direction) const;
- void set_rpc_client(uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args);
+ /**************************************************************************
+ * node_t API Calls
+ *************************************************************************/
+ void set_command_time(uhd::time_spec_t time, const size_t chan);
private:
/**************************************************************************
@@ -157,6 +198,10 @@ private:
//! Initialize property tree
void _init_prop_tree();
+ //! Init RPC interaction
+ void _init_mpm();
+
+ //! Set up sensor property nodes
void _init_mpm_sensors(const direction_t dir, const size_t chan_idx);
//! Map a frequency in Hz to an rx_band value. Will return
@@ -240,12 +285,21 @@ private:
//! Additional block args; gets set during set_rpc_client()
uhd::device_addr_t _block_args;
+ //! Reference to the MB controller
+ mpmd_mb_controller::sptr _n310_mb_control;
+
+ //! Reference to the MB timekeeper
+ uhd::rfnoc::mpmd_mb_controller::mpmd_timekeeper::sptr _n3xx_timekeeper;
+
//! Reference to the RPC client
uhd::rpc_client::sptr _rpcc;
//! Reference to the SPI core
uhd::spi_iface::sptr _spi;
+ //! Reference to wb_iface compat adapters (one per channel)
+ std::vector<uhd::timed_wb_iface::sptr> _wb_ifaces;
+
//! Reference to the TX LO
adf435x_iface::sptr _tx_lo;
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_cpld.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_cpld.cpp
index 679816af8..41f99cd68 100644
--- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_cpld.cpp
+++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_cpld.cpp
@@ -6,14 +6,14 @@
#include "magnesium_constants.hpp"
#include "magnesium_cpld_ctrl.hpp"
-#include "magnesium_radio_ctrl_impl.hpp"
+#include "magnesium_radio_control.hpp"
#include <uhd/utils/log.hpp>
using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::rfnoc;
-void magnesium_radio_ctrl_impl::_identify_with_leds(const int identify_duration)
+void magnesium_radio_control_impl::_identify_with_leds(const int identify_duration)
{
auto end_time =
std::chrono::steady_clock::now() + std::chrono::seconds(identify_duration);
@@ -36,7 +36,7 @@ void magnesium_radio_ctrl_impl::_identify_with_leds(const int identify_duration)
_cpld->reset();
}
-void magnesium_radio_ctrl_impl::_update_atr_switches(
+void magnesium_radio_control_impl::_update_atr_switches(
const magnesium_cpld_ctrl::chan_sel_t chan,
const direction_t dir,
const std::string& ant)
@@ -51,8 +51,7 @@ void magnesium_radio_ctrl_impl::_update_atr_switches(
// RX SW1. In all other cases, a TX state toggle (on to idle or vice
// versa) won't trigger a change of the TRX switch.
auto sw_trx = _sw_trx[chan];
- UHD_LOG_TRACE(
- unique_id(), "Updating all RX-ATR related switches for antenna==" << ant);
+ RFNOC_LOG_TRACE("Updating all RX-ATR related switches for antenna==" << ant);
if (ant == "TX/RX") {
rx_sw1 = magnesium_cpld_ctrl::RX_SW1_TRXSWITCHOUTPUT;
sw_trx = magnesium_cpld_ctrl::SW_TRX_RXCHANNELPATH;
@@ -96,7 +95,7 @@ void magnesium_radio_ctrl_impl::_update_atr_switches(
);
}
if (dir == TX_DIRECTION or dir == DX_DIRECTION) {
- UHD_LOG_TRACE(unique_id(), "Updating all TX-ATR related switches...");
+ RFNOC_LOG_TRACE("Updating all TX-ATR related switches...");
_cpld->set_tx_atr_bits(chan,
magnesium_cpld_ctrl::ON,
true, /* LED on */
@@ -117,11 +116,11 @@ void magnesium_radio_ctrl_impl::_update_atr_switches(
};
}
-void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq,
+void magnesium_radio_control_impl::_update_rx_freq_switches(const double freq,
const bool bypass_lnas,
const magnesium_cpld_ctrl::chan_sel_t chan_sel)
{
- UHD_LOG_TRACE(unique_id(),
+ RFNOC_LOG_TRACE(
"Update all RX freq related switches. f=" << freq
<< " Hz, "
"bypass LNAS: "
@@ -142,7 +141,7 @@ void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq,
not bypass_lnas
and (band == rx_band::BAND4 or band == rx_band::BAND5 or band == rx_band::BAND6);
const bool rx_lna2_enable = not bypass_lnas and not rx_lna1_enable;
- UHD_LOG_TRACE(unique_id(),
+ RFNOC_LOG_TRACE(
" Enabling LNA1: " << (rx_lna1_enable ? "Yes" : "No")
<< " Enabling LNA2: " << (rx_lna2_enable ? "Yes" : "No"));
// All the defaults are OK when using the bypass path.
@@ -199,7 +198,7 @@ void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq,
rx_sw6 = magnesium_cpld_ctrl::RX_SW6_UPPERFILTERBANKFROMSWITCH4;
break;
case rx_band::INVALID_BAND:
- UHD_LOG_ERROR(unique_id(), "Cannot map RX frequency to band: " << freq);
+ RFNOC_LOG_ERROR("Cannot map RX frequency to band: " << freq);
break;
default:
UHD_THROW_INVALID_CODE_PATH();
@@ -222,16 +221,15 @@ void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq,
enable_lowband_mixer);
}
-void magnesium_radio_ctrl_impl::_update_tx_freq_switches(const double freq,
+void magnesium_radio_control_impl::_update_tx_freq_switches(const double freq,
const bool bypass_amp,
const magnesium_cpld_ctrl::chan_sel_t chan_sel)
{
- UHD_LOG_TRACE(unique_id(),
- "Update all TX freq related switches. f=" << freq
- << " Hz, "
- "bypass amp: "
- << (bypass_amp ? "Yes" : "No")
- << ", chan=" << chan_sel);
+ RFNOC_LOG_TRACE("Update all TX freq related switches. f="
+ << freq
+ << " Hz, "
+ "bypass amp: "
+ << (bypass_amp ? "Yes" : "No") << ", chan=" << chan_sel);
auto tx_sw1 = magnesium_cpld_ctrl::TX_SW1_SHUTDOWNTXSW1;
auto tx_sw2 = magnesium_cpld_ctrl::TX_SW2_TOTXFILTERLP6400MHZ;
auto tx_sw3 = magnesium_cpld_ctrl::TX_SW3_BYPASSPATHTOTRXSW;
@@ -279,7 +277,7 @@ void magnesium_radio_ctrl_impl::_update_tx_freq_switches(const double freq,
tx_sw3 = magnesium_cpld_ctrl::TX_SW3_TOTXFILTERBANKS;
break;
case tx_band::INVALID_BAND:
- UHD_LOG_ERROR(unique_id(), "Cannot map TX frequency to band: " << freq);
+ RFNOC_LOG_ERROR("Cannot map TX frequency to band: " << freq);
break;
default:
UHD_THROW_INVALID_CODE_PATH();
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_gain.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_gain.cpp
index b66bd2efd..f515b2e33 100644
--- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_gain.cpp
+++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_gain.cpp
@@ -6,63 +6,62 @@
#include "magnesium_constants.hpp"
#include "magnesium_gain_table.hpp"
-#include "magnesium_radio_ctrl_impl.hpp"
-#include <uhd/exception.hpp>
+#include "magnesium_radio_control.hpp"
#include <uhd/utils/log.hpp>
+#include <uhd/exception.hpp>
using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::rfnoc;
using namespace magnesium;
-double magnesium_radio_ctrl_impl::_set_all_gain(
+double magnesium_radio_control_impl::_set_all_gain(
const double gain, const double freq, const size_t chan, const direction_t dir)
{
- UHD_LOG_TRACE(unique_id(),
- __func__ << "(gain=" << gain
- << "dB, "
- "freq="
- << freq
- << " Hz, "
- "chan="
- << chan
- << ", "
- "dir="
- << dir << ")");
+ RFNOC_LOG_TRACE(__func__ << "(gain=" << gain
+ << "dB, "
+ "freq="
+ << freq
+ << " Hz, "
+ "chan="
+ << chan
+ << ", "
+ "dir="
+ << dir);
const size_t ad9371_chan = chan;
auto chan_sel = static_cast<magnesium_cpld_ctrl::chan_sel_t>(chan);
gain_tuple_t gain_tuple;
std::string gp = _gain_profile[dir];
- UHD_LOG_TRACE(unique_id(), "Gain profile: " << gp);
+ RFNOC_LOG_TRACE("Gain profile: " << gp);
if (gp == "manual") {
- UHD_LOG_TRACE(unique_id(), "Manual gain mode. Getting gain from property tree.");
+ RFNOC_LOG_TRACE("Manual gain mode. Getting gain from property tree.");
gain_tuple = {DSA_MAX_GAIN - _dsa_att[dir],
((dir == RX_DIRECTION) ? AD9371_MAX_RX_GAIN : AD9371_MAX_TX_GAIN)
- _ad9371_att[dir],
_amp_bypass[dir]};
} else if (gp.find("default") != gp.npos) {
- UHD_LOG_TRACE(unique_id(), "Getting gain from gain table.");
+ RFNOC_LOG_TRACE("Getting gain from gain table.");
gain_tuple =
(dir == RX_DIRECTION)
? get_rx_gain_tuple(gain, _map_freq_to_rx_band(_rx_band_map, freq))
: get_tx_gain_tuple(gain, _map_freq_to_tx_band(_tx_band_map, freq));
if (gp == "default_rf_filter_bypass_always_on") {
- UHD_LOG_TRACE(unique_id(), "Enable filter bypass for all gains");
+ RFNOC_LOG_TRACE("Enable filter bypass for all gains");
gain_tuple.bypass = true;
} else if (gp == "default_rf_filter_bypass_always_off") {
- UHD_LOG_TRACE(unique_id(), "Disable filter bypass for all gains");
+ RFNOC_LOG_TRACE("Disable filter bypass for all gains");
gain_tuple.bypass = false;
}
} else {
- UHD_LOG_ERROR(unique_id(), "Unsupported gain mode: " << gp);
+ RFNOC_LOG_ERROR("Unsupported gain mode: " << gp);
throw uhd::value_error(
- str(boost::format("[%s] Unsupported gain mode: %s") % unique_id() % gp));
+ str(boost::format("[%s] Unsupported gain mode: %s") % get_unique_id() % gp));
}
const double ad9371_gain =
((dir == RX_DIRECTION) ? AD9371_MAX_RX_GAIN : AD9371_MAX_TX_GAIN)
- gain_tuple.ad9371_att;
- UHD_LOG_TRACE(unique_id(),
+ RFNOC_LOG_TRACE(
"AD9371 attenuation==" << gain_tuple.ad9371_att
<< " dB, "
"AD9371 gain=="
@@ -86,10 +85,10 @@ double magnesium_radio_ctrl_impl::_set_all_gain(
return gain;
}
-double magnesium_radio_ctrl_impl::_get_all_gain(
+double magnesium_radio_control_impl::_get_all_gain(
const size_t /* chan */, const direction_t dir)
{
- UHD_LOG_TRACE(unique_id(), "Getting all gain ");
+ RFNOC_LOG_TRACE("_get_all_gain()");
if (dir == RX_DIRECTION) {
return _all_rx_gain;
}
@@ -99,11 +98,12 @@ double magnesium_radio_ctrl_impl::_get_all_gain(
/******************************************************************************
* DSA Controls
*****************************************************************************/
-double magnesium_radio_ctrl_impl::_dsa_set_att(
+double magnesium_radio_control_impl::_dsa_set_att(
const double att, const size_t chan, const direction_t dir)
{
- UHD_LOG_TRACE(unique_id(),
- __func__ << "(att=" << att << "dB, chan=" << chan << ", dir=" << dir << ")")
+ RFNOC_LOG_TRACE(
+ __func__ << "(att="
+ << "att dB, chan=" << chan << ", dir=" << dir << ")")
const uint32_t dsa_val = 2 * att;
_set_dsa_val(chan, dir, dsa_val);
@@ -116,7 +116,7 @@ double magnesium_radio_ctrl_impl::_dsa_set_att(
return att;
}
-double magnesium_radio_ctrl_impl::_dsa_get_att(
+double magnesium_radio_control_impl::_dsa_get_att(
const size_t /*chan*/, const direction_t dir)
{
if (dir == RX_DIRECTION) {
@@ -125,21 +125,19 @@ double magnesium_radio_ctrl_impl::_dsa_get_att(
return _dsa_tx_att;
}
-void magnesium_radio_ctrl_impl::_set_dsa_val(
+void magnesium_radio_control_impl::_set_dsa_val(
const size_t chan, const direction_t dir, const uint32_t dsa_val)
{
// The DSA register holds 12 bits. The lower 6 bits are for RX, the upper
// 6 bits are for TX.
if (dir == RX_DIRECTION or dir == DX_DIRECTION) {
- UHD_LOG_TRACE(unique_id(),
- __func__ << "(chan=" << chan << ", dir=RX"
- << ", dsa_val=" << dsa_val << ")")
+ RFNOC_LOG_TRACE(__func__ << "(chan=" << chan << ", dir=RX"
+ << ", dsa_val=" << dsa_val << ")")
_gpio[chan]->set_gpio_out(dsa_val, 0x003F);
}
if (dir == TX_DIRECTION or dir == DX_DIRECTION) {
- UHD_LOG_TRACE(unique_id(),
- __func__ << "(chan=" << chan << ", dir=TX"
- << ", dsa_val=" << dsa_val << ")")
+ RFNOC_LOG_TRACE(__func__ << "(chan=" << chan << ", dir=TX"
+ << ", dsa_val=" << dsa_val << ")")
_gpio[chan]->set_gpio_out(dsa_val << 6, 0x0FC0);
}
}
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp
new file mode 100644
index 000000000..db2ec9494
--- /dev/null
+++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp
@@ -0,0 +1,446 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "magnesium_constants.hpp"
+#include "magnesium_radio_control.hpp"
+#include <uhd/transport/chdr.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/usrp/cores/spi_core_3000.hpp>
+#include <uhdlib/rfnoc/reg_iface_adapter.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/make_shared.hpp>
+#include <string>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::rfnoc;
+
+namespace {
+
+enum slave_select_t { SEN_CPLD = 1, SEN_TX_LO = 2, SEN_RX_LO = 4, SEN_PHASE_DAC = 8 };
+
+constexpr double MAGNESIUM_DEFAULT_FREQ = 2.5e9; // Hz
+constexpr double MAGNESIUM_DEFAULT_BANDWIDTH = 100e6; // Hz
+
+} // namespace
+
+void magnesium_radio_control_impl::_init_defaults()
+{
+ RFNOC_LOG_TRACE("_init_defaults()");
+ for (size_t chan = 0; chan < get_num_output_ports(); chan++) {
+ radio_control_impl::set_rx_frequency(MAGNESIUM_DEFAULT_FREQ, chan);
+ radio_control_impl::set_rx_gain(0, chan);
+ radio_control_impl::set_rx_antenna(MAGNESIUM_DEFAULT_RX_ANTENNA, chan);
+ radio_control_impl::set_rx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan);
+ }
+
+ for (size_t chan = 0; chan < get_num_input_ports(); chan++) {
+ radio_control_impl::set_tx_frequency(MAGNESIUM_DEFAULT_FREQ, chan);
+ radio_control_impl::set_tx_gain(0, chan);
+ radio_control_impl::set_tx_antenna(MAGNESIUM_DEFAULT_TX_ANTENNA, chan);
+ radio_control_impl::set_tx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan);
+ }
+
+ const auto block_args = get_block_args();
+ if (block_args.has_key("tx_gain_profile")) {
+ RFNOC_LOG_INFO("Using user specified TX gain profile: " << block_args.get(
+ "tx_gain_profile"));
+ _gain_profile[TX_DIRECTION] = block_args.get("tx_gain_profile");
+ }
+
+ if (block_args.has_key("rx_gain_profile")) {
+ RFNOC_LOG_INFO("Using user specified RX gain profile: " << block_args.get(
+ "rx_gain_profile"));
+ _gain_profile[RX_DIRECTION] = block_args.get("rx_gain_profile");
+ }
+
+ if (block_args.has_key("rx_band_map")) {
+ RFNOC_LOG_INFO("Using user specified RX band limits");
+ _remap_band_limits(block_args.get("rx_band_map"), RX_DIRECTION);
+ }
+
+ if (block_args.has_key("tx_band_map")) {
+ RFNOC_LOG_INFO("Using user specified TX band limits");
+ _remap_band_limits(block_args.get("tx_band_map"), TX_DIRECTION);
+ }
+}
+
+void magnesium_radio_control_impl::_init_peripherals()
+{
+ RFNOC_LOG_TRACE("Initializing peripherals...");
+ RFNOC_LOG_TRACE("Initializing SPI core...");
+ _spi = spi_core_3000::make(
+ [this](uint32_t addr, uint32_t data){ regs().poke32(addr, data, get_command_time(0)); },
+ [this](uint32_t addr){ return regs().peek32(addr, get_command_time(0)); },
+ regmap::REG_SPI_W,
+ 8,
+ regmap::REG_SPI_R);
+ RFNOC_LOG_TRACE("Initializing CPLD...");
+ RFNOC_LOG_TRACE("Creating new CPLD object...");
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 125;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+ RFNOC_LOG_TRACE("Making CPLD object...");
+ _cpld = std::make_shared<magnesium_cpld_ctrl>(
+ [this, spi_config](const uint32_t transaction) { // Write functor
+ this->_spi->write_spi(SEN_CPLD, spi_config, transaction, 24);
+ },
+ [this, spi_config](const uint32_t transaction) { // Read functor
+ return this->_spi->read_spi(SEN_CPLD, spi_config, transaction, 24);
+ });
+ _update_atr_switches(
+ magnesium_cpld_ctrl::BOTH, DX_DIRECTION, radio_control_impl::get_rx_antenna(0));
+ RFNOC_LOG_TRACE("Initializing TX LO...");
+ _tx_lo = adf435x_iface::make_adf4351([this](
+ const std::vector<uint32_t> transactions) {
+ for (const uint32_t transaction : transactions) {
+ this->_spi->write_spi(SEN_TX_LO, spi_config_t::EDGE_RISE, transaction, 32);
+ }
+ });
+ RFNOC_LOG_TRACE("Initializing RX LO...");
+ _rx_lo = adf435x_iface::make_adf4351([this](
+ const std::vector<uint32_t> transactions) {
+ for (const uint32_t transaction : transactions) {
+ this->_spi->write_spi(SEN_RX_LO, spi_config_t::EDGE_RISE, transaction, 32);
+ }
+ });
+
+ _gpio.clear(); // Following the as-if rule, this can get optimized out
+ for (size_t radio_idx = 0; radio_idx < get_num_input_ports(); radio_idx++) {
+ _wb_ifaces.push_back(RFNOC_MAKE_WB_IFACE(0, radio_idx));
+ RFNOC_LOG_TRACE("Initializing GPIOs for channel " << radio_idx);
+ _gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make(
+ _wb_ifaces.back(),
+ n310_regs::DB_GPIO_BASE + radio_idx * n310_regs::DB_GPIO_OFFSET,
+ n310_regs::DB_GPIO_RB + radio_idx * n310_regs::DB_GPIO_OFFSET));
+ // DSA and AD9371 gain bits do *not* toggle on ATR modes. If we ever
+ // connect anything else to this core, we might need to set_atr_mode()
+ // to MODE_ATR on those bits. For now, all bits simply do what they're
+ // told, and don't toggle on RX/TX state changes.
+ _gpio.back()->set_atr_mode(usrp::gpio_atr::MODE_GPIO, // Disable ATR mode
+ usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ _gpio.back()->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs
+ usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ }
+ RFNOC_LOG_TRACE("Initializing front-panel GPIO control...")
+ _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make(_wb_ifaces.front(),
+ n310_regs::FP_GPIO, n310_regs::RB_FP_GPIO);
+}
+
+void magnesium_radio_control_impl::_init_frontend_subtree(
+ uhd::property_tree::sptr subtree, const size_t chan_idx)
+{
+ const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx;
+ const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx;
+ RFNOC_LOG_TRACE("Adding non-RFNoC block properties for channel "
+ << chan_idx << " to prop tree path " << tx_fe_path << " and "
+ << rx_fe_path);
+ // TX Standard attributes
+ subtree->create<std::string>(tx_fe_path / "name").set(get_fe_name(chan_idx, TX_DIRECTION));
+ subtree->create<std::string>(tx_fe_path / "connection").set("IQ");
+ // RX Standard attributes
+ subtree->create<std::string>(rx_fe_path / "name").set(get_fe_name(chan_idx, RX_DIRECTION));
+ subtree->create<std::string>(rx_fe_path / "connection").set("IQ");
+ // TX Antenna
+ subtree->create<std::string>(tx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
+ this->set_tx_antenna(ant, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); });
+ subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
+ .set_publisher([this](){ return get_tx_antennas(0); })
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ });
+ // RX Antenna
+ subtree->create<std::string>(rx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
+ this->set_rx_antenna(ant, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); });
+ subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
+ .set_publisher([this](){ return get_rx_antennas(0); })
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ });
+ // TX frequency
+ subtree->create<double>(tx_fe_path / "freq" / "value")
+ .set_coercer([this, chan_idx](const double freq) {
+ return this->set_tx_frequency(freq, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); });
+ subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
+ .set_publisher([this, chan_idx](){ return get_tx_frequency_range(chan_idx); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ });
+ // RX frequency
+ subtree->create<double>(rx_fe_path / "freq" / "value")
+ .set_coercer([this, chan_idx](const double freq) {
+ return this->set_rx_frequency(freq, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); });
+ subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
+ .set_publisher([this, chan_idx](){ return get_rx_frequency_range(chan_idx); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ });
+ // TX bandwidth
+ subtree->create<double>(tx_fe_path / "bandwidth" / "value")
+ .set(AD9371_TX_MAX_BANDWIDTH)
+ .set_coercer([this, chan_idx](const double bw) {
+ return this->set_tx_bandwidth(bw, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); });
+ subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
+ .set_publisher([this, chan_idx](){ return get_tx_bandwidth_range(chan_idx); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ });
+ // RX bandwidth
+ subtree->create<double>(rx_fe_path / "bandwidth" / "value")
+ .set(AD9371_RX_MAX_BANDWIDTH)
+ .set_coercer([this, chan_idx](const double bw) {
+ return this->set_rx_bandwidth(bw, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() { return this->get_rx_bandwidth(chan_idx); });
+ subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
+ .set_publisher([this, chan_idx](){ return get_rx_bandwidth_range(chan_idx); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ });
+
+ // TX gains
+ std::vector<std::string> tx_gain_names = get_tx_gain_names(chan_idx);
+ tx_gain_names.push_back("all");
+ for (const auto gain_name : tx_gain_names) {
+ subtree->create<double>(tx_fe_path / "gains" / gain_name / "value")
+ .set_coercer([this, chan_idx, gain_name](const double gain) {
+ return this->set_tx_gain(gain, gain_name, chan_idx);
+ })
+ .set_publisher(
+ [this, chan_idx, gain_name]() { return get_tx_gain(gain_name, chan_idx); });
+ subtree->create<meta_range_t>(tx_fe_path / "gains" / gain_name / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update gain range!");
+ })
+ .set_publisher([this, gain_name, chan_idx]() { return get_tx_gain_range(gain_name, chan_idx); });
+ }
+ subtree->create<std::vector<std::string>>(tx_fe_path / "gains/all/profile/options")
+ .set_publisher(
+ [this, chan_idx]() { return get_tx_gain_profile_names(chan_idx); });
+ subtree->create<std::string>(tx_fe_path / "gains/all/profile/value")
+ .set_coercer([this, chan_idx](const std::string& profile) {
+ set_tx_gain_profile(profile, chan_idx);
+ return profile;
+ })
+ .set_publisher([this, chan_idx]() { return get_tx_gain_profile(chan_idx); });
+
+ // RX gains
+ std::vector<std::string> rx_gain_names = get_rx_gain_names(chan_idx);
+ rx_gain_names.push_back("all");
+ for (const auto gain_name : rx_gain_names) {
+ subtree->create<double>(rx_fe_path / "gains" / gain_name / "value")
+ .set_coercer([this, chan_idx, gain_name](const double gain) {
+ return this->set_rx_gain(gain, gain_name, chan_idx);
+ })
+ .set_publisher(
+ [this, chan_idx, gain_name]() { return get_rx_gain(gain_name, chan_idx); });
+ subtree->create<meta_range_t>(rx_fe_path / "gains" / gain_name / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update gain range!");
+ })
+ .set_publisher([this, gain_name, chan_idx]() { return get_rx_gain_range(gain_name, chan_idx); });
+ }
+ subtree->create<std::vector<std::string>>(rx_fe_path / "gains/all/profile/options")
+ .set_publisher(
+ [this, chan_idx]() { return get_rx_gain_profile_names(chan_idx); });
+ subtree->create<std::string>(rx_fe_path / "gains/all/profile/value")
+ .set_coercer([this, chan_idx](const std::string& profile) {
+ set_rx_gain_profile(profile, chan_idx);
+ return profile;
+ })
+ .set_publisher([this, chan_idx]() { return get_rx_gain_profile(chan_idx); });
+
+ // LO Specific
+ // RX LO
+ subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range")
+ .set_publisher([this, chan_idx]() {
+ return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx);
+ });
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / MAGNESIUM_LO1 / "source/options")
+ .set_publisher([this, chan_idx]() {
+ return this->get_rx_lo_sources(MAGNESIUM_LO1, chan_idx);
+ });
+ subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO1 / "source/value")
+ .add_coerced_subscriber([this, chan_idx](std::string src) {
+ this->set_rx_lo_source(src, MAGNESIUM_LO1, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() {
+ return this->get_rx_lo_source(MAGNESIUM_LO1, chan_idx);
+ });
+ subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value")
+ .set_publisher(
+ [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO1, chan_idx); })
+ .set_coercer([this, chan_idx](const double freq) {
+ return this->set_rx_lo_freq(freq, MAGNESIUM_LO1, chan_idx);
+ });
+ subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range")
+ .set_publisher([this, chan_idx]() {
+ return this->get_rx_lo_freq_range(MAGNESIUM_LO2, chan_idx);
+ });
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / MAGNESIUM_LO2 / "source/options")
+ .set_publisher([this, chan_idx]() {
+ return this->get_rx_lo_sources(MAGNESIUM_LO2, chan_idx);
+ });
+
+ subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO2 / "source/value")
+ .add_coerced_subscriber([this, chan_idx](std::string src) {
+ this->set_rx_lo_source(src, MAGNESIUM_LO2, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() {
+ return this->get_rx_lo_source(MAGNESIUM_LO2, chan_idx);
+ });
+ subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value")
+ .set_publisher(
+ [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO2, chan_idx); })
+ .set_coercer([this, chan_idx](double freq) {
+ return this->set_rx_lo_freq(freq, MAGNESIUM_LO2, chan_idx);
+ });
+ // TX LO
+ subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range")
+ .set_publisher([this, chan_idx]() {
+ return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx);
+ });
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / MAGNESIUM_LO1 / "source/options")
+ .set_publisher([this, chan_idx]() {
+ return this->get_tx_lo_sources(MAGNESIUM_LO1, chan_idx);
+ });
+ subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO1 / "source/value")
+ .add_coerced_subscriber([this, chan_idx](std::string src) {
+ this->set_tx_lo_source(src, MAGNESIUM_LO1, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() {
+ return this->get_tx_lo_source(MAGNESIUM_LO1, chan_idx);
+ });
+ subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value ")
+ .set_publisher(
+ [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO1, chan_idx); })
+ .set_coercer([this, chan_idx](double freq) {
+ return this->set_tx_lo_freq(freq, MAGNESIUM_LO1, chan_idx);
+ });
+ subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range")
+ .set_publisher([this, chan_idx]() {
+ return this->get_tx_lo_freq_range(MAGNESIUM_LO2, chan_idx);
+ });
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / MAGNESIUM_LO2 / "source/options")
+ .set_publisher([this, chan_idx]() {
+ return this->get_tx_lo_sources(MAGNESIUM_LO2, chan_idx);
+ });
+ subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO2 / "source/value")
+ .add_coerced_subscriber([this, chan_idx](std::string src) {
+ this->set_tx_lo_source(src, MAGNESIUM_LO2, chan_idx);
+ })
+ .set_publisher([this, chan_idx]() {
+ return this->get_tx_lo_source(MAGNESIUM_LO2, chan_idx);
+ });
+ subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value")
+ .set_publisher(
+ [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO2, chan_idx); })
+ .set_coercer([this, chan_idx](double freq) {
+ return this->set_tx_lo_freq(freq, MAGNESIUM_LO2, chan_idx);
+ });
+
+ // Sensors
+ auto rx_sensor_names = get_rx_sensor_names(chan_idx);
+ for (const auto& sensor_name : rx_sensor_names) {
+ RFNOC_LOG_TRACE("Adding RX sensor " << sensor_name);
+ get_tree()->create<sensor_value_t>(rx_fe_path / "sensors" / sensor_name)
+ .add_coerced_subscriber([](const sensor_value_t&) {
+ throw uhd::runtime_error("Attempting to write to sensor!");
+ })
+ .set_publisher([this, sensor_name, chan_idx]() {
+ return get_rx_sensor(sensor_name, chan_idx);
+ });
+ }
+ auto tx_sensor_names = get_tx_sensor_names(chan_idx);
+ for (const auto& sensor_name : tx_sensor_names) {
+ RFNOC_LOG_TRACE("Adding TX sensor " << sensor_name);
+ get_tree()->create<sensor_value_t>(tx_fe_path / "sensors" / sensor_name)
+ .add_coerced_subscriber([](const sensor_value_t&) {
+ throw uhd::runtime_error("Attempting to write to sensor!");
+ })
+ .set_publisher([this, sensor_name, chan_idx]() {
+ return get_tx_sensor(sensor_name, chan_idx);
+ });
+ }
+}
+
+void magnesium_radio_control_impl::_init_prop_tree()
+{
+ for (size_t chan_idx = 0; chan_idx < MAGNESIUM_NUM_CHANS; chan_idx++) {
+ this->_init_frontend_subtree(get_tree()->subtree(DB_PATH), chan_idx);
+ }
+
+ // DB EEPROM
+ get_tree()->create<eeprom_map_t>("eeprom")
+ .add_coerced_subscriber(
+ [this](const eeprom_map_t& db_eeprom) { set_db_eeprom(db_eeprom); })
+ .set_publisher([this]() { return get_db_eeprom(); });
+}
+
+void magnesium_radio_control_impl::_init_mpm()
+{
+ auto block_args = get_block_args();
+ RFNOC_LOG_TRACE("Instantiating AD9371 control object...");
+ _ad9371 = magnesium_ad9371_iface::uptr(
+ new magnesium_ad9371_iface(_rpcc, (_radio_slot == "A") ? 0 : 1));
+
+ if (block_args.has_key("identify")) {
+ const std::string identify_val = block_args.get("identify");
+ int identify_duration = std::atoi(identify_val.c_str());
+ if (identify_duration == 0) {
+ identify_duration = 5;
+ }
+ RFNOC_LOG_INFO("Running LED identification process for " << identify_duration
+ << " seconds.");
+ _identify_with_leds(identify_duration);
+ }
+
+ // Note: MCR gets set during the init() call (prior to this), which takes
+ // in arguments from the device args. So if block_args contains a
+ // master_clock_rate key, then it should better be whatever the device is
+ // configured to do.
+ _master_clock_rate =
+ _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate");
+ if (block_args.cast<double>("master_clock_rate", _master_clock_rate)
+ != _master_clock_rate) {
+ throw uhd::runtime_error(str(
+ boost::format("Master clock rate mismatch. Device returns %f MHz, "
+ "but should have been %f MHz.")
+ % (_master_clock_rate / 1e6)
+ % (block_args.cast<double>("master_clock_rate", _master_clock_rate) / 1e6)));
+ }
+ RFNOC_LOG_DEBUG("Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz.");
+ set_tick_rate(_master_clock_rate);
+ _n3xx_timekeeper->update_tick_rate(_master_clock_rate);
+ radio_control_impl::set_rate(_master_clock_rate);
+}
+
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp
deleted file mode 100644
index 405d5955e..000000000
--- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp
+++ /dev/null
@@ -1,847 +0,0 @@
-//
-// Copyright 2017 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "magnesium_radio_ctrl_impl.hpp"
-#include "magnesium_constants.hpp"
-#include "magnesium_gain_table.hpp"
-#include <uhd/exception.hpp>
-#include <uhd/rfnoc/node_ctrl_base.hpp>
-#include <uhd/transport/chdr.hpp>
-#include <uhd/types/direction.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/utils/math.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/format.hpp>
-#include <boost/make_shared.hpp>
-#include <cmath>
-#include <cstdlib>
-#include <sstream>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-using namespace uhd::math::fp_compare;
-
-namespace {
-/**************************************************************************
- * ADF4351 Controls
- *************************************************************************/
-/*!
- * \param lo_iface Reference to the LO object
- * \param freq Frequency (in Hz) of the tone to be generated from the LO
- * \param ref_clock_freq Frequency (in Hz) of the reference clock at the
- * PLL input of the LO
- * \param int_n_mode Integer-N mode on or off
- */
-double _lo_set_frequency(adf435x_iface::sptr lo_iface,
- const double freq,
- const double ref_clock_freq,
- const bool int_n_mode)
-{
- UHD_LOG_TRACE("MG/ADF4351",
- "Attempting to tune low band LO to " << freq << " Hz with ref clock freq "
- << ref_clock_freq);
- lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED);
- lo_iface->set_reference_freq(ref_clock_freq);
- lo_iface->set_prescaler(adf435x_iface::PRESCALER_4_5);
- const double actual_freq = lo_iface->set_frequency(freq, int_n_mode);
- lo_iface->set_output_power(
- adf435x_iface::RF_OUTPUT_A, adf435x_iface::OUTPUT_POWER_2DBM);
- lo_iface->set_output_power(
- adf435x_iface::RF_OUTPUT_B, adf435x_iface::OUTPUT_POWER_2DBM);
- lo_iface->set_charge_pump_current(adf435x_iface::CHARGE_PUMP_CURRENT_0_31MA);
- return actual_freq;
-}
-
-/*! Configure and enable LO
- *
- * Will tune it to requested frequency and enable outputs.
- *
- * \param lo_iface Reference to the LO object
- * \param lo_freq Frequency (in Hz) of the tone to be generated from the LO
- * \param ref_clock_freq Frequency (in Hz) of the reference clock at the
- * PLL input of the LO
- * \param int_n_mode Integer-N mode on or off
- * \returns the actual frequency the LO is running at
- */
-double _lo_enable(adf435x_iface::sptr lo_iface,
- const double lo_freq,
- const double ref_clock_freq,
- const bool int_n_mode)
-{
- const double actual_lo_freq =
- _lo_set_frequency(lo_iface, lo_freq, ref_clock_freq, int_n_mode);
- lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, true);
- lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, true);
- lo_iface->commit();
- return actual_lo_freq;
-}
-
-/*! Disable LO
- */
-void _lo_disable(adf435x_iface::sptr lo_iface)
-{
- lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, false);
- lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, false);
- lo_iface->commit();
-}
-} // namespace
-
-
-/******************************************************************************
- * Structors
- *****************************************************************************/
-UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(magnesium_radio_ctrl)
-{
- UHD_LOG_TRACE(unique_id(), "Entering magnesium_radio_ctrl_impl ctor...");
- const char radio_slot_name[2] = {'A', 'B'};
- _radio_slot = radio_slot_name[get_block_id().get_block_count()];
- UHD_LOG_TRACE(unique_id(), "Radio slot: " << _radio_slot);
- _rpc_prefix = (_radio_slot == "A") ? "db_0_" : "db_1_";
-
- _init_defaults();
- _init_peripherals();
- _init_prop_tree();
-}
-
-magnesium_radio_ctrl_impl::~magnesium_radio_ctrl_impl()
-{
- UHD_LOG_TRACE(unique_id(), "magnesium_radio_ctrl_impl::dtor() ");
-}
-
-
-/******************************************************************************
- * API Calls
- *****************************************************************************/
-double magnesium_radio_ctrl_impl::set_rate(double requested_rate)
-{
- meta_range_t rates;
- for (const double rate : MAGNESIUM_RADIO_RATES) {
- rates.push_back(range_t(rate));
- }
-
- const double rate = rates.clip(requested_rate);
- if (!math::frequencies_are_equal(requested_rate, rate)) {
- UHD_LOG_WARNING(unique_id(),
- "Coercing requested sample rate from " << (requested_rate / 1e6) << " to "
- << (rate / 1e6));
- }
-
- const double current_rate = get_rate();
- if (math::frequencies_are_equal(current_rate, rate)) {
- UHD_LOG_DEBUG(
- unique_id(), "Rate is already at " << rate << ". Skipping set_rate()");
- return current_rate;
- }
-
- std::lock_guard<std::mutex> l(_set_lock);
- // Now commit to device. First, disable LOs.
- _lo_disable(_tx_lo);
- _lo_disable(_rx_lo);
- const double new_rate = _ad9371->set_master_clock_rate(rate);
- // Frequency settings apply to both channels, no loop needed. Will also
- // re-enable the lowband LOs if they were used.
- set_rx_frequency(get_rx_frequency(0), 0);
- set_tx_frequency(get_tx_frequency(0), 0);
- // Gain and bandwidth need to be looped:
- for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) {
- set_rx_gain(get_rx_gain(radio_idx), radio_idx);
- set_tx_gain(get_rx_gain(radio_idx), radio_idx);
- set_rx_bandwidth(get_rx_bandwidth(radio_idx), radio_idx);
- set_tx_bandwidth(get_tx_bandwidth(radio_idx), radio_idx);
- }
- radio_ctrl_impl::set_rate(new_rate);
- return new_rate;
-}
-
-void magnesium_radio_ctrl_impl::set_tx_antenna(const std::string& ant, const size_t chan)
-{
- if (ant != get_tx_antenna(chan)) {
- throw uhd::value_error(
- str(boost::format("[%s] Requesting invalid TX antenna value: %s")
- % unique_id() % ant));
- }
- // We can't actually set the TX antenna, so let's stop here.
-}
-
-void magnesium_radio_ctrl_impl::set_rx_antenna(const std::string& ant, const size_t chan)
-{
- UHD_ASSERT_THROW(chan <= MAGNESIUM_NUM_CHANS);
- if (std::find(MAGNESIUM_RX_ANTENNAS.begin(), MAGNESIUM_RX_ANTENNAS.end(), ant)
- == MAGNESIUM_RX_ANTENNAS.end()) {
- throw uhd::value_error(
- str(boost::format("[%s] Requesting invalid RX antenna value: %s")
- % unique_id() % ant));
- }
- UHD_LOG_TRACE(unique_id(), "Setting RX antenna to " << ant << " for chan " << chan);
- magnesium_cpld_ctrl::chan_sel_t chan_sel = chan == 0 ? magnesium_cpld_ctrl::CHAN1
- : magnesium_cpld_ctrl::CHAN2;
- _update_atr_switches(chan_sel, RX_DIRECTION, ant);
-
- radio_ctrl_impl::set_rx_antenna(ant, chan);
-}
-
-double magnesium_radio_ctrl_impl::set_tx_frequency(
- const double req_freq, const size_t chan)
-{
- const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq);
- UHD_LOG_TRACE(unique_id(), "set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
- _desired_rf_freq[TX_DIRECTION] = freq;
- std::lock_guard<std::mutex> l(_set_lock);
- // We need to set the switches on both channels, because they share an LO.
- // This way, if we tune channel 0 it will not put channel 1 into a bad
- // state.
- _update_tx_freq_switches(freq, _tx_bypass_amp, magnesium_cpld_ctrl::BOTH);
- const std::string ad9371_source = this->get_tx_lo_source(MAGNESIUM_LO1, chan);
- const std::string adf4351_source = this->get_tx_lo_source(MAGNESIUM_LO2, chan);
- UHD_ASSERT_THROW(adf4351_source == "internal");
- double coerced_if_freq = freq;
-
- if (_map_freq_to_tx_band(_tx_band_map, freq) == tx_band::LOWBAND) {
- _is_low_band[TX_DIRECTION] = true;
- const double desired_low_freq = MAGNESIUM_TX_IF_FREQ - freq;
- coerced_if_freq =
- this->_set_tx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan)
- + freq;
- UHD_LOG_TRACE(unique_id(), "coerced_if_freq = " << coerced_if_freq);
- } else {
- _is_low_band[TX_DIRECTION] = false;
- _lo_disable(_tx_lo);
- }
- // external LO required to tune at 2xdesired_frequency.
- const double desired_if_freq = (ad9371_source == "internal") ? coerced_if_freq
- : 2 * coerced_if_freq;
-
- this->_set_tx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan);
- this->_update_freq(chan, TX_DIRECTION);
- this->_update_gain(chan, TX_DIRECTION);
- return radio_ctrl_impl::get_tx_frequency(chan);
-}
-
-void magnesium_radio_ctrl_impl::_update_gain(
- const size_t chan, const uhd::direction_t dir)
-{
- const std::string fe = (dir == TX_DIRECTION) ? "tx_frontends" : "rx_frontends";
- const double freq = (dir == TX_DIRECTION) ? this->get_tx_frequency(chan)
- : this->get_rx_frequency(chan);
- this->_set_all_gain(this->_get_all_gain(chan, dir), freq, chan, dir);
-}
-
-void magnesium_radio_ctrl_impl::_update_freq(
- const size_t chan, const uhd::direction_t dir)
-{
- const std::string ad9371_source = dir == TX_DIRECTION
- ? this->get_tx_lo_source(MAGNESIUM_LO1, chan)
- : this->get_rx_lo_source(MAGNESIUM_LO1, chan);
-
- const double ad9371_freq = ad9371_source == "external" ? _ad9371_freq[dir] / 2
- : _ad9371_freq[dir];
- const double rf_freq = _is_low_band[dir] ? ad9371_freq - _adf4351_freq[dir]
- : ad9371_freq;
-
- UHD_LOG_TRACE(unique_id(), "RF freq = " << rf_freq);
- UHD_ASSERT_THROW(fp_compare_epsilon<double>(rf_freq) >= 0);
- UHD_ASSERT_THROW(fp_compare_epsilon<double>(std::abs(rf_freq - _desired_rf_freq[dir]))
- <= _master_clock_rate / 2);
- if (dir == RX_DIRECTION) {
- radio_ctrl_impl::set_rx_frequency(rf_freq, chan);
- } else if (dir == TX_DIRECTION) {
- radio_ctrl_impl::set_tx_frequency(rf_freq, chan);
- } else {
- UHD_THROW_INVALID_CODE_PATH();
- }
-}
-
-double magnesium_radio_ctrl_impl::set_rx_frequency(
- const double req_freq, const size_t chan)
-{
- const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq);
- UHD_LOG_TRACE(unique_id(), "set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
- _desired_rf_freq[RX_DIRECTION] = freq;
- std::lock_guard<std::mutex> l(_set_lock);
- // We need to set the switches on both channels, because they share an LO.
- // This way, if we tune channel 0 it will not put channel 1 into a bad
- // state.
- _update_rx_freq_switches(freq, _rx_bypass_lnas, magnesium_cpld_ctrl::BOTH);
- const std::string ad9371_source = this->get_rx_lo_source(MAGNESIUM_LO1, chan);
- const std::string adf4351_source = this->get_rx_lo_source(MAGNESIUM_LO2, chan);
- UHD_ASSERT_THROW(adf4351_source == "internal");
- double coerced_if_freq = freq;
-
- if (_map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) {
- _is_low_band[RX_DIRECTION] = true;
- const double desired_low_freq = MAGNESIUM_RX_IF_FREQ - freq;
- coerced_if_freq =
- this->_set_rx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan)
- + freq;
- UHD_LOG_TRACE(unique_id(), "coerced_if_freq = " << coerced_if_freq);
- } else {
- _is_low_band[RX_DIRECTION] = false;
- _lo_disable(_rx_lo);
- }
- // external LO required to tune at 2xdesired_frequency.
- const double desired_if_freq = ad9371_source == "internal" ? coerced_if_freq
- : 2 * coerced_if_freq;
-
- this->_set_rx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan);
-
- this->_update_freq(chan, RX_DIRECTION);
- this->_update_gain(chan, RX_DIRECTION);
-
- return radio_ctrl_impl::get_rx_frequency(chan);
-}
-
-double magnesium_radio_ctrl_impl::get_tx_frequency(const size_t chan)
-{
- UHD_LOG_TRACE(unique_id(), "get_tx_frequency(chan=" << chan << ")");
- return radio_ctrl_impl::get_tx_frequency(chan);
-}
-
-double magnesium_radio_ctrl_impl::get_rx_frequency(const size_t chan)
-{
- UHD_LOG_TRACE(unique_id(), "get_rx_frequency(chan=" << chan << ")");
- return radio_ctrl_impl::get_rx_frequency(chan);
-}
-double magnesium_radio_ctrl_impl::set_rx_bandwidth(
- const double bandwidth, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- _ad9371->set_bandwidth(bandwidth, chan, RX_DIRECTION);
- // FIXME: setting analog bandwidth on AD9371 take no effect.
- // Remove this warning when ADI can confirm that it works.
- UHD_LOG_WARNING(unique_id(),
- "set_rx_bandwidth take no effect on AD9371. "
- "Default analog bandwidth is 100MHz");
- return AD9371_RX_MAX_BANDWIDTH;
-}
-
-double magnesium_radio_ctrl_impl::set_tx_bandwidth(
- const double bandwidth, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- _ad9371->set_bandwidth(bandwidth, chan, TX_DIRECTION);
- // FIXME: setting analog bandwidth on AD9371 take no effect.
- // Remove this warning when ADI can confirm that it works.
- UHD_LOG_WARNING(unique_id(),
- "set_tx_bandwidth take no effect on AD9371. "
- "Default analog bandwidth is 100MHz");
- return AD9371_TX_MAX_BANDWIDTH;
-}
-
-double magnesium_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(), "set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
- const double coerced_gain =
- _set_all_gain(gain, this->get_tx_frequency(chan), chan, TX_DIRECTION);
- radio_ctrl_impl::set_tx_gain(coerced_gain, chan);
- return coerced_gain;
-}
-
-double magnesium_radio_ctrl_impl::_set_tx_gain(
- const std::string& name, const double gain, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(),
- "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")");
- UHD_LOG_TRACE(unique_id(),
- "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")");
- double clip_gain = 0;
- if (name == MAGNESIUM_GAIN1) {
- clip_gain = uhd::clip(gain, AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN);
- _ad9371_att[TX_DIRECTION] = clip_gain;
- } else if (name == MAGNESIUM_GAIN2) {
- clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN);
- _dsa_att[TX_DIRECTION] = clip_gain;
- } else if (name == MAGNESIUM_AMP) {
- clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN;
- _amp_bypass[TX_DIRECTION] = clip_gain == 0.0;
- } else {
- throw uhd::value_error("Could not find gain element " + name);
- }
- UHD_LOG_TRACE(unique_id(), "_set_tx_gain calling update gain");
- this->_set_all_gain(this->_get_all_gain(chan, TX_DIRECTION),
- this->get_tx_frequency(chan),
- chan,
- TX_DIRECTION);
- return clip_gain;
-}
-
-double magnesium_radio_ctrl_impl::_get_tx_gain(
- const std::string& name, const size_t /*chan*/
-)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- if (name == MAGNESIUM_GAIN1) {
- return _ad9371_att[TX_DIRECTION];
- } else if (name == MAGNESIUM_GAIN2) {
- return _dsa_att[TX_DIRECTION];
- } else if (name == MAGNESIUM_AMP) {
- return _amp_bypass[TX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN;
- } else {
- throw uhd::value_error("Could not find gain element " + name);
- }
-}
-
-double magnesium_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(), "set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
- const double coerced_gain =
- _set_all_gain(gain, this->get_rx_frequency(chan), chan, RX_DIRECTION);
- radio_ctrl_impl::set_rx_gain(coerced_gain, chan);
- return coerced_gain;
-}
-
-double magnesium_radio_ctrl_impl::_set_rx_gain(
- const std::string& name, const double gain, const size_t chan)
-{
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(),
- "_set_rx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")");
- double clip_gain = 0;
- if (name == MAGNESIUM_GAIN1) {
- clip_gain = uhd::clip(gain, AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN);
- _ad9371_att[RX_DIRECTION] = clip_gain;
- } else if (name == MAGNESIUM_GAIN2) {
- clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN);
- _dsa_att[RX_DIRECTION] = clip_gain;
- } else if (name == MAGNESIUM_AMP) {
- clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN;
- _amp_bypass[RX_DIRECTION] = clip_gain == 0.0;
- } else {
- throw uhd::value_error("Could not find gain element " + name);
- }
- UHD_LOG_TRACE(unique_id(), "_set_rx_gain calling update gain");
- this->_set_all_gain(this->_get_all_gain(chan, RX_DIRECTION),
- this->get_rx_frequency(chan),
- chan,
- RX_DIRECTION);
- return clip_gain; // not really any coerced here (only clip) for individual gain
-}
-
-double magnesium_radio_ctrl_impl::_get_rx_gain(
- const std::string& name, const size_t /*chan*/
-)
-{
- std::lock_guard<std::mutex> l(_set_lock);
-
- if (name == MAGNESIUM_GAIN1) {
- return _ad9371_att[RX_DIRECTION];
- } else if (name == MAGNESIUM_GAIN2) {
- return _dsa_att[RX_DIRECTION];
- } else if (name == MAGNESIUM_AMP) {
- return _amp_bypass[RX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN;
- } else {
- throw uhd::value_error("Could not find gain element " + name);
- }
-}
-
-std::vector<std::string> magnesium_radio_ctrl_impl::get_rx_lo_names(const size_t /*chan*/
-)
-{
- return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2};
-}
-
-std::vector<std::string> magnesium_radio_ctrl_impl::get_rx_lo_sources(
- const std::string& name, const size_t /*chan*/
-)
-{
- if (name == MAGNESIUM_LO2) {
- return std::vector<std::string>{"internal"};
- } else if (name == MAGNESIUM_LO1) {
- return std::vector<std::string>{"internal", "external"};
- } else {
- throw uhd::value_error("Could not find LO stage " + name);
- }
-}
-
-freq_range_t magnesium_radio_ctrl_impl::get_rx_lo_freq_range(
- const std::string& name, const size_t /*chan*/
-)
-{
- if (name == MAGNESIUM_LO1) {
- return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ};
- } else if (name == MAGNESIUM_LO2) {
- return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ};
- } else {
- throw uhd::value_error("Could not find LO stage " + name);
- }
-}
-
-void magnesium_radio_ctrl_impl::set_rx_lo_source(
- const std::string& src, const std::string& name, const size_t /*chan*/
-)
-{
- // TODO: checking what options are there
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(), "Setting RX LO " << name << " to " << src);
-
- if (name == MAGNESIUM_LO1) {
- _ad9371->set_lo_source(src, RX_DIRECTION);
- } else {
- UHD_LOG_ERROR(unique_id(),
- "RX LO " << name << " does not support setting source to " << src);
- }
-}
-
-const std::string magnesium_radio_ctrl_impl::get_rx_lo_source(
- const std::string& name, const size_t /*chan*/
-)
-{
- if (name == MAGNESIUM_LO1) {
- // TODO: should we use this from cache?
- return _ad9371->get_lo_source(RX_DIRECTION);
- }
- return "internal";
-}
-
-double magnesium_radio_ctrl_impl::_set_rx_lo_freq(const std::string source,
- const std::string name,
- const double freq,
- const size_t chan)
-{
- double coerced_lo_freq = freq;
- if (source != "internal") {
- UHD_LOG_WARNING(
- unique_id(), "LO source is not internal. This set frequency will be ignored");
- if (name == MAGNESIUM_LO1) {
- // handle ad9371 external LO case
- coerced_lo_freq = freq;
- _ad9371_freq[RX_DIRECTION] = coerced_lo_freq;
- }
- } else {
- if (name == MAGNESIUM_LO1) {
- coerced_lo_freq = _ad9371->set_frequency(freq, chan, RX_DIRECTION);
- _ad9371_freq[RX_DIRECTION] = coerced_lo_freq;
- } else if (name == MAGNESIUM_LO2) {
- // TODO: no hardcode the init_n_mode
- coerced_lo_freq = _lo_enable(_rx_lo, freq, _master_clock_rate, false);
- _adf4351_freq[RX_DIRECTION] = coerced_lo_freq;
- } else {
- UHD_LOG_WARNING(unique_id(),
- "There's no LO with this name of "
- << name << " in the system. This set rx lo freq will be ignored");
- };
- }
- return coerced_lo_freq;
-}
-
-double magnesium_radio_ctrl_impl::set_rx_lo_freq(
- double freq, const std::string& name, const size_t chan)
-{
- UHD_LOG_TRACE(
- unique_id(), "Setting rx lo frequency for " << name << " with freq = " << freq);
- std::lock_guard<std::mutex> l(_set_lock);
- std::string source = this->get_rx_lo_source(name, chan);
- const double coerced_lo_freq = this->_set_rx_lo_freq(source, name, freq, chan);
- this->_update_freq(chan, RX_DIRECTION);
- this->_update_gain(chan, RX_DIRECTION);
- return coerced_lo_freq;
-}
-
-double magnesium_radio_ctrl_impl::get_rx_lo_freq(
- const std::string& name, const size_t chan)
-{
- UHD_LOG_TRACE(unique_id(), "Getting rx lo frequency for " << name);
- std::string source = this->get_rx_lo_source(name, chan);
- if (name == MAGNESIUM_LO1) {
- return _ad9371_freq[RX_DIRECTION];
- } else if (name == "adf4531") {
- return _adf4351_freq[RX_DIRECTION];
- } else {
- UHD_LOG_ERROR(unique_id(),
- "There's no LO with this name of "
- << name << " in the system. This set rx lo freq will be ignored");
- }
- UHD_THROW_INVALID_CODE_PATH();
-}
-
-// TX LO
-std::vector<std::string> magnesium_radio_ctrl_impl::get_tx_lo_names(const size_t /*chan*/
-)
-{
- return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2};
-}
-
-std::vector<std::string> magnesium_radio_ctrl_impl::get_tx_lo_sources(
- const std::string& name, const size_t /*chan*/
-)
-{
- if (name == MAGNESIUM_LO2) {
- return std::vector<std::string>{"internal"};
- } else if (name == MAGNESIUM_LO1) {
- return std::vector<std::string>{"internal", "external"};
- } else {
- throw uhd::value_error("Could not find LO stage " + name);
- }
-}
-
-freq_range_t magnesium_radio_ctrl_impl::get_tx_lo_freq_range(
- const std::string& name, const size_t /*chan*/
-)
-{
- if (name == MAGNESIUM_LO2) {
- return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ};
- } else if (name == MAGNESIUM_LO1) {
- return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ};
- } else {
- throw uhd::value_error("Could not find LO stage " + name);
- }
-}
-
-void magnesium_radio_ctrl_impl::set_tx_lo_source(
- const std::string& src, const std::string& name, const size_t /*chan*/
-)
-{
- // TODO: checking what options are there
- std::lock_guard<std::mutex> l(_set_lock);
- UHD_LOG_TRACE(unique_id(), "Setting TX LO " << name << " to " << src);
- if (name == MAGNESIUM_LO1) {
- _ad9371->set_lo_source(src, TX_DIRECTION);
- } else {
- UHD_LOG_ERROR(unique_id(),
- "TX LO " << name << " does not support setting source to " << src);
- }
-}
-
-const std::string magnesium_radio_ctrl_impl::get_tx_lo_source(
- const std::string& name, const size_t /*chan*/
-)
-{
- if (name == MAGNESIUM_LO1) {
- // TODO: should we use this from cache?
- return _ad9371->get_lo_source(TX_DIRECTION);
- }
- return "internal";
-}
-
-double magnesium_radio_ctrl_impl::_set_tx_lo_freq(const std::string source,
- const std::string name,
- const double freq,
- const size_t chan)
-{
- double coerced_lo_freq = freq;
- if (source != "internal") {
- UHD_LOG_WARNING(
- unique_id(), "LO source is not internal. This set frequency will be ignored");
- if (name == MAGNESIUM_LO1) {
- // handle ad9371 external LO case
- coerced_lo_freq = freq;
- _ad9371_freq[TX_DIRECTION] = coerced_lo_freq;
- }
- } else {
- if (name == MAGNESIUM_LO1) {
- coerced_lo_freq = _ad9371->set_frequency(freq, chan, TX_DIRECTION);
- _ad9371_freq[TX_DIRECTION] = coerced_lo_freq;
- } else if (name == MAGNESIUM_LO2) {
- // TODO: no hardcode the int_n_mode
- const bool int_n_mode = false;
- coerced_lo_freq = _lo_enable(_tx_lo, freq, _master_clock_rate, int_n_mode);
- _adf4351_freq[TX_DIRECTION] = coerced_lo_freq;
- } else {
- UHD_LOG_WARNING(unique_id(),
- "There's no LO with this name of "
- << name << " in the system. This set tx lo freq will be ignored");
- };
- }
- return coerced_lo_freq;
-}
-
-double magnesium_radio_ctrl_impl::set_tx_lo_freq(
- double freq, const std::string& name, const size_t chan)
-{
- UHD_LOG_TRACE(
- unique_id(), "Setting tx lo frequency for " << name << " with freq = " << freq);
- std::string source = this->get_tx_lo_source(name, chan);
- const double return_freq = this->_set_tx_lo_freq(source, name, freq, chan);
- this->_update_freq(chan, TX_DIRECTION);
- this->_update_gain(chan, TX_DIRECTION);
- return return_freq;
-}
-
-double magnesium_radio_ctrl_impl::get_tx_lo_freq(
- const std::string& name, const size_t chan)
-{
- UHD_LOG_TRACE(unique_id(), "Getting tx lo frequency for " << name);
- std::string source = this->get_tx_lo_source(name, chan);
- if (name == MAGNESIUM_LO1) {
- return _ad9371_freq[TX_DIRECTION];
- } else if (name == MAGNESIUM_LO2) {
- return _adf4351_freq[TX_DIRECTION];
- } else {
- UHD_LOG_ERROR(
- unique_id(), "There's no LO with this name of " << name << " in the system.");
- };
-
- UHD_THROW_INVALID_CODE_PATH();
-}
-
-
-size_t magnesium_radio_ctrl_impl::get_chan_from_dboard_fe(
- const std::string& fe, const direction_t /* dir */
-)
-{
- return boost::lexical_cast<size_t>(fe);
-}
-
-std::string magnesium_radio_ctrl_impl::get_dboard_fe_from_chan(
- const size_t chan, const direction_t /* dir */
-)
-{
- return std::to_string(chan);
-}
-
-
-void magnesium_radio_ctrl_impl::_remap_band_limits(
- const std::string band_map, const uhd::direction_t dir)
-{
- const size_t dflt_band_size = (dir == RX_DIRECTION) ? _rx_band_map.size()
- : _tx_band_map.size();
-
- std::vector<std::string> band_map_split;
- double band_lim;
-
- UHD_LOG_DEBUG(unique_id(), "Using user specified frequency band limits");
- boost::split(band_map_split, band_map, boost::is_any_of(";"));
- if (band_map_split.size() != dflt_band_size) {
- throw uhd::runtime_error((
- boost::format(
- "size %s of given frequency band map doesn't match the required size: %s")
- % band_map_split.size() % dflt_band_size)
- .str());
- }
- UHD_LOG_DEBUG(unique_id(), "newly used band limits: ");
- for (size_t i = 0; i < band_map_split.size(); i++) {
- try {
- band_lim = std::stod(band_map_split.at(i));
- } catch (...) {
- throw uhd::value_error(
- (boost::format("error while converting given frequency string %s "
- "to a double value")
- % band_map_split.at(i))
- .str());
- }
- UHD_LOG_DEBUG(unique_id(), "band " << i << " limit: " << band_lim << "Hz");
- if (dir == RX_DIRECTION)
- _rx_band_map.at(i) = band_lim;
- else
- _tx_band_map.at(i) = band_lim;
- }
-}
-
-
-void magnesium_radio_ctrl_impl::set_rpc_client(
- uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args)
-{
- _rpcc = rpcc;
- _block_args = block_args;
- UHD_LOG_TRACE(unique_id(), "Instantiating AD9371 control object...");
- _ad9371 = magnesium_ad9371_iface::uptr(
- new magnesium_ad9371_iface(_rpcc, (_radio_slot == "A") ? 0 : 1));
-
- if (block_args.has_key("identify")) {
- const std::string identify_val = block_args.get("identify");
- int identify_duration = std::atoi(identify_val.c_str());
- if (identify_duration == 0) {
- identify_duration = 5;
- }
- UHD_LOG_INFO(unique_id(),
- "Running LED identification process for " << identify_duration
- << " seconds.");
- _identify_with_leds(identify_duration);
- }
-
- if (block_args.has_key("tx_gain_profile")) {
- UHD_LOG_INFO(unique_id(),
- "Using user specified TX gain profile: " << block_args.get(
- "tx_gain_profile"));
- _gain_profile[TX_DIRECTION] = block_args.get("tx_gain_profile");
- }
-
- if (block_args.has_key("rx_gain_profile")) {
- UHD_LOG_INFO(unique_id(),
- "Using user specified RX gain profile: " << block_args.get(
- "rx_gain_profile"));
- _gain_profile[RX_DIRECTION] = block_args.get("rx_gain_profile");
- }
-
- if (block_args.has_key("rx_band_map")) {
- UHD_LOG_INFO(unique_id(), "Using user specified RX band limits");
- _remap_band_limits(block_args.get("rx_band_map"), RX_DIRECTION);
- }
-
- if (block_args.has_key("tx_band_map")) {
- UHD_LOG_INFO(unique_id(), "Using user specified TX band limits");
- _remap_band_limits(block_args.get("tx_band_map"), TX_DIRECTION);
- }
-
- // Note: MCR gets set during the init() call (prior to this), which takes
- // in arguments from the device args. So if block_args contains a
- // master_clock_rate key, then it should better be whatever the device is
- // configured to do.
- _master_clock_rate =
- _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate");
- if (block_args.cast<double>("master_clock_rate", _master_clock_rate)
- != _master_clock_rate) {
- throw uhd::runtime_error(str(
- boost::format("Master clock rate mismatch. Device returns %f MHz, "
- "but should have been %f MHz.")
- % (_master_clock_rate / 1e6)
- % (block_args.cast<double>("master_clock_rate", _master_clock_rate) / 1e6)));
- }
- UHD_LOG_DEBUG(
- unique_id(), "Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz.");
- radio_ctrl_impl::set_rate(_master_clock_rate);
-
- // EEPROM paths subject to change FIXME
- const size_t db_idx = get_block_id().get_block_count();
- _tree->access<eeprom_map_t>(_root_path / "eeprom")
- .add_coerced_subscriber([this, db_idx](const eeprom_map_t& db_eeprom) {
- this->_rpcc->notify_with_token("set_db_eeprom", db_idx, db_eeprom);
- })
- .set_publisher([this, db_idx]() {
- return this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx);
- });
-
- // Init sensors
- for (const auto& dir : std::vector<direction_t>{RX_DIRECTION, TX_DIRECTION}) {
- for (size_t chan_idx = 0; chan_idx < MAGNESIUM_NUM_CHANS; chan_idx++) {
- _init_mpm_sensors(dir, chan_idx);
- }
- }
-}
-
-bool magnesium_radio_ctrl_impl::get_lo_lock_status(const direction_t dir)
-{
- if (not(bool(_rpcc))) {
- UHD_LOG_DEBUG(unique_id(), "Reported no LO lock due to lack of RPC connection.");
- return false;
- }
-
- const std::string trx = (dir == RX_DIRECTION) ? "rx" : "tx";
- const size_t chan = 0; // They're the same after all
- const double freq = (dir == RX_DIRECTION) ? get_rx_frequency(chan)
- : get_tx_frequency(chan);
-
- bool lo_lock =
- _rpcc->request_with_token<bool>(_rpc_prefix + "get_ad9371_lo_lock", trx);
- UHD_LOG_TRACE(unique_id(),
- "AD9371 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No"));
- if (lo_lock and _map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) {
- lo_lock =
- lo_lock
- && _rpcc->request_with_token<bool>(_rpc_prefix + "get_lowband_lo_lock", trx);
- UHD_LOG_TRACE(unique_id(),
- "ADF4351 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No"));
- }
-
- return lo_lock;
-}
-
-UHD_RFNOC_BLOCK_REGISTER(magnesium_radio_ctrl, "MagnesiumRadio");
diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp
deleted file mode 100644
index 89db61428..000000000
--- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp
+++ /dev/null
@@ -1,713 +0,0 @@
-//
-// Copyright 2017 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "magnesium_constants.hpp"
-#include "magnesium_radio_ctrl_impl.hpp"
-#include <uhd/transport/chdr.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/types/sensors.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhdlib/usrp/cores/spi_core_3000.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/case_conv.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <string>
-#include <vector>
-using namespace uhd;
-using namespace uhd::rfnoc;
-
-namespace {
-enum slave_select_t { SEN_CPLD = 1, SEN_TX_LO = 2, SEN_RX_LO = 4, SEN_PHASE_DAC = 8 };
-
-constexpr double MAGNESIUM_DEFAULT_FREQ = 2.5e9; // Hz
-constexpr double MAGNESIUM_DEFAULT_BANDWIDTH = 100e6; // Hz
-constexpr char MAGNESIUM_DEFAULT_RX_ANTENNA[] = "RX2";
-constexpr char MAGNESIUM_DEFAULT_TX_ANTENNA[] = "TX/RX";
-
-//! Magnesium gain profile options
-const std::vector<std::string> MAGNESIUM_GP_OPTIONS = {"manual",
- "default",
- "default_rf_filter_bypass_always_on",
- "default_rf_filter_bypass_always_off"};
-} // namespace
-
-//! Helper function to extract single value of port number.
-//
-// Each GPIO pins can be controlled by each radio output ports.
-// This function convert the format of attribute "Radio_N_M"
-// to a single value port number = N*number_of_port_per_radio + M
-
-uint32_t extract_port_number(std::string radio_src_string, uhd::property_tree::sptr ptree)
-{
- std::string s_val = "0";
- std::vector<std::string> radio_strings;
- boost::algorithm::split(radio_strings,
- radio_src_string,
- boost::is_any_of("_/"),
- boost::token_compress_on);
- boost::to_lower(radio_strings[0]);
- if (radio_strings.size() < 3) {
- throw uhd::runtime_error(str(
- boost::format("%s is an invalid GPIO source string.") % radio_src_string));
- }
- size_t radio_num = std::stoi(radio_strings[1]);
- size_t port_num = std::stoi(radio_strings[2]);
- if (radio_strings[0] != "radio") {
- throw uhd::runtime_error(
- "Front panel GPIO bank can only accept a radio block as its driver.");
- }
- std::string radio_port_out = "Radio_" + radio_strings[1] + "/ports/out";
- std::string radio_port_path = radio_port_out + "/" + radio_strings[2];
- auto found = ptree->exists(fs_path("xbar") / radio_port_path);
- if (not found) {
- throw uhd::runtime_error(
- str(boost::format("Could not find radio port %s.\n") % radio_port_path));
- }
- size_t port_size = ptree->list(fs_path("xbar") / radio_port_out).size();
- return radio_num * port_size + port_num;
-}
-
-void magnesium_radio_ctrl_impl::_init_defaults()
-{
- UHD_LOG_TRACE(unique_id(), "Initializing defaults...");
- const size_t num_rx_chans = get_output_ports().size();
- const size_t num_tx_chans = get_input_ports().size();
-
- UHD_LOG_TRACE(unique_id(),
- "Num TX chans: " << num_tx_chans << " Num RX chans: " << num_rx_chans);
-
- for (size_t chan = 0; chan < num_rx_chans; chan++) {
- radio_ctrl_impl::set_rx_frequency(MAGNESIUM_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_rx_gain(0, chan);
- radio_ctrl_impl::set_rx_antenna(MAGNESIUM_DEFAULT_RX_ANTENNA, chan);
- radio_ctrl_impl::set_rx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan);
- }
-
- for (size_t chan = 0; chan < num_tx_chans; chan++) {
- radio_ctrl_impl::set_tx_frequency(MAGNESIUM_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_tx_gain(0, chan);
- radio_ctrl_impl::set_tx_antenna(MAGNESIUM_DEFAULT_TX_ANTENNA, chan);
- radio_ctrl_impl::set_tx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan);
- }
-
-
- /** Update default SPP (overwrites the default value from the XML file) **/
- const size_t max_bytes_header =
- uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t);
- const size_t default_spp =
- (_tree->access<size_t>("mtu/recv").get() - max_bytes_header)
- / (2 * sizeof(int16_t));
- UHD_LOG_DEBUG(unique_id(), "Setting default spp to " << default_spp);
- _tree->access<int>(get_arg_path("spp") / "value").set(default_spp);
-}
-
-void magnesium_radio_ctrl_impl::_init_peripherals()
-{
- UHD_LOG_TRACE(unique_id(), "Initializing peripherals...");
- UHD_LOG_TRACE(unique_id(), "Initializing SPI core...");
- _spi = spi_core_3000::make(
- _get_ctrl(0), regs::sr_addr(regs::SPI), regs::rb_addr(regs::RB_SPI));
- UHD_LOG_TRACE(unique_id(), "Initializing CPLD...");
- UHD_LOG_TRACE(unique_id(), "Creating new CPLD object...");
- spi_config_t spi_config;
- spi_config.use_custom_divider = true;
- spi_config.divider = 125;
- spi_config.mosi_edge = spi_config_t::EDGE_RISE;
- spi_config.miso_edge = spi_config_t::EDGE_FALL;
- UHD_LOG_TRACE(unique_id(), "Making CPLD object...");
- _cpld = std::make_shared<magnesium_cpld_ctrl>(
- [this, spi_config](const uint32_t transaction) { // Write functor
- this->_spi->write_spi(SEN_CPLD, spi_config, transaction, 24);
- },
- [this, spi_config](const uint32_t transaction) { // Read functor
- return this->_spi->read_spi(SEN_CPLD, spi_config, transaction, 24);
- });
- _update_atr_switches(
- magnesium_cpld_ctrl::BOTH, DX_DIRECTION, radio_ctrl_impl::get_rx_antenna(0));
- UHD_LOG_TRACE(unique_id(), "Initializing TX LO...");
- _tx_lo = adf435x_iface::make_adf4351([this](
- const std::vector<uint32_t> transactions) {
- for (const uint32_t transaction : transactions) {
- this->_spi->write_spi(SEN_TX_LO, spi_config_t::EDGE_RISE, transaction, 32);
- }
- });
- UHD_LOG_TRACE(unique_id(), "Initializing RX LO...");
- _rx_lo = adf435x_iface::make_adf4351([this](
- const std::vector<uint32_t> transactions) {
- for (const uint32_t transaction : transactions) {
- this->_spi->write_spi(SEN_RX_LO, spi_config_t::EDGE_RISE, transaction, 32);
- }
- });
-
- _gpio.clear(); // Following the as-if rule, this can get optimized out
- for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) {
- UHD_LOG_TRACE(unique_id(), "Initializing GPIOs for channel " << radio_idx);
- _gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make(_get_ctrl(radio_idx),
- regs::sr_addr(regs::GPIO),
- regs::rb_addr(regs::RB_DB_GPIO)));
- // DSA and AD9371 gain bits do *not* toggle on ATR modes. If we ever
- // connect anything else to this core, we might need to set_atr_mode()
- // to MODE_ATR on those bits. For now, all bits simply do what they're
- // told, and don't toggle on RX/TX state changes.
- _gpio.back()->set_atr_mode(usrp::gpio_atr::MODE_GPIO, // Disable ATR mode
- usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
- _gpio.back()->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs
- usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
- }
- UHD_LOG_TRACE(unique_id(), "Initializing front-panel GPIO control...")
- _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make(
- _get_ctrl(0), regs::sr_addr(regs::FP_GPIO), regs::rb_addr(regs::RB_FP_GPIO));
-}
-
-void magnesium_radio_ctrl_impl::_init_frontend_subtree(
- uhd::property_tree::sptr subtree, const size_t chan_idx)
-{
- const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx;
- const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx;
- UHD_LOG_TRACE(unique_id(),
- "Adding non-RFNoC block properties for channel "
- << chan_idx << " to prop tree path " << tx_fe_path << " and " << rx_fe_path);
- // TX Standard attributes
- subtree->create<std::string>(tx_fe_path / "name")
- .set(str(boost::format("Magnesium")));
- subtree->create<std::string>(tx_fe_path / "connection").set("IQ");
- // RX Standard attributes
- subtree->create<std::string>(rx_fe_path / "name")
- .set(str(boost::format("Magnesium")));
- subtree->create<std::string>(rx_fe_path / "connection").set("IQ");
- // TX Antenna
- subtree->create<std::string>(tx_fe_path / "antenna" / "value")
- .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
- this->set_tx_antenna(ant, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); });
- subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
- .set({MAGNESIUM_DEFAULT_TX_ANTENNA})
- .add_coerced_subscriber([](const std::vector<std::string>&) {
- throw uhd::runtime_error("Attempting to update antenna options!");
- });
- // RX Antenna
- subtree->create<std::string>(rx_fe_path / "antenna" / "value")
- .add_coerced_subscriber([this, chan_idx](const std::string& ant) {
- this->set_rx_antenna(ant, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); });
- subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
- .set(MAGNESIUM_RX_ANTENNAS)
- .add_coerced_subscriber([](const std::vector<std::string>&) {
- throw uhd::runtime_error("Attempting to update antenna options!");
- });
- // TX frequency
- subtree->create<double>(tx_fe_path / "freq" / "value")
- .set_coercer([this, chan_idx](const double freq) {
- return this->set_tx_frequency(freq, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); });
- subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
- .set(meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update freq range!");
- });
- // RX frequency
- subtree->create<double>(rx_fe_path / "freq" / "value")
- .set_coercer([this, chan_idx](const double freq) {
- return this->set_rx_frequency(freq, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); });
- subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
- .set(meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update freq range!");
- });
- // TX bandwidth
- subtree->create<double>(tx_fe_path / "bandwidth" / "value")
- .set(AD9371_TX_MAX_BANDWIDTH)
- .set_coercer([this, chan_idx](const double bw) {
- return this->set_tx_bandwidth(bw, chan_idx);
- })
- .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); });
- subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
- .set(meta_range_t(AD9371_TX_MIN_BANDWIDTH, AD9371_TX_MAX_BANDWIDTH))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update bandwidth range!");
- });
- // RX bandwidth
- subtree->create<double>(rx_fe_path / "bandwidth" / "value")
- .set(AD9371_RX_MAX_BANDWIDTH)
- .set_coercer([this, chan_idx](const double bw) {
- return this->set_rx_bandwidth(bw, chan_idx);
- });
- subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
- .set(meta_range_t(AD9371_RX_MIN_BANDWIDTH, AD9371_RX_MAX_BANDWIDTH))
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update bandwidth range!");
- });
- // TX gains
- subtree->create<double>(tx_fe_path / "gains" / "all" / "value")
- .set_coercer([this, chan_idx](
- const double gain) { return this->set_tx_gain(gain, chan_idx); })
- .set_publisher(
- [this, chan_idx]() { return radio_ctrl_impl::get_tx_gain(chan_idx); });
- subtree->create<meta_range_t>(tx_fe_path / "gains" / "all" / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[TX_DIRECTION] == "manual") {
- return meta_range_t(0.0, 0.0, 0.0);
- } else {
- return meta_range_t(ALL_TX_MIN_GAIN, ALL_TX_MAX_GAIN, ALL_TX_GAIN_STEP);
- }
- });
-
- subtree->create<std::vector<std::string>>(tx_fe_path / "gains/all/profile/options")
- .set(MAGNESIUM_GP_OPTIONS);
-
- subtree->create<std::string>(tx_fe_path / "gains/all/profile/value")
- .set_coercer([this](const std::string& profile) {
- // check if given profile is valid, otherwise use default profile
- std::string return_profile = profile;
- if (std::find(
- MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile)
- == MAGNESIUM_GP_OPTIONS.end()) {
- return_profile = "default";
- }
- _gain_profile[TX_DIRECTION] = return_profile;
- return return_profile;
- })
- .set_publisher([this]() { return _gain_profile[TX_DIRECTION]; });
-
- // RX gains
- subtree->create<double>(rx_fe_path / "gains" / "all" / "value")
- .set_coercer([this, chan_idx](
- const double gain) { return this->set_rx_gain(gain, chan_idx); })
- .set_publisher(
- [this, chan_idx]() { return radio_ctrl_impl::get_rx_gain(chan_idx); });
-
- subtree->create<meta_range_t>(rx_fe_path / "gains" / "all" / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[RX_DIRECTION] == "manual") {
- return meta_range_t(0.0, 0.0, 0.0);
- } else {
- return meta_range_t(ALL_RX_MIN_GAIN, ALL_RX_MAX_GAIN, ALL_RX_GAIN_STEP);
- }
- });
-
- subtree->create<std::vector<std::string>>(rx_fe_path / "gains/all/profile/options")
- .set(MAGNESIUM_GP_OPTIONS);
-
- subtree->create<std::string>(rx_fe_path / "gains/all/profile/value")
- .set_coercer([this](const std::string& profile) {
- // check if given profile is valid, otherwise use default profile
- std::string return_profile = profile;
- if (std::find(
- MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile)
- == MAGNESIUM_GP_OPTIONS.end()) {
- return_profile = "default";
- }
- _gain_profile[RX_DIRECTION] = return_profile;
- return return_profile;
- })
- .set_publisher([this]() { return _gain_profile[RX_DIRECTION]; });
-
- // TX mykonos attenuation
- subtree->create<double>(tx_fe_path / "gains" / MAGNESIUM_GAIN1 / "value")
- .set_coercer([this, chan_idx](const double gain) {
- return _set_tx_gain(MAGNESIUM_GAIN1, gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return this->_get_tx_gain(MAGNESIUM_GAIN1, chan_idx); });
-
- subtree->create<meta_range_t>(tx_fe_path / "gains" / MAGNESIUM_GAIN1 / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[TX_DIRECTION] == "manual") {
- return meta_range_t(
- AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN, AD9371_TX_GAIN_STEP);
- } else {
- return meta_range_t(0.0, 0.0, 0.0);
- }
- });
- // TX DSA
- subtree->create<double>(tx_fe_path / "gains" / MAGNESIUM_GAIN2 / "value")
- .set_coercer([this, chan_idx](const double gain) {
- return this->_set_tx_gain(MAGNESIUM_GAIN2, gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return this->_get_tx_gain(MAGNESIUM_GAIN2, chan_idx); });
-
- subtree->create<meta_range_t>(tx_fe_path / "gains" / MAGNESIUM_GAIN2 / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[TX_DIRECTION] == "manual") {
- return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_GAIN_STEP);
- } else {
- return meta_range_t(0.0, 0.0, 0.0);
- }
- });
- // TX amp
- subtree->create<double>(tx_fe_path / "gains" / MAGNESIUM_AMP / "value")
- .set_coercer([this, chan_idx](const double gain) {
- return this->_set_tx_gain(MAGNESIUM_AMP, gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return this->_get_tx_gain(MAGNESIUM_AMP, chan_idx); });
-
- subtree->create<meta_range_t>(tx_fe_path / "gains" / MAGNESIUM_AMP / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[TX_DIRECTION] == "manual") {
- return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP);
- } else {
- return meta_range_t(0.0, 0.0, 0.0);
- }
- });
-
- // RX mykonos attenuation
- subtree->create<double>(rx_fe_path / "gains" / MAGNESIUM_GAIN1 / "value")
- .set_coercer([this, chan_idx](const double gain) {
- UHD_VAR(gain);
- return this->_set_rx_gain(MAGNESIUM_GAIN1, gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return this->_get_rx_gain(MAGNESIUM_GAIN1, chan_idx); });
-
- subtree->create<meta_range_t>(rx_fe_path / "gains" / MAGNESIUM_GAIN1 / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[RX_DIRECTION] == "manual") {
- return meta_range_t(
- AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN, AD9371_RX_GAIN_STEP);
- } else {
- return meta_range_t(0.0, 0.0, 0.0);
- }
- });
- // RX DSA
- subtree->create<double>(rx_fe_path / "gains" / MAGNESIUM_GAIN2 / "value")
- .set_coercer([this, chan_idx](const double gain) {
- UHD_VAR(gain);
- return this->_set_rx_gain(MAGNESIUM_GAIN2, gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return this->_get_rx_gain(MAGNESIUM_GAIN2, chan_idx); });
-
- subtree->create<meta_range_t>(rx_fe_path / "gains" / MAGNESIUM_GAIN2 / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[RX_DIRECTION] == "manual") {
- return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_MAX_GAIN);
- } else {
- return meta_range_t(0.0, 0.0, 0.0);
- }
- });
-
- // RX amp
- subtree->create<double>(rx_fe_path / "gains" / MAGNESIUM_AMP / "value")
- .set_coercer([this, chan_idx](const double gain) {
- return this->_set_rx_gain(MAGNESIUM_AMP, gain, chan_idx);
- })
- .set_publisher(
- [this, chan_idx]() { return this->_get_rx_gain(MAGNESIUM_AMP, chan_idx); });
-
- subtree->create<meta_range_t>(rx_fe_path / "gains" / MAGNESIUM_AMP / "range")
- .add_coerced_subscriber([](const meta_range_t&) {
- throw uhd::runtime_error("Attempting to update gain range!");
- })
- .set_publisher([this]() {
- if (_gain_profile[RX_DIRECTION] == "manual") {
- return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP);
- } else {
- return meta_range_t(0.0, 0.0, 0.0);
- }
- });
-
- // TX LO lock sensor //////////////////////////////////////////////////////
- // Note: The lowband and AD9371 LO lock sensors are generated
- // programmatically in set_rpc_client(). The actual lo_locked publisher is
- // also set there.
- subtree->create<sensor_value_t>(tx_fe_path / "sensors" / "lo_locked")
- .set(sensor_value_t("all_los", false, "locked", "unlocked"))
- .add_coerced_subscriber([](const sensor_value_t&) {
- throw uhd::runtime_error("Attempting to write to sensor!");
- })
- .set_publisher([this]() {
- return sensor_value_t(
- "all_los", this->get_lo_lock_status(TX_DIRECTION), "locked", "unlocked");
- });
- // RX LO lock sensor (see not on TX LO lock sensor)
- subtree->create<sensor_value_t>(rx_fe_path / "sensors" / "lo_locked")
- .set(sensor_value_t("all_los", false, "locked", "unlocked"))
- .add_coerced_subscriber([](const sensor_value_t&) {
- throw uhd::runtime_error("Attempting to write to sensor!");
- })
- .set_publisher([this]() {
- return sensor_value_t(
- "all_los", this->get_lo_lock_status(RX_DIRECTION), "locked", "unlocked");
- });
- // LO Specific
- // RX LO
- subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range")
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx);
- });
- subtree
- ->create<std::vector<std::string>>(
- rx_fe_path / "los" / MAGNESIUM_LO1 / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_sources(MAGNESIUM_LO1, chan_idx);
- });
- subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO1 / "source/value")
- .add_coerced_subscriber([this, chan_idx](std::string src) {
- this->set_rx_lo_source(src, MAGNESIUM_LO1, chan_idx);
- })
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_source(MAGNESIUM_LO1, chan_idx);
- });
- subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value")
- .set_publisher(
- [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO1, chan_idx); })
- .set_coercer([this, chan_idx](const double freq) {
- return this->set_rx_lo_freq(freq, MAGNESIUM_LO1, chan_idx);
- });
-
- subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range")
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_freq_range(MAGNESIUM_LO2, chan_idx);
- });
- subtree
- ->create<std::vector<std::string>>(
- rx_fe_path / "los" / MAGNESIUM_LO2 / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_sources(MAGNESIUM_LO2, chan_idx);
- });
-
- subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO2 / "source/value")
- .add_coerced_subscriber([this, chan_idx](std::string src) {
- this->set_rx_lo_source(src, MAGNESIUM_LO2, chan_idx);
- })
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_source(MAGNESIUM_LO2, chan_idx);
- });
- subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value")
- .set_publisher(
- [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO2, chan_idx); })
- .set_coercer([this, chan_idx](double freq) {
- return this->set_rx_lo_freq(freq, MAGNESIUM_LO2, chan_idx);
- });
- // TX LO
- subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range")
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx);
- });
- subtree
- ->create<std::vector<std::string>>(
- tx_fe_path / "los" / MAGNESIUM_LO1 / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_tx_lo_sources(MAGNESIUM_LO1, chan_idx);
- });
- subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO1 / "source/value")
- .add_coerced_subscriber([this, chan_idx](std::string src) {
- this->set_tx_lo_source(src, MAGNESIUM_LO1, chan_idx);
- })
- .set_publisher([this, chan_idx]() {
- return this->get_tx_lo_source(MAGNESIUM_LO1, chan_idx);
- });
- subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value ")
- .set_publisher(
- [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO1, chan_idx); })
- .set_coercer([this, chan_idx](double freq) {
- return this->set_tx_lo_freq(freq, MAGNESIUM_LO1, chan_idx);
- });
-
- subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range")
- .set_publisher([this, chan_idx]() {
- return this->get_tx_lo_freq_range(MAGNESIUM_LO2, chan_idx);
- });
- subtree
- ->create<std::vector<std::string>>(
- tx_fe_path / "los" / MAGNESIUM_LO2 / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_tx_lo_sources(MAGNESIUM_LO2, chan_idx);
- });
-
- subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO2 / "source/value")
- .add_coerced_subscriber([this, chan_idx](std::string src) {
- this->set_tx_lo_source(src, MAGNESIUM_LO2, chan_idx);
- })
- .set_publisher([this, chan_idx]() {
- return this->get_tx_lo_source(MAGNESIUM_LO2, chan_idx);
- });
- subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value")
- .set_publisher(
- [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO2, chan_idx); })
- .set_coercer([this, chan_idx](double freq) {
- return this->set_tx_lo_freq(freq, MAGNESIUM_LO2, chan_idx);
- });
-}
-
-void magnesium_radio_ctrl_impl::_init_prop_tree()
-{
- const fs_path fe_base = fs_path("dboards") / _radio_slot;
- for (size_t chan_idx = 0; chan_idx < MAGNESIUM_NUM_CHANS; chan_idx++) {
- this->_init_frontend_subtree(_tree->subtree(fe_base), chan_idx);
- }
-
- // EEPROM paths subject to change FIXME
- _tree->create<eeprom_map_t>(_root_path / "eeprom").set(eeprom_map_t());
-
- // TODO change codec names
- _tree->create<int>("rx_codecs" / _radio_slot / "gains");
- _tree->create<int>("tx_codecs" / _radio_slot / "gains");
- _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("AD9371 Dual ADC");
- _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("AD9371 Dual DAC");
-
- // TODO remove this dirty hack
- if (not _tree->exists("tick_rate")) {
- _tree->create<double>("tick_rate").set_publisher([this]() {
- return this->get_rate();
- });
- }
-
- // *****FP_GPIO************************
- for (const auto& attr : usrp::gpio_atr::gpio_attr_map) {
- if (not _tree->exists(fs_path("gpio") / "FP0" / attr.second)) {
- switch (attr.first) {
- case usrp::gpio_atr::GPIO_SRC:
- // FIXME: move this creation of this branch of ptree out side of
- // radio impl;
- // since there's no data dependency between radio and SRC setting for
- // FP0
- _tree
- ->create<std::vector<std::string>>(
- fs_path("gpio") / "FP0" / attr.second)
- .set(std::vector<std::string>(
- 32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber(
- [this, attr](const std::vector<std::string> str_val) {
- uint32_t radio_src_value = 0;
- uint32_t master_value = 0;
- for (size_t i = 0; i < str_val.size(); i++) {
- if (str_val[i] == "PS") {
- master_value += 1 << i;
- ;
- } else {
- auto port_num =
- extract_port_number(str_val[i], _tree);
- radio_src_value =
- (1 << (2 * i)) * port_num + radio_src_value;
- }
- }
- _rpcc->notify_with_token(
- "set_fp_gpio_master", master_value);
- _rpcc->notify_with_token(
- "set_fp_gpio_radio_src", radio_src_value);
- });
- break;
- case usrp::gpio_atr::GPIO_CTRL:
- case usrp::gpio_atr::GPIO_DDR:
- _tree
- ->create<std::vector<std::string>>(
- fs_path("gpio") / "FP0" / attr.second)
- .set(std::vector<std::string>(
- 32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber(
- [this, attr](const std::vector<std::string> str_val) {
- uint32_t val = 0;
- for (size_t i = 0; i < str_val.size(); i++) {
- val += usrp::gpio_atr::gpio_attr_value_pair
- .at(attr.second)
- .at(str_val[i])
- << i;
- }
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- break;
- case usrp::gpio_atr::GPIO_READBACK: {
- _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second)
- .set_publisher([this]() { return _fp_gpio->read_gpio(); });
- } break;
- default:
- _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second)
- .set(0)
- .add_coerced_subscriber([this, attr](const uint32_t val) {
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- }
- } else {
- switch (attr.first) {
- case usrp::gpio_atr::GPIO_SRC:
- break;
- case usrp::gpio_atr::GPIO_CTRL:
- case usrp::gpio_atr::GPIO_DDR:
- _tree
- ->access<std::vector<std::string>>(
- fs_path("gpio") / "FP0" / attr.second)
- .set(std::vector<std::string>(
- 32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber(
- [this, attr](const std::vector<std::string> str_val) {
- uint32_t val = 0;
- for (size_t i = 0; i < str_val.size(); i++) {
- val += usrp::gpio_atr::gpio_attr_value_pair
- .at(attr.second)
- .at(str_val[i])
- << i;
- }
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- break;
- case usrp::gpio_atr::GPIO_READBACK:
- break;
- default:
- _tree->access<uint32_t>(fs_path("gpio") / "FP0" / attr.second)
- .set(0)
- .add_coerced_subscriber([this, attr](const uint32_t val) {
- _fp_gpio->set_gpio_attr(attr.first, val);
- });
- }
- }
- }
-}
-
-
-void magnesium_radio_ctrl_impl::_init_mpm_sensors(
- const direction_t dir, const size_t chan_idx)
-{
- const std::string trx = (dir == RX_DIRECTION) ? "RX" : "TX";
- const fs_path fe_path = fs_path("dboards") / _radio_slot
- / (dir == RX_DIRECTION ? "rx_frontends" : "tx_frontends")
- / chan_idx;
- auto sensor_list = _rpcc->request_with_token<std::vector<std::string>>(
- this->_rpc_prefix + "get_sensors", trx);
- UHD_LOG_TRACE(unique_id(),
- "Chan " << chan_idx << ": Found " << sensor_list.size() << " " << trx
- << " sensors.");
- for (const auto& sensor_name : sensor_list) {
- UHD_LOG_TRACE(unique_id(), "Adding " << trx << " sensor " << sensor_name);
- _tree->create<sensor_value_t>(fe_path / "sensors" / sensor_name)
- .add_coerced_subscriber([](const sensor_value_t&) {
- throw uhd::runtime_error("Attempting to write to sensor!");
- })
- .set_publisher([this, trx, sensor_name, chan_idx]() {
- return sensor_value_t(
- this->_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
- this->_rpc_prefix + "get_sensor", trx, sensor_name, chan_idx));
- });
- }
-}
diff --git a/host/lib/usrp/dboard/rhodium/CMakeLists.txt b/host/lib/usrp/dboard/rhodium/CMakeLists.txt
index 2b2e9744c..3159037c4 100644
--- a/host/lib/usrp/dboard/rhodium/CMakeLists.txt
+++ b/host/lib/usrp/dboard/rhodium/CMakeLists.txt
@@ -6,10 +6,10 @@
if(ENABLE_MPMD)
list(APPEND RHODIUM_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_impl.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_init.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_cpld.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_lo.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control_init.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control_cpld.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control_lo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rhodium_bands.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rhodium_cpld_ctrl.cpp
)
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp b/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp
index ffa206195..9e0a1d3d3 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp
@@ -4,8 +4,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
-#include "rhodium_radio_ctrl_impl.hpp"
#include "rhodium_constants.hpp"
+#include "rhodium_radio_control.hpp"
#include <uhd/utils/math.hpp>
using namespace uhd;
@@ -30,7 +30,7 @@ namespace {
* chosen to allow as much of the full bandwidth through unattenuated.
*
* Switch selection logic for these bands can be found in
- * rhodium_radio_ctrl_impl::_update_rx_freq_switches()
+ * rhodium_radio_control_impl::_update_rx_freq_switches()
*/
constexpr double RHODIUM_RX_BAND0_MIN_FREQ = RHODIUM_MIN_FREQ;
constexpr double RHODIUM_RX_BAND1_MIN_FREQ = 450e6;
@@ -55,7 +55,7 @@ namespace {
* bandwidth through unattenuated.
*
* Switch selection logic for these bands can be found in
- * rhodium_radio_ctrl_impl::_update_tx_freq_switches()
+ * rhodium_radio_control_impl::_update_tx_freq_switches()
*/
constexpr double RHODIUM_TX_BAND0_MIN_FREQ = RHODIUM_MIN_FREQ;
constexpr double RHODIUM_TX_BAND1_MIN_FREQ = 450e6;
@@ -67,9 +67,9 @@ namespace {
constexpr double RHODIUM_TX_BAND7_MIN_FREQ = 4100e6;
}
-rhodium_radio_ctrl_impl::rx_band
-rhodium_radio_ctrl_impl::_map_freq_to_rx_band(const double freq) {
-
+rhodium_radio_control_impl::rx_band rhodium_radio_control_impl::_map_freq_to_rx_band(
+ const double freq)
+{
auto freq_compare = fp_compare_epsilon<double>(freq, RHODIUM_FREQ_COMPARE_EPSILON);
if (freq_compare < RHODIUM_RX_BAND0_MIN_FREQ) {
@@ -95,9 +95,9 @@ rhodium_radio_ctrl_impl::_map_freq_to_rx_band(const double freq) {
}
}
-rhodium_radio_ctrl_impl::tx_band
-rhodium_radio_ctrl_impl::_map_freq_to_tx_band(const double freq) {
-
+rhodium_radio_control_impl::tx_band rhodium_radio_control_impl::_map_freq_to_tx_band(
+ const double freq)
+{
auto freq_compare = fp_compare_epsilon<double>(freq, RHODIUM_FREQ_COMPARE_EPSILON);
if (freq_compare < RHODIUM_TX_BAND0_MIN_FREQ) {
@@ -123,12 +123,12 @@ rhodium_radio_ctrl_impl::_map_freq_to_tx_band(const double freq) {
}
}
-bool rhodium_radio_ctrl_impl::_is_rx_lowband(const double freq)
+bool rhodium_radio_control_impl::_is_rx_lowband(const double freq)
{
return _map_freq_to_rx_band(freq) == rx_band::RX_BAND_0;
}
-bool rhodium_radio_ctrl_impl::_is_tx_lowband(const double freq)
+bool rhodium_radio_control_impl::_is_tx_lowband(const double freq)
{
return _map_freq_to_tx_band(freq) == tx_band::TX_BAND_0;
}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp b/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
index c52a73bca..69e6bf676 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
@@ -48,6 +48,8 @@ static constexpr double LO_MIN_POWER = 0.0;
static constexpr double LO_MAX_POWER = 63.0;
static constexpr double LO_POWER_STEP = 1.0;
+static constexpr double RHODIUM_DEFAULT_BANDWIDTH = 250e6; // Hz
+
static const std::vector<std::string> RHODIUM_RX_ANTENNAS = {
"TX/RX", "RX2", "CAL", "TERM"
};
@@ -57,10 +59,15 @@ static const std::vector<std::string> RHODIUM_TX_ANTENNAS = {
};
// These names are taken from radio_rhodium.xml
-static constexpr char SPUR_DODGING_ARG_NAME[] = "spur_dodging";
-static constexpr char SPUR_DODGING_THRESHOLD_ARG_NAME[] = "spur_dodging_threshold";
-static constexpr char HIGHBAND_SPUR_REDUCTION_ARG_NAME[] = "highband_spur_reduction";
+static constexpr char SPUR_DODGING_PROP_NAME[] = "spur_dodging";
+static constexpr char SPUR_DODGING_THRESHOLD_PROP_NAME[] = "spur_dodging_threshold";
+static constexpr char HIGHBAND_SPUR_REDUCTION_PROP_NAME[] = "highband_spur_reduction";
+static constexpr char RHODIUM_DEFAULT_SPUR_DOGING_MODE[] = "disabled";
+static constexpr double RHODIUM_DEFAULT_SPUR_DOGING_THRESHOLD = 2e6;
+static constexpr char RHODIUM_DEFAULT_HB_SPUR_REDUCTION_MODE[] = "disabled";
+
+static constexpr char RHODIUM_FPGPIO_BANK[] = "FP0";
static constexpr uint32_t RHODIUM_GPIO_MASK = 0x1F;
static constexpr uint32_t SW10_GPIO_MASK = 0x3;
static constexpr uint32_t LED_GPIO_MASK = 0x1C;
@@ -85,8 +92,9 @@ static constexpr char RHODIUM_LO_GAIN[] = "dsa";
//! LO output power
static constexpr char RHODIUM_LO_POWER[] = "lo";
-static constexpr int NUM_LO_OUTPUT_PORT_NAMES = 4;
+static constexpr char RHODIUM_FE_NAME[] = "Rhodium";
+static constexpr int NUM_LO_OUTPUT_PORT_NAMES = 4;
static constexpr std::array<const char*, NUM_LO_OUTPUT_PORT_NAMES> LO_OUTPUT_PORT_NAMES = {
"LO_OUT_0",
"LO_OUT_1",
@@ -96,4 +104,28 @@ static constexpr std::array<const char*, NUM_LO_OUTPUT_PORT_NAMES> LO_OUTPUT_POR
static constexpr size_t RHODIUM_NUM_CHANS = 1;
+namespace n320_regs {
+
+static constexpr uint32_t PERIPH_BASE = 0x80000;
+static constexpr uint32_t PERIPH_REG_OFFSET = 8;
+
+// db_control registers
+static constexpr uint32_t SR_MISC_OUTS = PERIPH_BASE + 160 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_SPI = PERIPH_BASE + 168 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_LEDS = PERIPH_BASE + 176 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_FP_GPIO = PERIPH_BASE + 184 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_DB_GPIO = PERIPH_BASE + 192 * PERIPH_REG_OFFSET;
+
+static constexpr uint32_t RB_MISC_IO = PERIPH_BASE + 16 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_SPI = PERIPH_BASE + 17 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_LEDS = PERIPH_BASE + 18 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_DB_GPIO = PERIPH_BASE + 19 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_FP_GPIO = PERIPH_BASE + 20 * PERIPH_REG_OFFSET;
+
+//! Delta between frontend offsets for channel 0 and 1
+constexpr uint32_t SR_TX_FE_BASE = PERIPH_BASE + 208 * PERIPH_REG_OFFSET;
+constexpr uint32_t SR_RX_FE_BASE = PERIPH_BASE + 224 * PERIPH_REG_OFFSET;
+
+} // namespace n320_regs
+
#endif /* INCLUDED_LIBUHD_RHODIUM_CONSTANTS_HPP */
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp
new file mode 100644
index 000000000..a3b072e74
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp
@@ -0,0 +1,723 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_radio_control.hpp"
+#include "rhodium_constants.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/rfnoc/registry.hpp>
+#include <uhd/transport/chdr.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhdlib/usrp/common/apply_corrections.hpp>
+#include <uhdlib/utils/narrow.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/format.hpp>
+#include <boost/make_shared.hpp>
+#include <cmath>
+#include <cstdlib>
+#include <sstream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+using namespace uhd::math::fp_compare;
+
+namespace {
+ constexpr char RX_FE_CONNECTION_LOWBAND[] = "QI";
+ constexpr char RX_FE_CONNECTION_HIGHBAND[] = "IQ";
+ constexpr char TX_FE_CONNECTION_LOWBAND[] = "QI";
+ constexpr char TX_FE_CONNECTION_HIGHBAND[] = "IQ";
+
+ constexpr double DEFAULT_IDENTIFY_DURATION = 5.0; // seconds
+
+ constexpr uint64_t SET_RATE_RPC_TIMEOUT_MS = 10000;
+
+}
+
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+rhodium_radio_control_impl::rhodium_radio_control_impl(make_args_ptr make_args)
+ : radio_control_impl(std::move(make_args))
+{
+ RFNOC_LOG_TRACE("Entering rhodium_radio_control_impl ctor...");
+ UHD_ASSERT_THROW(get_block_id().get_block_count() < 2);
+ const char radio_slot_name[] = {'A', 'B'};
+ _radio_slot = radio_slot_name[get_block_id().get_block_count()];
+ _rpc_prefix =
+ (_radio_slot == "A") ? "db_0_" : "db_1_";
+ RFNOC_LOG_TRACE("Radio slot: " << _radio_slot);
+ UHD_ASSERT_THROW(get_num_input_ports() == RHODIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(get_num_output_ports() == RHODIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(get_mb_controller());
+ _n320_mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller());
+ UHD_ASSERT_THROW(_n320_mb_control);
+ _n3xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>(
+ _n320_mb_control->get_timekeeper(0));
+ UHD_ASSERT_THROW(_n3xx_timekeeper);
+ _rpcc = _n320_mb_control->get_rpc_client();
+ UHD_ASSERT_THROW(_rpcc);
+
+ const auto all_dboard_info =
+ _rpcc->request<std::vector<std::map<std::string, std::string>>>(
+ "get_dboard_info");
+ RFNOC_LOG_TRACE("Hardware detected " << all_dboard_info.size() << " daughterboards.");
+
+ // If we two radio blocks, but there is only one dboard plugged in, we skip
+ // initialization. The board needs to be in slot A
+ if (all_dboard_info.size() > get_block_id().get_block_count()) {
+ _init_defaults();
+ _init_mpm();
+ _init_peripherals();
+ _init_prop_tree();
+ }
+
+ // Properties
+ for (auto& samp_rate_prop : _samp_rate_in) {
+ samp_rate_prop.set(_master_clock_rate);
+ }
+ for (auto& samp_rate_prop : _samp_rate_out) {
+ samp_rate_prop.set(_master_clock_rate);
+ }
+}
+
+rhodium_radio_control_impl::~rhodium_radio_control_impl()
+{
+ RFNOC_LOG_TRACE("rhodium_radio_control_impl::dtor() ");
+}
+
+
+/******************************************************************************
+ * RF API Calls
+ *****************************************************************************/
+double rhodium_radio_control_impl::set_rate(double requested_rate)
+{
+ meta_range_t rates;
+ for (const double rate : RHODIUM_RADIO_RATES) {
+ rates.push_back(range_t(rate));
+ }
+
+ const double rate = rates.clip(requested_rate);
+ if (!math::frequencies_are_equal(requested_rate, rate)) {
+ RFNOC_LOG_WARNING("Coercing requested sample rate from "
+ << (requested_rate / 1e6) << " MHz to " << (rate / 1e6)
+ << " MHz, the closest possible rate.");
+ }
+
+ const double current_rate = get_rate();
+ if (math::frequencies_are_equal(current_rate, rate)) {
+ RFNOC_LOG_DEBUG(
+ "Rate is already at " << (rate / 1e6) << " MHz. Skipping set_rate()");
+ return current_rate;
+ }
+
+ RFNOC_LOG_TRACE("Updating master clock rate to " << rate);
+ _master_clock_rate = _rpcc->request_with_token<double>(
+ SET_RATE_RPC_TIMEOUT_MS, "db_0_set_master_clock_rate", rate);
+ _n3xx_timekeeper->update_tick_rate(_master_clock_rate);
+ radio_control_impl::set_rate(_master_clock_rate);
+ // The lowband LO frequency will change with the master clock rate, so
+ // update the tuning of the device.
+ set_tx_frequency(get_tx_frequency(0), 0);
+ set_rx_frequency(get_rx_frequency(0), 0);
+
+ set_tick_rate(_master_clock_rate);
+ return _master_clock_rate;
+}
+
+void rhodium_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_antenna(ant=" << ant << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ if (!uhd::has(RHODIUM_TX_ANTENNAS, ant)) {
+ RFNOC_LOG_ERROR("Invalid TX antenna value: " << ant);
+ throw uhd::value_error("Requesting invalid TX antenna value!");
+ }
+
+ _update_tx_output_switches(ant);
+ // _update_atr will set the cached antenna value, so no need to do
+ // it here. See comments in _update_antenna for more info.
+ _update_atr(ant, TX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan)
+{
+ RFNOC_LOG_TRACE("Setting RX antenna to " << ant);
+ UHD_ASSERT_THROW(chan == 0);
+
+ if (!uhd::has(RHODIUM_RX_ANTENNAS, ant)) {
+ RFNOC_LOG_ERROR("Invalid RX antenna value: " << ant);
+ throw uhd::value_error("Requesting invalid RX antenna value!");
+ }
+
+ _update_rx_input_switches(ant);
+ // _update_atr will set the cached antenna value, so no need to do
+ // it here. See comments in _update_antenna for more info.
+ _update_atr(ant, RX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::_set_tx_fe_connection(const std::string& conn)
+{
+ RFNOC_LOG_TRACE("set_tx_fe_connection(conn=" << conn << ")");
+ if (conn != _tx_fe_connection) {
+ _tx_fe_core->set_mux(conn);
+ _tx_fe_connection = conn;
+ }
+}
+
+void rhodium_radio_control_impl::_set_rx_fe_connection(const std::string& conn)
+{
+ RFNOC_LOG_TRACE("set_rx_fe_connection(conn=" << conn << ")");
+ if (conn != _rx_fe_connection) {
+ _rx_fe_core->set_fe_connection(conn);
+ _rx_fe_connection = conn;
+ }
+}
+
+std::string rhodium_radio_control_impl::_get_tx_fe_connection() const
+{
+ return _tx_fe_connection;
+}
+
+std::string rhodium_radio_control_impl::_get_rx_fe_connection() const
+{
+ return _rx_fe_connection;
+}
+
+double rhodium_radio_control_impl::set_tx_frequency(const double freq, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ const auto old_freq = get_tx_frequency(0);
+ double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
+
+ if (freq != coerced_target_freq) {
+ RFNOC_LOG_DEBUG("Requested frequency is outside supported range. Coercing to "
+ << coerced_target_freq);
+ }
+
+ const bool is_highband = !_is_tx_lowband(coerced_target_freq);
+
+ const double target_lo_freq = is_highband ?
+ coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
+ const double actual_lo_freq =
+ set_tx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
+ const double coerced_freq = is_highband ?
+ actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
+ const auto conn = is_highband ?
+ TX_FE_CONNECTION_HIGHBAND : TX_FE_CONNECTION_LOWBAND;
+
+ // update the cached frequency value now so calls to set gain and update
+ // switches will read the new frequency
+ radio_control_impl::set_tx_frequency(coerced_freq, chan);
+
+ _set_tx_fe_connection(conn);
+ set_tx_gain(radio_control_impl::get_tx_gain(chan), 0);
+
+ if (_get_highband_spur_reduction_enabled(TX_DIRECTION)) {
+ if (_get_timed_command_enabled() and _is_tx_lowband(old_freq) != not is_highband) {
+ RFNOC_LOG_WARNING(
+ "Timed tuning commands that transition between lowband and highband, 450 "
+ "MHz, do not function correctly when highband_spur_reduction is enabled! "
+ "Disable highband_spur_reduction or avoid using timed tuning commands.");
+ }
+ RFNOC_LOG_TRACE("TX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
+ _rpcc->notify_with_token(_rpc_prefix + "enable_tx_lowband_lo", (!is_highband));
+ }
+ _update_tx_freq_switches(coerced_freq);
+ const bool enable_corrections = is_highband
+ and (get_tx_lo_source(RHODIUM_LO1, 0) == "internal");
+ _update_corrections(actual_lo_freq, TX_DIRECTION, enable_corrections);
+ // if TX lowband/highband changed and antenna is TX/RX,
+ // the ATR and SW1 need to be updated
+ _update_tx_output_switches(get_tx_antenna(0));
+ _update_atr(get_tx_antenna(0), TX_DIRECTION);
+
+ return coerced_freq;
+}
+
+double rhodium_radio_control_impl::set_rx_frequency(const double freq, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ const auto old_freq = get_rx_frequency(0);
+ double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
+
+ if (freq != coerced_target_freq) {
+ RFNOC_LOG_DEBUG("Requested frequency is outside supported range. Coercing to "
+ << coerced_target_freq);
+ }
+
+ const bool is_highband = !_is_rx_lowband(coerced_target_freq);
+
+ const double target_lo_freq = is_highband ?
+ coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
+ const double actual_lo_freq =
+ set_rx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
+ const double coerced_freq = is_highband ?
+ actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
+ const auto conn = is_highband ?
+ RX_FE_CONNECTION_HIGHBAND : RX_FE_CONNECTION_LOWBAND;
+
+ // update the cached frequency value now so calls to set gain and update
+ // switches will read the new frequency
+ radio_control_impl::set_rx_frequency(coerced_freq, chan);
+
+ _set_rx_fe_connection(conn);
+ set_rx_gain(radio_control_impl::get_rx_gain(chan), 0);
+
+ if (_get_highband_spur_reduction_enabled(RX_DIRECTION)) {
+ if (_get_timed_command_enabled() and _is_rx_lowband(old_freq) != not is_highband) {
+ RFNOC_LOG_WARNING(
+ "Timed tuning commands that transition between lowband and highband, 450 "
+ "MHz, do not function correctly when highband_spur_reduction is enabled! "
+ "Disable highband_spur_reduction or avoid using timed tuning commands.");
+ }
+ RFNOC_LOG_TRACE("RX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
+ _rpcc->notify_with_token(_rpc_prefix + "enable_rx_lowband_lo", (!is_highband));
+ }
+ _update_rx_freq_switches(coerced_freq);
+ const bool enable_corrections = is_highband
+ and (get_rx_lo_source(RHODIUM_LO1, 0) == "internal");
+ _update_corrections(actual_lo_freq, RX_DIRECTION, enable_corrections);
+
+ return coerced_freq;
+}
+
+void rhodium_radio_control_impl::set_tx_tune_args(
+ const uhd::device_addr_t& args, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan == 0);
+ _tune_args[uhd::TX_DIRECTION] = args;
+}
+
+void rhodium_radio_control_impl::set_rx_tune_args(
+ const uhd::device_addr_t& args, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan == 0);
+ _tune_args[uhd::RX_DIRECTION] = args;
+}
+
+double rhodium_radio_control_impl::set_tx_gain(const double gain, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ auto freq = this->get_tx_frequency(chan);
+ auto index = get_tx_gain_range(chan).clip(gain);
+
+ auto old_band = _is_tx_lowband(_tx_frequency_at_last_gain_write) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+ auto new_band = _is_tx_lowband(freq) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+
+ // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
+ if (radio_control_impl::get_tx_gain(chan) != index or old_band != new_band) {
+ RFNOC_LOG_TRACE("Writing new TX gain index: " << index);
+ _cpld->set_gain_index(index, new_band, TX_DIRECTION);
+ _tx_frequency_at_last_gain_write = freq;
+ radio_control_impl::set_tx_gain(index, chan);
+ } else {
+ RFNOC_LOG_TRACE(
+ "No change in index or band, skipped writing TX gain index: " << index);
+ }
+
+ return index;
+}
+
+double rhodium_radio_control_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ auto freq = this->get_rx_frequency(chan);
+ auto index = get_rx_gain_range(chan).clip(gain);
+
+ auto old_band = _is_rx_lowband(_rx_frequency_at_last_gain_write) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+ auto new_band = _is_rx_lowband(freq) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+
+ // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
+ if (radio_control_impl::get_rx_gain(chan) != index or old_band != new_band) {
+ RFNOC_LOG_TRACE("Writing new RX gain index: " << index);
+ _cpld->set_gain_index(index, new_band, RX_DIRECTION);
+ _rx_frequency_at_last_gain_write = freq;
+ radio_control_impl::set_rx_gain(index, chan);
+ } else {
+ RFNOC_LOG_TRACE(
+ "No change in index or band, skipped writing RX gain index: " << index);
+ }
+
+ return index;
+}
+
+void rhodium_radio_control_impl::_identify_with_leds(double identify_duration)
+{
+ auto duration_ms = static_cast<uint64_t>(identify_duration * 1000);
+ auto end_time =
+ std::chrono::steady_clock::now() + std::chrono::milliseconds(duration_ms);
+ bool led_state = true;
+ {
+ std::lock_guard<std::mutex> lock(_ant_mutex);
+ while (std::chrono::steady_clock::now() < end_time) {
+ auto atr = led_state ? (LED_RX | LED_RX2 | LED_TX) : 0;
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr, RHODIUM_GPIO_MASK);
+ led_state = !led_state;
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ }
+ }
+ _update_atr(get_tx_antenna(0), TX_DIRECTION);
+ _update_atr(get_rx_antenna(0), RX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::_update_atr(
+ const std::string& ant, const direction_t dir)
+{
+ // This function updates sw10 based on the value of both antennas, so we
+ // use a mutex to prevent other calls in this class instance from running
+ // at the same time.
+ std::lock_guard<std::mutex> lock(_ant_mutex);
+
+ RFNOC_LOG_TRACE(
+ "Updating ATRs for " << ((dir == RX_DIRECTION) ? "RX" : "TX") << " to " << ant);
+
+ const auto rx_ant = (dir == RX_DIRECTION) ? ant : get_rx_antenna(0);
+ const auto tx_ant = (dir == TX_DIRECTION) ? ant : get_tx_antenna(0);
+ const auto sw10_tx = _is_tx_lowband(get_tx_frequency(0)) ?
+ SW10_FROMTXLOWBAND : SW10_FROMTXHIGHBAND;
+
+
+ const uint32_t atr_idle = SW10_ISOLATION;
+
+ const uint32_t atr_rx = [rx_ant]{
+ if (rx_ant == "TX/RX") {
+ return SW10_TORX | LED_RX;
+ } else if (rx_ant == "RX2") {
+ return SW10_ISOLATION | LED_RX2;
+ } else {
+ return SW10_ISOLATION;
+ }
+ }();
+
+ const uint32_t atr_tx = (tx_ant == "TX/RX") ?
+ (sw10_tx | LED_TX) : SW10_ISOLATION;
+
+ const uint32_t atr_dx = [tx_ant, rx_ant, sw10_tx] {
+ uint32_t sw10_return;
+ if (tx_ant == "TX/RX") {
+ // if both channels are set to TX/RX, TX will override
+ sw10_return = sw10_tx | LED_TX;
+ } else if (rx_ant == "TX/RX") {
+ sw10_return = SW10_TORX | LED_RX;
+ } else {
+ sw10_return = SW10_ISOLATION;
+ }
+ sw10_return |= (rx_ant == "RX2") ? LED_RX2 : 0;
+ return sw10_return;
+ }();
+
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr_idle, RHODIUM_GPIO_MASK);
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, atr_rx, RHODIUM_GPIO_MASK);
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, atr_tx, RHODIUM_GPIO_MASK);
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, atr_dx, RHODIUM_GPIO_MASK);
+
+ RFNOC_LOG_TRACE(
+ str(boost::format("Wrote ATR registers i:0x%02X, r:0x%02X, t:0x%02X, d:0x%02X")
+ % atr_idle % atr_rx % atr_tx % atr_dx));
+
+ if (dir == RX_DIRECTION) {
+ radio_control_impl::set_rx_antenna(ant, 0);
+ } else {
+ radio_control_impl::set_tx_antenna(ant, 0);
+ }
+}
+
+void rhodium_radio_control_impl::_update_corrections(
+ const double freq, const direction_t dir, const bool enable)
+{
+ const std::string fe_path_part = dir == RX_DIRECTION ? "rx_fe_corrections"
+ : "tx_fe_corrections";
+ const fs_path fe_corr_path = FE_PATH / fe_path_part / 0;
+
+ if (enable) {
+ const std::vector<uint8_t> db_serial_u8 = get_db_eeprom().count("serial")
+ ? std::vector<uint8_t>()
+ : get_db_eeprom().at("serial");
+ const std::string db_serial =
+ db_serial_u8.empty() ? "unknown"
+ : std::string(db_serial_u8.begin(), db_serial_u8.end());
+ RFNOC_LOG_DEBUG("Loading any available frontend corrections for "
+ << ((dir == RX_DIRECTION) ? "RX" : "TX") << " at " << freq);
+ if (dir == RX_DIRECTION) {
+ apply_rx_fe_corrections(get_tree(), db_serial, fe_corr_path, freq);
+ } else {
+ apply_tx_fe_corrections(get_tree(), db_serial, fe_corr_path, freq);
+ }
+ } else {
+ RFNOC_LOG_DEBUG("Disabling frontend corrections for "
+ << ((dir == RX_DIRECTION) ? "RX" : "TX"));
+ if (dir == RX_DIRECTION) {
+ _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
+ } else {
+ _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
+ }
+ }
+}
+
+bool rhodium_radio_control_impl::_get_spur_dodging_enabled(uhd::direction_t dir) const
+{
+ // get the current tune_arg for spur_dodging
+ // if the tune_arg doesn't exist, use the radio block argument instead
+ const std::string spur_dodging_arg = _tune_args.at(dir).cast<std::string>(
+ SPUR_DODGING_PROP_NAME, _spur_dodging_mode.get());
+
+ RFNOC_LOG_TRACE("_get_spur_dodging_enabled returning " << spur_dodging_arg);
+ if (spur_dodging_arg == "enabled") {
+ return true;
+ } else if (spur_dodging_arg == "disabled") {
+ return false;
+ } else {
+ const std::string err_msg = str(
+ boost::format(
+ "Invalid spur_dodging argument: %s Valid options are [enabled, disabled]")
+ % spur_dodging_arg);
+ RFNOC_LOG_ERROR(err_msg);
+ throw uhd::value_error(err_msg);
+ }
+}
+
+double rhodium_radio_control_impl::_get_spur_dodging_threshold(uhd::direction_t dir) const
+{
+ // get the current tune_arg for spur_dodging_threshold
+ // if the tune_arg doesn't exist, use the radio block argument instead
+ const double threshold = _tune_args.at(dir).cast<double>(
+ SPUR_DODGING_THRESHOLD_PROP_NAME, _spur_dodging_threshold.get());
+ RFNOC_LOG_TRACE("_get_spur_dodging_threshold returning " << threshold);
+ return threshold;
+}
+
+bool rhodium_radio_control_impl::_get_highband_spur_reduction_enabled(
+ uhd::direction_t dir) const
+{
+ const std::string highband_spur_reduction_arg = _tune_args.at(dir).cast<std::string>(
+ HIGHBAND_SPUR_REDUCTION_PROP_NAME, _highband_spur_reduction_mode.get());
+
+ RFNOC_LOG_TRACE(__func__ << " returning " << highband_spur_reduction_arg);
+ if (highband_spur_reduction_arg == "enabled") {
+ return true;
+ } else if (highband_spur_reduction_arg == "disabled") {
+ return false;
+ } else {
+ throw uhd::value_error(
+ str(boost::format("Invalid highband_spur_reduction argument: %s Valid "
+ "options are [enabled, disabled]")
+ % highband_spur_reduction_arg));
+ }
+}
+
+bool rhodium_radio_control_impl::_get_timed_command_enabled() const
+{
+ return get_command_time(0) != time_spec_t::ASAP;
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_tx_antennas(const size_t) const
+{
+ return RHODIUM_RX_ANTENNAS;
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_rx_antennas(const size_t) const
+{
+ return RHODIUM_TX_ANTENNAS;
+}
+
+uhd::freq_range_t rhodium_radio_control_impl::get_tx_frequency_range(const size_t) const
+{
+ return meta_range_t(RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ, 1.0);
+}
+
+uhd::freq_range_t rhodium_radio_control_impl::get_rx_frequency_range(const size_t) const
+{
+ return meta_range_t(RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ, 1.0);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::get_tx_gain_range(const size_t) const
+{
+ return gain_range_t(TX_MIN_GAIN, TX_MAX_GAIN, TX_GAIN_STEP);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::get_rx_gain_range(const size_t) const
+{
+ return gain_range_t(RX_MIN_GAIN, RX_MAX_GAIN, RX_GAIN_STEP);
+}
+
+uhd::meta_range_t rhodium_radio_control_impl::get_tx_bandwidth_range(size_t) const
+{
+ return meta_range_t(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_BANDWIDTH);
+}
+
+uhd::meta_range_t rhodium_radio_control_impl::get_rx_bandwidth_range(size_t) const
+{
+ return meta_range_t(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_BANDWIDTH);
+}
+
+
+/**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+size_t rhodium_radio_control_impl::get_chan_from_dboard_fe(
+ const std::string& fe, const direction_t /* dir */
+ ) const
+{
+ UHD_ASSERT_THROW(boost::lexical_cast<size_t>(fe) == 0);
+ return 0;
+}
+
+std::string rhodium_radio_control_impl::get_dboard_fe_from_chan(
+ const size_t chan, const direction_t /* dir */
+ ) const
+{
+ UHD_ASSERT_THROW(chan == 0);
+ return "0";
+}
+
+std::string rhodium_radio_control_impl::get_fe_name(
+ const size_t, const uhd::direction_t) const
+{
+ return RHODIUM_FE_NAME;
+}
+
+/**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+std::vector<std::string> rhodium_radio_control_impl::get_gpio_banks() const
+{
+ return {RHODIUM_FPGPIO_BANK};
+}
+
+void rhodium_radio_control_impl::set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value)
+{
+ if (bank != RHODIUM_FPGPIO_BANK) {
+ RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank);
+ throw uhd::key_error("Invalid GPIO bank!");
+ }
+ if (!gpio_atr::gpio_attr_rev_map.count(attr)) {
+ RFNOC_LOG_ERROR("Invalid GPIO attr: " << attr);
+ throw uhd::key_error("Invalid GPIO attr!");
+ }
+
+ const gpio_atr::gpio_attr_t gpio_attr = gpio_atr::gpio_attr_rev_map.at(attr);
+
+ if (gpio_attr == gpio_atr::GPIO_READBACK) {
+ RFNOC_LOG_WARNING("Cannot set READBACK attr.");
+ return;
+ }
+
+ _fp_gpio->set_gpio_attr(gpio_attr, value);
+}
+
+uint32_t rhodium_radio_control_impl::get_gpio_attr(
+ const std::string& bank, const std::string& attr)
+{
+ if (bank != RHODIUM_FPGPIO_BANK) {
+ RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank);
+ throw uhd::key_error("Invalid GPIO bank!");
+ }
+
+ return _fp_gpio->get_attr_reg(usrp::gpio_atr::gpio_attr_rev_map.at(attr));
+}
+
+/******************************************************************************
+ * EEPROM API
+ *****************************************************************************/
+void rhodium_radio_control_impl::set_db_eeprom(const eeprom_map_t& db_eeprom)
+{
+ const size_t db_idx = get_block_id().get_block_count();
+ _rpcc->notify_with_token("set_db_eeprom", db_idx, db_eeprom);
+ _db_eeprom = this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx);
+}
+
+eeprom_map_t rhodium_radio_control_impl::get_db_eeprom()
+{
+ return _db_eeprom;
+}
+
+/**************************************************************************
+ * Sensor API
+ *************************************************************************/
+std::vector<std::string> rhodium_radio_control_impl::get_rx_sensor_names(size_t) const
+{
+ return _rx_sensor_names;
+}
+
+sensor_value_t rhodium_radio_control_impl::get_rx_sensor(
+ const std::string& name, size_t chan)
+{
+ if (!uhd::has(_rx_sensor_names, name)) {
+ RFNOC_LOG_ERROR("Invalid RX sensor name: " << name);
+ throw uhd::key_error("Invalid RX sensor name!");
+ }
+ if (name == "lo_locked") {
+ return sensor_value_t(
+ "all_los", this->get_lo_lock_status(RX_DIRECTION), "locked", "unlocked");
+ }
+ return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
+ _rpc_prefix + "get_sensor", "RX", name, chan));
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_tx_sensor_names(size_t) const
+{
+ return _tx_sensor_names;
+}
+
+sensor_value_t rhodium_radio_control_impl::get_tx_sensor(
+ const std::string& name, size_t chan)
+{
+ if (!uhd::has(_rx_sensor_names, name)) {
+ RFNOC_LOG_ERROR("Invalid RX sensor name: " << name);
+ throw uhd::key_error("Invalid RX sensor name!");
+ }
+ if (name == "lo_locked") {
+ return sensor_value_t(
+ "all_los", this->get_lo_lock_status(TX_DIRECTION), "locked", "unlocked");
+ }
+ return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
+ _rpc_prefix + "get_sensor", "TX", name, chan));
+}
+
+bool rhodium_radio_control_impl::get_lo_lock_status(const direction_t dir) const
+{
+ return (dir == RX_DIRECTION) ? _rx_lo->get_lock_status() : _tx_lo->get_lock_status();
+}
+
+/**************************************************************************
+ * node_t API Calls
+ *************************************************************************/
+void rhodium_radio_control_impl::set_command_time(
+ uhd::time_spec_t time, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan == 0);
+ node_t::set_command_time(time, chan);
+ _wb_iface->set_time(time);
+}
+
+// Register the block
+UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
+ rhodium_radio_control, RADIO_BLOCK, N320, "Radio", true, "radio_clk", "bus_clk");
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.hpp
index fad987b98..a70db79cc 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.hpp
@@ -1,5 +1,6 @@
//
// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
@@ -7,28 +8,28 @@
#ifndef INCLUDED_LIBUHD_RFNOC_RHODIUM_RADIO_CTRL_IMPL_HPP
#define INCLUDED_LIBUHD_RFNOC_RHODIUM_RADIO_CTRL_IMPL_HPP
+#include "rhodium_constants.hpp"
#include "rhodium_cpld_ctrl.hpp"
#include "rhodium_cpld_regs.hpp"
+#include <uhd/types/serial.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+#include <uhdlib/rfnoc/radio_control_impl.hpp>
+#include <uhdlib/rfnoc/rpc_block_ctrl.hpp>
#include <uhdlib/usrp/common/lmx2592.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
-#include <uhdlib/rfnoc/rpc_block_ctrl.hpp>
-#include <uhdlib/rfnoc/radio_ctrl_impl.hpp>
#include <uhdlib/usrp/cores/rx_frontend_core_3000.hpp>
#include <uhdlib/usrp/cores/tx_frontend_core_200.hpp>
-#include <uhd/types/serial.hpp>
-#include <uhd/usrp/dboard_manager.hpp>
-#include <uhd/usrp/gpio_defs.hpp>
#include <mutex>
-namespace uhd {
- namespace rfnoc {
+namespace uhd { namespace rfnoc {
/*! \brief Provide access to an Rhodium radio.
*/
-class rhodium_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl
+class rhodium_radio_control_impl : public radio_control_impl
{
public:
- typedef boost::shared_ptr<rhodium_radio_ctrl_impl> sptr;
+ typedef boost::shared_ptr<rhodium_radio_control_impl> sptr;
//! Frequency bands for RX. Bands are a function of the analog filter banks
enum class rx_band {
@@ -59,54 +60,108 @@ public:
/************************************************************************
* Structors
***********************************************************************/
- UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(rhodium_radio_ctrl)
- virtual ~rhodium_radio_ctrl_impl();
+ rhodium_radio_control_impl(make_args_ptr make_args);
+ virtual ~rhodium_radio_control_impl();
/************************************************************************
- * API calls
+ * RF API calls
***********************************************************************/
// Note: We use the cached values in radio_ctrl_impl, so most getters are
// not reimplemented here
- double set_rate(const double rate);
+ double set_rate(double rate);
+ // Setters
void set_tx_antenna(const std::string &ant, const size_t chan);
void set_rx_antenna(const std::string &ant, const size_t chan);
-
double set_tx_frequency(const double freq, const size_t chan);
double set_rx_frequency(const double freq, const size_t chan);
-
- double set_tx_bandwidth(const double bandwidth, const size_t chan);
- double set_rx_bandwidth(const double bandwidth, const size_t chan);
-
+ void set_tx_tune_args(const uhd::device_addr_t&, const size_t chan);
+ void set_rx_tune_args(const uhd::device_addr_t&, const size_t chan);
double set_tx_gain(const double gain, const size_t chan);
double set_rx_gain(const double gain, const size_t chan);
- // LO Property Getters
- std::vector<std::string> get_tx_lo_names(const size_t chan);
- std::vector<std::string> get_rx_lo_names(const size_t chan);
- std::vector<std::string> get_tx_lo_sources(const std::string& name, const size_t chan);
- std::vector<std::string> get_rx_lo_sources(const std::string& name, const size_t chan);
- freq_range_t get_tx_lo_freq_range(const std::string& name, const size_t chan);
- freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan);
+ // Getters
+ std::vector<std::string> get_tx_antennas(const size_t) const;
+ std::vector<std::string> get_rx_antennas(const size_t) const;
+ uhd::freq_range_t get_tx_frequency_range(const size_t) const;
+ uhd::freq_range_t get_rx_frequency_range(const size_t) const;
+ uhd::gain_range_t get_tx_gain_range(const size_t) const;
+ uhd::gain_range_t get_rx_gain_range(const size_t) const;
+ uhd::meta_range_t get_tx_bandwidth_range(size_t) const;
+ uhd::meta_range_t get_rx_bandwidth_range(size_t) const;
- // LO Frequency Control
- double set_tx_lo_freq(const double freq, const std::string& name, const size_t chan);
- double set_rx_lo_freq(const double freq, const std::string& name, const size_t chan);
- double get_tx_lo_freq(const std::string& name, const size_t chan);
+ /**************************************************************************
+ * LO Controls
+ *************************************************************************/
+ std::vector<std::string> get_rx_lo_names(const size_t chan) const;
+ std::vector<std::string> get_rx_lo_sources(
+ const std::string& name, const size_t chan) const;
+ freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan) const;
+ void set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan);
+ const std::string get_rx_lo_source(const std::string& name, const size_t chan);
+ double set_rx_lo_freq(double freq, const std::string& name, const size_t chan);
double get_rx_lo_freq(const std::string& name, const size_t chan);
-
- // LO Source Control
- void set_tx_lo_source(const std::string& src, const std::string& name, const size_t chan);
- void set_rx_lo_source(const std::string& src, const std::string& name, const size_t chan);
+ std::vector<std::string> get_tx_lo_names(const size_t chan) const;
+ std::vector<std::string> get_tx_lo_sources(
+ const std::string& name, const size_t chan) const;
+ freq_range_t get_tx_lo_freq_range(const std::string& name, const size_t chan);
+ void set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan);
const std::string get_tx_lo_source(const std::string& name, const size_t chan);
- const std::string get_rx_lo_source(const std::string& name, const size_t chan);
-
+ double set_tx_lo_freq(const double freq, const std::string& name, const size_t chan);
+ double get_tx_lo_freq(const std::string& name, const size_t chan);
// LO Export Control
- void set_tx_lo_export_enabled(const bool enabled, const std::string& name, const size_t chan);
- void set_rx_lo_export_enabled(const bool enabled, const std::string& name, const size_t chan);
+ void set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan);
+ void set_rx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan);
bool get_tx_lo_export_enabled(const std::string& name, const size_t chan);
bool get_rx_lo_export_enabled(const std::string& name, const size_t chan);
+ /**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+ std::vector<std::string> get_gpio_banks() const;
+ void set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value);
+ uint32_t get_gpio_attr(const std::string& bank, const std::string& attr);
+
+ /**************************************************************************
+ * EEPROM API
+ *************************************************************************/
+ void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom);
+ uhd::eeprom_map_t get_db_eeprom();
+
+ /**************************************************************************
+ * Sensor API
+ *************************************************************************/
+ std::vector<std::string> get_rx_sensor_names(size_t chan) const;
+ uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan);
+ std::vector<std::string> get_tx_sensor_names(size_t chan) const;
+ uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan);
+
+ /**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+ std::string get_slot_name() const
+ {
+ return _radio_slot;
+ }
+ size_t get_chan_from_dboard_fe(
+ const std::string& fe, const uhd::direction_t direction) const;
+ std::string get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t direction) const;
+ std::string get_fe_name(const size_t chan, const uhd::direction_t direction) const;
+
+ /**************************************************************************
+ * node_t API Calls
+ *************************************************************************/
+ void set_command_time(uhd::time_spec_t time, const size_t chan);
+
+ /************************************************************************
+ * ??? calls
+ ***********************************************************************/
// LO Distribution Control
void set_tx_lo_output_enabled(const bool enabled, const std::string& port_name, const size_t chan);
void set_rx_lo_output_enabled(const bool enabled, const std::string& port_name, const size_t chan);
@@ -139,38 +194,49 @@ public:
double get_tx_lo_power(const std::string &name, const size_t chan);
double get_rx_lo_power(const std::string &name, const size_t chan);
- size_t get_chan_from_dboard_fe(const std::string &fe, const direction_t dir);
- std::string get_dboard_fe_from_chan(const size_t chan, const direction_t dir);
-
- void set_rpc_client(
- uhd::rpc_client::sptr rpcc,
- const uhd::device_addr_t &block_args
- );
private:
/**************************************************************************
+ * noc_block_base API
+ *************************************************************************/
+ //! Safely shut down all peripherals
+ //
+ // Reminder: After this is called, no peeks and pokes are allowed!
+ void deinit()
+ {
+ RFNOC_LOG_TRACE("deinit()");
+ // Remove access to all peripherals
+ _wb_iface.reset();
+ _spi.reset();
+ _tx_lo.reset();
+ _rx_lo.reset();
+ _cpld.reset();
+ _gpio.reset();
+ _fp_gpio.reset();
+ _rx_fe_core.reset();
+ _tx_fe_core.reset();
+ }
+
+ /**************************************************************************
* Helpers
*************************************************************************/
//! Initialize all the peripherals connected to this block
void _init_peripherals();
+ //! Sync up with MPM
+ void _init_mpm();
+
//! Set state of this class to sensible defaults
void _init_defaults();
//! Init a subtree for the RF frontends
- void _init_frontend_subtree(
- uhd::property_tree::sptr subtree,
- const size_t chan_idx
- );
+ void _init_frontend_subtree(uhd::property_tree::sptr subtree);
//! Initialize property tree
void _init_prop_tree();
//! Discover and initialize any mpm sensors
- void _init_mpm_sensors(
- const direction_t dir,
- const size_t chan_idx
- );
+ void _init_mpm_sensors(const direction_t dir, const size_t chan_idx);
//! Get the frequency range for an LO
freq_range_t _get_lo_freq_range(const std::string &name) const;
@@ -235,8 +301,6 @@ private:
// NOTE: Returns false if frequency is out of Rh's tx frequency range
static bool _is_tx_lowband(const double freq);
- //! Return the gain range for dir
- static uhd::gain_range_t _get_gain_range(const direction_t dir);
//! Return the gain range of the LMX LO
static uhd::gain_range_t _get_lo_gain_range();
//! Return the power setting range of the LMX LO
@@ -303,12 +367,18 @@ private:
//! Daughterboard info from MPM
std::map<std::string, std::string> _dboard_info;
- //! Additional block args; gets set during set_rpc_client()
- uhd::device_addr_t _block_args;
+ //! Reference to the MB controller
+ mpmd_mb_controller::sptr _n320_mb_control;
+
+ //! Reference to the MB timekeeper
+ uhd::rfnoc::mpmd_mb_controller::mpmd_timekeeper::sptr _n3xx_timekeeper;
//! Reference to the RPC client
uhd::rpc_client::sptr _rpcc;
+ //! Reference to wb_iface compat adapter. This will call into this->regs()
+ uhd::timed_wb_iface::sptr _wb_iface;
+
//! Reference to the SPI core
uhd::spi_iface::sptr _spi;
@@ -343,7 +413,8 @@ private:
std::string _rx_fe_connection;
std::string _tx_fe_connection;
//! Desired RF frequency
- std::map<direction_t,double> _desired_rf_freq = { {RX_DIRECTION, 2.44e9}, {TX_DIRECTION, 2.44e9} };
+ std::map<direction_t, double> _desired_rf_freq = {
+ {RX_DIRECTION, 2.44e9}, {TX_DIRECTION, 2.44e9}};
//! Frequency at which gain setting was last applied. The CPLD requires a new gain
// control write when switching between lowband and highband frequencies, so save
// the frequency when sending a gain control command.
@@ -356,7 +427,8 @@ private:
double _lo_rx_power = 0.0;
double _lo_tx_power = 0.0;
//! Gain profile
- std::map<direction_t,std::string> _gain_profile = { {RX_DIRECTION, "default"}, {TX_DIRECTION, "default"} };
+ std::map<direction_t, std::string> _gain_profile = {
+ {RX_DIRECTION, "default"}, {TX_DIRECTION, "default"}};
//! LO source
std::string _rx_lo_source = "internal";
@@ -377,7 +449,30 @@ private:
bool _lo_dist_rx_out_enabled[4] = { false, false, false, false };
bool _lo_dist_tx_out_enabled[4] = { false, false, false, false };
-}; /* class radio_ctrl_impl */
+ std::unordered_map<uhd::direction_t, uhd::device_addr_t, std::hash<size_t>>
+ _tune_args{{uhd::RX_DIRECTION, uhd::device_addr_t()},
+ {uhd::TX_DIRECTION, uhd::device_addr_t()}};
+
+ //! Cache the contents of the DB EEPROM
+ uhd::eeprom_map_t _db_eeprom;
+
+ //! Cached list of RX sensor names
+ std::vector<std::string> _rx_sensor_names{"lo_locked"};
+ //! Cached list of TX sensor names
+ std::vector<std::string> _tx_sensor_names{"lo_locked"};
+
+ property_t<std::string> _spur_dodging_mode{SPUR_DODGING_PROP_NAME,
+ RHODIUM_DEFAULT_SPUR_DOGING_MODE,
+ {res_source_info::USER}};
+ property_t<double> _spur_dodging_threshold{SPUR_DODGING_THRESHOLD_PROP_NAME,
+ RHODIUM_DEFAULT_SPUR_DOGING_THRESHOLD,
+ {res_source_info::USER}};
+ property_t<std::string> _highband_spur_reduction_mode{
+ HIGHBAND_SPUR_REDUCTION_PROP_NAME,
+ RHODIUM_DEFAULT_HB_SPUR_REDUCTION_MODE,
+ {res_source_info::USER}};
+
+}; /* class rhodium_radio_control_impl */
}} /* namespace uhd::rfnoc */
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp
new file mode 100644
index 000000000..f4f17e8ab
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp
@@ -0,0 +1,252 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_constants.hpp"
+#include "rhodium_cpld_ctrl.hpp"
+#include "rhodium_radio_control.hpp"
+#include <uhd/utils/log.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+namespace {
+
+const char* rx_band_to_log(rhodium_radio_control_impl::rx_band rx_band)
+{
+ switch (rx_band) {
+ case rhodium_radio_control_impl::rx_band::RX_BAND_0:
+ return "0";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_1:
+ return "1";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_2:
+ return "2";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_3:
+ return "3";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_4:
+ return "4";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_5:
+ return "5";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_6:
+ return "6";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_7:
+ return "7";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_INVALID:
+ return "INVALID";
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+const char* tx_band_to_log(rhodium_radio_control_impl::tx_band tx_band)
+{
+ switch (tx_band) {
+ case rhodium_radio_control_impl::tx_band::TX_BAND_0:
+ return "0";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_1:
+ return "1";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_2:
+ return "2";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_3:
+ return "3";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_4:
+ return "4";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_5:
+ return "5";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_6:
+ return "6";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_7:
+ return "7";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_INVALID:
+ return "INVALID";
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+} // namespace
+
+void rhodium_radio_control_impl::_update_rx_freq_switches(const double freq)
+{
+ RFNOC_LOG_TRACE("Update all RX freq related switches. f=" << freq << " Hz");
+ const auto band = _map_freq_to_rx_band(freq);
+ RFNOC_LOG_TRACE("Selected band " << rx_band_to_log(band));
+
+ // select values for lowband/highband switches
+ const bool is_lowband = (band == rx_band::RX_BAND_0);
+ auto rx_sw2_sw7 = is_lowband ? rhodium_cpld_ctrl::RX_SW2_SW7_LOWBANDFILTERBANK
+ : rhodium_cpld_ctrl::RX_SW2_SW7_HIGHBANDFILTERBANK;
+ auto rx_hb_lb_sel = is_lowband ? rhodium_cpld_ctrl::RX_HB_LB_SEL_LOWBAND
+ : rhodium_cpld_ctrl::RX_HB_LB_SEL_HIGHBAND;
+
+ // select values for filter bank switches
+ rhodium_cpld_ctrl::rx_sw3_t rx_sw3;
+ rhodium_cpld_ctrl::rx_sw4_sw5_t rx_sw4_sw5;
+ rhodium_cpld_ctrl::rx_sw6_t rx_sw6;
+ switch (band) {
+ case rx_band::RX_BAND_0:
+ // Low band doesn't use the filter banks, use configuration for band 1
+ case rx_band::RX_BAND_1:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_2:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0760X1100MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_3:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1100X1410MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_4:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1410X2050MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_5:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER2050X3000MHZ;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER2050X3000MHZ;
+ break;
+ case rx_band::RX_BAND_6:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER3000X4500MHZ;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER3000X4500MHZ;
+ break;
+ case rx_band::RX_BAND_7:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER4500X6000MHZ;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER4500X6000MHZ;
+ break;
+ case rx_band::RX_BAND_INVALID:
+ throw uhd::runtime_error(
+ str(boost::format("Cannot map RX frequency to band: %f") % freq));
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ // commit settings to cpld
+ _cpld->set_rx_switches(rx_sw2_sw7, rx_sw3, rx_sw4_sw5, rx_sw6, rx_hb_lb_sel);
+}
+
+void rhodium_radio_control_impl::_update_tx_freq_switches(const double freq)
+{
+ RFNOC_LOG_TRACE("Update all TX freq related switches. f=" << freq << " Hz");
+ const auto band = _map_freq_to_tx_band(freq);
+ RFNOC_LOG_TRACE("Selected band " << tx_band_to_log(band));
+
+ // select values for lowband/highband switches
+ const bool is_lowband = (band == tx_band::TX_BAND_0);
+ auto tx_hb_lb_sel = is_lowband ? rhodium_cpld_ctrl::TX_HB_LB_SEL_LOWBAND
+ : rhodium_cpld_ctrl::TX_HB_LB_SEL_HIGHBAND;
+
+ // select values for filter bank switches
+ rhodium_cpld_ctrl::tx_sw2_t tx_sw2;
+ rhodium_cpld_ctrl::tx_sw3_sw4_t tx_sw3_sw4;
+ rhodium_cpld_ctrl::tx_sw5_t tx_sw5;
+ switch (band) {
+ case tx_band::TX_BAND_0:
+ // Low band doesn't use the filter banks, use configuration for band 1
+ case tx_band::TX_BAND_1:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_2:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1000MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_3:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1350MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_4:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1900MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_5:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP3000MHZ;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP3000MHZ;
+ break;
+ case tx_band::TX_BAND_6:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP4100MHZ;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP4100MHZ;
+ break;
+ case tx_band::TX_BAND_7:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP6000MHZ;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP6000MHZ;
+ break;
+ case tx_band::TX_BAND_INVALID:
+ throw uhd::runtime_error(
+ str(boost::format("Cannot map TX frequency to band: %f") % freq));
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ // commit settings to cpld
+ _cpld->set_tx_switches(tx_sw2, tx_sw3_sw4, tx_sw5, tx_hb_lb_sel);
+}
+
+void rhodium_radio_control_impl::_update_rx_input_switches(const std::string& input)
+{
+ RFNOC_LOG_TRACE("Update all RX input related switches. input=" << input);
+ const rhodium_cpld_ctrl::cal_iso_sw_t cal_iso =
+ (input == "CAL") ? rhodium_cpld_ctrl::CAL_ISO_CALLOOPBACK
+ : rhodium_cpld_ctrl::CAL_ISO_ISOLATION;
+ const rhodium_cpld_ctrl::rx_sw1_t sw1 = [input] {
+ if (input == "TX/RX") {
+ return rhodium_cpld_ctrl::RX_SW1_FROMTXRXINPUT;
+ } else if (input == "RX2") {
+ return rhodium_cpld_ctrl::RX_SW1_FROMRX2INPUT;
+ } else if (input == "CAL") {
+ return rhodium_cpld_ctrl::RX_SW1_FROMCALLOOPBACK;
+ } else if (input == "TERM") {
+ return rhodium_cpld_ctrl::RX_SW1_ISOLATION;
+ } else {
+ throw uhd::runtime_error(
+ "Invalid antenna in _update_rx_input_switches: " + input);
+ }
+ }();
+
+ RFNOC_LOG_TRACE("Selected switch values:"
+ " sw1="
+ << sw1 << " cal_iso=" << cal_iso);
+ _cpld->set_rx_input_switches(sw1, cal_iso);
+}
+
+void rhodium_radio_control_impl::_update_tx_output_switches(const std::string& output)
+{
+ RFNOC_LOG_TRACE("Update all TX output related switches. output=" << output);
+ rhodium_cpld_ctrl::tx_sw1_t sw1;
+
+ if (output == "TX/RX") {
+ // SW1 needs to select low/high band
+ if (_is_tx_lowband(get_tx_frequency(0))) {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_TOLOWBAND;
+ } else {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_TOSWITCH2;
+ }
+ } else if (output == "CAL") {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_TOCALLOOPBACK;
+ } else if (output == "TERM") {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_ISOLATION;
+ } else {
+ throw uhd::runtime_error(
+ "Invalid antenna in _update_tx_output_switches: " + output);
+ }
+
+ RFNOC_LOG_TRACE("Selected switch values: sw1=" << sw1);
+ _cpld->set_tx_output_switches(sw1);
+}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp
new file mode 100644
index 000000000..d6b7afd09
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp
@@ -0,0 +1,611 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_constants.hpp"
+#include "rhodium_radio_control.hpp"
+#include <uhd/transport/chdr.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/rfnoc/reg_iface_adapter.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
+#include <uhdlib/usrp/cores/spi_core_3000.hpp>
+#include <string>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+namespace {
+enum slave_select_t {
+ SEN_CPLD = 8,
+ SEN_TX_LO = 1,
+ SEN_RX_LO = 2,
+ SEN_LO_DIST = 4 /* Unused */
+};
+
+constexpr double RHODIUM_DEFAULT_FREQ = 2.5e9; // Hz
+// An invalid default index ensures that set gain will apply settings
+// the first time it is called
+constexpr double RHODIUM_DEFAULT_INVALID_GAIN = -1; // gain index
+constexpr double RHODIUM_DEFAULT_GAIN = 0; // gain index
+constexpr double RHODIUM_DEFAULT_LO_GAIN = 30; // gain index
+constexpr char RHODIUM_DEFAULT_RX_ANTENNA[] = "RX2";
+constexpr char RHODIUM_DEFAULT_TX_ANTENNA[] = "TX/RX";
+constexpr auto RHODIUM_DEFAULT_MASH_ORDER = lmx2592_iface::mash_order_t::THIRD;
+
+//! Returns the SPI config used by the CPLD
+spi_config_t _get_cpld_spi_config()
+{
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 10;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+
+ return spi_config;
+}
+
+//! Returns the SPI config used by the TX LO
+spi_config_t _get_tx_lo_spi_config()
+{
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 10;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+
+ return spi_config;
+}
+
+//! Returns the SPI config used by the RX LO
+spi_config_t _get_rx_lo_spi_config()
+{
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 10;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+
+ return spi_config;
+}
+
+std::function<void(uint32_t)> _generate_write_spi(
+ uhd::spi_iface::sptr spi, slave_select_t slave, spi_config_t config)
+{
+ return [spi, slave, config](const uint32_t transaction) {
+ spi->write_spi(slave, config, transaction, 24);
+ };
+}
+
+std::function<uint32_t(uint32_t)> _generate_read_spi(
+ uhd::spi_iface::sptr spi, slave_select_t slave, spi_config_t config)
+{
+ return [spi, slave, config](const uint32_t transaction) {
+ return spi->read_spi(slave, config, transaction, 24);
+ };
+}
+} // namespace
+
+void rhodium_radio_control_impl::_init_defaults()
+{
+ RFNOC_LOG_TRACE("Initializing defaults...");
+ const size_t num_rx_chans = get_num_output_ports();
+ const size_t num_tx_chans = get_num_input_ports();
+ UHD_ASSERT_THROW(num_tx_chans == RHODIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(num_rx_chans == RHODIUM_NUM_CHANS);
+
+ for (size_t chan = 0; chan < num_rx_chans; chan++) {
+ radio_control_impl::set_rx_frequency(RHODIUM_DEFAULT_FREQ, chan);
+ radio_control_impl::set_rx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
+ radio_control_impl::set_rx_antenna(RHODIUM_DEFAULT_RX_ANTENNA, chan);
+ radio_control_impl::set_rx_bandwidth(RHODIUM_DEFAULT_BANDWIDTH, chan);
+ }
+
+ for (size_t chan = 0; chan < num_tx_chans; chan++) {
+ radio_control_impl::set_tx_frequency(RHODIUM_DEFAULT_FREQ, chan);
+ radio_control_impl::set_tx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
+ radio_control_impl::set_tx_antenna(RHODIUM_DEFAULT_TX_ANTENNA, chan);
+ radio_control_impl::set_tx_bandwidth(RHODIUM_DEFAULT_BANDWIDTH, chan);
+ }
+
+ register_property(&_spur_dodging_mode);
+ register_property(&_spur_dodging_threshold);
+ register_property(&_highband_spur_reduction_mode);
+
+ // Update configurable block arguments from the device arguments provided
+ const auto block_args = get_block_args();
+ if (block_args.has_key(SPUR_DODGING_PROP_NAME)) {
+ _spur_dodging_mode.set(block_args.get(SPUR_DODGING_PROP_NAME));
+ }
+ if (block_args.has_key(SPUR_DODGING_THRESHOLD_PROP_NAME)) {
+ _spur_dodging_threshold.set(block_args.cast<double>(
+ SPUR_DODGING_THRESHOLD_PROP_NAME, RHODIUM_DEFAULT_SPUR_DOGING_THRESHOLD));
+ }
+ if (block_args.has_key(HIGHBAND_SPUR_REDUCTION_PROP_NAME)) {
+ _highband_spur_reduction_mode.set(
+ block_args.get(HIGHBAND_SPUR_REDUCTION_PROP_NAME));
+ }
+}
+
+void rhodium_radio_control_impl::_init_peripherals()
+{
+ RFNOC_LOG_TRACE("Initializing SPI core...");
+ _spi = spi_core_3000::make(
+ [this](uint32_t addr, uint32_t data) {
+ regs().poke32(addr, data, get_command_time(0));
+ },
+ [this](uint32_t addr) { return regs().peek32(addr, get_command_time(0)); },
+ regmap::REG_SPI_W,
+ 8,
+ regmap::REG_SPI_R);
+ _wb_iface = RFNOC_MAKE_WB_IFACE(0, 0);
+
+ RFNOC_LOG_TRACE("Initializing CPLD...");
+ _cpld = std::make_shared<rhodium_cpld_ctrl>(
+ _generate_write_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()),
+ _generate_read_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()));
+
+ RFNOC_LOG_TRACE("Initializing TX frontend DSP core...")
+ _tx_fe_core = tx_frontend_core_200::make(_wb_iface, n320_regs::SR_TX_FE_BASE);
+ _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
+ _tx_fe_core->populate_subtree(get_tree()->subtree(FE_PATH / "tx_fe_corrections" / 0));
+
+ RFNOC_LOG_TRACE("Initializing RX frontend DSP core...")
+ _rx_fe_core = rx_frontend_core_3000::make(_wb_iface, n320_regs::SR_TX_FE_BASE);
+ _rx_fe_core->set_adc_rate(_master_clock_rate);
+ _rx_fe_core->set_dc_offset(rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE);
+ _rx_fe_core->set_dc_offset_auto(rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE);
+ _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
+ _rx_fe_core->populate_subtree(get_tree()->subtree(FE_PATH / "rx_fe_corrections" / 0));
+
+ RFNOC_LOG_TRACE("Writing initial gain values...");
+ set_tx_gain(RHODIUM_DEFAULT_GAIN, 0);
+ set_tx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
+ set_rx_gain(RHODIUM_DEFAULT_GAIN, 0);
+ set_rx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
+
+ RFNOC_LOG_TRACE("Initializing TX LO...");
+ _tx_lo = lmx2592_iface::make(
+ _generate_write_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()),
+ _generate_read_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()));
+
+ RFNOC_LOG_TRACE("Writing initial TX LO state...");
+ _tx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
+ _tx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
+
+ RFNOC_LOG_TRACE("Initializing RX LO...");
+ _rx_lo = lmx2592_iface::make(
+ _generate_write_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()),
+ _generate_read_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()));
+
+ RFNOC_LOG_TRACE("Writing initial RX LO state...");
+ _rx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
+ _rx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
+
+ RFNOC_LOG_TRACE("Initializing GPIOs...");
+ // DB GPIOs
+ _gpio = usrp::gpio_atr::gpio_atr_3000::make(_wb_iface,
+ n320_regs::SR_DB_GPIO,
+ n320_regs::RB_DB_GPIO,
+ n320_regs::PERIPH_REG_OFFSET);
+ _gpio->set_atr_mode(usrp::gpio_atr::MODE_ATR, // Enable ATR mode for Rhodium bits
+ RHODIUM_GPIO_MASK);
+ _gpio->set_atr_mode(usrp::gpio_atr::MODE_GPIO, // Disable ATR mode for unused bits
+ ~RHODIUM_GPIO_MASK);
+ _gpio->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs
+ usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ _fp_gpio = gpio_atr::gpio_atr_3000::make(_wb_iface,
+ n320_regs::SR_FP_GPIO,
+ n320_regs::RB_FP_GPIO,
+ n320_regs::PERIPH_REG_OFFSET);
+
+ RFNOC_LOG_TRACE("Set initial ATR values...");
+ _update_atr(RHODIUM_DEFAULT_TX_ANTENNA, TX_DIRECTION);
+ _update_atr(RHODIUM_DEFAULT_RX_ANTENNA, RX_DIRECTION);
+
+ // Updating the TX frequency path may include an update to SW10, which is
+ // GPIO controlled, so this must follow CPLD and GPIO initialization
+ RFNOC_LOG_TRACE("Writing initial switch values...");
+ _update_tx_freq_switches(RHODIUM_DEFAULT_FREQ);
+ _update_rx_freq_switches(RHODIUM_DEFAULT_FREQ);
+
+ // Antenna setting requires both CPLD and GPIO control
+ RFNOC_LOG_TRACE("Setting initial antenna settings");
+ _update_tx_output_switches(RHODIUM_DEFAULT_TX_ANTENNA);
+ _update_rx_input_switches(RHODIUM_DEFAULT_RX_ANTENNA);
+
+ RFNOC_LOG_TRACE("Checking for existence of LO Distribution board");
+ _lo_dist_present =
+ _rpcc->request_with_token<bool>(_rpc_prefix + "is_lo_dist_present");
+ RFNOC_LOG_DEBUG(
+ "LO distribution board is" << (_lo_dist_present ? "" : " NOT") << " present");
+
+ RFNOC_LOG_TRACE("Reading EEPROM content...");
+ const size_t db_idx = get_block_id().get_block_count();
+ _db_eeprom = this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx);
+}
+
+// Reminder: The property must not own any properties, it can only interact with
+// the API of this block!
+void rhodium_radio_control_impl::_init_frontend_subtree(uhd::property_tree::sptr subtree)
+{
+ const fs_path tx_fe_path = fs_path("tx_frontends") / 0;
+ const fs_path rx_fe_path = fs_path("rx_frontends") / 0;
+ RFNOC_LOG_TRACE("Adding non-RFNoC block properties for channel 0"
+ << " to prop tree path " << tx_fe_path << " and " << rx_fe_path);
+ // TX Standard attributes
+ subtree->create<std::string>(tx_fe_path / "name").set(RHODIUM_FE_NAME);
+ subtree->create<std::string>(tx_fe_path / "connection")
+ .add_coerced_subscriber(
+ [this](const std::string& conn) { this->_set_tx_fe_connection(conn); })
+ .set_publisher([this]() { return this->_get_tx_fe_connection(); });
+ subtree->create<device_addr_t>(tx_fe_path / "tune_args")
+ .set(device_addr_t())
+ .add_coerced_subscriber(
+ [this](const device_addr_t& args) { set_tx_tune_args(args, 0); })
+ .set_publisher([this]() { return _tune_args.at(uhd::TX_DIRECTION); });
+ // RX Standard attributes
+ subtree->create<std::string>(rx_fe_path / "name").set(RHODIUM_FE_NAME);
+ subtree->create<std::string>(rx_fe_path / "connection")
+ .add_coerced_subscriber(
+ [this](const std::string& conn) { this->_set_rx_fe_connection(conn); })
+ .set_publisher([this]() { return this->_get_rx_fe_connection(); });
+ subtree->create<device_addr_t>(rx_fe_path / "tune_args")
+ .set(device_addr_t())
+ .add_coerced_subscriber(
+ [this](const device_addr_t& args) { set_rx_tune_args(args, 0); })
+ .set_publisher([this]() { return _tune_args.at(uhd::RX_DIRECTION); });
+ ;
+ // TX Antenna
+ subtree->create<std::string>(tx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber(
+ [this](const std::string& ant) { this->set_tx_antenna(ant, 0); })
+ .set_publisher([this]() { return this->get_tx_antenna(0); });
+ subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ })
+ .set_publisher([this]() { return get_tx_antennas(0); });
+ // RX Antenna
+ subtree->create<std::string>(rx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber(
+ [this](const std::string& ant) { this->set_rx_antenna(ant, 0); })
+ .set_publisher([this]() { return this->get_rx_antenna(0); });
+ subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ })
+ .set_publisher([this]() { return get_rx_antennas(0); });
+ // TX frequency
+ subtree->create<double>(tx_fe_path / "freq" / "value")
+ .set_coercer(
+ [this](const double freq) { return this->set_tx_frequency(freq, 0); })
+ .set_publisher([this]() { return this->get_tx_frequency(0); });
+ subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ })
+ .set_publisher([this]() { return get_tx_frequency_range(0); });
+ // RX frequency
+ subtree->create<double>(rx_fe_path / "freq" / "value")
+ .set_coercer(
+ [this](const double freq) { return this->set_rx_frequency(freq, 0); })
+ .set_publisher([this]() { return this->get_rx_frequency(0); });
+ subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ })
+ .set_publisher([this]() { return get_rx_frequency_range(0); });
+ // TX bandwidth
+ subtree->create<double>(tx_fe_path / "bandwidth" / "value")
+ .set_coercer([this](const double bw) { return this->set_tx_bandwidth(bw, 0); })
+ .set_publisher([this]() { return this->get_tx_bandwidth(0); });
+ subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ })
+ .set_publisher([this]() { return get_tx_bandwidth_range(0); });
+ // RX bandwidth
+ subtree->create<double>(rx_fe_path / "bandwidth" / "value")
+ .set_coercer([this](const double bw) { return this->set_rx_bandwidth(bw, 0); })
+ .set_publisher([this]() { return this->get_rx_bandwidth(0); });
+ subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ })
+ .set_publisher([this]() { return get_rx_bandwidth_range(0); });
+ // TX gains
+ subtree->create<double>(tx_fe_path / "gains" / "all" / "value")
+ .set_coercer([this](const double gain) { return this->set_tx_gain(gain, 0); })
+ .set_publisher([this]() { return radio_control_impl::get_tx_gain(0); });
+ subtree->create<meta_range_t>(tx_fe_path / "gains" / "all" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update gain range!");
+ })
+ .set_publisher([this]() { return get_tx_gain_range(0); });
+ // RX gains
+ subtree->create<double>(rx_fe_path / "gains" / "all" / "value")
+ .set_coercer([this](const double gain) { return this->set_rx_gain(gain, 0); })
+ .set_publisher([this]() { return radio_control_impl::get_rx_gain(0); });
+ subtree->create<meta_range_t>(rx_fe_path / "gains" / "all" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update gain range!");
+ })
+ .set_publisher([this]() { return get_rx_gain_range(0); });
+
+ // LO Specific
+ // RX LO
+ // RX LO1 Frequency
+ subtree->create<double>(rx_fe_path / "los" / RHODIUM_LO1 / "freq/value")
+ .set_publisher([this]() { return this->get_rx_lo_freq(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double freq) {
+ return this->set_rx_lo_freq(freq, RHODIUM_LO1, 0);
+ });
+ subtree->create<meta_range_t>(rx_fe_path / "los" / RHODIUM_LO1 / "freq/range")
+ .set_publisher([this]() { return this->get_rx_lo_freq_range(RHODIUM_LO1, 0); });
+ // RX LO1 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "source/options")
+ .set_publisher([this]() { return this->get_rx_lo_sources(RHODIUM_LO1, 0); });
+ subtree->create<std::string>(rx_fe_path / "los" / RHODIUM_LO1 / "source/value")
+ .add_coerced_subscriber([this](const std::string& src) {
+ this->set_rx_lo_source(src, RHODIUM_LO1, 0);
+ })
+ .set_publisher([this]() { return this->get_rx_lo_source(RHODIUM_LO1, 0); });
+ // RX LO1 Export
+ subtree->create<bool>(rx_fe_path / "los" / RHODIUM_LO1 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_rx_lo_export_enabled(enabled, RHODIUM_LO1, 0);
+ });
+ // RX LO1 Gain
+ subtree
+ ->create<double>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "value")
+ .set_publisher([this]() { return this->get_rx_lo_gain(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_rx_lo_gain(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_gain_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO gain range!");
+ });
+ // RX LO1 Output Power
+ subtree
+ ->create<double>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "value")
+ .set_publisher([this]() { return this->get_rx_lo_power(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_rx_lo_power(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_power_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO output power range!");
+ });
+ // RX LO2 Frequency
+ subtree->create<double>(rx_fe_path / "los" / RHODIUM_LO2 / "freq/value")
+ .set_publisher([this]() { return this->get_rx_lo_freq(RHODIUM_LO2, 0); })
+ .set_coercer(
+ [this](double freq) { return this->set_rx_lo_freq(freq, RHODIUM_LO2, 0); });
+ subtree->create<meta_range_t>(rx_fe_path / "los" / RHODIUM_LO2 / "freq/range")
+ .set_publisher([this]() { return this->get_rx_lo_freq_range(RHODIUM_LO2, 0); });
+ // RX LO2 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / RHODIUM_LO2 / "source/options")
+ .set_publisher([this]() { return this->get_rx_lo_sources(RHODIUM_LO2, 0); });
+ subtree->create<std::string>(rx_fe_path / "los" / RHODIUM_LO2 / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_rx_lo_source(src, RHODIUM_LO2, 0); })
+ .set_publisher([this]() { return this->get_rx_lo_source(RHODIUM_LO2, 0); });
+ // RX LO2 Export
+ subtree->create<bool>(rx_fe_path / "los" / RHODIUM_LO2 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_rx_lo_export_enabled(enabled, RHODIUM_LO2, 0);
+ });
+ // RX ALL LOs
+ subtree->create<std::string>(rx_fe_path / "los" / ALL_LOS / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_rx_lo_source(src, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_rx_lo_source(ALL_LOS, 0); });
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / ALL_LOS / "source/options")
+ .set_publisher([this]() { return this->get_rx_lo_sources(ALL_LOS, 0); });
+ subtree->create<bool>(rx_fe_path / "los" / ALL_LOS / "export")
+ .add_coerced_subscriber(
+ [this](bool enabled) { this->set_rx_lo_export_enabled(enabled, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_rx_lo_export_enabled(ALL_LOS, 0); });
+ // TX LO
+ // TX LO1 Frequency
+ subtree->create<double>(tx_fe_path / "los" / RHODIUM_LO1 / "freq/value ")
+ .set_publisher([this]() { return this->get_tx_lo_freq(RHODIUM_LO1, 0); })
+ .set_coercer(
+ [this](double freq) { return this->set_tx_lo_freq(freq, RHODIUM_LO1, 0); });
+ subtree->create<meta_range_t>(tx_fe_path / "los" / RHODIUM_LO1 / "freq/range")
+ .set_publisher([this]() { return this->get_rx_lo_freq_range(RHODIUM_LO1, 0); });
+ // TX LO1 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "source/options")
+ .set_publisher([this]() { return this->get_tx_lo_sources(RHODIUM_LO1, 0); });
+ subtree->create<std::string>(tx_fe_path / "los" / RHODIUM_LO1 / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_tx_lo_source(src, RHODIUM_LO1, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_source(RHODIUM_LO1, 0); });
+ // TX LO1 Export
+ subtree->create<bool>(tx_fe_path / "los" / RHODIUM_LO1 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_tx_lo_export_enabled(enabled, RHODIUM_LO1, 0);
+ });
+ // TX LO1 Gain
+ subtree
+ ->create<double>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "value")
+ .set_publisher([this]() { return this->get_tx_lo_gain(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_tx_lo_gain(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_gain_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO gain range!");
+ });
+ // TX LO1 Output Power
+ subtree
+ ->create<double>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "value")
+ .set_publisher([this]() { return this->get_tx_lo_power(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_tx_lo_power(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_power_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO output power range!");
+ });
+ // TX LO2 Frequency
+ subtree->create<double>(tx_fe_path / "los" / RHODIUM_LO2 / "freq/value")
+ .set_publisher([this]() { return this->get_tx_lo_freq(RHODIUM_LO2, 0); })
+ .set_coercer(
+ [this](double freq) { return this->set_tx_lo_freq(freq, RHODIUM_LO2, 0); });
+ subtree->create<meta_range_t>(tx_fe_path / "los" / RHODIUM_LO2 / "freq/range")
+ .set_publisher([this]() { return this->get_tx_lo_freq_range(RHODIUM_LO2, 0); });
+ // TX LO2 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / RHODIUM_LO2 / "source/options")
+ .set_publisher([this]() { return this->get_tx_lo_sources(RHODIUM_LO2, 0); });
+ subtree->create<std::string>(tx_fe_path / "los" / RHODIUM_LO2 / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_tx_lo_source(src, RHODIUM_LO2, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_source(RHODIUM_LO2, 0); });
+ // TX LO2 Export
+ subtree->create<bool>(tx_fe_path / "los" / RHODIUM_LO2 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_tx_lo_export_enabled(enabled, RHODIUM_LO2, 0);
+ });
+ // TX ALL LOs
+ subtree->create<std::string>(tx_fe_path / "los" / ALL_LOS / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_tx_lo_source(src, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_source(ALL_LOS, 0); });
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / ALL_LOS / "source/options")
+ .set_publisher([this]() { return this->get_tx_lo_sources(ALL_LOS, 0); });
+ subtree->create<bool>(tx_fe_path / "los" / ALL_LOS / "export")
+ .add_coerced_subscriber(
+ [this](bool enabled) { this->set_tx_lo_export_enabled(enabled, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_export_enabled(ALL_LOS, 0); });
+
+ // LO Distribution Output Ports
+ if (_lo_dist_present) {
+ for (const auto& port : LO_OUTPUT_PORT_NAMES) {
+ subtree
+ ->create<bool>(tx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port
+ / "export")
+ .add_coerced_subscriber([this, port](bool enabled) {
+ this->set_tx_lo_output_enabled(enabled, port, 0);
+ })
+ .set_publisher(
+ [this, port]() { return this->get_tx_lo_output_enabled(port, 0); });
+ subtree
+ ->create<bool>(rx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port
+ / "export")
+ .add_coerced_subscriber([this, port](bool enabled) {
+ this->set_rx_lo_output_enabled(enabled, port, 0);
+ })
+ .set_publisher(
+ [this, port]() { return this->get_rx_lo_output_enabled(port, 0); });
+ }
+ }
+
+ // Sensors
+ auto rx_sensor_names = get_rx_sensor_names(0);
+ for (const auto& sensor_name : rx_sensor_names) {
+ RFNOC_LOG_TRACE("Adding RX sensor " << sensor_name);
+ get_tree()
+ ->create<sensor_value_t>(rx_fe_path / "sensors" / sensor_name)
+ .add_coerced_subscriber([](const sensor_value_t&) {
+ throw uhd::runtime_error("Attempting to write to sensor!");
+ })
+ .set_publisher(
+ [this, sensor_name]() { return get_rx_sensor(sensor_name, 0); });
+ }
+ auto tx_sensor_names = get_tx_sensor_names(0);
+ for (const auto& sensor_name : tx_sensor_names) {
+ RFNOC_LOG_TRACE("Adding TX sensor " << sensor_name);
+ get_tree()
+ ->create<sensor_value_t>(tx_fe_path / "sensors" / sensor_name)
+ .add_coerced_subscriber([](const sensor_value_t&) {
+ throw uhd::runtime_error("Attempting to write to sensor!");
+ })
+ .set_publisher(
+ [this, sensor_name]() { return get_tx_sensor(sensor_name, 0); });
+ }
+}
+
+void rhodium_radio_control_impl::_init_prop_tree()
+{
+ this->_init_frontend_subtree(get_tree()->subtree(DB_PATH));
+ get_tree()->create<std::string>(fs_path("rx_codecs") / "name").set("ad9695-625");
+ get_tree()->create<std::string>(fs_path("tx_codecs") / "name").set("dac37j82");
+}
+
+void rhodium_radio_control_impl::_init_mpm()
+{
+ auto block_args = get_block_args();
+ if (block_args.has_key("identify")) {
+ const std::string identify_val = block_args.get("identify");
+ int identify_duration = std::atoi(identify_val.c_str());
+ if (identify_duration == 0) {
+ identify_duration = 5;
+ }
+ RFNOC_LOG_INFO("Running LED identification process for " << identify_duration
+ << " seconds.");
+ _identify_with_leds(identify_duration);
+ }
+
+ // Note: MCR gets set during the init() call (prior to this), which takes
+ // in arguments from the device args. So if block_args contains a
+ // master_clock_rate key, then it should better be whatever the device is
+ // configured to do.
+ _master_clock_rate =
+ _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate");
+ if (block_args.cast<double>("master_clock_rate", _master_clock_rate)
+ != _master_clock_rate) {
+ throw uhd::runtime_error(
+ std::string("Master clock rate mismatch. Device returns ")
+ + std::to_string(_master_clock_rate)
+ + " MHz, "
+ "but should have been "
+ + std::to_string(
+ block_args.cast<double>("master_clock_rate", _master_clock_rate))
+ + " MHz.");
+ }
+ RFNOC_LOG_DEBUG("Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz.");
+ set_tick_rate(_master_clock_rate);
+ _n3xx_timekeeper->update_tick_rate(_master_clock_rate);
+ radio_control_impl::set_rate(_master_clock_rate);
+
+ // Unlike N310, N320 does not have any MPM sensors.
+}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp
new file mode 100644
index 000000000..717a1c94f
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp
@@ -0,0 +1,713 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_constants.hpp"
+#include "rhodium_radio_control.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/utils/narrow.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+namespace {
+constexpr int NUM_THRESHOLDS = 13;
+constexpr std::array<double, NUM_THRESHOLDS> FREQ_THRESHOLDS = {
+ 0.45e9, 0.5e9, 1e9, 1.5e9, 2e9, 2.5e9, 3e9, 3.55e9, 4e9, 4.5e9, 5e9, 5.5e9, 6e9};
+constexpr std::array<int, NUM_THRESHOLDS> LMX_GAIN_VALUES = {
+ 18, 18, 17, 17, 17, 16, 12, 11, 11, 11, 10, 13, 18};
+const std::array<int, NUM_THRESHOLDS> DSA_RX_GAIN_VALUES = {
+ 19, 19, 21, 21, 20, 20, 22, 21, 20, 22, 22, 24, 26};
+const std::array<int, NUM_THRESHOLDS> DSA_TX_GAIN_VALUES = {
+ 19, 19, 21, 21, 20, 20, 22, 21, 22, 24, 24, 26, 28};
+
+// Describes the lowband LO in terms of the master clock rate
+const std::map<double, double> MCR_TO_LOWBAND_IF = {
+ {200e6, 1200e6},
+ {245.76e6, 1228.8e6},
+ {250e6, 1500e6},
+};
+
+// validation helpers
+
+std::vector<std::string> _get_lo_names()
+{
+ return {RHODIUM_LO1, RHODIUM_LO2};
+}
+
+void _validate_lo_name(const std::string& name, const std::string& function_name)
+{
+ if (!uhd::has(_get_lo_names(), name) and name != radio_control::ALL_LOS) {
+ throw uhd::value_error(
+ str(boost::format("%s was called with an invalid LO name: %s") % function_name
+ % name));
+ }
+}
+
+// object agnostic helpers
+std::vector<std::string> _get_lo_sources(const std::string& name)
+{
+ if (name == RHODIUM_LO1 or name == radio_control::ALL_LOS) {
+ return {"internal", "external"};
+ } else {
+ return {"internal"};
+ }
+}
+} // namespace
+
+/******************************************************************************
+ * Property Getters
+ *****************************************************************************/
+
+std::vector<std::string> rhodium_radio_control_impl::get_tx_lo_names(
+ const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_tx_lo_names(chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ return _get_lo_names();
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_rx_lo_names(
+ const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_rx_lo_names(chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ return _get_lo_names();
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_tx_lo_sources(
+ const std::string& name, const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_tx_lo_sources(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_sources");
+
+ return _get_lo_sources(name);
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_rx_lo_sources(
+ const std::string& name, const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_rx_lo_sources(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_sources");
+
+ return _get_lo_sources(name);
+}
+
+freq_range_t rhodium_radio_control_impl::_get_lo_freq_range(const std::string& name) const
+{
+ if (name == RHODIUM_LO1) {
+ return freq_range_t{RHODIUM_LO1_MIN_FREQ, RHODIUM_LO1_MAX_FREQ};
+ } else if (name == RHODIUM_LO2) {
+ // The Lowband LO is a fixed frequency
+ return freq_range_t{_get_lowband_lo_freq(), _get_lowband_lo_freq()};
+ } else {
+ throw uhd::runtime_error(
+ "LO frequency range must be retrieved for each stage individually");
+ }
+}
+
+freq_range_t rhodium_radio_control_impl::get_tx_lo_freq_range(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_freq_range");
+
+ return _get_lo_freq_range(name);
+}
+
+freq_range_t rhodium_radio_control_impl::get_rx_lo_freq_range(
+ const std::string& name, const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_rx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_freq_range");
+
+ return _get_lo_freq_range(name);
+}
+
+/******************************************************************************
+ * Frequency Control
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::set_tx_lo_freq(
+ const double freq, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_tx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO frequency must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO cannot be tuned");
+ return _get_lowband_lo_freq();
+ }
+
+ const auto sd_enabled = _get_spur_dodging_enabled(TX_DIRECTION);
+ const auto sd_threshold = _get_spur_dodging_threshold(TX_DIRECTION);
+
+ _tx_lo_freq = _tx_lo->set_frequency(freq, sd_enabled, sd_threshold);
+ set_tx_lo_gain(_get_lo_dsa_setting(_tx_lo_freq, TX_DIRECTION), RHODIUM_LO1, chan);
+ set_tx_lo_power(_get_lo_power_setting(_tx_lo_freq), RHODIUM_LO1, chan);
+ _cpld->set_tx_lo_path(_tx_lo_freq);
+
+ return _tx_lo_freq;
+}
+
+double rhodium_radio_control_impl::set_rx_lo_freq(
+ const double freq, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_rx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO frequency must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO cannot be tuned");
+ return _get_lowband_lo_freq();
+ }
+
+ const auto sd_enabled = _get_spur_dodging_enabled(RX_DIRECTION);
+ const auto sd_threshold = _get_spur_dodging_threshold(RX_DIRECTION);
+
+ _rx_lo_freq = _rx_lo->set_frequency(freq, sd_enabled, sd_threshold);
+ set_rx_lo_gain(_get_lo_dsa_setting(_rx_lo_freq, RX_DIRECTION), RHODIUM_LO1, chan);
+ set_rx_lo_power(_get_lo_power_setting(_rx_lo_freq), RHODIUM_LO1, chan);
+ _cpld->set_rx_lo_path(_rx_lo_freq);
+
+ return _rx_lo_freq;
+}
+
+double rhodium_radio_control_impl::get_tx_lo_freq(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_freq(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO frequency must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _tx_lo_freq : _get_lowband_lo_freq();
+}
+
+double rhodium_radio_control_impl::get_rx_lo_freq(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_freq(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO frequency must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _rx_lo_freq : _get_lowband_lo_freq();
+}
+
+/******************************************************************************
+ * Source Control
+ *****************************************************************************/
+
+void rhodium_radio_control_impl::set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_tx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_source");
+
+ if (name == RHODIUM_LO2) {
+ if (src != "internal") {
+ throw uhd::value_error("The Lowband LO can only be set to internal");
+ }
+ return;
+ }
+
+ if (src == "internal") {
+ _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
+ _cpld->set_tx_lo_source(
+ rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_INTERNAL);
+ } else if (src == "external") {
+ _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
+ _cpld->set_tx_lo_source(
+ rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_EXTERNAL);
+ } else {
+ throw uhd::value_error(
+ str(boost::format("set_tx_lo_source was called with an invalid LO source: %s "
+ "Valid sources are [internal, external]")
+ % src));
+ }
+
+ const bool enable_corrections = not _is_tx_lowband(get_tx_frequency(0))
+ and src == "internal";
+ _update_corrections(get_tx_frequency(0), TX_DIRECTION, enable_corrections);
+
+ _tx_lo_source = src;
+}
+
+void rhodium_radio_control_impl::set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_rx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_source");
+
+ if (name == RHODIUM_LO2) {
+ if (src != "internal") {
+ throw uhd::value_error("The Lowband LO can only be set to internal");
+ }
+ return;
+ }
+
+ if (src == "internal") {
+ _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
+ _cpld->set_rx_lo_source(
+ rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_INTERNAL);
+ } else if (src == "external") {
+ _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
+ _cpld->set_rx_lo_source(
+ rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_EXTERNAL);
+ } else {
+ throw uhd::value_error(
+ str(boost::format("set_rx_lo_source was called with an invalid LO source: %s "
+ "Valid sources for LO1 are [internal, external]")
+ % src));
+ }
+
+ const bool enable_corrections = not _is_rx_lowband(get_rx_frequency(0))
+ and src == "internal";
+ _update_corrections(get_rx_frequency(0), RX_DIRECTION, enable_corrections);
+
+ _rx_lo_source = src;
+}
+
+const std::string rhodium_radio_control_impl::get_tx_lo_source(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_source(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_source");
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_source : "internal";
+}
+
+const std::string rhodium_radio_control_impl::get_rx_lo_source(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_source(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_source");
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_source : "internal";
+}
+
+/******************************************************************************
+ * Export Control
+ *****************************************************************************/
+
+void rhodium_radio_control_impl::_set_lo1_export_enabled(
+ const bool enabled, const direction_t dir)
+{
+ auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
+ _lo_ctrl->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_B, enabled);
+ if (_lo_dist_present) {
+ const auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
+ _rpcc->notify_with_token(_rpc_prefix + "enable_lo_export", direction, enabled);
+ }
+}
+
+void rhodium_radio_control_impl::set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_lo_export_enabled(enabled=" << enabled << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_export_enabled");
+
+ if (name == RHODIUM_LO2) {
+ if (enabled) {
+ throw uhd::value_error("The lowband LO cannot be exported");
+ }
+ return;
+ }
+
+ _set_lo1_export_enabled(enabled, TX_DIRECTION);
+ _tx_lo_exported = enabled;
+}
+
+void rhodium_radio_control_impl::set_rx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_lo_export_enabled(enabled=" << enabled << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_export_enabled");
+
+ if (name == RHODIUM_LO2) {
+ if (enabled) {
+ throw uhd::value_error("The lowband LO cannot be exported");
+ }
+ return;
+ }
+
+ _set_lo1_export_enabled(enabled, RX_DIRECTION);
+ _rx_lo_exported = enabled;
+}
+
+bool rhodium_radio_control_impl::get_tx_lo_export_enabled(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_export_enabled");
+
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_exported : false;
+}
+
+bool rhodium_radio_control_impl::get_rx_lo_export_enabled(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_export_enabled");
+
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_exported : false;
+}
+
+/******************************************************************************
+ * LO Distribution Control
+ *****************************************************************************/
+
+void rhodium_radio_control_impl::_validate_output_port(
+ const std::string& port_name, const std::string& function_name)
+{
+ if (!_lo_dist_present) {
+ throw uhd::runtime_error(
+ str(boost::format(
+ "%s can only be called if the LO distribution board was detected")
+ % function_name));
+ }
+
+ if (!uhd::has(LO_OUTPUT_PORT_NAMES, port_name)) {
+ throw uhd::value_error(
+ str(boost::format("%s was called with an invalid LO output port: %s Valid "
+ "ports are [LO_OUT_0, LO_OUT_1, LO_OUT_2, LO_OUT_3]")
+ % function_name % port_name));
+ }
+}
+
+void rhodium_radio_control_impl::_set_lo_output_enabled(
+ const bool enabled, const std::string& port_name, const direction_t dir)
+{
+ auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
+ auto name_iter =
+ std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
+ auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
+
+ _rpcc->notify_with_token(_rpc_prefix + "enable_lo_output", direction, index, enabled);
+ auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled
+ : _lo_dist_tx_out_enabled;
+ out_enabled[index] = enabled;
+}
+
+void rhodium_radio_control_impl::set_tx_lo_output_enabled(
+ const bool enabled, const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_lo_output_enabled(enabled=" << enabled
+ << ", port_name=" << port_name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "set_tx_lo_output_enabled");
+
+ _set_lo_output_enabled(enabled, port_name, TX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::set_rx_lo_output_enabled(
+ const bool enabled, const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_lo_output_enabled(enabled=" << enabled
+ << ", port_name=" << port_name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "set_rx_lo_output_enabled");
+
+ _set_lo_output_enabled(enabled, port_name, RX_DIRECTION);
+}
+
+bool rhodium_radio_control_impl::_get_lo_output_enabled(
+ const std::string& port_name, const direction_t dir)
+{
+ auto name_iter =
+ std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
+ auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
+
+ auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled
+ : _lo_dist_tx_out_enabled;
+ return out_enabled[index];
+}
+
+bool rhodium_radio_control_impl::get_tx_lo_output_enabled(
+ const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "get_tx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "get_tx_lo_output_enabled");
+
+ return _get_lo_output_enabled(port_name, TX_DIRECTION);
+}
+
+bool rhodium_radio_control_impl::get_rx_lo_output_enabled(
+ const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "get_rx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "get_rx_lo_output_enabled");
+
+ return _get_lo_output_enabled(port_name, RX_DIRECTION);
+}
+
+/******************************************************************************
+ * Gain Control
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::set_tx_lo_gain(
+ double gain, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_tx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable gain");
+ return 0.0;
+ }
+
+ auto index = _get_lo_gain_range().clip(gain);
+
+ _cpld->set_lo_gain(index, TX_DIRECTION);
+ _lo_tx_gain = index;
+ return _lo_tx_gain;
+}
+
+double rhodium_radio_control_impl::set_rx_lo_gain(
+ double gain, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_rx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable gain");
+ return 0.0;
+ }
+
+ auto index = _get_lo_gain_range().clip(gain);
+
+ _cpld->set_lo_gain(index, RX_DIRECTION);
+ _lo_rx_gain = index;
+ return _lo_rx_gain;
+}
+
+double rhodium_radio_control_impl::get_tx_lo_gain(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_gain(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_rx_gain : 0.0;
+}
+
+double rhodium_radio_control_impl::get_rx_lo_gain(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_gain(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_tx_gain : 0.0;
+}
+
+/******************************************************************************
+ * Output Power Control
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::_set_lo1_power(
+ const double power, const direction_t dir)
+{
+ auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
+ auto coerced_power = static_cast<uint32_t>(_get_lo_power_range().clip(power, true));
+
+ _lo_ctrl->set_output_power(lmx2592_iface::RF_OUTPUT_A, coerced_power);
+ return coerced_power;
+}
+
+double rhodium_radio_control_impl::set_tx_lo_power(
+ const double power, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_lo_power(power=" << power << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable output power");
+ return 0.0;
+ }
+
+ _lo_tx_power = _set_lo1_power(power, TX_DIRECTION);
+ return _lo_tx_power;
+}
+
+double rhodium_radio_control_impl::set_rx_lo_power(
+ const double power, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_lo_power(power=" << power << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable output power");
+ return 0.0;
+ }
+
+ _lo_rx_power = _set_lo1_power(power, RX_DIRECTION);
+ return _lo_rx_power;
+}
+
+double rhodium_radio_control_impl::get_tx_lo_power(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_power(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_tx_power : 0.0;
+}
+
+double rhodium_radio_control_impl::get_rx_lo_power(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_power(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_rx_power : 0.0;
+}
+
+/******************************************************************************
+ * Helper Functions
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::_get_lowband_lo_freq() const
+{
+ return MCR_TO_LOWBAND_IF.at(_master_clock_rate);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::_get_lo_gain_range()
+{
+ return gain_range_t(LO_MIN_GAIN, LO_MAX_GAIN, LO_GAIN_STEP);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::_get_lo_power_range()
+{
+ return gain_range_t(LO_MIN_POWER, LO_MAX_POWER, LO_POWER_STEP);
+}
+
+int rhodium_radio_control_impl::_get_lo_dsa_setting(
+ const double freq, const direction_t dir)
+{
+ int index = 0;
+ while (freq > FREQ_THRESHOLDS[index + 1]) {
+ index++;
+ }
+
+ const double freq_low = FREQ_THRESHOLDS[index];
+ const double freq_high = FREQ_THRESHOLDS[index + 1];
+
+ const auto& gain_table = (dir == RX_DIRECTION) ? DSA_RX_GAIN_VALUES
+ : DSA_TX_GAIN_VALUES;
+ const double gain_low = gain_table[index];
+ const double gain_high = gain_table[index + 1];
+
+
+ const double slope = (gain_high - gain_low) / (freq_high - freq_low);
+ const double gain_at_freq = gain_low + (freq - freq_low) * slope;
+
+ RFNOC_LOG_TRACE("Interpolated DSA Gain is " << gain_at_freq);
+ return static_cast<int>(std::round(gain_at_freq));
+}
+
+unsigned int rhodium_radio_control_impl::_get_lo_power_setting(double freq)
+{
+ int index = 0;
+ while (freq > FREQ_THRESHOLDS[index + 1]) {
+ index++;
+ }
+
+ const double freq_low = FREQ_THRESHOLDS[index];
+ const double freq_high = FREQ_THRESHOLDS[index + 1];
+ const double power_low = LMX_GAIN_VALUES[index];
+ const double power_high = LMX_GAIN_VALUES[index + 1];
+ const double slope = (power_high - power_low) / (freq_high - freq_low);
+ const double power_at_freq = power_low + (freq - freq_low) * slope;
+
+ RFNOC_LOG_TRACE("Interpolated LMX Power is " << power_at_freq);
+ return uhd::narrow_cast<unsigned int>(std::lround(power_at_freq));
+}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp
deleted file mode 100644
index 846a4eac6..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_cpld_ctrl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhd/utils/log.hpp>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-namespace {
- const char* rx_band_to_log(rhodium_radio_ctrl_impl::rx_band rx_band)
- {
- switch (rx_band)
- {
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_0:
- return "0";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_1:
- return "1";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_2:
- return "2";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_3:
- return "3";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_4:
- return "4";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_5:
- return "5";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_6:
- return "6";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_7:
- return "7";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_INVALID:
- return "INVALID";
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- }
-
- const char* tx_band_to_log(rhodium_radio_ctrl_impl::tx_band tx_band)
- {
- switch (tx_band)
- {
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_0:
- return "0";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_1:
- return "1";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_2:
- return "2";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_3:
- return "3";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_4:
- return "4";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_5:
- return "5";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_6:
- return "6";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_7:
- return "7";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_INVALID:
- return "INVALID";
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- }
-}
-
-void rhodium_radio_ctrl_impl::_update_rx_freq_switches(
- const double freq
-) {
- UHD_LOG_TRACE(unique_id(),
- "Update all RX freq related switches. f=" << freq << " Hz, "
- );
- const auto band = _map_freq_to_rx_band(freq);
- UHD_LOG_TRACE(unique_id(),
- "Selected band " << rx_band_to_log(band));
-
- // select values for lowband/highband switches
- const bool is_lowband = (band == rx_band::RX_BAND_0);
- auto rx_sw2_sw7 = is_lowband ?
- rhodium_cpld_ctrl::RX_SW2_SW7_LOWBANDFILTERBANK :
- rhodium_cpld_ctrl::RX_SW2_SW7_HIGHBANDFILTERBANK;
- auto rx_hb_lb_sel = is_lowband ?
- rhodium_cpld_ctrl::RX_HB_LB_SEL_LOWBAND :
- rhodium_cpld_ctrl::RX_HB_LB_SEL_HIGHBAND;
-
- // select values for filter bank switches
- rhodium_cpld_ctrl::rx_sw3_t rx_sw3;
- rhodium_cpld_ctrl::rx_sw4_sw5_t rx_sw4_sw5;
- rhodium_cpld_ctrl::rx_sw6_t rx_sw6;
- switch (band)
- {
- case rx_band::RX_BAND_0:
- // Low band doesn't use the filter banks, use configuration for band 1
- case rx_band::RX_BAND_1:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_2:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0760X1100MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_3:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1100X1410MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_4:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1410X2050MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_5:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER2050X3000MHZ;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER2050X3000MHZ;
- break;
- case rx_band::RX_BAND_6:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER3000X4500MHZ;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER3000X4500MHZ;
- break;
- case rx_band::RX_BAND_7:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER4500X6000MHZ;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER4500X6000MHZ;
- break;
- case rx_band::RX_BAND_INVALID:
- throw uhd::runtime_error(str(boost::format(
- "Cannot map RX frequency to band: %f") % freq));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
-
- // commit settings to cpld
- _cpld->set_rx_switches(
- rx_sw2_sw7,
- rx_sw3,
- rx_sw4_sw5,
- rx_sw6,
- rx_hb_lb_sel
- );
-}
-
-void rhodium_radio_ctrl_impl::_update_tx_freq_switches(
- const double freq
-){
- UHD_LOG_TRACE(unique_id(),
- "Update all TX freq related switches. f=" << freq << " Hz, "
- );
-
- const auto band = _map_freq_to_tx_band(freq);
-
- UHD_LOG_TRACE(unique_id(),
- "Selected band " << tx_band_to_log(band));
-
- // select values for lowband/highband switches
- const bool is_lowband = (band == tx_band::TX_BAND_0);
- auto tx_hb_lb_sel = is_lowband ?
- rhodium_cpld_ctrl::TX_HB_LB_SEL_LOWBAND :
- rhodium_cpld_ctrl::TX_HB_LB_SEL_HIGHBAND;
-
- // select values for filter bank switches
- rhodium_cpld_ctrl::tx_sw2_t tx_sw2;
- rhodium_cpld_ctrl::tx_sw3_sw4_t tx_sw3_sw4;
- rhodium_cpld_ctrl::tx_sw5_t tx_sw5;
- switch (band)
- {
- case tx_band::TX_BAND_0:
- // Low band doesn't use the filter banks, use configuration for band 1
- case tx_band::TX_BAND_1:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_2:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1000MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_3:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1350MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_4:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1900MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_5:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP3000MHZ;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP3000MHZ;
- break;
- case tx_band::TX_BAND_6:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP4100MHZ;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP4100MHZ;
- break;
- case tx_band::TX_BAND_7:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP6000MHZ;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP6000MHZ;
- break;
- case tx_band::TX_BAND_INVALID:
- throw uhd::runtime_error(str(boost::format(
- "Cannot map TX frequency to band: %f") % freq));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
-
- // commit settings to cpld
- _cpld->set_tx_switches(
- tx_sw2,
- tx_sw3_sw4,
- tx_sw5,
- tx_hb_lb_sel
- );
-}
-
-void rhodium_radio_ctrl_impl::_update_rx_input_switches(
- const std::string &input
-) {
- UHD_LOG_TRACE(unique_id(),
- "Update all RX input related switches. input=" << input
- );
- const rhodium_cpld_ctrl::cal_iso_sw_t cal_iso = (input == "CAL") ?
- rhodium_cpld_ctrl::CAL_ISO_CALLOOPBACK :
- rhodium_cpld_ctrl::CAL_ISO_ISOLATION;
- const rhodium_cpld_ctrl::rx_sw1_t sw1 = [input]{
- if (input == "TX/RX")
- {
- return rhodium_cpld_ctrl::RX_SW1_FROMTXRXINPUT;
- }
- else if (input == "RX2") {
- return rhodium_cpld_ctrl::RX_SW1_FROMRX2INPUT;
- }
- else if (input == "CAL") {
- return rhodium_cpld_ctrl::RX_SW1_FROMCALLOOPBACK;
- }
- else if (input == "TERM") {
- return rhodium_cpld_ctrl::RX_SW1_ISOLATION;
- }
- else {
- throw uhd::runtime_error("Invalid antenna in _update_rx_input_switches: " + input);
- }
- }();
-
- UHD_LOG_TRACE(unique_id(),
- "Selected switch values:"
- " sw1=" << sw1 <<
- " cal_iso=" << cal_iso
- );
- _cpld->set_rx_input_switches(sw1, cal_iso);
-}
-
-void rhodium_radio_ctrl_impl::_update_tx_output_switches(
- const std::string &output
-) {
- UHD_LOG_TRACE(unique_id(),
- "Update all TX output related switches. output=" << output
- );
- rhodium_cpld_ctrl::tx_sw1_t sw1;
-
- if (output == "TX/RX")
- {
- //SW1 needs to select low/high band
- if (_is_tx_lowband(get_tx_frequency(0)))
- {
- sw1 = rhodium_cpld_ctrl::TX_SW1_TOLOWBAND;
- }
- else {
- sw1 = rhodium_cpld_ctrl::TX_SW1_TOSWITCH2;
- }
- }
- else if (output == "CAL") {
- sw1 = rhodium_cpld_ctrl::TX_SW1_TOCALLOOPBACK;
- }
- else if (output == "TERM") {
- sw1 = rhodium_cpld_ctrl::TX_SW1_ISOLATION;
- }
- else {
- throw uhd::runtime_error("Invalid antenna in _update_tx_output_switches: " + output);
- }
-
- UHD_LOG_TRACE(unique_id(),
- "Selected switch values: sw1=" << sw1
- );
-
- _cpld->set_tx_output_switches(sw1);
-}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp
deleted file mode 100644
index d6dbbc594..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp
+++ /dev/null
@@ -1,677 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhdlib/utils/narrow.hpp>
-#include <uhdlib/usrp/common/apply_corrections.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/rfnoc/node_ctrl_base.hpp>
-#include <uhd/transport/chdr.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/utils/math.hpp>
-#include <uhd/types/direction.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/exception.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/format.hpp>
-#include <sstream>
-#include <cmath>
-#include <cstdlib>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-using namespace uhd::math::fp_compare;
-
-namespace {
- constexpr char RX_FE_CONNECTION_LOWBAND[] = "QI";
- constexpr char RX_FE_CONNECTION_HIGHBAND[] = "IQ";
- constexpr char TX_FE_CONNECTION_LOWBAND[] = "QI";
- constexpr char TX_FE_CONNECTION_HIGHBAND[] = "IQ";
-
- constexpr double DEFAULT_IDENTIFY_DURATION = 5.0; // seconds
-
- constexpr uint64_t SET_RATE_RPC_TIMEOUT_MS = 10000;
-
- const fs_path TX_FE_PATH = fs_path("tx_frontends") / 0 / "tune_args";
- const fs_path RX_FE_PATH = fs_path("rx_frontends") / 0 / "tune_args";
-
- device_addr_t _get_tune_args(uhd::property_tree::sptr tree, std::string _radio_slot, uhd::direction_t dir)
- {
- const auto subtree = tree->subtree(fs_path("dboards") / _radio_slot);
- const auto tune_arg_path = (dir == RX_DIRECTION) ? RX_FE_PATH : TX_FE_PATH;
- return subtree->access<device_addr_t>(tune_arg_path).get();
- }
-}
-
-
-/******************************************************************************
- * Structors
- *****************************************************************************/
-UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(rhodium_radio_ctrl)
-{
- UHD_LOG_TRACE(unique_id(), "Entering rhodium_radio_ctrl_impl ctor...");
- const char radio_slot_name[] = {'A', 'B'};
- _radio_slot = radio_slot_name[get_block_id().get_block_count()];
- _rpc_prefix =
- (_radio_slot == "A") ? "db_0_" : "db_1_";
- UHD_LOG_TRACE(unique_id(), "Radio slot: " << _radio_slot);
-}
-
-rhodium_radio_ctrl_impl::~rhodium_radio_ctrl_impl()
-{
- UHD_LOG_TRACE(unique_id(), "rhodium_radio_ctrl_impl::dtor() ");
-}
-
-
-/******************************************************************************
- * API Calls
- *****************************************************************************/
-double rhodium_radio_ctrl_impl::set_rate(double requested_rate)
-{
- meta_range_t rates;
- for (const double rate : RHODIUM_RADIO_RATES) {
- rates.push_back(range_t(rate));
- }
-
- const double rate = rates.clip(requested_rate);
- if (!math::frequencies_are_equal(requested_rate, rate)) {
- UHD_LOG_WARNING(unique_id(),
- "Coercing requested sample rate from " << (requested_rate / 1e6) << " MHz to " <<
- (rate / 1e6) << " MHz, the closest possible rate.");
- }
-
- const double current_rate = get_rate();
- if (math::frequencies_are_equal(current_rate, rate)) {
- UHD_LOG_DEBUG(
- unique_id(), "Rate is already at " << (rate / 1e6) << " MHz. Skipping set_rate()");
- return current_rate;
- }
-
- // The master clock rate is always set by requesting db0's clock rate.
- UHD_LOG_TRACE(unique_id(), "Updating master clock rate to " << rate);
- auto new_rate = _rpcc->request_with_token<double>(
- SET_RATE_RPC_TIMEOUT_MS, "db_0_set_master_clock_rate", rate);
- // The lowband LO frequency will change with the master clock rate, so
- // update the tuning of the device.
- set_tx_frequency(get_tx_frequency(0), 0);
- set_rx_frequency(get_rx_frequency(0), 0);
-
- radio_ctrl_impl::set_rate(new_rate);
- return new_rate;
-}
-
-void rhodium_radio_ctrl_impl::set_tx_antenna(
- const std::string &ant,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_antenna(ant=" << ant << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- if (!uhd::has(RHODIUM_TX_ANTENNAS, ant)) {
- throw uhd::value_error(str(
- boost::format("[%s] Requesting invalid TX antenna value: %s")
- % unique_id()
- % ant
- ));
- }
-
- _update_tx_output_switches(ant);
- // _update_atr will set the cached antenna value, so no need to do
- // it here. See comments in _update_antenna for more info.
- _update_atr(ant, TX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::set_rx_antenna(
- const std::string &ant,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "Setting RX antenna to " << ant);
- UHD_ASSERT_THROW(chan == 0);
-
- if (!uhd::has(RHODIUM_RX_ANTENNAS, ant)) {
- throw uhd::value_error(str(
- boost::format("[%s] Requesting invalid RX antenna value: %s")
- % unique_id()
- % ant
- ));
- }
-
- _update_rx_input_switches(ant);
- // _update_atr will set the cached antenna value, so no need to do
- // it here. See comments in _update_antenna for more info.
- _update_atr(ant, RX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::_set_tx_fe_connection(const std::string &conn)
-{
- UHD_LOG_TRACE(unique_id(), "set_tx_fe_connection(conn=" << conn << ")");
-
- if (conn != _tx_fe_connection)
- {
- _tx_fe_core->set_mux(conn);
- _tx_fe_connection = conn;
- }
-}
-
-void rhodium_radio_ctrl_impl::_set_rx_fe_connection(const std::string &conn)
-{
- UHD_LOG_TRACE(unique_id(), "set_rx_fe_connection(conn=" << conn << ")");
-
- if (conn != _rx_fe_connection)
- {
- _rx_fe_core->set_fe_connection(conn);
- _rx_fe_connection = conn;
- }
-}
-
-std::string rhodium_radio_ctrl_impl::_get_tx_fe_connection() const
-{
- UHD_LOG_TRACE(unique_id(), "get_tx_fe_connection()");
-
- return _tx_fe_connection;
-}
-
-std::string rhodium_radio_ctrl_impl::_get_rx_fe_connection() const
-{
- UHD_LOG_TRACE(unique_id(), "get_rx_fe_connection()");
-
- return _rx_fe_connection;
-}
-
-double rhodium_radio_ctrl_impl::set_tx_frequency(
- const double freq,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- const auto old_freq = get_tx_frequency(0);
- double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
-
- if (freq != coerced_target_freq) {
- UHD_LOG_DEBUG(unique_id(), "Requested frequency is outside supported range. Coercing to " << coerced_target_freq);
- }
-
- const bool is_highband = !_is_tx_lowband(coerced_target_freq);
-
- const double target_lo_freq = is_highband ?
- coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
- const double actual_lo_freq =
- set_tx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
- const double coerced_freq = is_highband ?
- actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
- const auto conn = is_highband ?
- TX_FE_CONNECTION_HIGHBAND : TX_FE_CONNECTION_LOWBAND;
-
- // update the cached frequency value now so calls to set gain and update
- // switches will read the new frequency
- radio_ctrl_impl::set_tx_frequency(coerced_freq, chan);
-
- _set_tx_fe_connection(conn);
- set_tx_gain(get_tx_gain(chan), 0);
-
- if (_get_highband_spur_reduction_enabled(TX_DIRECTION)) {
- if (_get_timed_command_enabled() and _is_tx_lowband(old_freq) != not is_highband) {
- UHD_LOG_WARNING(unique_id(),
- "Timed tuning commands that transition between lowband and highband, 450 "
- "MHz, do not function correctly when highband_spur_reduction is enabled! "
- "Disable highband_spur_reduction or avoid using timed tuning commands.");
- }
- UHD_LOG_TRACE(
- unique_id(), "TX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
- _rpcc->notify_with_token(_rpc_prefix + "enable_tx_lowband_lo", (!is_highband));
- }
- _update_tx_freq_switches(coerced_freq);
- const bool enable_corrections = is_highband
- and (get_tx_lo_source(RHODIUM_LO1, 0) == "internal");
- _update_corrections(actual_lo_freq, TX_DIRECTION, enable_corrections);
- // if TX lowband/highband changed and antenna is TX/RX,
- // the ATR and SW1 need to be updated
- _update_tx_output_switches(get_tx_antenna(0));
- _update_atr(get_tx_antenna(0), TX_DIRECTION);
-
- return coerced_freq;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_frequency(
- const double freq,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- const auto old_freq = get_rx_frequency(0);
- double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
-
- if (freq != coerced_target_freq) {
- UHD_LOG_DEBUG(unique_id(), "Requested frequency is outside supported range. Coercing to " << coerced_target_freq);
- }
-
- const bool is_highband = !_is_rx_lowband(coerced_target_freq);
-
- const double target_lo_freq = is_highband ?
- coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
- const double actual_lo_freq =
- set_rx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
- const double coerced_freq = is_highband ?
- actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
- const auto conn = is_highband ?
- RX_FE_CONNECTION_HIGHBAND : RX_FE_CONNECTION_LOWBAND;
-
- // update the cached frequency value now so calls to set gain and update
- // switches will read the new frequency
- radio_ctrl_impl::set_rx_frequency(coerced_freq, chan);
-
- _set_rx_fe_connection(conn);
- set_rx_gain(get_rx_gain(chan), 0);
-
- if (_get_highband_spur_reduction_enabled(RX_DIRECTION)) {
- if (_get_timed_command_enabled() and _is_rx_lowband(old_freq) != not is_highband) {
- UHD_LOG_WARNING(unique_id(),
- "Timed tuning commands that transition between lowband and highband, 450 "
- "MHz, do not function correctly when highband_spur_reduction is enabled! "
- "Disable highband_spur_reduction or avoid using timed tuning commands.");
- }
- UHD_LOG_TRACE(
- unique_id(), "RX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
- _rpcc->notify_with_token(_rpc_prefix + "enable_rx_lowband_lo", (!is_highband));
- }
- _update_rx_freq_switches(coerced_freq);
- const bool enable_corrections = is_highband
- and (get_rx_lo_source(RHODIUM_LO1, 0) == "internal");
- _update_corrections(actual_lo_freq, RX_DIRECTION, enable_corrections);
-
- return coerced_freq;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_bandwidth(
- const double bandwidth,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_bandwidth(bandwidth=" << bandwidth << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return get_rx_bandwidth(chan);
-}
-
-double rhodium_radio_ctrl_impl::set_tx_bandwidth(
- const double bandwidth,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_bandwidth(bandwidth=" << bandwidth << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return get_tx_bandwidth(chan);
-}
-
-double rhodium_radio_ctrl_impl::set_tx_gain(
- const double gain,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- auto freq = this->get_tx_frequency(chan);
- auto index = _get_gain_range(TX_DIRECTION).clip(gain);
-
- auto old_band = _is_tx_lowband(_tx_frequency_at_last_gain_write) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
- auto new_band = _is_tx_lowband(freq) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
-
- // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
- if (get_tx_gain(chan) != index or old_band != new_band) {
- UHD_LOG_TRACE(unique_id(), "Writing new TX gain index: " << index);
- _cpld->set_gain_index(index, new_band, TX_DIRECTION);
- _tx_frequency_at_last_gain_write = freq;
- radio_ctrl_impl::set_tx_gain(index, chan);
- } else {
- UHD_LOG_TRACE(unique_id(), "No change in index or band, skipped writing TX gain index: " << index);
- }
-
- return index;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_gain(
- const double gain,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- auto freq = this->get_rx_frequency(chan);
- auto index = _get_gain_range(RX_DIRECTION).clip(gain);
-
- auto old_band = _is_rx_lowband(_rx_frequency_at_last_gain_write) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
- auto new_band = _is_rx_lowband(freq) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
-
- // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
- if (get_rx_gain(chan) != index or old_band != new_band) {
- UHD_LOG_TRACE(unique_id(), "Writing new RX gain index: " << index);
- _cpld->set_gain_index(index, new_band, RX_DIRECTION);
- _rx_frequency_at_last_gain_write = freq;
- radio_ctrl_impl::set_rx_gain(index, chan);
- } else {
- UHD_LOG_TRACE(unique_id(), "No change in index or band, skipped writing RX gain index: " << index);
- }
-
- return index;
-}
-
-void rhodium_radio_ctrl_impl::_identify_with_leds(
- double identify_duration
-) {
- auto duration_ms = static_cast<uint64_t>(identify_duration * 1000);
- auto end_time =
- std::chrono::steady_clock::now() + std::chrono::milliseconds(duration_ms);
- bool led_state = true;
- {
- std::lock_guard<std::mutex> lock(_ant_mutex);
- while (std::chrono::steady_clock::now() < end_time) {
- auto atr = led_state ? (LED_RX | LED_RX2 | LED_TX) : 0;
- _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr, RHODIUM_GPIO_MASK);
- led_state = !led_state;
- std::this_thread::sleep_for(std::chrono::milliseconds(500));
- }
- }
- _update_atr(get_tx_antenna(0), TX_DIRECTION);
- _update_atr(get_rx_antenna(0), RX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::_update_atr(
- const std::string& ant,
- const direction_t dir
-) {
- // This function updates sw10 based on the value of both antennas, so we
- // use a mutex to prevent other calls in this class instance from running
- // at the same time.
- std::lock_guard<std::mutex> lock(_ant_mutex);
-
- UHD_LOG_TRACE(unique_id(),
- "Updating ATRs for " << ((dir == RX_DIRECTION) ? "RX" : "TX") << " to " << ant);
-
- const auto rx_ant = (dir == RX_DIRECTION) ? ant : get_rx_antenna(0);
- const auto tx_ant = (dir == TX_DIRECTION) ? ant : get_tx_antenna(0);
- const auto sw10_tx = _is_tx_lowband(get_tx_frequency(0)) ?
- SW10_FROMTXLOWBAND : SW10_FROMTXHIGHBAND;
-
-
- const uint32_t atr_idle = SW10_ISOLATION;
-
- const uint32_t atr_rx = [rx_ant]{
- if (rx_ant == "TX/RX") {
- return SW10_TORX | LED_RX;
- } else if (rx_ant == "RX2") {
- return SW10_ISOLATION | LED_RX2;
- } else {
- return SW10_ISOLATION;
- }
- }();
-
- const uint32_t atr_tx = (tx_ant == "TX/RX") ?
- (sw10_tx | LED_TX) : SW10_ISOLATION;
-
- const uint32_t atr_dx = [tx_ant, rx_ant, sw10_tx] {
- uint32_t sw10_return;
- if (tx_ant == "TX/RX") {
- // if both channels are set to TX/RX, TX will override
- sw10_return = sw10_tx | LED_TX;
- } else if (rx_ant == "TX/RX") {
- sw10_return = SW10_TORX | LED_RX;
- } else {
- sw10_return = SW10_ISOLATION;
- }
- sw10_return |= (rx_ant == "RX2") ? LED_RX2 : 0;
- return sw10_return;
- }();
-
- _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr_idle, RHODIUM_GPIO_MASK);
- _gpio->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, atr_rx, RHODIUM_GPIO_MASK);
- _gpio->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, atr_tx, RHODIUM_GPIO_MASK);
- _gpio->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, atr_dx, RHODIUM_GPIO_MASK);
-
- UHD_LOG_TRACE(unique_id(),
- str(boost::format("Wrote ATR registers i:0x%02X, r:0x%02X, t:0x%02X, d:0x%02X")
- % atr_idle % atr_rx % atr_tx % atr_dx));
-
- if (dir == RX_DIRECTION) {
- radio_ctrl_impl::set_rx_antenna(ant, 0);
- } else {
- radio_ctrl_impl::set_tx_antenna(ant, 0);
- }
-}
-
-void rhodium_radio_ctrl_impl::_update_corrections(
- const double freq,
- const direction_t dir,
- const bool enable)
-{
- const std::string fe_path_part = dir == RX_DIRECTION ? "rx_fe_corrections"
- : "tx_fe_corrections";
- const fs_path fe_corr_path = _root_path / fe_path_part / 0;
- const fs_path dboard_path = fs_path("dboards") / _radio_slot;
-
- if (enable)
- {
- UHD_LOG_DEBUG(unique_id(),
- "Loading any available frontend corrections for "
- << ((dir == RX_DIRECTION) ? "RX" : "TX") << " at " << freq);
- if (dir == RX_DIRECTION) {
- apply_rx_fe_corrections(_tree, dboard_path, fe_corr_path, freq);
- } else {
- apply_tx_fe_corrections(_tree, dboard_path, fe_corr_path, freq);
- }
- } else {
- UHD_LOG_DEBUG(unique_id(),
- "Disabling frontend corrections for "
- << ((dir == RX_DIRECTION) ? "RX" : "TX"));
- if (dir == RX_DIRECTION) {
- _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
- } else {
- _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
- }
- }
-
-}
-
-uhd::gain_range_t rhodium_radio_ctrl_impl::_get_gain_range(direction_t dir)
-{
- if (dir == RX_DIRECTION) {
- return gain_range_t(RX_MIN_GAIN, RX_MAX_GAIN, RX_GAIN_STEP);
- } else if (dir == TX_DIRECTION) {
- return gain_range_t(TX_MIN_GAIN, TX_MAX_GAIN, TX_GAIN_STEP);
- } else {
- UHD_THROW_INVALID_CODE_PATH();
- }
-}
-
-bool rhodium_radio_ctrl_impl::_get_spur_dodging_enabled(uhd::direction_t dir) const
-{
- UHD_ASSERT_THROW(_tree->exists(get_arg_path(SPUR_DODGING_ARG_NAME) / "value"));
- auto block_value = _tree->access<std::string>(get_arg_path(SPUR_DODGING_ARG_NAME) / "value").get();
- auto dict = _get_tune_args(_tree, _radio_slot, dir);
-
- // get the current tune_arg for spur_dodging
- // if the tune_arg doesn't exist, use the radio block argument instead
- std::string spur_dodging_arg = dict.cast<std::string>(
- SPUR_DODGING_ARG_NAME,
- block_value);
-
- if (spur_dodging_arg == "enabled")
- {
- UHD_LOG_TRACE(unique_id(), "_get_spur_dodging_enabled returning enabled");
- return true;
- }
- else if (spur_dodging_arg == "disabled") {
- UHD_LOG_TRACE(unique_id(), "_get_spur_dodging_enabled returning disabled");
- return false;
- }
- else {
- throw uhd::value_error(
- str(boost::format("Invalid spur_dodging argument: %s Valid options are [enabled, disabled]")
- % spur_dodging_arg));
- }
-}
-
-double rhodium_radio_ctrl_impl::_get_spur_dodging_threshold(uhd::direction_t dir) const
-{
- UHD_ASSERT_THROW(_tree->exists(get_arg_path(SPUR_DODGING_THRESHOLD_ARG_NAME) / "value"));
- auto block_value = _tree->access<double>(get_arg_path(SPUR_DODGING_THRESHOLD_ARG_NAME) / "value").get();
- auto dict = _get_tune_args(_tree, _radio_slot, dir);
-
- // get the current tune_arg for spur_dodging_threshold
- // if the tune_arg doesn't exist, use the radio block argument instead
- auto threshold = dict.cast<double>(SPUR_DODGING_THRESHOLD_ARG_NAME, block_value);
-
- UHD_LOG_TRACE(unique_id(), "_get_spur_dodging_threshold returning " << threshold);
-
- return threshold;
-}
-
-bool rhodium_radio_ctrl_impl::_get_highband_spur_reduction_enabled(uhd::direction_t dir) const
-{
- UHD_ASSERT_THROW(
- _tree->exists(get_arg_path(HIGHBAND_SPUR_REDUCTION_ARG_NAME) / "value"));
- auto block_value = _tree
- ->access<std::string>(
- get_arg_path(HIGHBAND_SPUR_REDUCTION_ARG_NAME) / "value")
- .get();
- auto dict = _get_tune_args(_tree, _radio_slot, dir);
-
- // get the current tune_arg for highband_spur_reduction
- // if the tune_arg doesn't exist, use the radio block argument instead
- std::string highband_spur_reduction_arg =
- dict.cast<std::string>(HIGHBAND_SPUR_REDUCTION_ARG_NAME, block_value);
-
- if (highband_spur_reduction_arg == "enabled") {
- UHD_LOG_TRACE(unique_id(), __func__ << " returning enabled");
- return true;
- } else if (highband_spur_reduction_arg == "disabled") {
- UHD_LOG_TRACE(unique_id(), __func__ << " returning disabled");
- return false;
- } else {
- throw uhd::value_error(
- str(boost::format("Invalid highband_spur_reduction argument: %s Valid "
- "options are [enabled, disabled]")
- % highband_spur_reduction_arg));
- }
-}
-
-bool rhodium_radio_ctrl_impl::_get_timed_command_enabled() const
-{
- auto& prop = _tree->access<time_spec_t>(fs_path("time") / "cmd");
- // if timed commands are never set, the property will be empty
- // if timed commands were set but cleared, time_spec will be set to 0.0
- return !prop.empty() and prop.get() != time_spec_t(0.0);
-}
-
-size_t rhodium_radio_ctrl_impl::get_chan_from_dboard_fe(
- const std::string &fe, const direction_t /* dir */
-) {
- UHD_ASSERT_THROW(boost::lexical_cast<size_t>(fe) == 0);
- return 0;
-}
-
-std::string rhodium_radio_ctrl_impl::get_dboard_fe_from_chan(
- const size_t chan,
- const direction_t /* dir */
-) {
- UHD_ASSERT_THROW(chan == 0);
- return "0";
-}
-
-void rhodium_radio_ctrl_impl::set_rpc_client(
- uhd::rpc_client::sptr rpcc,
- const uhd::device_addr_t &block_args
-) {
- _rpcc = rpcc;
- _block_args = block_args;
-
- // Get and verify the MCR before _init_peripherals, which will use this value
- // Note: MCR gets set during the init() call (prior to this), which takes
- // in arguments from the device args. So if block_args contains a
- // master_clock_rate key, then it should better be whatever the device is
- // configured to do.
- _master_clock_rate = _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate");
- if (block_args.cast<double>("master_clock_rate", _master_clock_rate)
- != _master_clock_rate) {
- throw uhd::runtime_error(str(
- boost::format("Master clock rate mismatch. Device returns %f MHz, "
- "but should have been %f MHz.")
- % (_master_clock_rate / 1e6)
- % (block_args.cast<double>(
- "master_clock_rate", _master_clock_rate) / 1e6)
- ));
- }
- UHD_LOG_DEBUG(unique_id(),
- "Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz.");
- radio_ctrl_impl::set_rate(_master_clock_rate);
-
- UHD_LOG_TRACE(unique_id(), "Checking for existence of Rhodium DB in slot " << _radio_slot);
- const auto all_dboard_info = _rpcc->request<std::vector<std::map<std::string, std::string>>>("get_dboard_info");
-
- // There is a bug that if only one DB is plugged into slot B the vector
- // will only have 1 element but not be correlated to slot B at all.
- // For now, we assume a 1 element array means the DB is in slot A.
- if (all_dboard_info.size() <= get_block_id().get_block_count())
- {
- UHD_LOG_DEBUG(unique_id(), "No DB detected in slot " << _radio_slot);
- // Name and master clock rate are needed for RFNoC init, so set the
- // name now and let this function continue to set the MCR
- _tree->subtree(fs_path("dboards") / _radio_slot / "tx_frontends" / "0")
- ->create<std::string>("name").set("Unknown");
- _tree->subtree(fs_path("dboards") / _radio_slot / "rx_frontends" / "0")
- ->create<std::string>("name").set("Unknown");
- }
- else {
- _dboard_info = all_dboard_info.at(get_block_id().get_block_count());
- UHD_LOG_DEBUG(unique_id(),
- "Rhodium DB detected in slot " << _radio_slot <<
- ". Serial: " << _dboard_info.at("serial"));
- _init_defaults();
- _init_peripherals();
- _init_prop_tree();
-
- if (block_args.has_key("identify")) {
- const std::string identify_val = block_args.get("identify");
- double identify_duration = 0.0;
- try {
- identify_duration = std::stod(identify_val);
- if (!std::isnormal(identify_duration)) {
- identify_duration = DEFAULT_IDENTIFY_DURATION;
- }
- } catch (std::invalid_argument) {
- identify_duration = DEFAULT_IDENTIFY_DURATION;
- }
-
- UHD_LOG_INFO(unique_id(),
- "Running LED identification process for " << identify_duration
- << " seconds.");
- _identify_with_leds(identify_duration);
- }
- }
-}
-
-bool rhodium_radio_ctrl_impl::get_lo_lock_status(
- const direction_t dir
-) const
-{
- return
- ((dir == RX_DIRECTION) or _tx_lo->get_lock_status()) and
- ((dir == TX_DIRECTION) or _rx_lo->get_lock_status());
-}
-
-UHD_RFNOC_BLOCK_REGISTER(rhodium_radio_ctrl, "RhodiumRadio");
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp
deleted file mode 100644
index 356932bc2..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp
+++ /dev/null
@@ -1,843 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhdlib/usrp/cores/spi_core_3000.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/types/sensors.hpp>
-#include <uhd/transport/chdr.hpp>
-#include <vector>
-#include <string>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-namespace {
- enum slave_select_t {
- SEN_CPLD = 8,
- SEN_TX_LO = 1,
- SEN_RX_LO = 2,
- SEN_LO_DIST = 4 /* Unused */
- };
-
- constexpr uint32_t TX_FE_BASE = 224;
- constexpr uint32_t RX_FE_BASE = 232;
-
- constexpr double RHODIUM_DEFAULT_FREQ = 2.5e9; // Hz
- // An invalid default index ensures that set gain will apply settings
- // the first time it is called
- constexpr double RHODIUM_DEFAULT_INVALID_GAIN = -1; // gain index
- constexpr double RHODIUM_DEFAULT_GAIN = 0; // gain index
- constexpr double RHODIUM_DEFAULT_LO_GAIN = 30; // gain index
- constexpr char RHODIUM_DEFAULT_RX_ANTENNA[] = "RX2";
- constexpr char RHODIUM_DEFAULT_TX_ANTENNA[] = "TX/RX";
- constexpr double RHODIUM_DEFAULT_BANDWIDTH = 250e6; // Hz
- constexpr auto RHODIUM_DEFAULT_MASH_ORDER = lmx2592_iface::mash_order_t::THIRD;
-
- //! Rhodium gain profile options
- const std::vector<std::string> RHODIUM_GP_OPTIONS = {
- "default"
- };
-
- //! Returns the SPI config used by the CPLD
- spi_config_t _get_cpld_spi_config() {
- spi_config_t spi_config;
- spi_config.use_custom_divider = true;
- spi_config.divider = 10;
- spi_config.mosi_edge = spi_config_t::EDGE_RISE;
- spi_config.miso_edge = spi_config_t::EDGE_FALL;
-
- return spi_config;
- }
-
- //! Returns the SPI config used by the TX LO
- spi_config_t _get_tx_lo_spi_config() {
- spi_config_t spi_config;
- spi_config.use_custom_divider = true;
- spi_config.divider = 10;
- spi_config.mosi_edge = spi_config_t::EDGE_RISE;
- spi_config.miso_edge = spi_config_t::EDGE_FALL;
-
- return spi_config;
- }
-
- //! Returns the SPI config used by the RX LO
- spi_config_t _get_rx_lo_spi_config() {
- spi_config_t spi_config;
- spi_config.use_custom_divider = true;
- spi_config.divider = 10;
- spi_config.mosi_edge = spi_config_t::EDGE_RISE;
- spi_config.miso_edge = spi_config_t::EDGE_FALL;
-
- return spi_config;
- }
-
- std::function<void(uint32_t)> _generate_write_spi(
- uhd::spi_iface::sptr spi,
- slave_select_t slave,
- spi_config_t config
- ) {
- return [spi, slave, config](const uint32_t transaction) {
- spi->write_spi(slave, config, transaction, 24);
- };
- }
-
- std::function<uint32_t(uint32_t)> _generate_read_spi(
- uhd::spi_iface::sptr spi,
- slave_select_t slave,
- spi_config_t config
- ) {
- return [spi, slave, config](const uint32_t transaction) {
- return spi->read_spi(slave, config, transaction, 24);
- };
- }
-}
-
-void rhodium_radio_ctrl_impl::_init_defaults()
-{
- UHD_LOG_TRACE(unique_id(), "Initializing defaults...");
- const size_t num_rx_chans = get_output_ports().size();
- const size_t num_tx_chans = get_input_ports().size();
-
- UHD_LOG_TRACE(unique_id(),
- "Num TX chans: " << num_tx_chans
- << " Num RX chans: " << num_rx_chans);
-
- for (size_t chan = 0; chan < num_rx_chans; chan++) {
- radio_ctrl_impl::set_rx_frequency(RHODIUM_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_rx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
- radio_ctrl_impl::set_rx_antenna(RHODIUM_DEFAULT_RX_ANTENNA, chan);
- radio_ctrl_impl::set_rx_bandwidth(RHODIUM_DEFAULT_BANDWIDTH, chan);
- }
-
- for (size_t chan = 0; chan < num_tx_chans; chan++) {
- radio_ctrl_impl::set_tx_frequency(RHODIUM_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_tx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
- radio_ctrl_impl::set_tx_antenna(RHODIUM_DEFAULT_TX_ANTENNA, chan);
- radio_ctrl_impl::set_tx_bandwidth(RHODIUM_DEFAULT_BANDWIDTH, chan);
- }
-
- /** Update default SPP (overwrites the default value from the XML file) **/
- const size_t max_bytes_header =
- uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t);
- const size_t default_spp =
- (_tree->access<size_t>("mtu/recv").get() - max_bytes_header)
- / (2 * sizeof(int16_t));
- UHD_LOG_DEBUG(unique_id(),
- "Setting default spp to " << default_spp);
- _tree->access<int>(get_arg_path("spp") / "value").set(default_spp);
-
- // Update configurable block arguments from the device arguments provided
- if (_block_args.has_key(SPUR_DODGING_ARG_NAME)) {
- _tree->access<std::string>(get_arg_path(SPUR_DODGING_ARG_NAME) / "value")
- .set(_block_args.get(SPUR_DODGING_ARG_NAME));
- }
-
- if (_block_args.has_key(SPUR_DODGING_THRESHOLD_ARG_NAME)) {
- _tree->access<double>(get_arg_path(SPUR_DODGING_THRESHOLD_ARG_NAME) / "value")
- .set(boost::lexical_cast<double>(_block_args.get(SPUR_DODGING_THRESHOLD_ARG_NAME)));
- }
-
- if (_block_args.has_key(HIGHBAND_SPUR_REDUCTION_ARG_NAME)) {
- _tree
- ->access<std::string>(
- get_arg_path(HIGHBAND_SPUR_REDUCTION_ARG_NAME) / "value")
- .set(_block_args.get(HIGHBAND_SPUR_REDUCTION_ARG_NAME));
- }
-}
-
-void rhodium_radio_ctrl_impl::_init_peripherals()
-{
- UHD_LOG_TRACE(unique_id(), "Initializing peripherals...");
-
- UHD_LOG_TRACE(unique_id(), "Initializing SPI core...");
- _spi = spi_core_3000::make(_get_ctrl(0),
- regs::sr_addr(regs::SPI),
- regs::rb_addr(regs::RB_SPI)
- );
-
- UHD_LOG_TRACE(unique_id(), "Initializing CPLD...");
- _cpld = std::make_shared<rhodium_cpld_ctrl>(
- _generate_write_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()),
- _generate_read_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()));
-
- UHD_LOG_TRACE(unique_id(), "Initializing TX frontend DSP core...")
- _tx_fe_core = tx_frontend_core_200::make(_get_ctrl(0), regs::sr_addr(TX_FE_BASE));
- _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
- _tx_fe_core->populate_subtree(_tree->subtree(_root_path / "tx_fe_corrections" / 0));
-
- UHD_LOG_TRACE(unique_id(), "Initializing RX frontend DSP core...")
- _rx_fe_core = rx_frontend_core_3000::make(_get_ctrl(0), regs::sr_addr(RX_FE_BASE));
- _rx_fe_core->set_adc_rate(_master_clock_rate);
- _rx_fe_core->set_dc_offset(rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE);
- _rx_fe_core->set_dc_offset_auto(rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE);
- _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
- _rx_fe_core->populate_subtree(_tree->subtree(_root_path / "rx_fe_corrections" / 0));
-
- UHD_LOG_TRACE(unique_id(), "Writing initial gain values...");
- set_tx_gain(RHODIUM_DEFAULT_GAIN, 0);
- set_tx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
- set_rx_gain(RHODIUM_DEFAULT_GAIN, 0);
- set_rx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
-
- UHD_LOG_TRACE(unique_id(), "Initializing TX LO...");
- _tx_lo = lmx2592_iface::make(
- _generate_write_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()),
- _generate_read_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()));
-
- UHD_LOG_TRACE(unique_id(), "Writing initial TX LO state...");
- _tx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
- _tx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
-
- UHD_LOG_TRACE(unique_id(), "Initializing RX LO...");
- _rx_lo = lmx2592_iface::make(
- _generate_write_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()),
- _generate_read_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()));
-
- UHD_LOG_TRACE(unique_id(), "Writing initial RX LO state...");
- _rx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
- _rx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
-
- UHD_LOG_TRACE(unique_id(), "Initializing GPIOs...");
- _gpio =
- usrp::gpio_atr::gpio_atr_3000::make(
- _get_ctrl(0),
- regs::sr_addr(regs::GPIO),
- regs::rb_addr(regs::RB_DB_GPIO)
- );
- _gpio->set_atr_mode(
- usrp::gpio_atr::MODE_ATR, // Enable ATR mode for Rhodium bits
- RHODIUM_GPIO_MASK
- );
- _gpio->set_atr_mode(
- usrp::gpio_atr::MODE_GPIO, // Disable ATR mode for unused bits
- ~RHODIUM_GPIO_MASK
- );
- _gpio->set_gpio_ddr(
- usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs
- usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL
- );
-
- UHD_LOG_TRACE(unique_id(), "Set initial ATR values...");
- _update_atr(RHODIUM_DEFAULT_TX_ANTENNA, TX_DIRECTION);
- _update_atr(RHODIUM_DEFAULT_RX_ANTENNA, RX_DIRECTION);
-
- // Updating the TX frequency path may include an update to SW10, which is
- // GPIO controlled, so this must follow CPLD and GPIO initialization
- UHD_LOG_TRACE(unique_id(), "Writing initial switch values...");
- _update_tx_freq_switches(RHODIUM_DEFAULT_FREQ);
- _update_rx_freq_switches(RHODIUM_DEFAULT_FREQ);
-
- // Antenna setting requires both CPLD and GPIO control
- UHD_LOG_TRACE(unique_id(), "Setting initial antenna settings");
- _update_tx_output_switches(RHODIUM_DEFAULT_TX_ANTENNA);
- _update_rx_input_switches(RHODIUM_DEFAULT_RX_ANTENNA);
-
- UHD_LOG_TRACE(unique_id(), "Checking for existence of LO Distribution board");
- _lo_dist_present = _rpcc->request_with_token<bool>(_rpc_prefix + "is_lo_dist_present");
- UHD_LOG_DEBUG(unique_id(), str(boost::format("LO distribution board is%s present") % (_lo_dist_present ? "" : " NOT")));
-}
-
-void rhodium_radio_ctrl_impl::_init_frontend_subtree(
- uhd::property_tree::sptr subtree,
- const size_t chan_idx
-) {
- const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx;
- const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx;
- UHD_LOG_TRACE(unique_id(),
- "Adding non-RFNoC block properties for channel " << chan_idx <<
- " to prop tree path " << tx_fe_path << " and " << rx_fe_path);
- // TX Standard attributes
- subtree->create<std::string>(tx_fe_path / "name")
- .set(str(boost::format("Rhodium")))
- ;
- subtree->create<std::string>(tx_fe_path / "connection")
- .add_coerced_subscriber([this](const std::string& conn){
- this->_set_tx_fe_connection(conn);
- })
- .set_publisher([this](){
- return this->_get_tx_fe_connection();
- })
- ;
- subtree->create<device_addr_t>(tx_fe_path / "tune_args")
- .set(device_addr_t())
- ;
- // RX Standard attributes
- subtree->create<std::string>(rx_fe_path / "name")
- .set(str(boost::format("Rhodium")))
- ;
- subtree->create<std::string>(rx_fe_path / "connection")
- .add_coerced_subscriber([this](const std::string& conn){
- this->_set_rx_fe_connection(conn);
- })
- .set_publisher([this](){
- return this->_get_rx_fe_connection();
- })
- ;
- subtree->create<device_addr_t>(rx_fe_path / "tune_args")
- .set(device_addr_t())
- ;
- // TX Antenna
- subtree->create<std::string>(tx_fe_path / "antenna" / "value")
- .add_coerced_subscriber([this, chan_idx](const std::string &ant){
- this->set_tx_antenna(ant, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return this->get_tx_antenna(chan_idx);
- })
- ;
- subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
- .set(RHODIUM_TX_ANTENNAS)
- .add_coerced_subscriber([](const std::vector<std::string> &){
- throw uhd::runtime_error(
- "Attempting to update antenna options!");
- })
- ;
- // RX Antenna
- subtree->create<std::string>(rx_fe_path / "antenna" / "value")
- .add_coerced_subscriber([this, chan_idx](const std::string &ant){
- this->set_rx_antenna(ant, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return this->get_rx_antenna(chan_idx);
- })
- ;
- subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
- .set(RHODIUM_RX_ANTENNAS)
- .add_coerced_subscriber([](const std::vector<std::string> &){
- throw uhd::runtime_error(
- "Attempting to update antenna options!");
- })
- ;
- // TX frequency
- subtree->create<double>(tx_fe_path / "freq" / "value")
- .set_coercer([this, chan_idx](const double freq){
- return this->set_tx_frequency(freq, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return this->get_tx_frequency(chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
- .set(meta_range_t(RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ, 1.0))
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error(
- "Attempting to update freq range!");
- })
- ;
- // RX frequency
- subtree->create<double>(rx_fe_path / "freq" / "value")
- .set_coercer([this, chan_idx](const double freq){
- return this->set_rx_frequency(freq, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return this->get_rx_frequency(chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
- .set(meta_range_t(RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ, 1.0))
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error(
- "Attempting to update freq range!");
- })
- ;
- // TX bandwidth
- subtree->create<double>(tx_fe_path / "bandwidth" / "value")
- .set_coercer([this, chan_idx](const double bw){
- return this->set_tx_bandwidth(bw, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return this->get_tx_bandwidth(chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
- .set(meta_range_t(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_BANDWIDTH))
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error(
- "Attempting to update bandwidth range!");
- })
- ;
- // RX bandwidth
- subtree->create<double>(rx_fe_path / "bandwidth" / "value")
- .set_coercer([this, chan_idx](const double bw){
- return this->set_rx_bandwidth(bw, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return this->get_rx_bandwidth(chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
- .set(meta_range_t(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_BANDWIDTH))
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error(
- "Attempting to update bandwidth range!");
- })
- ;
- // TX gains
- subtree->create<double>(tx_fe_path / "gains" / "all" / "value")
- .set_coercer([this, chan_idx](const double gain){
- return this->set_tx_gain(gain, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return radio_ctrl_impl::get_tx_gain(chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "gains" / "all" / "range")
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error(
- "Attempting to update gain range!");
- })
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_gain_range(TX_DIRECTION);
- })
- ;
-
- subtree->create<std::vector<std::string>>(tx_fe_path / "gains/all/profile/options")
- .set(RHODIUM_GP_OPTIONS);
-
- subtree->create<std::string>(tx_fe_path / "gains/all/profile/value")
- .set_coercer([this](const std::string& profile){
- std::string return_profile = profile;
- if (!uhd::has(RHODIUM_GP_OPTIONS, profile))
- {
- return_profile = "default";
- }
- _gain_profile[TX_DIRECTION] = return_profile;
- return return_profile;
- })
- .set_publisher([this](){
- return _gain_profile[TX_DIRECTION];
- })
- ;
-
- // RX gains
- subtree->create<double>(rx_fe_path / "gains" / "all" / "value")
- .set_coercer([this, chan_idx](const double gain){
- return this->set_rx_gain(gain, chan_idx);
- })
- .set_publisher([this, chan_idx](){
- return radio_ctrl_impl::get_rx_gain(chan_idx);
- })
- ;
-
- subtree->create<meta_range_t>(rx_fe_path / "gains" / "all" / "range")
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error(
- "Attempting to update gain range!");
- })
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_gain_range(RX_DIRECTION);
- })
- ;
-
- subtree->create<std::vector<std::string> >(rx_fe_path / "gains/all/profile/options")
- .set(RHODIUM_GP_OPTIONS);
-
- subtree->create<std::string>(rx_fe_path / "gains/all/profile/value")
- .set_coercer([this](const std::string& profile){
- std::string return_profile = profile;
- if (!uhd::has(RHODIUM_GP_OPTIONS, profile))
- {
- return_profile = "default";
- }
- _gain_profile[RX_DIRECTION] = return_profile;
- return return_profile;
- })
- .set_publisher([this](){
- return _gain_profile[RX_DIRECTION];
- })
- ;
-
- // TX LO lock sensor
- subtree->create<sensor_value_t>(tx_fe_path / "sensors" / "lo_locked")
- .set(sensor_value_t("all_los", false, "locked", "unlocked"))
- .add_coerced_subscriber([](const sensor_value_t &){
- throw uhd::runtime_error(
- "Attempting to write to sensor!");
- })
- .set_publisher([this](){
- return sensor_value_t(
- "all_los",
- this->get_lo_lock_status(TX_DIRECTION),
- "locked", "unlocked"
- );
- })
- ;
- // RX LO lock sensor
- subtree->create<sensor_value_t>(rx_fe_path / "sensors" / "lo_locked")
- .set(sensor_value_t("all_los", false, "locked", "unlocked"))
- .add_coerced_subscriber([](const sensor_value_t &){
- throw uhd::runtime_error(
- "Attempting to write to sensor!");
- })
- .set_publisher([this](){
- return sensor_value_t(
- "all_los",
- this->get_lo_lock_status(RX_DIRECTION),
- "locked", "unlocked"
- );
- })
- ;
- //LO Specific
- //RX LO
- //RX LO1 Frequency
- subtree->create<double>(rx_fe_path / "los"/RHODIUM_LO1/"freq/value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double freq){
- return this->set_rx_lo_freq(freq, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los"/RHODIUM_LO1/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq_range(RHODIUM_LO1, chan_idx);
- })
- ;
- //RX LO1 Source
- subtree->create<std::vector<std::string>>(rx_fe_path / "los"/RHODIUM_LO1/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_sources(RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<std::string>(rx_fe_path / "los"/RHODIUM_LO1/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_rx_lo_source(src, RHODIUM_LO1,chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_source(RHODIUM_LO1, chan_idx);
- })
- ;
- //RX LO1 Export
- subtree->create<bool>(rx_fe_path / "los"/RHODIUM_LO1/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_rx_lo_export_enabled(enabled, RHODIUM_LO1, chan_idx);
- })
- ;
- //RX LO1 Gain
- subtree->create<double>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_gain(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_rx_lo_gain(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_gain_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO gain range!");
- })
- ;
- //RX LO1 Output Power
- subtree->create<double>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_power(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_rx_lo_power(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_power_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO output power range!");
- })
- ;
- //RX LO2 Frequency
- subtree->create<double>(rx_fe_path / "los"/RHODIUM_LO2/"freq/value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq(RHODIUM_LO2, chan_idx);
- })
- .set_coercer([this,chan_idx](double freq){
- return this->set_rx_lo_freq(freq, RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los"/RHODIUM_LO2/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq_range(RHODIUM_LO2, chan_idx);
- })
- ;
- //RX LO2 Source
- subtree->create<std::vector<std::string>>(rx_fe_path / "los"/RHODIUM_LO2/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_sources(RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<std::string>(rx_fe_path / "los"/RHODIUM_LO2/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_rx_lo_source(src, RHODIUM_LO2, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_source(RHODIUM_LO2, chan_idx);
- })
- ;
- //RX LO2 Export
- subtree->create<bool>(rx_fe_path / "los"/RHODIUM_LO2/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_rx_lo_export_enabled(enabled, RHODIUM_LO2, chan_idx);
- });
- //RX ALL LOs
- subtree->create<std::string>(rx_fe_path / "los" / ALL_LOS / "source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src) {
- this->set_rx_lo_source(src, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx]() {
- return this->get_rx_lo_source(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<std::vector<std::string>>(rx_fe_path / "los" / ALL_LOS / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_sources(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<bool>(rx_fe_path / "los" / ALL_LOS / "export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_rx_lo_export_enabled(enabled, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_export_enabled(ALL_LOS, chan_idx);
- })
- ;
- //TX LO
- //TX LO1 Frequency
- subtree->create<double>(tx_fe_path / "los"/RHODIUM_LO1/"freq/value ")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_freq(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](double freq){
- return this->set_tx_lo_freq(freq, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los"/RHODIUM_LO1/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq_range(RHODIUM_LO1, chan_idx);
- })
- ;
- //TX LO1 Source
- subtree->create<std::vector<std::string>>(tx_fe_path / "los"/RHODIUM_LO1/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_sources(RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<std::string>(tx_fe_path / "los"/RHODIUM_LO1/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_tx_lo_source(src, RHODIUM_LO1, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_source(RHODIUM_LO1, chan_idx);
- })
- ;
- //TX LO1 Export
- subtree->create<bool>(tx_fe_path / "los"/RHODIUM_LO1/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_tx_lo_export_enabled(enabled, RHODIUM_LO1, chan_idx);
- })
- ;
- //TX LO1 Gain
- subtree->create<double>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "value")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_gain(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_tx_lo_gain(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_gain_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO gain range!");
- })
- ;
- //TX LO1 Output Power
- subtree->create<double>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "value")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_power(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_tx_lo_power(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_power_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO output power range!");
- })
- ;
- //TX LO2 Frequency
- subtree->create<double>(tx_fe_path / "los"/RHODIUM_LO2/"freq/value")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_freq(RHODIUM_LO2, chan_idx);
- })
- .set_coercer([this,chan_idx](double freq){
- return this->set_tx_lo_freq(freq, RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los"/RHODIUM_LO2/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_freq_range(RHODIUM_LO2,chan_idx);
- })
- ;
- //TX LO2 Source
- subtree->create<std::vector<std::string>>(tx_fe_path / "los"/RHODIUM_LO2/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_sources(RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<std::string>(tx_fe_path / "los"/RHODIUM_LO2/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_tx_lo_source(src, RHODIUM_LO2, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_source(RHODIUM_LO2, chan_idx);
- })
- ;
- //TX LO2 Export
- subtree->create<bool>(tx_fe_path / "los"/RHODIUM_LO2/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_tx_lo_export_enabled(enabled, RHODIUM_LO2, chan_idx);
- })
- ;
- //TX ALL LOs
- subtree->create<std::string>(tx_fe_path / "los" / ALL_LOS / "source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src) {
- this->set_tx_lo_source(src, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx]() {
- return this->get_tx_lo_source(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<std::vector<std::string>>(tx_fe_path / "los" / ALL_LOS / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_tx_lo_sources(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<bool>(tx_fe_path / "los" / ALL_LOS / "export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_tx_lo_export_enabled(enabled, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_export_enabled(ALL_LOS, chan_idx);
- })
- ;
-
- //LO Distribution Output Ports
- if (_lo_dist_present) {
- for (const auto& port : LO_OUTPUT_PORT_NAMES) {
- subtree->create<bool>(tx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port / "export")
- .add_coerced_subscriber([this, chan_idx, port](bool enabled) {
- this->set_tx_lo_output_enabled(enabled, port, chan_idx);
- })
- .set_publisher([this, chan_idx, port]() {
- return this->get_tx_lo_output_enabled(port, chan_idx);
- })
- ;
- subtree->create<bool>(rx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port / "export")
- .add_coerced_subscriber([this, chan_idx, port](bool enabled) {
- this->set_rx_lo_output_enabled(enabled, port, chan_idx);
- })
- .set_publisher([this, chan_idx, port]() {
- return this->get_rx_lo_output_enabled(port, chan_idx);
- })
- ;
- }
- }
-}
-
-void rhodium_radio_ctrl_impl::_init_prop_tree()
-{
- const fs_path fe_base = fs_path("dboards") / _radio_slot;
- this->_init_frontend_subtree(_tree->subtree(fe_base), 0);
-
- // legacy EEPROM paths
- auto eeprom_get = [this]() {
- auto eeprom = dboard_eeprom_t();
- eeprom.id = boost::lexical_cast<uint16_t>(_dboard_info.at("pid"));
- eeprom.revision = _dboard_info.at("rev");
- eeprom.serial = _dboard_info.at("serial");
- return eeprom;
- };
-
- auto eeprom_set = [](dboard_eeprom_t) {
- throw uhd::not_implemented_error("Setting DB EEPROM from this interface not implemented");
- };
-
- _tree->create<dboard_eeprom_t>(fe_base / "rx_eeprom")
- .set_publisher(eeprom_get)
- .add_coerced_subscriber(eeprom_set);
-
- _tree->create<dboard_eeprom_t>(fe_base / "tx_eeprom")
- .set_publisher(eeprom_get)
- .add_coerced_subscriber(eeprom_set);
-
- // EEPROM paths subject to change FIXME
- _tree->create<eeprom_map_t>(_root_path / "eeprom")
- .set(eeprom_map_t());
-
- _tree->create<int>("rx_codecs" / _radio_slot / "gains");
- _tree->create<int>("tx_codecs" / _radio_slot / "gains");
- _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("ad9695-625");
- _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("dac37j82");
-
- // The tick_rate is equivalent to the master clock rate of the DB in slot A
- if (_radio_slot == "A")
- {
- UHD_ASSERT_THROW(!_tree->exists("tick_rate"));
- // set_rate sets the clock rate of the entire device, not just this DB,
- // so only add DB A's set and get functions to the tree.
- _tree->create<double>("tick_rate")
- .set_publisher([this](){ return this->get_rate(); })
- .add_coerced_subscriber([this](double rate) { return this->set_rate(rate); })
- ;
- }
-}
-
-void rhodium_radio_ctrl_impl::_init_mpm_sensors(
- const direction_t dir,
- const size_t chan_idx
-) {
- const std::string trx = (dir == RX_DIRECTION) ? "RX" : "TX";
- const fs_path fe_path =
- fs_path("dboards") / _radio_slot /
- (dir == RX_DIRECTION ? "rx_frontends" : "tx_frontends") / chan_idx;
- auto sensor_list =
- _rpcc->request_with_token<std::vector<std::string>>(
- this->_rpc_prefix + "get_sensors", trx);
- UHD_LOG_TRACE(unique_id(),
- "Chan " << chan_idx << ": Found "
- << sensor_list.size() << " " << trx << " sensors.");
- for (const auto &sensor_name : sensor_list) {
- UHD_LOG_TRACE(unique_id(),
- "Adding " << trx << " sensor " << sensor_name);
- _tree->create<sensor_value_t>(fe_path / "sensors" / sensor_name)
- .add_coerced_subscriber([](const sensor_value_t &){
- throw uhd::runtime_error(
- "Attempting to write to sensor!");
- })
- .set_publisher([this, trx, sensor_name, chan_idx](){
- return sensor_value_t(
- this->_rpcc->request_with_token<sensor_value_t::sensor_map_t>(
- this->_rpc_prefix + "get_sensor",
- trx, sensor_name, chan_idx)
- );
- })
- ;
- }
-}
-
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp
deleted file mode 100644
index 405862485..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp
+++ /dev/null
@@ -1,726 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhdlib/utils/narrow.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/types/direction.hpp>
-#include <uhd/exception.hpp>
-#include <boost/format.hpp>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-namespace {
- constexpr int NUM_THRESHOLDS = 13;
- constexpr std::array<double, NUM_THRESHOLDS> FREQ_THRESHOLDS =
- {0.45e9, 0.5e9, 1e9, 1.5e9, 2e9, 2.5e9, 3e9, 3.55e9, 4e9, 4.5e9, 5e9, 5.5e9, 6e9};
- constexpr std::array<int, NUM_THRESHOLDS> LMX_GAIN_VALUES =
- {18, 18, 17, 17, 17, 16, 12, 11, 11, 11, 10, 13, 18};
- const std::array<int, NUM_THRESHOLDS> DSA_RX_GAIN_VALUES =
- {19, 19, 21, 21, 20, 20, 22, 21, 20, 22, 22, 24, 26};
- const std::array<int, NUM_THRESHOLDS> DSA_TX_GAIN_VALUES =
- {19, 19, 21, 21, 20, 20, 22, 21, 22, 24, 24, 26, 28};
-
- // Describes the lowband LO in terms of the master clock rate
- const std::map<double, double> MCR_TO_LOWBAND_IF = {
- {200e6, 1200e6},
- {245.76e6, 1228.8e6},
- {250e6, 1500e6},
- };
-
- // validation helpers
-
- std::vector<std::string> _get_lo_names()
- {
- return { RHODIUM_LO1, RHODIUM_LO2 };
- }
-
- void _validate_lo_name(const std::string& name, const std::string& function_name)
- {
- if (!uhd::has(_get_lo_names(), name) and name != radio_ctrl::ALL_LOS) {
- throw uhd::value_error(str(boost::format(
- "%s was called with an invalid LO name: %s")
- % function_name
- % name));
- }
- }
-
- // object agnostic helpers
- std::vector<std::string> _get_lo_sources(const std::string& name)
- {
- if (name == RHODIUM_LO1 or name == radio_ctrl::ALL_LOS) {
- return { "internal", "external" };
- } else {
- return { "internal" };
- }
- }
-}
-
-/******************************************************************************
- * Property Getters
- *****************************************************************************/
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_tx_lo_names(
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_names(chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return _get_lo_names();
-}
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_rx_lo_names(
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_names(chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return _get_lo_names();
-}
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_tx_lo_sources(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_sources(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_sources");
-
- return _get_lo_sources(name);
-}
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_rx_lo_sources(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_sources(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_sources");
-
- return _get_lo_sources(name);
-}
-
-freq_range_t rhodium_radio_ctrl_impl::_get_lo_freq_range(const std::string &name) const
-{
- if (name == RHODIUM_LO1) {
- return freq_range_t{ RHODIUM_LO1_MIN_FREQ, RHODIUM_LO1_MAX_FREQ };
- } else if (name == RHODIUM_LO2) {
- // The Lowband LO is a fixed frequency
- return freq_range_t{ _get_lowband_lo_freq(), _get_lowband_lo_freq() };
- } else {
- throw uhd::runtime_error(
- "LO frequency range must be retrieved for each stage individually");
- }
-}
-
-freq_range_t rhodium_radio_ctrl_impl::get_tx_lo_freq_range(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_freq_range");
-
- return _get_lo_freq_range(name);
-}
-
-freq_range_t rhodium_radio_ctrl_impl::get_rx_lo_freq_range(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_freq_range");
-
- return _get_lo_freq_range(name);
-}
-
-/******************************************************************************
- * Frequency Control
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::set_tx_lo_freq(
- const double freq,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO frequency must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO cannot be tuned");
- return _get_lowband_lo_freq();
- }
-
- const auto sd_enabled = _get_spur_dodging_enabled(TX_DIRECTION);
- const auto sd_threshold = _get_spur_dodging_threshold(TX_DIRECTION);
-
- _tx_lo_freq = _tx_lo->set_frequency(freq, sd_enabled, sd_threshold);
- set_tx_lo_gain(_get_lo_dsa_setting(_tx_lo_freq, TX_DIRECTION), RHODIUM_LO1, chan);
- set_tx_lo_power(_get_lo_power_setting(_tx_lo_freq), RHODIUM_LO1, chan);
- _cpld->set_tx_lo_path(_tx_lo_freq);
-
- return _tx_lo_freq;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_lo_freq(
- const double freq,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO frequency must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO cannot be tuned");
- return _get_lowband_lo_freq();
- }
-
- const auto sd_enabled = _get_spur_dodging_enabled(RX_DIRECTION);
- const auto sd_threshold = _get_spur_dodging_threshold(RX_DIRECTION);
-
- _rx_lo_freq = _rx_lo->set_frequency(freq, sd_enabled, sd_threshold);
- set_rx_lo_gain(_get_lo_dsa_setting(_rx_lo_freq, RX_DIRECTION), RHODIUM_LO1, chan);
- set_rx_lo_power(_get_lo_power_setting(_rx_lo_freq), RHODIUM_LO1, chan);
- _cpld->set_rx_lo_path(_rx_lo_freq);
-
- return _rx_lo_freq;
-}
-
-double rhodium_radio_ctrl_impl::get_tx_lo_freq(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_freq(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO frequency must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _tx_lo_freq : _get_lowband_lo_freq();
-}
-
-double rhodium_radio_ctrl_impl::get_rx_lo_freq(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_freq(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO frequency must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _rx_lo_freq : _get_lowband_lo_freq();
-}
-
-/******************************************************************************
- * Source Control
- *****************************************************************************/
-
-void rhodium_radio_ctrl_impl::set_tx_lo_source(
- const std::string& src,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_source");
-
- if (name == RHODIUM_LO2) {
- if (src != "internal") {
- throw uhd::value_error("The Lowband LO can only be set to internal");
- }
- return;
- }
-
- if (src == "internal") {
- _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
- _cpld->set_tx_lo_source(rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_INTERNAL);
- } else if (src == "external") {
- _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
- _cpld->set_tx_lo_source(rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_EXTERNAL);
- } else {
- throw uhd::value_error(str(boost::format("set_tx_lo_source was called with an invalid LO source: %s Valid sources are [internal, external]") % src));
- }
-
- const bool enable_corrections = not _is_tx_lowband(get_tx_frequency(0))
- and src == "internal";
- _update_corrections(get_tx_frequency(0), TX_DIRECTION, enable_corrections);
-
- _tx_lo_source = src;
-}
-
-void rhodium_radio_ctrl_impl::set_rx_lo_source(
- const std::string& src,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_source");
-
- if (name == RHODIUM_LO2) {
- if (src != "internal") {
- throw uhd::value_error("The Lowband LO can only be set to internal");
- }
- return;
- }
-
- if (src == "internal") {
- _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
- _cpld->set_rx_lo_source(rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_INTERNAL);
- } else if (src == "external") {
- _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
- _cpld->set_rx_lo_source(rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_EXTERNAL);
- } else {
- throw uhd::value_error(str(boost::format("set_rx_lo_source was called with an invalid LO source: %s Valid sources for LO1 are [internal, external]") % src));
- }
-
- const bool enable_corrections = not _is_rx_lowband(get_rx_frequency(0))
- and src == "internal";
- _update_corrections(get_rx_frequency(0), RX_DIRECTION, enable_corrections);
-
- _rx_lo_source = src;
-}
-
-const std::string rhodium_radio_ctrl_impl::get_tx_lo_source(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_source(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_source");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_source : "internal";
-}
-
-const std::string rhodium_radio_ctrl_impl::get_rx_lo_source(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_source(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_source");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_source : "internal";
-}
-
-/******************************************************************************
- * Export Control
- *****************************************************************************/
-
-void rhodium_radio_ctrl_impl::_set_lo1_export_enabled(
- const bool enabled,
- const direction_t dir
-) {
- auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
- _lo_ctrl->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_B, enabled);
- if (_lo_dist_present) {
- const auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
- _rpcc->notify_with_token(_rpc_prefix + "enable_lo_export", direction, enabled);
- }
-}
-
-void rhodium_radio_ctrl_impl::set_tx_lo_export_enabled(
- const bool enabled,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_export_enabled(enabled=" << enabled << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_export_enabled");
-
- if (name == RHODIUM_LO2) {
- if (enabled) {
- throw uhd::value_error("The lowband LO cannot be exported");
- }
- return;
- }
-
- _set_lo1_export_enabled(enabled, TX_DIRECTION);
- _tx_lo_exported = enabled;
-}
-
-void rhodium_radio_ctrl_impl::set_rx_lo_export_enabled(
- const bool enabled,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_export_enabled(enabled=" << enabled << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_export_enabled");
-
- if (name == RHODIUM_LO2) {
- if (enabled) {
- throw uhd::value_error("The lowband LO cannot be exported");
- }
- return;
- }
-
- _set_lo1_export_enabled(enabled, RX_DIRECTION);
- _rx_lo_exported = enabled;
-}
-
-bool rhodium_radio_ctrl_impl::get_tx_lo_export_enabled(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_export_enabled");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_exported : false;
-}
-
-bool rhodium_radio_ctrl_impl::get_rx_lo_export_enabled(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_export_enabled");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_exported : false;
-}
-
-/******************************************************************************
- * LO Distribution Control
- *****************************************************************************/
-
-void rhodium_radio_ctrl_impl::_validate_output_port(const std::string& port_name, const std::string& function_name)
-{
- if (!_lo_dist_present) {
- throw uhd::runtime_error(str(boost::format(
- "%s can only be called if the LO distribution board was detected") % function_name));
- }
-
- if (!uhd::has(LO_OUTPUT_PORT_NAMES, port_name)) {
- throw uhd::value_error(str(boost::format(
- "%s was called with an invalid LO output port: %s Valid ports are [LO_OUT_0, LO_OUT_1, LO_OUT_2, LO_OUT_3]")
- % function_name % port_name));
- }
-}
-
-void rhodium_radio_ctrl_impl::_set_lo_output_enabled(
- const bool enabled,
- const std::string& port_name,
- const direction_t dir
-) {
- auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
- auto name_iter = std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
- auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
-
- _rpcc->notify_with_token(_rpc_prefix + "enable_lo_output", direction, index, enabled);
- auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled : _lo_dist_tx_out_enabled;
- out_enabled[index] = enabled;
-}
-
-void rhodium_radio_ctrl_impl::set_tx_lo_output_enabled(
- const bool enabled,
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_output_enabled(enabled=" << enabled << ", port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "set_tx_lo_output_enabled");
-
- _set_lo_output_enabled(enabled, port_name, TX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::set_rx_lo_output_enabled(
- const bool enabled,
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_output_enabled(enabled=" << enabled << ", port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "set_rx_lo_output_enabled");
-
- _set_lo_output_enabled(enabled, port_name, RX_DIRECTION);
-}
-
-bool rhodium_radio_ctrl_impl::_get_lo_output_enabled(
- const std::string& port_name,
- const direction_t dir
-) {
- auto name_iter = std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
- auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
-
- auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled : _lo_dist_tx_out_enabled;
- return out_enabled[index];
-}
-
-bool rhodium_radio_ctrl_impl::get_tx_lo_output_enabled(
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "get_tx_lo_output_enabled");
-
- return _get_lo_output_enabled(port_name, TX_DIRECTION);
-}
-
-bool rhodium_radio_ctrl_impl::get_rx_lo_output_enabled(
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "get_rx_lo_output_enabled");
-
- return _get_lo_output_enabled(port_name, RX_DIRECTION);
-}
-
-/******************************************************************************
- * Gain Control
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::set_tx_lo_gain(
- double gain,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable gain");
- return 0.0;
- }
-
- auto index = _get_lo_gain_range().clip(gain);
-
- _cpld->set_lo_gain(index, TX_DIRECTION);
- _lo_tx_gain = index;
- return _lo_tx_gain;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_lo_gain(
- double gain,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable gain");
- return 0.0;
- }
-
- auto index = _get_lo_gain_range().clip(gain);
-
- _cpld->set_lo_gain(index, RX_DIRECTION);
- _lo_rx_gain = index;
- return _lo_rx_gain;
-}
-
-double rhodium_radio_ctrl_impl::get_tx_lo_gain(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_gain(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_rx_gain : 0.0;
-}
-
-double rhodium_radio_ctrl_impl::get_rx_lo_gain(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_gain(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_tx_gain : 0.0;
-}
-
-/******************************************************************************
- * Output Power Control
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::_set_lo1_power(
- const double power,
- const direction_t dir
-) {
- auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
- auto coerced_power = static_cast<uint32_t>(_get_lo_power_range().clip(power, true));
-
- _lo_ctrl->set_output_power(lmx2592_iface::RF_OUTPUT_A, coerced_power);
- return coerced_power;
-}
-
-double rhodium_radio_ctrl_impl::set_tx_lo_power(
- const double power,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_power(power=" << power << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable output power");
- return 0.0;
- }
-
- _lo_tx_power = _set_lo1_power(power, TX_DIRECTION);
- return _lo_tx_power;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_lo_power(
- const double power,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_power(power=" << power << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable output power");
- return 0.0;
- }
-
- _lo_rx_power = _set_lo1_power(power, RX_DIRECTION);
- return _lo_rx_power;
-}
-
-double rhodium_radio_ctrl_impl::get_tx_lo_power(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_power(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_tx_power : 0.0;
-}
-
-double rhodium_radio_ctrl_impl::get_rx_lo_power(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_power(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_rx_power : 0.0;
-}
-
-/******************************************************************************
- * Helper Functions
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::_get_lowband_lo_freq() const
-{
- return MCR_TO_LOWBAND_IF.at(_master_clock_rate);
-}
-
-uhd::gain_range_t rhodium_radio_ctrl_impl::_get_lo_gain_range()
-{
- return gain_range_t(LO_MIN_GAIN, LO_MAX_GAIN, LO_GAIN_STEP);
-}
-
-uhd::gain_range_t rhodium_radio_ctrl_impl::_get_lo_power_range()
-{
- return gain_range_t(LO_MIN_POWER, LO_MAX_POWER, LO_POWER_STEP);
-}
-
-int rhodium_radio_ctrl_impl::_get_lo_dsa_setting(const double freq, const direction_t dir) {
- int index = 0;
- while (freq > FREQ_THRESHOLDS[index+1]) {
- index++;
- }
-
- const double freq_low = FREQ_THRESHOLDS[index];
- const double freq_high = FREQ_THRESHOLDS[index+1];
-
- const auto& gain_table = (dir == RX_DIRECTION) ? DSA_RX_GAIN_VALUES : DSA_TX_GAIN_VALUES;
- const double gain_low = gain_table[index];
- const double gain_high = gain_table[index+1];
-
-
- const double slope = (gain_high - gain_low) / (freq_high - freq_low);
- const double gain_at_freq = gain_low + (freq - freq_low) * slope;
-
- UHD_LOG_TRACE(unique_id(), "Interpolated DSA Gain is " << gain_at_freq);
- return static_cast<int>(std::round(gain_at_freq));
-}
-
-unsigned int rhodium_radio_ctrl_impl::_get_lo_power_setting(double freq) {
- int index = 0;
- while (freq > FREQ_THRESHOLDS[index+1]) {
- index++;
- }
-
- const double freq_low = FREQ_THRESHOLDS[index];
- const double freq_high = FREQ_THRESHOLDS[index+1];
- const double power_low = LMX_GAIN_VALUES[index];
- const double power_high = LMX_GAIN_VALUES[index+1];
-
-
- const double slope = (power_high - power_low) / (freq_high - freq_low);
- const double power_at_freq = power_low + (freq - freq_low) * slope;
-
- UHD_LOG_TRACE(unique_id(), "Interpolated LMX Power is " << power_at_freq);
- return uhd::narrow_cast<unsigned int>(std::lround(power_at_freq));
-}
diff --git a/host/lib/usrp/mpmd/CMakeLists.txt b/host/lib/usrp/mpmd/CMakeLists.txt
index 21511e6da..18c9c6cbd 100644
--- a/host/lib/usrp/mpmd/CMakeLists.txt
+++ b/host/lib/usrp/mpmd/CMakeLists.txt
@@ -21,21 +21,21 @@ if(ENABLE_MPMD)
${CMAKE_CURRENT_SOURCE_DIR}/mpmd_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mpmd_mboard_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mpmd_mb_controller.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_mb_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mpmd_prop_tree.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_xport.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_xport_mgr.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_xport_ctrl_udp.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_link_if_mgr.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_link_if_ctrl_udp.cpp
)
if(ENABLE_LIBERIO)
LIBUHD_APPEND_SOURCES(
- ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_xport_ctrl_liberio.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_link_if_ctrl_liberio.cpp
)
endif(ENABLE_LIBERIO)
if(ENABLE_DPDK)
LIBUHD_APPEND_SOURCES(
- ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_xport_ctrl_dpdk_udp.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/mpmd_link_if_ctrl_dpdk_udp.cpp
)
endif(ENABLE_DPDK)
diff --git a/host/lib/usrp/mpmd/mpmd_find.cpp b/host/lib/usrp/mpmd/mpmd_find.cpp
index 2b8e1350d..3e8bcc72f 100644
--- a/host/lib/usrp/mpmd/mpmd_find.cpp
+++ b/host/lib/usrp/mpmd/mpmd_find.cpp
@@ -8,6 +8,7 @@
#include "mpmd_devices.hpp"
#include "mpmd_impl.hpp"
+#include "mpmd_link_if_mgr.hpp"
#include <uhdlib/transport/dpdk_common.hpp>
#include <uhd/transport/if_addrs.hpp>
#include <uhd/transport/udp_simple.hpp>
@@ -89,7 +90,7 @@ device_addrs_t mpmd_find_with_addr(
// Create result to return
device_addr_t new_addr;
- new_addr[xport::MGMT_ADDR_KEY] = recv_addr;
+ new_addr[MGMT_ADDR_KEY] = recv_addr;
new_addr["type"] = "mpmd"; // hwd will overwrite this
// remove ident string and put other informations into device_args dict
result.erase(result.begin());
@@ -101,7 +102,7 @@ device_addrs_t mpmd_find_with_addr(
el,
[](const char& in) { return in == '='; },
boost::token_compress_on);
- if (value[0] != xport::MGMT_ADDR_KEY) {
+ if (value[0] != MGMT_ADDR_KEY) {
new_addr[value[0]] = value[1];
}
}
@@ -132,12 +133,12 @@ device_addrs_t mpmd_find_with_addrs(const device_addrs_t& hints)
found_devices.reserve(hints.size());
for (const auto& hint : hints) {
if (not(hint.has_key(xport::FIRST_ADDR_KEY)
- or hint.has_key(xport::MGMT_ADDR_KEY))) {
+ or hint.has_key(MGMT_ADDR_KEY))) {
UHD_LOG_DEBUG("MPMD FIND", "No address given in hint " << hint.to_string());
continue;
}
const std::string mgmt_addr =
- hint.get(xport::MGMT_ADDR_KEY, hint.get(xport::FIRST_ADDR_KEY, ""));
+ hint.get(MGMT_ADDR_KEY, hint.get(xport::FIRST_ADDR_KEY, ""));
device_addrs_t reply_addrs = mpmd_find_with_addr(mgmt_addr, hint);
if (reply_addrs.size() > 1) {
UHD_LOG_ERROR("MPMD",
@@ -217,7 +218,7 @@ device_addrs_t mpmd_find(const device_addr_t& hint_)
// Scenario 1): User gave us at least one address
if (not hints.empty()
and (hints[0].has_key(xport::FIRST_ADDR_KEY)
- or hints[0].has_key(xport::MGMT_ADDR_KEY))) {
+ or hints[0].has_key(MGMT_ADDR_KEY))) {
// Note: We don't try and connect to the devices in this mode, because
// we only get here if the user specified addresses, and we assume she
// knows what she's doing.
diff --git a/host/lib/usrp/mpmd/mpmd_image_loader.cpp b/host/lib/usrp/mpmd/mpmd_image_loader.cpp
index d5c7d3da9..3a285da3e 100644
--- a/host/lib/usrp/mpmd/mpmd_image_loader.cpp
+++ b/host/lib/usrp/mpmd/mpmd_image_loader.cpp
@@ -13,6 +13,7 @@
#include <uhd/types/component_file.hpp>
#include <uhd/types/eeprom.hpp>
#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem/convenience.hpp>
#include <fstream>
@@ -216,6 +217,7 @@ static bool mpmd_image_loader(const image_loader::image_loader_args_t& image_loa
return true;
}
+
}} // namespace uhd::
UHD_STATIC_BLOCK(register_mpm_image_loader)
diff --git a/host/lib/usrp/mpmd/mpmd_impl.cpp b/host/lib/usrp/mpmd/mpmd_impl.cpp
index 05d847060..30a3c5804 100644
--- a/host/lib/usrp/mpmd/mpmd_impl.cpp
+++ b/host/lib/usrp/mpmd/mpmd_impl.cpp
@@ -14,11 +14,11 @@
#include <uhd/utils/tasks.hpp>
#include <uhdlib/rfnoc/radio_ctrl_impl.hpp>
#include <uhdlib/rfnoc/rpc_block_ctrl.hpp>
-#include <../device3/device3_impl.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
+#include <chrono>
#include <future>
#include <memory>
#include <mutex>
@@ -34,11 +34,10 @@ namespace {
/*************************************************************************
* Local constants
************************************************************************/
-const size_t MPMD_CROSSBAR_MAX_LADDR = 255;
//! Most pessimistic time for a CHDR query to go to device and back
const double MPMD_CHDR_MAX_RTT = 0.02;
-//! MPM Compatibility number
-const std::vector<size_t> MPM_COMPAT_NUM = {1, 2};
+//! MPM Compatibility number {MAJOR, MINOR}
+const std::vector<size_t> MPM_COMPAT_NUM = {2, 0};
/*************************************************************************
* Helper functions
@@ -155,7 +154,7 @@ const std::string mpmd_impl::MPM_ECHO_CMD = "MPM-ECHO";
* Structors
****************************************************************************/
mpmd_impl::mpmd_impl(const device_addr_t& device_args)
- : usrp::device3_impl(), _device_args(device_args)
+ : rfnoc_device(), _device_args(device_args)
{
const device_addrs_t mb_args = separate_device_addr(device_args);
const size_t num_mboards = mb_args.size();
@@ -175,20 +174,12 @@ mpmd_impl::mpmd_impl(const device_addr_t& device_args)
_mb.push_back(claim_and_make(mb_args[mb_i]));
}
- // Next figure out the number of base xport addresses. This way, we
- // can run _mb[*]->init() in parallel on all the _mb.
- // This can *not* be parallelized.
- std::vector<size_t> base_xport_addr(num_mboards, 2); // Starts at 2 [sic]
- for (size_t mb_i = 0; mb_i < num_mboards - 1; ++mb_i) {
- base_xport_addr[mb_i + 1] = base_xport_addr[mb_i] + _mb[mb_i]->num_xbars;
- }
-
if (not skip_init) {
// Run the actual device initialization. This can run in parallel.
for (size_t mb_i = 0; mb_i < num_mboards; ++mb_i) {
// Note: This is the only place we do compat number checks. They're
// effectively disabled for skip_init=1
- setup_mb(_mb[mb_i].get(), mb_i, base_xport_addr[mb_i]);
+ setup_mb(_mb[mb_i].get(), mb_i);
}
} else {
UHD_LOG_DEBUG("MPMD", "Claimed device, but skipped init.");
@@ -205,12 +196,6 @@ mpmd_impl::mpmd_impl(const device_addr_t& device_args)
}
if (not skip_init) {
- // This can be parallelized, because the blocks of individual mboards
- // live on different subtrees.
- for (size_t mb_i = 0; mb_i < mb_args.size(); ++mb_i) {
- setup_rfnoc_blocks(_mb[mb_i].get(), mb_i, mb_args[mb_i]);
- }
-
// FIXME this section only makes sense for when the time source is external.
// So, check for that, or something similar.
// This section of code assumes that the prop tree is set and we have access
@@ -218,12 +203,6 @@ mpmd_impl::mpmd_impl(const device_addr_t& device_args)
if (device_args.has_key("sync_time")) {
reset_time_synchronized(_tree);
}
-
- auto filtered_block_args = device_args; // TODO actually filter
- // Blocks will finalize their own setup in this function. They have
- // (and might need) full access to the prop tree, the timekeepers, etc.
- // This is already internally parallelized.
- setup_rpc_blocks(filtered_block_args, serialize_init);
} else {
UHD_LOG_INFO("MPMD", "Claimed device without full initialization.");
}
@@ -231,7 +210,6 @@ mpmd_impl::mpmd_impl(const device_addr_t& device_args)
mpmd_impl::~mpmd_impl()
{
- _rfnoc_block_ctrl.clear();
_tree.reset();
_mb.clear();
}
@@ -241,7 +219,7 @@ mpmd_impl::~mpmd_impl()
****************************************************************************/
mpmd_mboard_impl::uptr mpmd_impl::claim_and_make(const uhd::device_addr_t& device_args)
{
- const std::string rpc_addr = device_args.get(xport::MGMT_ADDR_KEY);
+ const std::string rpc_addr = device_args.get(MGMT_ADDR_KEY);
UHD_LOGGER_DEBUG("MPMD") << "Device args: `" << device_args.to_string()
<< "'. RPC address: " << rpc_addr;
@@ -254,8 +232,7 @@ mpmd_mboard_impl::uptr mpmd_impl::claim_and_make(const uhd::device_addr_t& devic
return mpmd_mboard_impl::make(device_args, rpc_addr);
}
-void mpmd_impl::setup_mb(
- mpmd_mboard_impl* mb, const size_t mb_index, const size_t base_xport_addr)
+void mpmd_impl::setup_mb(mpmd_mboard_impl* mb, const size_t mb_index)
{
assert_compat_number_throw("MPM",
MPM_COMPAT_NUM,
@@ -264,94 +241,8 @@ void mpmd_impl::setup_mb(
UHD_LOG_DEBUG("MPMD", "Initializing mboard " << mb_index);
mb->init();
- for (size_t xbar_index = 0; xbar_index < mb->num_xbars; xbar_index++) {
- mb->set_xbar_local_addr(xbar_index, base_xport_addr + xbar_index);
- }
-}
-
-void mpmd_impl::setup_rfnoc_blocks(mpmd_mboard_impl* mb,
- const size_t mb_index,
- const uhd::device_addr_t& ctrl_xport_args)
-{
- UHD_LOG_TRACE(
- "MPMD", "Mboard " << mb_index << " reports " << mb->num_xbars << " crossbar(s).");
- // TODO: The args apply to all xbars, which may or may not be true
- for (size_t xbar_index = 0; xbar_index < mb->num_xbars; xbar_index++) {
- // Pull the number of blocks and base port from the args, if available.
- // Otherwise, get the values from MPM.
- const size_t num_blocks =
- ctrl_xport_args.has_key("rfnoc_num_blocks")
- ? ctrl_xport_args.cast<size_t>("rfnoc_num_blocks", 0)
- : mb->rpc->request<size_t>("get_num_blocks", xbar_index);
- const size_t base_port =
- ctrl_xport_args.has_key("rfnoc_base_port")
- ? ctrl_xport_args.cast<size_t>("rfnoc_base_port", 0)
- : mb->rpc->request<size_t>("get_base_port", xbar_index);
- const size_t local_addr = mb->get_xbar_local_addr(xbar_index);
- UHD_LOGGER_TRACE("MPMD")
- << "Enumerating RFNoC blocks for xbar " << xbar_index
- << ". Total blocks: " << num_blocks << " Base port: " << base_port
- << " Local address: " << local_addr;
- if (ctrl_xport_args.has_key("rfnoc_num_blocks")
- or ctrl_xport_args.has_key("rfnoc_base_port")) {
- // TODO: Remove this warning once we're confident this is
- // (relatively) safe and useful. Also add documentation to
- // usrp_n3xx.dox
- UHD_LOGGER_WARNING("MPMD")
- << "Overriding default RFNoC configuration. You are using an "
- << "experimental development feature, which may go away in "
- << "future versions.";
- }
-
- try {
- enumerate_rfnoc_blocks(mb_index,
- num_blocks,
- base_port,
- uhd::sid_t(0, 0, local_addr, 0),
- ctrl_xport_args);
- } catch (const std::exception& ex) {
- UHD_LOGGER_ERROR("MPMD") << "Failure during block enumeration: " << ex.what();
- throw uhd::runtime_error("Failed to run enumerate_rfnoc_blocks()");
- }
- }
-}
-
-void mpmd_impl::setup_rpc_blocks(
- const device_addr_t& block_args, const bool serialize_init)
-{
- std::vector<std::future<void>> task_list;
- // If we don't force async, most compilers, at least now, will default to
- // deferred.
- const auto launch_policy = serialize_init ? std::launch::deferred
- : std::launch::async;
-
- // Preload all the tasks (they might start running on emplace_back)
- for (const auto& block_ctrl : _rfnoc_block_ctrl) {
- auto rpc_block_id = block_ctrl->get_block_id();
- if (has_block<uhd::rfnoc::rpc_block_ctrl>(rpc_block_id)) {
- const size_t mboard_idx = rpc_block_id.get_device_no();
- auto rpc_block_ctrl =
- get_block_ctrl<uhd::rfnoc::rpc_block_ctrl>(rpc_block_id);
- auto rpc_sptr = _mb[mboard_idx]->rpc;
- task_list.emplace_back(std::async(
- launch_policy, [rpc_block_id, rpc_block_ctrl, &block_args, rpc_sptr]() {
- UHD_LOGGER_DEBUG("MPMD")
- << "Adding RPC access to block: " << rpc_block_id
- << " Block args: " << block_args.to_string();
- rpc_block_ctrl->set_rpc_client(rpc_sptr, block_args);
- }));
- }
- }
-
- // Execute all the calls to set_rpc_client(), either concurrently, or
- // serially
- for (auto& task : task_list) {
- task.get();
- }
-}
-
-size_t mpmd_impl::get_mtu(const size_t mb_index, const uhd::direction_t dir) {
- return _mb[mb_index]->get_mtu(dir);
+ UHD_ASSERT_THROW(mb->mb_ctrl);
+ register_mb_controller(mb_index, mb->mb_ctrl);
}
/*****************************************************************************
diff --git a/host/lib/usrp/mpmd/mpmd_impl.hpp b/host/lib/usrp/mpmd/mpmd_impl.hpp
index bdb6bd691..e1dde49b5 100644
--- a/host/lib/usrp/mpmd/mpmd_impl.hpp
+++ b/host/lib/usrp/mpmd/mpmd_impl.hpp
@@ -7,14 +7,14 @@
#ifndef INCLUDED_MPMD_IMPL_HPP
#define INCLUDED_MPMD_IMPL_HPP
-#include "../device3/device3_impl.hpp"
-#include "mpmd_xport_mgr.hpp"
#include <uhd/property_tree.hpp>
#include <uhd/stream.hpp>
-#include <uhd/transport/muxed_zero_copy_if.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/types/dict.hpp>
#include <uhd/utils/tasks.hpp>
+#include <uhdlib/rfnoc/rfnoc_device.hpp>
+#include <uhdlib/rfnoc/clock_iface.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
#include <uhdlib/utils/rpc.hpp>
#include <boost/optional.hpp>
#include <map>
@@ -34,6 +34,8 @@ static constexpr size_t MPMD_DEFAULT_RPC_TIMEOUT = 2000;
static constexpr size_t MPMD_SHORT_RPC_TIMEOUT = 2000;
//! Claimer loop timeout value for RPC calls (ms).
static constexpr size_t MPMD_CLAIMER_RPC_TIMEOUT = 10000;
+//! Ethernet address for management and RPC communication
+static const std::string MGMT_ADDR_KEY = "mgmt_addr";
namespace uhd { namespace mpmd {
@@ -46,6 +48,11 @@ public:
using uptr = std::unique_ptr<mpmd_mboard_impl>;
using dev_info = std::map<std::string, std::string>;
+ //! MPMD-specific implementation of the mb_iface
+ //
+ // This handles the transport management
+ class mpmd_mb_iface;
+
/*** Static helper *******************************************************/
/*! Will run some checks to determine if this device can be reached from
* the current UHD session
@@ -73,9 +80,11 @@ public:
*/
static uptr make(const uhd::device_addr_t& mb_args, const std::string& addr);
- /*** Init ****************************************************************/
+ /*** API *****************************************************************/
void init();
+ uhd::rfnoc::mb_iface& get_mb_iface();
+
/*** Public attributes ***************************************************/
//! These are the args given by the user, with some filtering/preprocessing
uhd::device_addr_t mb_args;
@@ -88,6 +97,12 @@ public:
// to be populated at all.
std::vector<uhd::device_addr_t> dboard_info;
+ //! Reference to this motherboards mb_iface
+ std::unique_ptr<mpmd_mb_iface> mb_iface;
+
+ //! Reference to this motherboards mb_controller
+ uhd::rfnoc::mpmd_mb_controller::sptr mb_ctrl;
+
/*! Reference to the RPC client for this motherboard
*
* We store a shared ptr, because we might share it with some of the RFNoC
@@ -95,41 +110,9 @@ public:
*/
uhd::rpc_client::sptr rpc;
- //! Number of RFNoC crossbars on this device
- const size_t num_xbars;
-
/*************************************************************************
* API
************************************************************************/
- //! Configure a crossbar to have a certain local address
- void set_xbar_local_addr(const size_t xbar_index, const size_t local_addr);
-
- //! Return the local address of a given crossbar
- size_t get_xbar_local_addr(const size_t xbar_index) const
- {
- return xbar_local_addrs.at(xbar_index);
- }
-
- //! Device-specific make_transport implementation
- //
- // A major difference to the mpmd_impl::make_transport() is the meaning of
- // the first argument (\p sid). mpmd_impl::make_transport() will add a
- // source part to the SID which needs to be taken into account in this
- // function.
- //
- // \param sid The full SID of this transport (UHD to device)
- // \param xport_type Transport type (CTRL, RX_DATA, ...)
- // \param args Any kind of args passed in via get_?x_stream()
- uhd::both_xports_t make_transport(const sid_t& sid,
- usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& args);
-
- size_t get_mtu(const uhd::direction_t dir) const;
-
-
- uhd::device_addr_t get_rx_hints() const;
- uhd::device_addr_t get_tx_hints() const;
-
/*! Setting this flag will enable a mode where a reclaim failure is
* acceptable.
*
@@ -177,18 +160,10 @@ private:
/*************************************************************************
* Private attributes
************************************************************************/
- //! Stores a list of local addresses of the crossbars. The local address is
- // what we use when addressing a crossbar in a CHDR header.
- std::vector<size_t> xbar_local_addrs;
-
/*! Continuously reclaims the device.
*/
uhd::task::sptr _claimer_task;
- uhd::mpmd::xport::mpmd_xport_mgr::uptr _xport_mgr;
- uhd::device_addr_t send_args;
- uhd::device_addr_t recv_args;
-
/*! This flag is only used within the claim() function. Go look there if you
* really need to know what it does.
*/
@@ -208,7 +183,7 @@ private:
* are taken care of by MPM itself, it is not necessary to write a specific
* derived class for every single type of MPM device.
*/
-class mpmd_impl : public uhd::usrp::device3_impl
+class mpmd_impl : public uhd::rfnoc::detail::rfnoc_device
{
public:
//! Device arg key which will allow finding all devices, even those not
@@ -242,17 +217,17 @@ public:
/**************************************************************************
* API
************************************************************************/
- uhd::both_xports_t make_transport(const uhd::sid_t&,
- uhd::usrp::device3_impl::xport_type_t,
- const uhd::device_addr_t&);
-
- //! get mtu
- size_t get_mtu(const size_t, const uhd::direction_t);
+ uhd::rfnoc::mb_iface& get_mb_iface(const size_t mb_idx)
+ {
+ if (mb_idx >= _mb.size()) {
+ throw uhd::index_error(
+ std::string("Cannot get mb_iface, invalid motherboard index: ")
+ + std::to_string(mb_idx));
+ }
+ return _mb.at(mb_idx)->get_mb_iface();
+ }
private:
- uhd::device_addr_t get_rx_hints(size_t mb_index);
- uhd::device_addr_t get_tx_hints(size_t mb_index);
-
/*************************************************************************
* Private methods/helpers
************************************************************************/
@@ -272,21 +247,7 @@ private:
* \param device_args Device args
*
*/
- void setup_mb(
- mpmd_mboard_impl* mb, const size_t mb_index, const size_t base_xport_addr);
-
- //! Setup all RFNoC blocks running on mboard \p mb_i
- void setup_rfnoc_blocks(
- mpmd_mboard_impl* mb, const size_t mb_i, const uhd::device_addr_t& block_args);
-
- //! Configure all blocks that require access to an RPC client
- void setup_rpc_blocks(
- const uhd::device_addr_t& block_args, const bool serialize_init);
-
- /*! Return the index of the motherboard given the local address of a
- * crossbar
- */
- size_t identify_mboard_by_xbar_addr(const size_t xbar_addr) const;
+ void setup_mb(mpmd_mboard_impl* mb, const size_t mb_index);
/*! Initialize property tree for a single device.
*
@@ -297,7 +258,6 @@ private:
static void init_property_tree(
uhd::property_tree::sptr tree, fs_path mb_path, mpmd_mboard_impl* mb);
-
/*************************************************************************
* Private attributes
************************************************************************/
diff --git a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_base.hpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_base.hpp
new file mode 100644
index 000000000..a9cb456e5
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_base.hpp
@@ -0,0 +1,56 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_MPMD_XPORT_CTRL_BASE_HPP
+#define INCLUDED_MPMD_XPORT_CTRL_BASE_HPP
+
+#include "mpmd_link_if_mgr.hpp"
+#include <uhd/types/device_addr.hpp>
+#include <uhdlib/rfnoc/chdr_packet.hpp>
+#include <uhdlib/transport/links.hpp>
+#include <memory>
+
+namespace uhd { namespace mpmd { namespace xport {
+
+/*! Transport manager implementation base
+ */
+class mpmd_link_if_ctrl_base
+{
+public:
+ using uptr = std::unique_ptr<mpmd_link_if_ctrl_base>;
+ virtual ~mpmd_link_if_ctrl_base() {}
+
+ virtual size_t get_num_links() const = 0;
+
+ /*! Return links object
+ *
+ * \param link_idx The number of the link to use. link_idx < get_num_links()
+ * must hold true. link_idx is often 0. Example: When
+ * the underlying transport is Ethernet, and the user
+ * specified both addr and second_addr, then get_num_links()
+ * equals 2 and link_idx can also be 1.
+ * \param link_type CTRL, RX_DATA, or TX_DATA (for configuring the link)
+ * \param link_args Link-specific additional information that the underlying
+ * mpmd_link_if_ctrl instantiation can use
+ */
+ virtual uhd::transport::both_links_t get_link(const size_t link_idx,
+ const uhd::transport::link_type_t link_type,
+ const uhd::device_addr_t& link_args) = 0;
+
+ //! Return the underlying link's MTU in bytes
+ virtual size_t get_mtu(const uhd::direction_t dir) const = 0;
+
+ //! Return the rate of the underlying link in bytes/sec
+ virtual double get_link_rate(const size_t link_idx) const = 0;
+
+ //! Get the packet factory associated with this link
+ virtual const uhd::rfnoc::chdr::chdr_packet_factory& get_packet_factory() const = 0;
+};
+
+}}} /* namespace uhd::mpmd::xport */
+
+#endif /* INCLUDED_MPMD_XPORT_CTRL_BASE_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_dpdk_udp.cpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.cpp
index 38d295728..baf0dde3e 100644
--- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_dpdk_udp.cpp
+++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.cpp
@@ -5,8 +5,8 @@
//
#include "mpmd_impl.hpp"
-#include "mpmd_xport_mgr.hpp"
-#include "mpmd_xport_ctrl_dpdk_udp.hpp"
+#include "mpmd_link_if_mgr.hpp"
+#include "mpmd_link_if_ctrl_dpdk_udp.hpp"
#include <uhd/transport/udp_simple.hpp>
#include <uhd/transport/udp_constants.hpp>
#include <uhdlib/transport/dpdk_zero_copy.hpp>
@@ -161,7 +161,7 @@ size_t discover_mtu(
}
-mpmd_xport_ctrl_dpdk_udp::mpmd_xport_ctrl_dpdk_udp(
+mpmd_link_if_ctrl_dpdk_udp::mpmd_link_if_ctrl_dpdk_udp(
const uhd::device_addr_t& mb_args
) : _mb_args(mb_args)
, _ctx(uhd::transport::uhd_dpdk_ctx::get())
@@ -193,8 +193,8 @@ mpmd_xport_ctrl_dpdk_udp::mpmd_xport_ctrl_dpdk_udp(
}
uhd::both_xports_t
-mpmd_xport_ctrl_dpdk_udp::make_transport(
- mpmd_xport_mgr::xport_info_t &xport_info,
+mpmd_link_if_ctrl_dpdk_udp::make_transport(
+ mpmd_link_if_mgr::xport_info_t &xport_info,
const usrp::device3_impl::xport_type_t xport_type,
const uhd::device_addr_t& xport_args
) {
@@ -264,14 +264,14 @@ mpmd_xport_ctrl_dpdk_udp::make_transport(
return xports;
}
-bool mpmd_xport_ctrl_dpdk_udp::is_valid(
- const mpmd_xport_mgr::xport_info_t& xport_info
+bool mpmd_link_if_ctrl_dpdk_udp::is_valid(
+ const mpmd_link_if_mgr::xport_info_t& xport_info
) const {
int dpdk_port_id = _ctx.get_route(xport_info.at("ipv4"));
return (dpdk_port_id >= 0);
}
-size_t mpmd_xport_ctrl_dpdk_udp::get_mtu(const uhd::direction_t /*dir*/) const
+size_t mpmd_link_if_ctrl_dpdk_udp::get_mtu(const uhd::direction_t /*dir*/) const
{
return _mtu;
}
diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_dpdk_udp.hpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.hpp
index 943cd44d6..4423b4340 100644
--- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_dpdk_udp.hpp
+++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_dpdk_udp.hpp
@@ -7,7 +7,7 @@
#ifndef INCLUDED_MPMD_XPORT_CTRL_DPDK_UDP_HPP
#define INCLUDED_MPMD_XPORT_CTRL_DPDK_UDP_HPP
-#include "mpmd_xport_ctrl_base.hpp"
+#include "mpmd_link_if_ctrl_base.hpp"
#include <uhd/types/device_addr.hpp>
#include <uhdlib/transport/dpdk_zero_copy.hpp>
#include "../device3/device3_impl.hpp"
@@ -18,21 +18,21 @@ namespace uhd { namespace mpmd { namespace xport {
*
* Opens UDP sockets
*/
-class mpmd_xport_ctrl_dpdk_udp : public mpmd_xport_ctrl_base
+class mpmd_link_if_ctrl_dpdk_udp : public mpmd_link_if_ctrl_base
{
public:
- mpmd_xport_ctrl_dpdk_udp(
+ mpmd_link_if_ctrl_dpdk_udp(
const uhd::device_addr_t& mb_args
);
both_xports_t make_transport(
- mpmd_xport_mgr::xport_info_t& xport_info,
+ mpmd_link_if_mgr::xport_info_t& xport_info,
const usrp::device3_impl::xport_type_t xport_type,
const uhd::device_addr_t& xport_args
);
bool is_valid(
- const mpmd_xport_mgr::xport_info_t& xport_info
+ const mpmd_link_if_mgr::xport_info_t& xport_info
) const;
size_t get_mtu(
diff --git a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.cpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.cpp
new file mode 100644
index 000000000..ed8f4395e
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.cpp
@@ -0,0 +1,135 @@
+//
+// Copyright 2017 Ettus Research, National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "mpmd_link_if_ctrl_liberio.hpp"
+#include <uhd/rfnoc/constants.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/transport/inline_io_service.hpp>
+#include <uhdlib/transport/liberio_link.hpp>
+
+using namespace uhd;
+using namespace uhd::transport;
+using namespace uhd::mpmd::xport;
+
+const uhd::rfnoc::chdr::chdr_packet_factory mpmd_link_if_ctrl_liberio::_pkt_factory(
+ uhd::rfnoc::CHDR_W_64, ENDIANNESS_LITTLE);
+
+namespace {
+
+//! The default MTU will be this number times the page size
+const size_t LIBERIO_PAGES_PER_BUF = 2;
+//! The default MTU
+const size_t LIBERIO_DEFAULT_MTU = LIBERIO_PAGES_PER_BUF * getpagesize();
+//! The default link_rate (8 Bytes * 200 MHz)
+const double LIBERIO_DEFAULT_LINK_RATE = 200e6 * 8;
+//! Number of descriptors that liberio allocates (receive)
+const size_t LIBERIO_NUM_RECV_FRAMES = 128;
+//! Number of descriptors that liberio allocates (send)
+const size_t LIBERIO_NUM_SEND_FRAMES = 128;
+//! MTU for largest non-data packet accepted (arbitrarily-determined...)
+// Note: Management frames must fit here, and it determines the padded RX size
+const size_t LIBERIO_MAX_NONDATA_PACKET_SIZE = 128;
+
+std::vector<mpmd_link_if_ctrl_liberio::liberio_link_info_t>
+get_liberio_info_from_xport_info(
+ const mpmd_link_if_mgr::xport_info_list_t& link_info_list)
+{
+ std::vector<mpmd_link_if_ctrl_liberio::liberio_link_info_t> result;
+ for (const auto& link_info : link_info_list) {
+ if (!link_info.count("tx_dev")) {
+ UHD_LOG_ERROR("MPMD::XPORT::LIBERIO",
+ "Invalid response from get_chdr_link_options()! No `tx_dev' key!");
+ throw uhd::runtime_error(
+ "Invalid response from get_chdr_link_options()! No `tx_dev' key!");
+ }
+ if (!link_info.count("rx_dev")) {
+ UHD_LOG_ERROR("MPMD::XPORT::LIBERIO",
+ "Invalid response from get_chdr_link_options()! No `rx_dev' key!");
+ throw uhd::runtime_error(
+ "Invalid response from get_chdr_link_options()! No `rx_dev' key!");
+ }
+ const std::string tx_dev = link_info.at("tx_dev");
+ const std::string rx_dev = link_info.at("rx_dev");
+ result.emplace_back(
+ mpmd_link_if_ctrl_liberio::liberio_link_info_t{tx_dev, rx_dev});
+ }
+
+ return result;
+}
+
+} // namespace
+
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+mpmd_link_if_ctrl_liberio::mpmd_link_if_ctrl_liberio(const uhd::device_addr_t& mb_args,
+ const mpmd_link_if_mgr::xport_info_list_t& xport_info)
+ : _mb_args(mb_args)
+ , _recv_args(filter_args(mb_args, "recv"))
+ , _send_args(filter_args(mb_args, "send"))
+ , _dma_channels(get_liberio_info_from_xport_info(xport_info))
+ , _link_rate(LIBERIO_DEFAULT_LINK_RATE) // FIXME
+{
+ // nop
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+uhd::transport::both_links_t mpmd_link_if_ctrl_liberio::get_link(const size_t link_idx,
+ const uhd::transport::link_type_t link_type,
+ const uhd::device_addr_t& link_args)
+{
+ UHD_ASSERT_THROW(link_idx == 0);
+ if (_next_channel >= _dma_channels.size()) {
+ UHD_LOG_ERROR(
+ "MPMD::XPORT::LIBERIO", "Cannot create liberio link: DMA channels exhausted");
+ throw uhd::runtime_error("Cannot create liberio link: DMA channels exhausted");
+ }
+ auto link_info = _dma_channels.at(_next_channel++);
+
+ /* FIXME: Should have common infrastructure for creating I/O services */
+ auto io_srv = uhd::transport::inline_io_service::make();
+ link_params_t link_params;
+ if (link_type == link_type_t::RX_DATA) {
+ link_params.recv_frame_size = get_mtu(uhd::RX_DIRECTION); // FIXME
+ link_params.send_frame_size = LIBERIO_MAX_NONDATA_PACKET_SIZE; // FIXME
+ } else if (link_type == link_type_t::TX_DATA) {
+ link_params.recv_frame_size = LIBERIO_MAX_NONDATA_PACKET_SIZE; // FIXME
+ link_params.send_frame_size = get_mtu(uhd::TX_DIRECTION); // FIXME
+ } else {
+ link_params.recv_frame_size = LIBERIO_MAX_NONDATA_PACKET_SIZE; // FIXME
+ link_params.send_frame_size = LIBERIO_MAX_NONDATA_PACKET_SIZE; // FIXME
+ }
+ link_params.num_recv_frames = LIBERIO_NUM_RECV_FRAMES; // FIXME
+ link_params.num_send_frames = LIBERIO_NUM_SEND_FRAMES; // FIXME
+
+ // Liberio doesn't need in-band flow control, so pretend have very large buffers
+ link_params.recv_buff_size = std::numeric_limits<size_t>::max();
+ link_params.send_buff_size = std::numeric_limits<size_t>::max();
+ auto link = uhd::transport::liberio_link::make(
+ link_info.first, link_info.second, link_params);
+ io_srv->attach_send_link(link);
+ io_srv->attach_recv_link(link);
+ return std::tuple<io_service::sptr,
+ send_link_if::sptr,
+ size_t,
+ recv_link_if::sptr,
+ size_t,
+ bool>(io_srv,
+ link,
+ link_params.send_buff_size,
+ link,
+ link_params.recv_buff_size,
+ false);
+}
+
+size_t mpmd_link_if_ctrl_liberio::get_mtu(const uhd::direction_t /*dir*/) const
+{
+ return LIBERIO_DEFAULT_MTU; // FIXME
+}
diff --git a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.hpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.hpp
new file mode 100644
index 000000000..09fb24f8e
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_liberio.hpp
@@ -0,0 +1,64 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_MPMD_XPORT_CTRL_LIBERIO_HPP
+#define INCLUDED_MPMD_XPORT_CTRL_LIBERIO_HPP
+
+#include "mpmd_link_if_ctrl_base.hpp"
+#include <uhd/types/device_addr.hpp>
+
+namespace uhd { namespace mpmd { namespace xport {
+
+/*! Liberio transport manager
+ */
+class mpmd_link_if_ctrl_liberio : public mpmd_link_if_ctrl_base
+{
+public:
+ /* For liberio, get_chdr_link_options returns information about DMA engines.
+ * We assume there is only ever one liberio link available
+ * first = tx path
+ * second = rx path
+ */
+ using liberio_link_info_t = std::pair<std::string, std::string>;
+
+ mpmd_link_if_ctrl_liberio(const uhd::device_addr_t& mb_args,
+ const mpmd_link_if_mgr::xport_info_list_t& xport_info);
+
+ size_t get_num_links() const { return 1; }
+
+ uhd::transport::both_links_t get_link(
+ const size_t link_idx, const uhd::transport::link_type_t link_type,
+ const uhd::device_addr_t& link_args);
+
+ size_t get_mtu(const uhd::direction_t) const;
+
+ double get_link_rate(const size_t /*link_idx*/) const { return _link_rate; }
+
+ const uhd::rfnoc::chdr::chdr_packet_factory& get_packet_factory() const
+ {
+ return _pkt_factory;
+ }
+
+private:
+
+ const uhd::device_addr_t _mb_args;
+ const uhd::dict<std::string, std::string> _recv_args;
+ const uhd::dict<std::string, std::string> _send_args;
+ //! A list of DMA channels we can use for links
+ std::vector<liberio_link_info_t> _dma_channels;
+ double _link_rate;
+
+ /*! An index representing the next DMA channel to use, for a simple
+ * allocation of channels. For get_link(), increment for each new link, and
+ * throw an exception if _next_channel > number of DMA channels.
+ */
+ size_t _next_channel = 0;
+ static const uhd::rfnoc::chdr::chdr_packet_factory _pkt_factory;
+};
+
+}}} /* namespace uhd::mpmd::xport */
+
+#endif /* INCLUDED_MPMD_XPORT_CTRL_LIBERIO_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.cpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.cpp
new file mode 100644
index 000000000..c2d746f92
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.cpp
@@ -0,0 +1,279 @@
+//
+// Copyright 2017 Ettus Research, National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "mpmd_link_if_ctrl_udp.hpp"
+#include "mpmd_impl.hpp"
+#include "mpmd_link_if_mgr.hpp"
+#include <uhd/transport/udp_constants.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhdlib/transport/inline_io_service.hpp>
+#include <uhdlib/transport/udp_boost_asio_link.hpp>
+#include <uhdlib/utils/narrow.hpp>
+#include <string>
+
+using namespace uhd;
+using namespace uhd::transport;
+using namespace uhd::mpmd::xport;
+
+const uhd::rfnoc::chdr::chdr_packet_factory mpmd_link_if_ctrl_udp::_pkt_factory(
+ uhd::rfnoc::CHDR_W_64, ENDIANNESS_BIG);
+
+namespace {
+
+//! Maximum CHDR packet size in bytes
+const size_t MPMD_10GE_DATA_FRAME_MAX_SIZE = 8000;
+
+//! Maximum CHDR packet size in bytes
+const size_t MPMD_10GE_ASYNCMSG_FRAME_MAX_SIZE = 1472;
+
+//! Number of send/recv frames
+const size_t MPMD_ETH_NUM_FRAMES = 32;
+
+//!
+const double MPMD_BUFFER_DEPTH = 20.0e-3; // s
+//! For MTU discovery, the time we wait for a packet before calling it
+// oversized (seconds).
+const double MPMD_MTU_DISCOVERY_TIMEOUT = 0.02;
+
+// TODO: move these to appropriate header file for all other devices
+const size_t MAX_RATE_1GIGE = 1e9 / 8; // byte/s
+const size_t MAX_RATE_10GIGE = 10e9 / 8; // byte/s
+
+
+mpmd_link_if_ctrl_udp::udp_link_info_map get_udp_info_from_xport_info(
+ const mpmd_link_if_mgr::xport_info_list_t& link_info_list)
+{
+ mpmd_link_if_ctrl_udp::udp_link_info_map result;
+ for (const auto& link_info : link_info_list) {
+ if (!link_info.count("ipv4")) {
+ UHD_LOG_ERROR("MPMD::XPORT::UDP",
+ "Invalid response from get_chdr_link_options()! No `ipv4' key!");
+ throw uhd::runtime_error(
+ "Invalid response from get_chdr_link_options()! No `ipv4' key!");
+ }
+ if (!link_info.count("port")) {
+ UHD_LOG_ERROR("MPMD::XPORT::UDP",
+ "Invalid response from get_chdr_link_options()! No `port' key!");
+ throw uhd::runtime_error(
+ "Invalid response from get_chdr_link_options()! No `port' key!");
+ }
+ const std::string udp_port = link_info.at("port");
+ const size_t link_rate = link_info.count("link_rate")
+ ? std::stoul(link_info.at("link_rate"))
+ : MAX_RATE_1GIGE;
+ result.emplace(link_info.at("ipv4"),
+ mpmd_link_if_ctrl_udp::udp_link_info_t{udp_port, link_rate});
+ }
+
+ return result;
+}
+
+std::vector<std::string> get_addrs_from_mb_args(const uhd::device_addr_t& mb_args,
+ const mpmd_link_if_ctrl_udp::udp_link_info_map& link_info_list)
+{
+ // mb_args must always include addr
+ if (not mb_args.has_key(FIRST_ADDR_KEY)) {
+ UHD_LOG_WARNING("MPMD::XPORT::UDP",
+ "The `" << FIRST_ADDR_KEY
+ << "' key must be specified in "
+ "device args to create an Ethernet transport to an RFNoC block");
+ return {};
+ }
+ std::vector<std::string> addrs{mb_args[FIRST_ADDR_KEY]};
+ if (mb_args.has_key(SECOND_ADDR_KEY)) {
+ addrs.push_back(mb_args[SECOND_ADDR_KEY]);
+ }
+ // This is where in UHD we encode the knowledge about what
+ // get_chdr_link_options() returns to us.
+ for (const auto& ip_addr : addrs) {
+ if (link_info_list.count(ip_addr)) {
+ continue;
+ }
+ UHD_LOG_WARNING("MPMD::XPORT::UDP",
+ "Cannot create UDP link to device: The IP address `"
+ << ip_addr << "' is requested, but not reachable.");
+ return {};
+ }
+
+ return addrs;
+}
+
+/*! Do a binary search to discover MTU
+ *
+ * Uses the MPM echo service to figure out MTU. We simply send a bunch of
+ * packets and see if they come back until we converged on the path MTU.
+ * The end result must lie between \p min_frame_size and \p max_frame_size.
+ *
+ * \param address IP address
+ * \param port UDP port (yeah it's a string!)
+ * \param min_frame_size Minimum frame size, initialize algorithm to start
+ * with this value
+ * \param max_frame_size Maximum frame size, initialize algorithm to start
+ * with this value
+ * \param echo_timeout Timeout value in seconds. For frame sizes that
+ * exceed the MTU, we don't expect a response, and this
+ * is the amount of time we'll wait before we assume
+ * the frame size exceeds the MTU.
+ */
+size_t discover_mtu(const std::string& address,
+ const std::string& port,
+ size_t min_frame_size,
+ size_t max_frame_size,
+ const double echo_timeout = 0.020)
+{
+ const size_t echo_prefix_offset = uhd::mpmd::mpmd_impl::MPM_ECHO_CMD.size();
+ const size_t mtu_hdr_len = echo_prefix_offset + 10;
+ UHD_ASSERT_THROW(min_frame_size < max_frame_size);
+ UHD_ASSERT_THROW(min_frame_size % 4 == 0);
+ UHD_ASSERT_THROW(max_frame_size % 4 == 0);
+ UHD_ASSERT_THROW(min_frame_size >= echo_prefix_offset + mtu_hdr_len);
+ using namespace uhd::transport;
+ // The return port will probably differ from the discovery port, so we
+ // need a "broadcast" UDP connection; using make_connected() would
+ // drop packets
+ udp_simple::sptr udp = udp_simple::make_broadcast(address, port);
+ std::string send_buf(uhd::mpmd::mpmd_impl::MPM_ECHO_CMD);
+ send_buf.resize(max_frame_size, '#');
+ UHD_ASSERT_THROW(send_buf.size() == max_frame_size);
+ std::vector<uint8_t> recv_buf;
+ recv_buf.resize(max_frame_size, ' ');
+
+ // Little helper to check returned packets match the sent ones
+ auto require_bufs_match = [&recv_buf, &send_buf, mtu_hdr_len](const size_t len) {
+ if (len < mtu_hdr_len
+ or std::memcmp((void*)&recv_buf[0], (void*)&send_buf[0], mtu_hdr_len) != 0) {
+ throw uhd::runtime_error("Unexpected content of MTU "
+ "discovery return packet!");
+ }
+ };
+ UHD_LOG_TRACE("MPMD", "Determining UDP MTU... ");
+ size_t seq_no = 0;
+ while (min_frame_size < max_frame_size) {
+ // Only test multiples of 4 bytes!
+ const size_t test_frame_size = (max_frame_size / 2 + min_frame_size / 2 + 3)
+ & ~size_t(3);
+ // Encode sequence number and current size in the string, makes it
+ // easy to debug in code or Wireshark. Is also used for identifying
+ // response packets.
+ std::sprintf(
+ &send_buf[echo_prefix_offset], ";%04lu,%04lu", seq_no++, test_frame_size);
+ UHD_LOG_TRACE("MPMD", "Testing frame size " << test_frame_size);
+ udp->send(boost::asio::buffer(&send_buf[0], test_frame_size));
+
+ const size_t len = udp->recv(boost::asio::buffer(recv_buf), echo_timeout);
+ if (len == 0) {
+ // Nothing received, so this is probably too big
+ max_frame_size = test_frame_size - 4;
+ } else if (len >= test_frame_size) {
+ // Size went through, so bump the minimum
+ require_bufs_match(len);
+ min_frame_size = test_frame_size;
+ } else if (len < test_frame_size) {
+ // This is an odd case. Something must have snipped the packet
+ // on the way back. Still, we'll just back off and try
+ // something smaller.
+ UHD_LOG_DEBUG("MPMD", "Unexpected packet truncation during MTU discovery.");
+ require_bufs_match(len);
+ max_frame_size = len;
+ }
+ }
+ UHD_LOG_DEBUG("MPMD", "Path MTU for address " << address << ": " << min_frame_size);
+ return min_frame_size;
+}
+
+} // namespace
+
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+mpmd_link_if_ctrl_udp::mpmd_link_if_ctrl_udp(const uhd::device_addr_t& mb_args,
+ const mpmd_link_if_mgr::xport_info_list_t& xport_info)
+ : _mb_args(mb_args)
+ , _recv_args(filter_args(mb_args, "recv"))
+ , _send_args(filter_args(mb_args, "send"))
+ , _udp_info(get_udp_info_from_xport_info(xport_info))
+ , _mtu(MPMD_10GE_DATA_FRAME_MAX_SIZE)
+{
+ const std::string mpm_discovery_port = _mb_args.get(
+ mpmd_impl::MPM_DISCOVERY_PORT_KEY, std::to_string(mpmd_impl::MPM_DISCOVERY_PORT));
+ auto discover_mtu_for_ip = [mpm_discovery_port](const std::string& ip_addr) {
+ return discover_mtu(ip_addr,
+ mpm_discovery_port,
+ IP_PROTOCOL_MIN_MTU_SIZE - IP_PROTOCOL_UDP_PLUS_IP_HEADER,
+ MPMD_10GE_DATA_FRAME_MAX_SIZE,
+ MPMD_MTU_DISCOVERY_TIMEOUT);
+ };
+
+ const std::vector<std::string> requested_addrs(
+ get_addrs_from_mb_args(mb_args, _udp_info));
+ for (const auto& ip_addr : requested_addrs) {
+ try {
+ // If MTU discovery fails, we gracefully recover, but declare that
+ // link invalid.
+ _mtu = std::min(_mtu, discover_mtu_for_ip(ip_addr));
+ _available_addrs.push_back(ip_addr);
+ } catch (const uhd::exception& ex) {
+ UHD_LOG_WARNING("MPMD::XPORT::UDP",
+ "Error during MTU discovery on address " << ip_addr << ": " << ex.what());
+ }
+ }
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+uhd::transport::both_links_t mpmd_link_if_ctrl_udp::get_link(const size_t link_idx,
+ const uhd::transport::link_type_t /*link_type*/,
+ const uhd::device_addr_t& /*link_args*/)
+{
+ UHD_ASSERT_THROW(link_idx < _available_addrs.size());
+ const std::string ip_addr = _available_addrs.at(link_idx);
+ const std::string udp_port = _udp_info.at(ip_addr).udp_port;
+
+ /* FIXME: Should have common infrastructure for creating I/O services */
+ auto io_srv = uhd::transport::inline_io_service::make();
+ link_params_t link_params;
+ link_params.num_recv_frames = MPMD_ETH_NUM_FRAMES; // FIXME
+ link_params.num_send_frames = MPMD_ETH_NUM_FRAMES; // FIXME
+ link_params.recv_frame_size = get_mtu(uhd::RX_DIRECTION); // FIXME
+ link_params.send_frame_size = get_mtu(uhd::TX_DIRECTION); // FIXME
+ link_params.recv_buff_size = MPMD_BUFFER_DEPTH * MAX_RATE_10GIGE; // FIXME
+ link_params.send_buff_size = MPMD_BUFFER_DEPTH * MAX_RATE_10GIGE; // FIXME
+ auto link = uhd::transport::udp_boost_asio_link::make(ip_addr,
+ udp_port,
+ link_params,
+ link_params.recv_buff_size, // FIXME
+ link_params.send_buff_size); // FIXME
+ io_srv->attach_send_link(link);
+ io_srv->attach_recv_link(link);
+ return std::tuple<io_service::sptr,
+ send_link_if::sptr,
+ size_t,
+ recv_link_if::sptr,
+ size_t,
+ bool>(
+ io_srv, link, link_params.send_buff_size, link, link_params.recv_buff_size, true);
+}
+
+size_t mpmd_link_if_ctrl_udp::get_num_links() const
+{
+ return _available_addrs.size();
+}
+
+//! Return the rate of the underlying link in bytes/sec
+double mpmd_link_if_ctrl_udp::get_link_rate(const size_t link_idx) const
+{
+ UHD_ASSERT_THROW(link_idx < get_num_links());
+ return _udp_info.at(_available_addrs.at(link_idx)).link_rate;
+}
+
+const uhd::rfnoc::chdr::chdr_packet_factory&
+mpmd_link_if_ctrl_udp::get_packet_factory() const
+{
+ return _pkt_factory;
+}
diff --git a/host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.hpp b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.hpp
new file mode 100644
index 000000000..4c8ecade7
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_link_if_ctrl_udp.hpp
@@ -0,0 +1,61 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_MPMD_XPORT_CTRL_UDP_HPP
+#define INCLUDED_MPMD_XPORT_CTRL_UDP_HPP
+
+#include "mpmd_link_if_ctrl_base.hpp"
+#include "mpmd_link_if_mgr.hpp"
+#include <uhd/types/device_addr.hpp>
+#include <unordered_map>
+
+namespace uhd { namespace mpmd { namespace xport {
+
+/*! UDP link interface controller
+ *
+ * Opens UDP sockets
+ */
+class mpmd_link_if_ctrl_udp : public mpmd_link_if_ctrl_base
+{
+public:
+ struct udp_link_info_t
+ {
+ std::string udp_port;
+ size_t link_rate;
+ };
+
+ using udp_link_info_map = std::unordered_map<std::string, udp_link_info_t>;
+
+ mpmd_link_if_ctrl_udp(const uhd::device_addr_t& mb_args,
+ const mpmd_link_if_mgr::xport_info_list_t& xport_info);
+
+ size_t get_num_links() const;
+ uhd::transport::both_links_t get_link(const size_t link_idx,
+ const uhd::transport::link_type_t link_type,
+ const uhd::device_addr_t& link_args);
+ size_t get_mtu(const uhd::direction_t) const
+ {
+ return _mtu;
+ }
+ double get_link_rate(const size_t link_idx) const;
+ const uhd::rfnoc::chdr::chdr_packet_factory& get_packet_factory() const;
+
+private:
+ const uhd::device_addr_t _mb_args;
+ const uhd::dict<std::string, std::string> _recv_args;
+ const uhd::dict<std::string, std::string> _send_args;
+ //!
+ udp_link_info_map _udp_info;
+ //! A list of IP addresses we can connect our CHDR connections to
+ std::vector<std::string> _available_addrs;
+ //! MTU
+ size_t _mtu;
+ static const uhd::rfnoc::chdr::chdr_packet_factory _pkt_factory;
+};
+
+}}} /* namespace uhd::mpmd::xport */
+
+#endif /* INCLUDED_MPMD_XPORT_CTRL_UDP_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_link_if_mgr.cpp b/host/lib/usrp/mpmd/mpmd_link_if_mgr.cpp
new file mode 100644
index 000000000..6bb6cae3a
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_link_if_mgr.cpp
@@ -0,0 +1,135 @@
+//
+// Copyright 2017 Ettus Research, National Instruments Company
+// Copyright 2019 Ettus Research, National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "mpmd_link_if_mgr.hpp"
+#include "mpmd_impl.hpp"
+#include "mpmd_link_if_ctrl_base.hpp"
+#include "mpmd_link_if_ctrl_udp.hpp"
+#ifdef HAVE_LIBERIO
+# include "mpmd_link_if_ctrl_liberio.hpp"
+#endif
+#ifdef HAVE_DPDK
+# include "mpmd_link_if_ctrl_dpdk_udp.hpp"
+#endif
+
+uhd::dict<std::string, std::string> uhd::mpmd::xport::filter_args(
+ const uhd::device_addr_t& args, const std::string& prefix)
+{
+ uhd::dict<std::string, std::string> filtered_args;
+ for (const std::string& key : args.keys()) {
+ if (key.find(prefix) != std::string::npos) {
+ filtered_args[key] = args[key];
+ }
+ }
+
+ return filtered_args;
+}
+
+using namespace uhd::mpmd::xport;
+
+class mpmd_link_if_mgr_impl : public mpmd_link_if_mgr
+{
+public:
+ mpmd_link_if_mgr_impl(const uhd::device_addr_t& mb_args) : _mb_args(mb_args) {}
+
+ /**************************************************************************
+ * API (see mpmd_link_if_mgr.hpp)
+ *************************************************************************/
+ bool connect(const std::string& link_type, const xport_info_list_t& xport_info)
+ {
+ auto link_if_ctrl = make_link_if_ctrl(link_type, xport_info);
+ if (!link_if_ctrl) {
+ UHD_LOG_WARNING(
+ "MPMD::XPORT", "Unable to create xport ctrl for link type " << link_type);
+ return false;
+ }
+ if (link_if_ctrl->get_num_links() == 0) {
+ UHD_LOG_TRACE("MPMD::XPORT",
+ "Link type " << link_type
+ << " has no valid links in this configuration.");
+ return false;
+ }
+ const size_t xport_idx = _link_if_ctrls.size();
+ for (size_t link_idx = 0; link_idx < link_if_ctrl->get_num_links(); link_idx++) {
+ _link_link_if_ctrl_map.push_back(std::make_pair(xport_idx, link_idx));
+ }
+ _link_if_ctrls.push_back(std::move(link_if_ctrl));
+ return true;
+ }
+
+ size_t get_num_links()
+ {
+ return _link_link_if_ctrl_map.size();
+ }
+
+ uhd::transport::both_links_t get_link(const size_t link_idx,
+ const uhd::transport::link_type_t link_type,
+ const uhd::device_addr_t& link_args)
+ {
+ const size_t link_if_ctrl_idx = _link_link_if_ctrl_map.at(link_idx).first;
+ const size_t xport_link_idx = _link_link_if_ctrl_map.at(link_idx).second;
+ return _link_if_ctrls.at(link_if_ctrl_idx)
+ ->get_link(xport_link_idx, link_type, link_args);
+ }
+
+ size_t get_mtu(const size_t link_idx, const uhd::direction_t dir) const
+ {
+ return _link_if_ctrls.at(_link_link_if_ctrl_map.at(link_idx).first)->get_mtu(dir);
+ }
+
+ const uhd::rfnoc::chdr::chdr_packet_factory& get_packet_factory(
+ const size_t link_idx) const
+ {
+ const size_t link_if_ctrl_idx = _link_link_if_ctrl_map.at(link_idx).first;
+ return _link_if_ctrls.at(link_if_ctrl_idx)->get_packet_factory();
+ }
+
+private:
+ /**************************************************************************
+ * Private methods / helpers
+ *************************************************************************/
+ mpmd_link_if_ctrl_base::uptr make_link_if_ctrl(
+ const std::string& link_type, const xport_info_list_t& xport_info)
+ {
+ // Here, we hard-code the list of available transport types
+ if (link_type == "udp") {
+#ifdef HAVE_DPDK
+ // if (_mb_args.has_key("use_dpdk")) {
+ // return std::make_unique<mpmd_link_if_ctrl_dpdk_udp>(_mb_args,
+ // xport_info);
+ //}
+#endif
+ return std::make_unique<mpmd_link_if_ctrl_udp>(_mb_args, xport_info);
+#ifdef HAVE_LIBERIO
+ } else if (link_type == "liberio") {
+ return std::make_unique<mpmd_link_if_ctrl_liberio>(_mb_args, xport_info);
+#endif
+ }
+ UHD_LOG_WARNING("MPMD", "Cannot instantiate transport medium " << link_type);
+ return nullptr;
+ }
+
+ /**************************************************************************
+ * Private attributes
+ *************************************************************************/
+ //! Cache available xport manager implementations
+ //
+ // Should only every be populated by connect()
+ std::vector<mpmd_link_if_ctrl_base::uptr> _link_if_ctrls;
+ // Maps link index to link_if_ctrl index. To look up the xport ctrl for link
+ // number L, do something like this:
+ // auto& link_if_ctrl = _link_if_ctrls.at(_link_link_if_ctrl_map.at(L).first);
+ std::vector<std::pair<size_t, size_t>> _link_link_if_ctrl_map;
+
+ //! Motherboard args, can contain things like 'recv_buff_size'
+ const uhd::device_addr_t _mb_args;
+};
+
+mpmd_link_if_mgr::uptr mpmd_link_if_mgr::make(const uhd::device_addr_t& mb_args)
+{
+ return std::make_unique<mpmd_link_if_mgr_impl>(mb_args);
+}
diff --git a/host/lib/usrp/mpmd/mpmd_xport_mgr.hpp b/host/lib/usrp/mpmd/mpmd_link_if_mgr.hpp
index 3d96e5ec6..4b0ba4212 100644
--- a/host/lib/usrp/mpmd/mpmd_xport_mgr.hpp
+++ b/host/lib/usrp/mpmd/mpmd_link_if_mgr.hpp
@@ -4,11 +4,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
-#ifndef INCLUDED_MPMD_XPORT_MGR_HPP
-#define INCLUDED_MPMD_XPORT_MGR_HPP
+#ifndef INCLUDED_MPMD_LINK_IF_MGR_HPP
+#define INCLUDED_MPMD_LINK_IF_MGR_HPP
-#include "../device3/device3_impl.hpp"
+#include <uhd/types/device_addr.hpp>
#include <uhd/types/dict.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhdlib/rfnoc/chdr_packet.hpp>
+#include <uhdlib/transport/links.hpp>
#include <map>
#include <memory>
#include <string>
@@ -20,8 +23,6 @@ namespace uhd { namespace mpmd { namespace xport {
* Transport specifiers
*/
-//! Ethernet address for management and RPC communication
-const std::string MGMT_ADDR_KEY = "mgmt_addr";
//! Primary Ethernet address for streaming and RFNoC communication
const std::string FIRST_ADDR_KEY = "addr";
//! Secondary Ethernet address for streaming and RFNoC communication
@@ -45,13 +46,13 @@ uhd::dict<std::string, std::string> filter_args(
* medium. For example, if the medium is Ethernet/UDP, this class will create
* sockets.
*/
-class mpmd_xport_mgr
+class mpmd_link_if_mgr
{
public:
- using uptr = std::unique_ptr<mpmd_xport_mgr>;
+ using uptr = std::unique_ptr<mpmd_link_if_mgr>;
using xport_info_t = std::map<std::string, std::string>;
using xport_info_list_t = std::vector<std::map<std::string, std::string>>;
- virtual ~mpmd_xport_mgr() {}
+ virtual ~mpmd_link_if_mgr() {}
/*! Return a reference to a transport manager
*
@@ -65,6 +66,45 @@ public:
*/
static uptr make(const uhd::device_addr_t& mb_args);
+ /*! Attempt to open a CHDR-capable link to the remote device
+ *
+ * This will compare the mb_args (passed in at construction) with
+ * \p xport_info to see if it can connect this way. For example, if
+ * \p xport_type is "udp", then it will see if it can find the `addr` key
+ * from mb_args in the \p xport_info. If yes, it will use that for
+ * connections.
+ *
+ * \param xport_type The type of xport ("udp", "liberio", ...)
+ * \param xport_info The available information on this transport. For
+ * example, if the xport_type is "udp", then this would
+ * contain the available IP addresses.
+ * \returns true on success
+ */
+ virtual bool connect(
+ const std::string& xport_type, const xport_info_list_t& xport_info) = 0;
+
+ /*! The number of available links
+ *
+ * If zero, it means that there is no valid connection to the device.
+ *
+ */
+ virtual size_t get_num_links() = 0;
+
+ /*! Return links object
+ *
+ * \param link_idx The number of the link to use. link_idx < get_num_links()
+ * must hold true. link_idx is often 0. Example: When
+ * the underlying transport is Ethernet, and the user
+ * specified both addr and second_addr, then get_num_links()
+ * equals 2 and link_idx can also be 1.
+ * \param link_type CTRL, RX_DATA, or TX_DATA (for configuring the link)
+ * \param link_args Link-specific additional information that the underlying
+ * mpmd_link_if_ctrl instantiation can use
+ */
+ virtual uhd::transport::both_links_t get_link(const size_t link_idx,
+ const uhd::transport::link_type_t link_type,
+ const uhd::device_addr_t& link_args) = 0;
+
/*! Create a transports object
*
* Implementation details depend on the underlying implementation.
@@ -90,16 +130,28 @@ public:
* The latter needs to get sent back to MPM to complete the
* transport handshake.
*/
- virtual both_xports_t make_transport(const xport_info_list_t& xport_info_list,
- const usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args,
- xport_info_t& xport_info_out) = 0;
+ //virtual both_xports_t make_transport(const xport_info_list_t& xport_info_list,
+ //const usrp::device3_impl::xport_type_t xport_type,
+ //const uhd::device_addr_t& xport_args,
+ //xport_info_t& xport_info_out) = 0;
/*! Return the path MTU for whatever this manager lets us do
*/
- virtual size_t get_mtu(const uhd::direction_t dir) const = 0;
+ virtual size_t get_mtu(const size_t link_idx, const uhd::direction_t dir) const = 0;
+
+ /*! Get packet factory from associated link_mgr
+ *
+ * \param link_idx The number of the link to use. link_idx < get_num_links()
+ * must hold true. link_idx is often 0. Example: When
+ * the underlying transport is Ethernet, and the user
+ * specified both addr and second_addr, then get_num_links()
+ * equals 2 and link_idx can also be 1.
+ * \return a CHDR packet factory
+ */
+ virtual const uhd::rfnoc::chdr::chdr_packet_factory& get_packet_factory(
+ const size_t link_idx) const = 0;
};
}}} /* namespace uhd::mpmd::xport */
-#endif /* INCLUDED_MPMD_XPORT_MGR_HPP */
+#endif /* INCLUDED_MPMD_LINK_IF_MGR_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_mb_controller.cpp b/host/lib/usrp/mpmd/mpmd_mb_controller.cpp
index 6c2954fb8..e9310d01d 100644
--- a/host/lib/usrp/mpmd/mpmd_mb_controller.cpp
+++ b/host/lib/usrp/mpmd/mpmd_mb_controller.cpp
@@ -4,11 +4,34 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
-#include "mpmd_mb_controller.hpp"
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
using namespace uhd::rfnoc;
+using namespace uhd;
+namespace {
+ //! Default timeout value for tRPC calls that we know can take long (ms)
+ constexpr size_t MPMD_DEFAULT_LONG_TIMEOUT = 12000; // ms
+}
+
+mpmd_mb_controller::mpmd_mb_controller(
+ uhd::rpc_client::sptr rpcc, uhd::device_addr_t device_info)
+ : _rpc(rpcc), _device_info(device_info)
+{
+ const size_t num_tks = _rpc->request_with_token<size_t>("get_num_timekeepers");
+ for (size_t tk_idx = 0; tk_idx < num_tks; tk_idx++) {
+ register_timekeeper(tk_idx, std::make_shared<mpmd_timekeeper>(tk_idx, _rpc));
+ }
+
+ auto sensor_list =
+ _rpc->request_with_token<std::vector<std::string>>("get_mb_sensors");
+ UHD_LOG_DEBUG("MPMD", "Found " << sensor_list.size() << " motherboard sensors.");
+ _sensor_names.insert(sensor_list.cbegin(), sensor_list.cend());
+}
+/******************************************************************************
+ * Timekeeper API
+ *****************************************************************************/
uint64_t mpmd_mb_controller::mpmd_timekeeper::get_ticks_now()
{
return _rpc->request_with_token<uint64_t>("get_timekeeper_time", _tk_idx, false);
@@ -34,3 +57,120 @@ void mpmd_mb_controller::mpmd_timekeeper::set_period(const uint64_t period_ns)
_rpc->notify_with_token("set_tick_period", _tk_idx, period_ns);
}
+void mpmd_mb_controller::mpmd_timekeeper::update_tick_rate(const double tick_rate)
+{
+ set_tick_rate(tick_rate);
+}
+
+/******************************************************************************
+ * Motherboard Control API (see mb_controller.hpp)
+ *****************************************************************************/
+std::string mpmd_mb_controller::get_mboard_name() const
+{
+ return _device_info.get("name", "UNKNOWN");
+}
+
+void mpmd_mb_controller::set_time_source(const std::string& source)
+{
+ _rpc->notify_with_token(MPMD_DEFAULT_LONG_TIMEOUT, "set_time_source", source);
+}
+
+std::string mpmd_mb_controller::get_time_source() const
+{
+ return _rpc->request_with_token<std::string>("get_time_source");
+}
+
+std::vector<std::string> mpmd_mb_controller::get_time_sources() const
+{
+ return _rpc->request_with_token<std::vector<std::string>>("get_time_sources");
+}
+
+void mpmd_mb_controller::set_clock_source(const std::string& source)
+{
+ _rpc->notify_with_token(MPMD_DEFAULT_LONG_TIMEOUT, "set_clock_source", source);
+}
+
+std::string mpmd_mb_controller::get_clock_source() const
+{
+ return _rpc->request_with_token<std::string>("get_clock_source");
+}
+
+std::vector<std::string> mpmd_mb_controller::get_clock_sources() const
+{
+ return _rpc->request_with_token<std::vector<std::string>>("get_clock_sources");
+}
+
+void mpmd_mb_controller::set_sync_source(const std::string& clock_source, const std::string& time_source)
+{
+ uhd::device_addr_t sync_source;
+ sync_source["clock_source"] = clock_source;
+ sync_source["time_source"] = time_source;
+ set_sync_source(sync_source);
+}
+
+void mpmd_mb_controller::set_sync_source(const device_addr_t& sync_source)
+{
+ std::map<std::string, std::string> sync_source_map;
+ for (const auto& key : sync_source.keys()) {
+ sync_source_map[key] = sync_source.get(key);
+ }
+ _rpc->notify_with_token(
+ MPMD_DEFAULT_LONG_TIMEOUT, "set_clock_source", sync_source_map);
+}
+
+device_addr_t mpmd_mb_controller::get_sync_source() const
+{
+ const auto sync_source_map =
+ _rpc->request_with_token<std::map<std::string, std::string>>("get_sync_source");
+ return device_addr_t(sync_source_map);
+}
+
+std::vector<device_addr_t> mpmd_mb_controller::get_sync_sources()
+{
+ std::vector<device_addr_t> result;
+ const auto sync_sources =
+ _rpc->request_with_token<std::vector<std::map<std::string, std::string>>>(
+ "get_sync_sources");
+ for (auto& sync_source : sync_sources) {
+ result.push_back(device_addr_t(sync_source));
+ }
+
+ return result;
+}
+
+void mpmd_mb_controller::set_clock_source_out(const bool /*enb*/)
+{
+ throw uhd::not_implemented_error(
+ "set_clock_source_out() not implemented on this device!");
+}
+
+void mpmd_mb_controller::set_time_source_out(const bool /*enb*/)
+{
+ throw uhd::not_implemented_error(
+ "set_time_source_out() not implemented on this device!");
+}
+
+sensor_value_t mpmd_mb_controller::get_sensor(const std::string& name)
+{
+ if (!_sensor_names.count(name)) {
+ throw uhd::key_error(std::string("Invalid motherboard sensor name: ") + name);
+ }
+ return sensor_value_t(
+ _rpc->request_with_token<sensor_value_t::sensor_map_t>("get_mb_sensor", name));
+}
+
+std::vector<std::string> mpmd_mb_controller::get_sensor_names()
+{
+ std::vector<std::string> sensor_names(_sensor_names.cbegin(), _sensor_names.cend());
+ return sensor_names;
+}
+
+uhd::usrp::mboard_eeprom_t mpmd_mb_controller::get_eeprom()
+{
+ auto mb_eeprom =
+ _rpc->request_with_token<std::map<std::string, std::string>>("get_mb_eeprom");
+ uhd::usrp::mboard_eeprom_t mb_eeprom_dict(
+ mb_eeprom.cbegin(), mb_eeprom.cend());
+ return mb_eeprom_dict;
+}
+
diff --git a/host/lib/usrp/mpmd/mpmd_mb_controller.hpp b/host/lib/usrp/mpmd/mpmd_mb_controller.hpp
deleted file mode 100644
index 65e5dc468..000000000
--- a/host/lib/usrp/mpmd/mpmd_mb_controller.hpp
+++ /dev/null
@@ -1,73 +0,0 @@
-//
-// Copyright 2019 Ettus Research, a National Instruments Brand
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#ifndef INCLUDED_LIBUHD_MPMD_MB_CONTROLLER_HPP
-#define INCLUDED_LIBUHD_MPMD_MB_CONTROLLER_HPP
-
-#include <uhd/rfnoc/mb_controller.hpp>
-#include <uhdlib/utils/rpc.hpp>
-
-namespace uhd { namespace rfnoc {
-
-/*! X300-Specific version of the mb_controller
- *
- * Reminder: There is one of these per motherboard.
- */
-class mpmd_mb_controller : public mb_controller
-{
-public:
-
-
- //! Return reference to the RPC client
- uhd::rpc_client::sptr get_rpc_client() { return _rpc; }
-
- //! X300-specific version of the timekeeper controls
- class mpmd_timekeeper : public mb_controller::timekeeper
- {
- public:
- mpmd_timekeeper(const size_t tk_idx, uhd::rpc_client::sptr rpc_client)
- : _tk_idx(tk_idx), _rpc(rpc_client)
- {
- // nop
- }
-
- uint64_t get_ticks_now();
-
- uint64_t get_ticks_last_pps();
-
- void set_ticks_now(const uint64_t ticks);
-
- void set_ticks_next_pps(const uint64_t ticks);
-
- void set_period(const uint64_t period_ns);
-
- private:
- /*! Shorthand to perform an RPC request. Saves some typing.
- */
- template <typename return_type, typename... Args>
- return_type request(std::string const& func_name, Args&&... args)
- {
- UHD_LOG_TRACE("X300MBCTRL", "[RPC] Calling " << func_name);
- return _rpc->request_with_token<return_type>(
- func_name, std::forward<Args>(args)...);
- };
-
- const size_t _tk_idx;
-
- uhd::rpc_client::sptr _rpc;
- };
-
-private:
- /**************************************************************************
- * Attributes
- *************************************************************************/
- //! Reference to RPC interface
- uhd::rpc_client::sptr _rpc;
-};
-
-}} // namespace uhd::rfnoc
-
-#endif /* INCLUDED_LIBUHD_MPMD_MB_CONTROLLER_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_mb_iface.cpp b/host/lib/usrp/mpmd/mpmd_mb_iface.cpp
new file mode 100644
index 000000000..e713cc7a3
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_mb_iface.cpp
@@ -0,0 +1,301 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "mpmd_mb_iface.hpp"
+#include "mpmd_link_if_mgr.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/rfnoc/device_id.hpp>
+
+using namespace uhd::rfnoc;
+using namespace uhd::mpmd;
+
+mpmd_mboard_impl::mpmd_mb_iface::mpmd_mb_iface(
+ const uhd::device_addr_t& mb_args, uhd::rpc_client::sptr rpc)
+ : _mb_args(mb_args), _rpc(rpc), _link_if_mgr(xport::mpmd_link_if_mgr::make(mb_args))
+{
+ _remote_device_id = allocate_device_id();
+ UHD_LOG_TRACE("MPMD::MB_IFACE", "Assigning device_id " << _remote_device_id);
+ _rpc->notify_with_token("set_device_id", _remote_device_id);
+}
+
+/******************************************************************************
+ * mpmd_mb_iface API
+ *****************************************************************************/
+void mpmd_mboard_impl::mpmd_mb_iface::init()
+{
+ UHD_LOG_TRACE("MPMD::MB_IFACE", "Requesting clock ifaces...");
+ auto clock_ifaces = _rpc->request_with_token<clock_iface_list_t>("get_clocks");
+ for (auto& clock : clock_ifaces) {
+ auto iface = std::make_shared<uhd::rfnoc::clock_iface>(
+ clock.at("name"), std::stod(clock.at("freq")), clock.count("mutable"));
+ _clock_ifaces[clock.at("name")] = iface;
+ iface->set_running(true);
+ UHD_LOG_DEBUG("MPMD::MB_IFACE",
+ "Adding clock iface `"
+ << clock.at("name") << "`, frequency: " << (iface->get_freq() / 1e6)
+ << " MHz, mutable: " << (iface->is_mutable() ? "Yes" : "No"));
+ }
+ UHD_LOG_TRACE("MPMD::MB_IFACE", "Requesting CHDR link types...");
+ auto chdr_link_types =
+ _rpc->request_with_token<std::vector<std::string>>("get_chdr_link_types");
+ UHD_LOG_TRACE(
+ "MPMD::MB_IFACE", "Found " << chdr_link_types.size() << " link type(s)");
+ for (const auto& type : chdr_link_types) {
+ UHD_LOG_TRACE("MPMD::MB_IFACE", "Trying link type `" << type << "'");
+ const auto xport_info =
+ _rpc->request_with_token<xport::mpmd_link_if_mgr::xport_info_list_t>(
+ "get_chdr_link_options", type);
+ // User may have specified: addr=192.168.10.2, second_addr=
+ // MPM may have said: "my addresses are 192.168.10.2 and 192.168.20.2"
+ if (_link_if_mgr->connect(type, xport_info)) {
+ UHD_LOG_TRACE("MPMD::MB_IFACE", "Link type " << type << " successful.");
+ }
+ }
+
+ if (_link_if_mgr->get_num_links() == 0) {
+ UHD_LOG_ERROR("MPMD::MB_IFACE", "No CHDR connection available!");
+ throw uhd::runtime_error("No CHDR connection available!");
+ }
+
+ for (size_t link_idx = 0; link_idx < _link_if_mgr->get_num_links(); link_idx++) {
+ _local_device_id_map.emplace(allocate_device_id(), link_idx);
+ }
+}
+
+/******************************************************************************
+ * mb_iface API
+ *****************************************************************************/
+uint16_t mpmd_mboard_impl::mpmd_mb_iface::get_proto_ver()
+{
+ return _rpc->request_with_token<uint16_t>("get_proto_ver");
+}
+
+uhd::rfnoc::chdr_w_t mpmd_mboard_impl::mpmd_mb_iface::get_chdr_w()
+{
+ const auto chdr_w_bits = _rpc->request_with_token<size_t>("get_chdr_width");
+ switch (chdr_w_bits) {
+ case 512:
+ return CHDR_W_512;
+ case 256:
+ return CHDR_W_256;
+ case 128:
+ return CHDR_W_128;
+ case 64:
+ return CHDR_W_64;
+ }
+ throw uhd::runtime_error(std::string("Device reporting invalid CHDR width: ")
+ + std::to_string(chdr_w_bits));
+}
+
+uhd::endianness_t mpmd_mboard_impl::mpmd_mb_iface::get_endianness(
+ const uhd::rfnoc::device_id_t local_device_id)
+{
+ uhd::rfnoc::device_id_t lookup_id = local_device_id;
+ if (lookup_id == NULL_DEVICE_ID) {
+ for (auto& ids : _local_device_id_map) {
+ lookup_id = ids.first;
+ break;
+ }
+ }
+ const size_t link_idx = _local_device_id_map.at(lookup_id);
+ auto& pkt_factory = _link_if_mgr->get_packet_factory(link_idx);
+ return pkt_factory.get_endianness();
+}
+
+uhd::rfnoc::device_id_t mpmd_mboard_impl::mpmd_mb_iface::get_remote_device_id()
+{
+ return _remote_device_id;
+}
+
+std::vector<device_id_t> mpmd_mboard_impl::mpmd_mb_iface::get_local_device_ids()
+{
+ std::vector<device_id_t> device_ids;
+ for (auto& local_dev_id_pair : _local_device_id_map) {
+ device_ids.push_back(local_dev_id_pair.first);
+ }
+ return device_ids;
+}
+
+uhd::transport::adapter_id_t mpmd_mboard_impl::mpmd_mb_iface::get_adapter_id(
+ const uhd::rfnoc::device_id_t local_device_id)
+{
+ return _adapter_map.at(local_device_id);
+}
+
+void mpmd_mboard_impl::mpmd_mb_iface::reset_network()
+{
+ // FIXME
+}
+
+uhd::rfnoc::clock_iface::sptr mpmd_mboard_impl::mpmd_mb_iface::get_clock_iface(
+ const std::string& clock_name)
+{
+ if (_clock_ifaces.count(clock_name)) {
+ return _clock_ifaces.at(clock_name);
+ } else {
+ UHD_LOG_ERROR("MPMD::MB_IFACE", "Invalid timebase clock name: " + clock_name);
+ throw uhd::key_error(
+ "[MPMD_MB::IFACE] Invalid timebase clock name: " + clock_name);
+ }
+}
+
+uhd::rfnoc::chdr_ctrl_xport::sptr mpmd_mboard_impl::mpmd_mb_iface::make_ctrl_transport(
+ uhd::rfnoc::device_id_t local_device_id, const uhd::rfnoc::sep_id_t& local_epid)
+{
+ if (!_local_device_id_map.count(local_device_id)) {
+ throw uhd::key_error(std::string("[MPMD::MB_IFACE] Cannot create control "
+ "transport: Unknown local device ID ")
+ + std::to_string(local_device_id));
+ }
+ const size_t link_idx = _local_device_id_map.at(local_device_id);
+ uhd::transport::io_service::sptr io_srv;
+ uhd::transport::send_link_if::sptr send_link;
+ uhd::transport::recv_link_if::sptr recv_link;
+ std::tie(io_srv, send_link, std::ignore, recv_link, std::ignore, std::ignore) =
+ _link_if_mgr->get_link(
+ link_idx, uhd::transport::link_type_t::CTRL, uhd::device_addr_t());
+
+ /* Associate local device ID with the adapter */
+ _adapter_map[local_device_id] = send_link->get_send_adapter_id();
+
+ auto pkt_factory = _link_if_mgr->get_packet_factory(link_idx);
+ auto xport = uhd::rfnoc::chdr_ctrl_xport::make(io_srv,
+ send_link,
+ recv_link,
+ pkt_factory,
+ local_epid,
+ send_link->get_num_send_frames(),
+ recv_link->get_num_recv_frames());
+ return xport;
+}
+
+uhd::rfnoc::chdr_rx_data_xport::uptr
+mpmd_mboard_impl::mpmd_mb_iface::make_rx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args)
+{
+ const uhd::rfnoc::sep_addr_t local_sep_addr = addrs.second;
+
+ if (!_local_device_id_map.count(local_sep_addr.first)) {
+ throw uhd::key_error(std::string("[MPMD::MB_IFACE] Cannot create RX data "
+ "transport: Unknown local device ID ")
+ + std::to_string(local_sep_addr.first));
+ }
+ const size_t link_idx = _local_device_id_map.at(local_sep_addr.first);
+
+ uhd::transport::io_service::sptr io_srv;
+ uhd::transport::send_link_if::sptr send_link;
+ uhd::transport::recv_link_if::sptr recv_link;
+ bool lossy_xport;
+ size_t recv_buff_size;
+ std::tie(io_srv, send_link, std::ignore, recv_link, recv_buff_size, lossy_xport) =
+ _link_if_mgr->get_link(
+ link_idx, uhd::transport::link_type_t::RX_DATA, xport_args);
+
+ /* Associate local device ID with the adapter */
+ _adapter_map[local_sep_addr.first] = send_link->get_send_adapter_id();
+
+ // TODO: configure this based on the transport type
+ const stream_buff_params_t recv_capacity = {
+ recv_buff_size, uhd::rfnoc::MAX_FC_CAPACITY_PKTS};
+
+ const double ratio = 1.0 / 32;
+
+ // Configure flow control frequency to use bytes only for UDP
+ stream_buff_params_t fc_freq = {
+ static_cast<uint64_t>(std::ceil(double(recv_buff_size) * ratio)),
+ uhd::rfnoc::MAX_FC_FREQ_PKTS};
+
+ stream_buff_params_t fc_headroom = {0, 0};
+
+ // Create the data transport
+ auto pkt_factory = _link_if_mgr->get_packet_factory(link_idx);
+ auto fc_params = chdr_rx_data_xport::configure_sep(io_srv,
+ recv_link,
+ send_link,
+ pkt_factory,
+ mgmt_portal,
+ epids,
+ pyld_buff_fmt,
+ mdata_buff_fmt,
+ recv_capacity,
+ fc_freq,
+ fc_headroom,
+ lossy_xport);
+ auto rx_xport = std::make_unique<chdr_rx_data_xport>(io_srv,
+ recv_link,
+ send_link,
+ pkt_factory,
+ epids,
+ recv_link->get_num_recv_frames(),
+ fc_params);
+
+ return rx_xport;
+}
+
+uhd::rfnoc::chdr_tx_data_xport::uptr
+mpmd_mboard_impl::mpmd_mb_iface::make_tx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args)
+{
+ const uhd::rfnoc::sep_addr_t local_sep_addr = addrs.first;
+
+ if (!_local_device_id_map.count(local_sep_addr.first)) {
+ throw uhd::key_error(std::string("[MPMD::MB_IFACE] Cannot create TX data "
+ "transport: Unknown local device ID ")
+ + std::to_string(local_sep_addr.first));
+ }
+ const size_t link_idx = _local_device_id_map.at(local_sep_addr.first);
+
+ uhd::transport::io_service::sptr io_srv;
+ uhd::transport::send_link_if::sptr send_link;
+ uhd::transport::recv_link_if::sptr recv_link;
+ bool lossy_xport;
+ std::tie(io_srv, send_link, std::ignore, recv_link, std::ignore, lossy_xport) =
+ _link_if_mgr->get_link(
+ link_idx, uhd::transport::link_type_t::TX_DATA, xport_args);
+
+ /* Associate local device ID with the adapter */
+ _adapter_map[local_sep_addr.first] = send_link->get_send_adapter_id();
+
+ // TODO: configure this based on the transport type
+ const double fc_freq_ratio = 1.0 / 8;
+ const double fc_headroom_ratio = 0;
+
+ auto pkt_factory = _link_if_mgr->get_packet_factory(link_idx);
+ const auto buff_capacity = chdr_tx_data_xport::configure_sep(io_srv,
+ recv_link,
+ send_link,
+ pkt_factory,
+ mgmt_portal,
+ epids,
+ pyld_buff_fmt,
+ mdata_buff_fmt,
+ fc_freq_ratio,
+ fc_headroom_ratio);
+
+ // Create the data transport
+ auto tx_xport = std::make_unique<chdr_tx_data_xport>(io_srv,
+ recv_link,
+ send_link,
+ pkt_factory,
+ epids,
+ send_link->get_num_send_frames(),
+ buff_capacity);
+
+
+ return tx_xport;
+}
diff --git a/host/lib/usrp/mpmd/mpmd_mb_iface.hpp b/host/lib/usrp/mpmd/mpmd_mb_iface.hpp
new file mode 100644
index 000000000..4e47dd35a
--- /dev/null
+++ b/host/lib/usrp/mpmd/mpmd_mb_iface.hpp
@@ -0,0 +1,68 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_MPMD_MB_IFACE_HPP
+#define INCLUDED_MPMD_MB_IFACE_HPP
+
+#include "mpmd_impl.hpp"
+#include "mpmd_link_if_mgr.hpp"
+#include <uhdlib/rfnoc/mb_iface.hpp>
+#include <map>
+#include <unordered_map>
+
+namespace uhd { namespace mpmd {
+
+class mpmd_mboard_impl::mpmd_mb_iface : public uhd::rfnoc::mb_iface
+{
+public:
+ using uptr = std::unique_ptr<mpmd_mb_iface>;
+ using clock_iface_list_t = std::vector<std::map<std::string, std::string>>;
+ mpmd_mb_iface(const uhd::device_addr_t& mb_args, uhd::rpc_client::sptr rpc);
+ ~mpmd_mb_iface() = default;
+
+ /*** mpmd_mb_iface API calls *****************************************/
+ //! Initialize transports
+ void init();
+
+ /*** mb_iface API calls **********************************************/
+ uint16_t get_proto_ver();
+ uhd::rfnoc::chdr_w_t get_chdr_w();
+ uhd::endianness_t get_endianness(const uhd::rfnoc::device_id_t local_device_id);
+ uhd::rfnoc::device_id_t get_remote_device_id();
+ std::vector<uhd::rfnoc::device_id_t> get_local_device_ids();
+ uhd::transport::adapter_id_t get_adapter_id(const uhd::rfnoc::device_id_t local_device_id);
+ void reset_network();
+ uhd::rfnoc::clock_iface::sptr get_clock_iface(const std::string& clock_name);
+ uhd::rfnoc::chdr_ctrl_xport::sptr make_ctrl_transport(
+ uhd::rfnoc::device_id_t local_device_id, const uhd::rfnoc::sep_id_t& local_epid);
+ uhd::rfnoc::chdr_rx_data_xport::uptr make_rx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args);
+ uhd::rfnoc::chdr_tx_data_xport::uptr make_tx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args);
+
+private:
+ uhd::device_addr_t _mb_args;
+ uhd::rpc_client::sptr _rpc;
+ xport::mpmd_link_if_mgr::uptr _link_if_mgr;
+ uhd::rfnoc::device_id_t _remote_device_id;
+ std::map<uhd::rfnoc::device_id_t, size_t> _local_device_id_map;
+ std::unordered_map<uhd::rfnoc::device_id_t, uhd::transport::adapter_id_t> _adapter_map;
+ std::map<std::string, uhd::rfnoc::clock_iface::sptr> _clock_ifaces;
+};
+
+}} /* namespace uhd::mpmd */
+
+#endif /* INCLUDED_MPMD_MB_IFACE_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_mboard_impl.cpp b/host/lib/usrp/mpmd/mpmd_mboard_impl.cpp
index 83b47b485..5c8fd5485 100644
--- a/host/lib/usrp/mpmd/mpmd_mboard_impl.cpp
+++ b/host/lib/usrp/mpmd/mpmd_mboard_impl.cpp
@@ -6,11 +6,13 @@
//
#include "mpmd_impl.hpp"
+#include "mpmd_mb_iface.hpp"
#include <uhd/transport/udp_simple.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/safe_call.hpp>
#include <chrono>
#include <thread>
+#include <memory>
namespace {
/*************************************************************************
@@ -177,8 +179,8 @@ boost::optional<device_addr_t> mpmd_mboard_impl::is_device_reachable(
{
UHD_LOG_TRACE(
"MPMD", "Checking accessibility of device `" << device_addr.to_string() << "'");
- UHD_ASSERT_THROW(device_addr.has_key(xport::MGMT_ADDR_KEY));
- const std::string rpc_addr = device_addr.get(xport::MGMT_ADDR_KEY);
+ UHD_ASSERT_THROW(device_addr.has_key(MGMT_ADDR_KEY));
+ const std::string rpc_addr = device_addr.get(MGMT_ADDR_KEY);
const size_t rpc_port =
device_addr.cast<size_t>(mpmd_impl::MPM_RPC_PORT_KEY, mpmd_impl::MPM_RPC_PORT);
// 1) Read back device info
@@ -258,15 +260,10 @@ mpmd_mboard_impl::mpmd_mboard_impl(
const device_addr_t& mb_args_, const std::string& rpc_server_addr)
: mb_args(mb_args_)
, rpc(make_mpm_rpc_client(rpc_server_addr, mb_args_))
- , num_xbars(rpc->request<size_t>("get_num_xbars"))
, _claim_rpc(make_mpm_rpc_client(rpc_server_addr, mb_args, MPMD_CLAIMER_RPC_TIMEOUT))
- // xbar_local_addrs is not yet valid after this!
- , xbar_local_addrs(num_xbars, 0xFF)
- , _xport_mgr(xport::mpmd_xport_mgr::make(mb_args))
{
UHD_LOGGER_TRACE("MPMD") << "Initializing mboard, connecting to RPC server address: "
- << rpc_server_addr << " mboard args: " << mb_args.to_string()
- << " number of crossbars: " << num_xbars;
+ << rpc_server_addr << " mboard args: " << mb_args.to_string();
_claimer_task = claim_device_and_make_task();
if (mb_args_.has_key(MPMD_MEAS_LATENCY_KEY)) {
@@ -278,7 +275,7 @@ mpmd_mboard_impl::mpmd_mboard_impl(
for (const auto& info_pair : device_info_dict) {
device_info[info_pair.first] = info_pair.second;
}
- UHD_LOGGER_TRACE("MPMD") << "MPM reports device info: " << device_info.to_string();
+ UHD_LOG_DEBUG("MPMD", "MPM reports device info: " << device_info.to_string());
/// Get dboard info
const auto dboards_info = rpc->request<std::vector<dev_info>>("get_dboard_info");
UHD_ASSERT_THROW(this->dboard_info.size() == 0);
@@ -293,19 +290,16 @@ mpmd_mboard_impl::mpmd_mboard_impl(
this->dboard_info.push_back(this_db_info);
}
- for (const std::string& key : mb_args_.keys()) {
- if (key.find("recv") != std::string::npos)
- recv_args[key] = mb_args_[key];
- if (key.find("send") != std::string::npos)
- send_args[key] = mb_args_[key];
- }
+ // Initialize mb_iface and mb_controller
+ mb_iface = std::make_unique<mpmd_mb_iface>(mb_args, rpc);
+ mb_ctrl = std::make_shared<rfnoc::mpmd_mb_controller>(rpc, device_info);
}
mpmd_mboard_impl::~mpmd_mboard_impl()
{
// Destroy the claimer task to avoid spurious asynchronous reclaim call
// after the unclaim.
- UHD_SAFE_CALL(dump_logs(); _claimer_task.reset(); _xport_mgr.reset();
+ UHD_SAFE_CALL(dump_logs(); _claimer_task.reset();
if (not rpc->request_with_token<bool>("unclaim")) {
UHD_LOG_WARNING("MPMD", "Failure to ack unclaim!");
});
@@ -317,79 +311,15 @@ mpmd_mboard_impl::~mpmd_mboard_impl()
void mpmd_mboard_impl::init()
{
init_device(rpc, mb_args);
- // RFNoC block clocks are now on. Noc-IDs can be read back.
+ mb_iface->init();
}
/*****************************************************************************
* API
****************************************************************************/
-void mpmd_mboard_impl::set_xbar_local_addr(
- const size_t xbar_index, const size_t local_addr)
-{
- UHD_ASSERT_THROW(
- rpc->request_with_token<bool>("set_xbar_local_addr", xbar_index, local_addr));
- UHD_ASSERT_THROW(xbar_index < xbar_local_addrs.size());
- xbar_local_addrs.at(xbar_index) = local_addr;
-}
-
-uhd::both_xports_t mpmd_mboard_impl::make_transport(const sid_t& sid,
- usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args)
-{
- const std::string xport_type_str = [xport_type]() {
- switch (xport_type) {
- case mpmd_impl::CTRL:
- return "CTRL";
- case mpmd_impl::ASYNC_MSG:
- return "ASYNC_MSG";
- case mpmd_impl::RX_DATA:
- return "RX_DATA";
- case mpmd_impl::TX_DATA:
- return "TX_DATA";
- default:
- UHD_THROW_INVALID_CODE_PATH();
- };
- }();
-
- UHD_LOGGER_TRACE("MPMD") << __func__
- << "(): Creating new transport of type: " << xport_type_str;
-
- using namespace uhd::mpmd::xport;
- const auto xport_info_list =
- rpc->request_with_token<mpmd_xport_mgr::xport_info_list_t>(
- "request_xport", sid.get_dst(), sid.get_src(), xport_type_str);
- UHD_LOGGER_TRACE("MPMD") << __func__ << "(): request_xport() gave us "
- << xport_info_list.size() << " option(s).";
- if (xport_info_list.empty()) {
- UHD_LOG_ERROR("MPMD", "No viable transport path found!");
- throw uhd::runtime_error("No viable transport path found!");
- }
-
- xport::mpmd_xport_mgr::xport_info_t xport_info_out;
- auto xports = _xport_mgr->make_transport(
- xport_info_list, xport_type, xport_args, xport_info_out);
-
- if (not rpc->request_with_token<bool>("commit_xport", xport_info_out)) {
- UHD_LOG_ERROR("MPMD", "Failed to create UDP transport!");
- throw uhd::runtime_error("commit_xport() failed!");
- }
-
- return xports;
-}
-
-size_t mpmd_mboard_impl::get_mtu(const uhd::direction_t dir) const
-{
- return _xport_mgr->get_mtu(dir);
-}
-
-uhd::device_addr_t mpmd_mboard_impl::get_rx_hints() const
-{
- return recv_args;
-}
-
-uhd::device_addr_t mpmd_mboard_impl::get_tx_hints() const
+uhd::rfnoc::mb_iface& mpmd_mboard_impl::get_mb_iface()
{
- return send_args;
+ return *(mb_iface.get());
}
/*****************************************************************************
diff --git a/host/lib/usrp/mpmd/mpmd_prop_tree.cpp b/host/lib/usrp/mpmd/mpmd_prop_tree.cpp
index 51b88b0e6..dc559f91b 100644
--- a/host/lib/usrp/mpmd/mpmd_prop_tree.cpp
+++ b/host/lib/usrp/mpmd/mpmd_prop_tree.cpp
@@ -194,16 +194,4 @@ void mpmd_impl::init_property_tree(
return _get_component_info(comp_name, mb);
}); // Done adding component to property tree
}
-
- /*** MTUs ***********************************************************/
- tree->create<size_t>(mb_path / "mtu/recv")
- .add_coerced_subscriber([](const size_t) {
- throw uhd::runtime_error("Attempting to write read-only value (MTU)!");
- })
- .set_publisher([mb]() { return mb->get_mtu(uhd::RX_DIRECTION); });
- tree->create<size_t>(mb_path / "mtu/send")
- .add_coerced_subscriber([](const size_t) {
- throw uhd::runtime_error("Attempting to write read-only value (MTU)!");
- })
- .set_publisher([mb]() { return mb->get_mtu(uhd::TX_DIRECTION); });
}
diff --git a/host/lib/usrp/mpmd/mpmd_xport.cpp b/host/lib/usrp/mpmd/mpmd_xport.cpp
deleted file mode 100644
index 3ef6a074c..000000000
--- a/host/lib/usrp/mpmd/mpmd_xport.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// Copyright 2017 Ettus Research, National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-// make_transport logic for mpmd_impl. Note that mpmd_xport_mgr.* has most of
-// the actual transport logic, this for transport-related APIs.
-
-#include "mpmd_impl.hpp"
-#include "mpmd_xport_mgr.hpp"
-
-using namespace uhd;
-using namespace uhd::mpmd;
-
-uhd::device_addr_t mpmd_impl::get_rx_hints(size_t mb_index)
-{
- return _mb.at(mb_index)->get_rx_hints();
-}
-
-uhd::device_addr_t mpmd_impl::get_tx_hints(size_t mb_index)
-{
- return _mb.at(mb_index)->get_tx_hints();
-}
-
-size_t mpmd_impl::identify_mboard_by_xbar_addr(const size_t xbar_addr) const
-{
- for (size_t mb_index = 0; mb_index < _mb.size(); mb_index++) {
- for (size_t xbar_index = 0; xbar_index < _mb[mb_index]->num_xbars; xbar_index++) {
- if (_mb.at(mb_index)->get_xbar_local_addr(xbar_index) == xbar_addr) {
- return mb_index;
- }
- }
- }
- throw uhd::lookup_error(
- str(boost::format("Cannot identify mboard for crossbar address %d") % xbar_addr));
-}
-
-both_xports_t mpmd_impl::make_transport(const sid_t& dst_address,
- usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& args)
-{
- const size_t mb_index = identify_mboard_by_xbar_addr(dst_address.get_dst_addr());
-
- const sid_t sid(0,
- 0, // Not actually an address, more of an 'ignore me' value
- dst_address.get_dst_addr(),
- dst_address.get_dst_endpoint());
- UHD_LOGGER_TRACE("MPMD") << "Creating new transport to mboard: " << mb_index
- << " SID: " << sid.to_pp_string_hex()
- << " User-defined xport args: " << args.to_string();
-
- both_xports_t xports = _mb[mb_index]->make_transport(sid, xport_type, args);
- UHD_LOGGER_TRACE("MPMD") << "xport info: send_sid=="
- << xports.send_sid.to_pp_string_hex()
- << " recv_sid==" << xports.recv_sid.to_pp_string_hex()
- << " endianness=="
- << (xports.endianness == uhd::ENDIANNESS_BIG ? "BE" : "LE")
- << " recv_buff_size==" << xports.recv_buff_size
- << " send_buff_size==" << xports.send_buff_size;
- return xports;
-}
diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp
deleted file mode 100644
index a7fff9262..000000000
--- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Copyright 2017 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#ifndef INCLUDED_MPMD_XPORT_CTRL_BASE_HPP
-#define INCLUDED_MPMD_XPORT_CTRL_BASE_HPP
-
-#include "../device3/device3_impl.hpp"
-#include "mpmd_xport_mgr.hpp"
-#include <uhd/types/device_addr.hpp>
-#include <memory>
-
-namespace uhd { namespace mpmd { namespace xport {
-
-/*! Transport manager implementation base
- */
-class mpmd_xport_ctrl_base
-{
-public:
- using uptr = std::unique_ptr<mpmd_xport_ctrl_base>;
- virtual ~mpmd_xport_ctrl_base() {}
-
- /*! This is the final step of a make_transport() sequence
- *
- * \param xport_info Contains all necessary transport info. The
- * implementation may update this!
- * \param xport_type CTRL, ASYNC_MSG, ... (see xport_type_t)
- * \param xport_args Additional arguments. These can come from the user.
- */
- virtual both_xports_t make_transport(mpmd_xport_mgr::xport_info_t& xport_info,
- const usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args) = 0;
-
- //! Assert if an xport_info is even valid/feasible/available
- virtual bool is_valid(const mpmd_xport_mgr::xport_info_t& xport_info) const = 0;
-
- virtual size_t get_mtu(const uhd::direction_t dir) const = 0;
-};
-
-}}} /* namespace uhd::mpmd::xport */
-
-#endif /* INCLUDED_MPMD_XPORT_CTRL_BASE_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp
deleted file mode 100644
index c53eb97a1..000000000
--- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-//
-// Copyright 2017 Ettus Research, National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "mpmd_xport_ctrl_liberio.hpp"
-#include "../transport/liberio_zero_copy.hpp"
-#include <uhd/transport/udp_zero_copy.hpp>
-#include <uhd/utils/byteswap.hpp>
-#include <uhd/rfnoc/constants.hpp>
-
-using namespace uhd;
-using namespace uhd::mpmd::xport;
-
-namespace {
-
-//! Max frame size of a control packet in bytes
-const size_t LIBERIO_CTRL_FRAME_MAX_SIZE = 128;
-//! Max frame size of an async message packet in bytes
-const size_t LIBERIO_ASYNC_FRAME_MAX_SIZE = 256;
-//! Max frame size of a flow control packet in bytes
-const size_t LIBERIO_FC_FRAME_MAX_SIZE = 64;
-//! The max MTU will be this number times the page size
-const size_t LIBERIO_PAGES_PER_BUF = 2;
-//! Number of descriptors that liberio allocates (receive)
-const size_t LIBERIO_NUM_RECV_FRAMES = 128;
-//! Number of descriptors that liberio allocates (send)
-const size_t LIBERIO_NUM_SEND_FRAMES = 128;
-
-uint32_t extract_sid_from_pkt(void* pkt, size_t)
-{
- return uhd::sid_t(uhd::wtohx(static_cast<const uint32_t*>(pkt)[1])).get_dst();
-}
-
-} // namespace
-
-mpmd_xport_ctrl_liberio::mpmd_xport_ctrl_liberio(const uhd::device_addr_t& mb_args)
- : _mb_args(mb_args)
- , _recv_args(filter_args(mb_args, "recv"))
- , _send_args(filter_args(mb_args, "send"))
-{
- // nop
-}
-
-
-uhd::both_xports_t mpmd_xport_ctrl_liberio::make_transport(
- mpmd_xport_mgr::xport_info_t& xport_info,
- const usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args_)
-{
- auto xport_args = (xport_type == usrp::device3_impl::CTRL) ?
- uhd::device_addr_t() : xport_args_;
-
- // Constrain by this transport's MTU and the MTU passed in
- const size_t send_mtu = std::min(get_mtu(uhd::TX_DIRECTION),
- xport_args.cast<size_t>("mtu", get_mtu(uhd::TX_DIRECTION)));
- const size_t recv_mtu = std::min(get_mtu(uhd::RX_DIRECTION),
- xport_args.cast<size_t>("mtu", get_mtu(uhd::RX_DIRECTION)));
- size_t send_frame_size = xport_args.cast<size_t>("send_frame_size", send_mtu);
- size_t recv_frame_size = xport_args.cast<size_t>("recv_frame_size", recv_mtu);
-
- // Check any user supplied frame sizes and constrain to MTU
- if (xport_args.has_key("send_frame_size") and
- xport_type == usrp::device3_impl::TX_DATA)
- {
- if (send_frame_size > send_mtu) {
- UHD_LOGGER_WARNING("MPMD")
- << boost::format("Requested send_frame_size of %d exceeds the "
- "maximum supported by the hardware. Using %d.")
- % send_frame_size % send_mtu;
- send_frame_size = send_mtu;
- }
- }
- if (xport_args.has_key("recv_frame_size") and
- xport_type == usrp::device3_impl::RX_DATA)
- {
- size_t recv_frame_size = xport_args.cast<size_t>("recv_frame_size", recv_mtu);
- if (recv_frame_size > recv_mtu) {
- UHD_LOGGER_WARNING("MPMD")
- << boost::format("Requested recv_frame_size of %d exceeds the "
- "maximum supported by the hardware. Using %d.")
- % recv_frame_size % recv_mtu;
- recv_frame_size = recv_mtu;
- }
- }
-
- transport::zero_copy_xport_params default_buff_args;
- /* default ones for RX / TX, override below */
-
- default_buff_args.send_frame_size = send_mtu;
- default_buff_args.recv_frame_size = recv_mtu;
- default_buff_args.num_recv_frames = LIBERIO_NUM_RECV_FRAMES;
- default_buff_args.num_send_frames = LIBERIO_NUM_SEND_FRAMES;
-
- if (xport_type == usrp::device3_impl::CTRL) {
- default_buff_args.send_frame_size = LIBERIO_CTRL_FRAME_MAX_SIZE;
- default_buff_args.recv_frame_size = LIBERIO_CTRL_FRAME_MAX_SIZE;
- default_buff_args.num_recv_frames = uhd::rfnoc::CMD_FIFO_SIZE /
- uhd::rfnoc::MAX_CMD_PKT_SIZE;
- default_buff_args.num_send_frames = uhd::rfnoc::CMD_FIFO_SIZE /
- uhd::rfnoc::MAX_CMD_PKT_SIZE;
- } else if (xport_type == usrp::device3_impl::ASYNC_MSG) {
- default_buff_args.send_frame_size = LIBERIO_ASYNC_FRAME_MAX_SIZE;
- default_buff_args.recv_frame_size = LIBERIO_ASYNC_FRAME_MAX_SIZE;
- } else if (xport_type == usrp::device3_impl::RX_DATA) {
- default_buff_args.recv_frame_size = recv_frame_size;
- default_buff_args.send_frame_size = LIBERIO_FC_FRAME_MAX_SIZE;
- } else {
- default_buff_args.recv_frame_size = LIBERIO_FC_FRAME_MAX_SIZE;
- default_buff_args.send_frame_size = send_frame_size;
- }
-
- const std::string tx_dev = xport_info["tx_dev"];
- const std::string rx_dev = xport_info["rx_dev"];
-
- both_xports_t xports;
- xports.lossless = true;
- xports.endianness = uhd::ENDIANNESS_LITTLE;
- xports.send_sid = sid_t(xport_info["send_sid"]);
- xports.recv_sid = xports.send_sid.reversed();
-
- if (xport_type == usrp::device3_impl::CTRL) {
- UHD_ASSERT_THROW(xport_info["muxed"] == "True");
- if (not _ctrl_dma_xport) {
- _ctrl_dma_xport =
- make_muxed_liberio_xport(tx_dev, rx_dev, default_buff_args,
- uhd::rfnoc::MAX_NUM_BLOCKS * uhd::rfnoc::MAX_NUM_PORTS);
- }
-
- UHD_LOGGER_TRACE("MPMD")
- << "Making (muxed) stream with num " << xports.recv_sid.get_dst();
- xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst());
- } else if (xport_type == usrp::device3_impl::ASYNC_MSG) {
- UHD_ASSERT_THROW(xport_info["muxed"] == "True");
- if (not _async_msg_dma_xport) {
- _async_msg_dma_xport =
- make_muxed_liberio_xport(tx_dev, rx_dev, default_buff_args,
- uhd::rfnoc::MAX_NUM_BLOCKS * uhd::rfnoc::MAX_NUM_PORTS);
- }
-
- UHD_LOGGER_TRACE("MPMD")
- << "making (muxed) stream with num " << xports.recv_sid.get_dst();
- xports.recv = _async_msg_dma_xport->make_stream(xports.recv_sid.get_dst());
- } else {
- // Create muxed transport in case of less DMA channels
- if (xport_info["muxed"] == "True") {
- if (not _data_dma_xport) {
- _data_dma_xport =
- make_muxed_liberio_xport(tx_dev, rx_dev, default_buff_args,
- uhd::rfnoc::MAX_NUM_BLOCKS * uhd::rfnoc::MAX_NUM_PORTS);
- }
-
- UHD_LOGGER_TRACE("MPMD")
- << "Making (muxed) stream with num " << xports.recv_sid.get_dst();
- xports.recv = _data_dma_xport->make_stream(xports.recv_sid.get_dst());
- }
- else {
- xports.recv =
- transport::liberio_zero_copy::make(tx_dev, rx_dev, default_buff_args);
- }
- }
-
- // Finish both_xports_t object and return:
- xports.recv_buff_size = default_buff_args.recv_frame_size *
- default_buff_args.num_recv_frames;
- xports.send_buff_size = default_buff_args.send_frame_size *
- default_buff_args.num_send_frames;
- xports.send = xports.recv;
- return xports;
-}
-
-bool mpmd_xport_ctrl_liberio::is_valid(
- const mpmd_xport_mgr::xport_info_t& xport_info) const
-{
- return xport_info.at("type") == "liberio";
-}
-
-size_t mpmd_xport_ctrl_liberio::get_mtu(const uhd::direction_t /*dir*/) const
-{
- return LIBERIO_PAGES_PER_BUF * getpagesize();
-}
-
-uhd::transport::muxed_zero_copy_if::sptr
-mpmd_xport_ctrl_liberio::make_muxed_liberio_xport(const std::string& tx_dev,
- const std::string& rx_dev,
- const uhd::transport::zero_copy_xport_params& buff_args,
- const size_t max_muxed_ports)
-{
- auto base_xport = transport::liberio_zero_copy::make(tx_dev, rx_dev, buff_args);
-
- return uhd::transport::muxed_zero_copy_if::make(
- base_xport, extract_sid_from_pkt, max_muxed_ports);
-}
diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp
deleted file mode 100644
index 36f02fe46..000000000
--- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// Copyright 2017 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#ifndef INCLUDED_MPMD_XPORT_ctrl_liberio_HPP
-#define INCLUDED_MPMD_XPORT_ctrl_liberio_HPP
-
-#include "../device3/device3_impl.hpp"
-#include "mpmd_xport_ctrl_base.hpp"
-#include <uhd/transport/muxed_zero_copy_if.hpp>
-#include <uhd/types/device_addr.hpp>
-
-namespace uhd { namespace mpmd { namespace xport {
-
-/*! Liberio transport manager
- */
-class mpmd_xport_ctrl_liberio : public mpmd_xport_ctrl_base
-{
-public:
- mpmd_xport_ctrl_liberio(const uhd::device_addr_t& mb_args);
-
- /*! Open DMA interface to kernel (and thus to FPGA DMA engine)
- */
- both_xports_t make_transport(mpmd_xport_mgr::xport_info_t& xport_info,
- const usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args);
-
- bool is_valid(const mpmd_xport_mgr::xport_info_t& xport_info) const;
-
- size_t get_mtu(const uhd::direction_t dir) const;
-
-private:
- /*! Create a muxed liberio transport for control packets */
- uhd::transport::muxed_zero_copy_if::sptr make_muxed_liberio_xport(
- const std::string& tx_dev,
- const std::string& rx_dev,
- const uhd::transport::zero_copy_xport_params& buff_args,
- const size_t max_muxed_ports);
-
- const uhd::device_addr_t _mb_args;
- const uhd::dict<std::string, std::string> _recv_args;
- const uhd::dict<std::string, std::string> _send_args;
-
- //! Control transport for one liberio connection
- uhd::transport::muxed_zero_copy_if::sptr _ctrl_dma_xport;
- //! Data transport for one liberio connection
- uhd::transport::muxed_zero_copy_if::sptr _data_dma_xport;
- //! Control transport for one liberio connection
- uhd::transport::muxed_zero_copy_if::sptr _async_msg_dma_xport;
-};
-
-}}} /* namespace uhd::mpmd::xport */
-
-#endif /* INCLUDED_MPMD_XPORT_ctrl_liberio_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp
deleted file mode 100644
index 20b94899c..000000000
--- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-//
-// Copyright 2017 Ettus Research, National Instruments Company
-// Copyright 2019 Ettus Research, National Instruments Brand
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "mpmd_xport_ctrl_udp.hpp"
-#include "mpmd_impl.hpp"
-#include "mpmd_xport_mgr.hpp"
-#include <uhd/transport/udp_constants.hpp>
-#include <uhd/transport/udp_simple.hpp>
-#include <uhd/transport/udp_zero_copy.hpp>
-
-
-using namespace uhd;
-using namespace uhd::mpmd::xport;
-
-namespace {
-
-//! Maximum CHDR packet size in bytes
-const size_t MPMD_10GE_DATA_FRAME_MAX_SIZE = 4000;
-
-//! Default number of send frames
-const size_t MPMD_UDP_DEFAULT_NUM_SEND_FRAMES = 1;
-//! Default number of recv frames
-const size_t MPMD_UDP_DEFAULT_NUM_RECV_FRAMES = 1;
-//! Default message frame size
-const size_t MPMD_UDP_MSG_FRAME_SIZE = 256;
-//! Default 1GbE send frame size
-const size_t MPMD_UDP_1GE_DEFAULT_SEND_FRAME_SIZE = 1472;
-//! Default 1GbE receive frame size
-const size_t MPMD_UDP_1GE_DEFAULT_RECV_FRAME_SIZE = 1472;
-//! Default 10GbE send frame size
-const size_t MPMD_UDP_10GE_DEFAULT_SEND_FRAME_SIZE = 4000;
-//! Default 10GbE receive frame size
-const size_t MPMD_UDP_10GE_DEFAULT_RECV_FRAME_SIZE = 4000;
-
-//!
-const double MPMD_BUFFER_DEPTH = 50.0e-3; // s
-//! For MTU discovery, the time we wait for a packet before calling it
-// oversized (seconds).
-const double MPMD_MTU_DISCOVERY_TIMEOUT = 0.02;
-
-// TODO: move these to appropriate header file for all other devices
-const double MAX_RATE_1GIGE = 1e9 / 8; // byte/s
-const double MAX_RATE_10GIGE = 10e9 / 8; // byte/s
-
-std::vector<std::string> get_addrs_from_mb_args(const uhd::device_addr_t& mb_args)
-{
- // mb_args must always include addr
- if (not mb_args.has_key(FIRST_ADDR_KEY)) {
- throw uhd::runtime_error(
- "The " + FIRST_ADDR_KEY
- + " key must be specified in "
- "device args to create an Ethernet transport to an RFNoC block");
- }
- std::vector<std::string> addrs{mb_args[FIRST_ADDR_KEY]};
- if (mb_args.has_key(SECOND_ADDR_KEY)) {
- addrs.push_back(mb_args[SECOND_ADDR_KEY]);
- }
- return addrs;
-}
-
-/*! Do a binary search to discover MTU
- *
- * Uses the MPM echo service to figure out MTU. We simply send a bunch of
- * packets and see if they come back until we converged on the path MTU.
- * The end result must lie between \p min_frame_size and \p max_frame_size.
- *
- * \param address IP address
- * \param port UDP port (yeah it's a string!)
- * \param min_frame_size Minimum frame size, initialize algorithm to start
- * with this value
- * \param max_frame_size Maximum frame size, initialize algorithm to start
- * with this value
- * \param echo_timeout Timeout value in seconds. For frame sizes that
- * exceed the MTU, we don't expect a response, and this
- * is the amount of time we'll wait before we assume
- * the frame size exceeds the MTU.
- */
-size_t discover_mtu(const std::string& address,
- const std::string& port,
- size_t min_frame_size,
- size_t max_frame_size,
- const double echo_timeout = 0.020)
-{
- const size_t echo_prefix_offset = uhd::mpmd::mpmd_impl::MPM_ECHO_CMD.size();
- const size_t mtu_hdr_len = echo_prefix_offset + 10;
- UHD_ASSERT_THROW(min_frame_size < max_frame_size);
- UHD_ASSERT_THROW(min_frame_size % 4 == 0);
- UHD_ASSERT_THROW(max_frame_size % 4 == 0);
- UHD_ASSERT_THROW(min_frame_size >= echo_prefix_offset + mtu_hdr_len);
- using namespace uhd::transport;
- // The return port will probably differ from the discovery port, so we
- // need a "broadcast" UDP connection; using make_connected() would
- // drop packets
- udp_simple::sptr udp = udp_simple::make_broadcast(address, port);
- std::string send_buf(uhd::mpmd::mpmd_impl::MPM_ECHO_CMD);
- send_buf.resize(max_frame_size, '#');
- UHD_ASSERT_THROW(send_buf.size() == max_frame_size);
- std::vector<uint8_t> recv_buf;
- recv_buf.resize(max_frame_size, ' ');
-
- // Little helper to check returned packets match the sent ones
- auto require_bufs_match = [&recv_buf, &send_buf, mtu_hdr_len](const size_t len) {
- if (len < mtu_hdr_len
- or std::memcmp((void*)&recv_buf[0], (void*)&send_buf[0], mtu_hdr_len) != 0) {
- throw uhd::runtime_error("Unexpected content of MTU "
- "discovery return packet!");
- }
- };
- UHD_LOG_TRACE("MPMD", "Determining UDP MTU... ");
- size_t seq_no = 0;
- while (min_frame_size < max_frame_size) {
- // Only test multiples of 4 bytes!
- const size_t test_frame_size = (max_frame_size / 2 + min_frame_size / 2 + 3)
- & ~size_t(3);
- // Encode sequence number and current size in the string, makes it
- // easy to debug in code or Wireshark. Is also used for identifying
- // response packets.
- std::sprintf(
- &send_buf[echo_prefix_offset], ";%04lu,%04lu", seq_no++, test_frame_size);
- UHD_LOG_TRACE("MPMD", "Testing frame size " << test_frame_size);
- udp->send(boost::asio::buffer(&send_buf[0], test_frame_size));
-
- const size_t len = udp->recv(boost::asio::buffer(recv_buf), echo_timeout);
- if (len == 0) {
- // Nothing received, so this is probably too big
- max_frame_size = test_frame_size - 4;
- } else if (len >= test_frame_size) {
- // Size went through, so bump the minimum
- require_bufs_match(len);
- min_frame_size = test_frame_size;
- } else if (len < test_frame_size) {
- // This is an odd case. Something must have snipped the packet
- // on the way back. Still, we'll just back off and try
- // something smaller.
- UHD_LOG_DEBUG("MPMD", "Unexpected packet truncation during MTU discovery.");
- require_bufs_match(len);
- max_frame_size = len;
- }
- }
- UHD_LOG_DEBUG("MPMD", "Path MTU for address " << address << ": " << min_frame_size);
- return min_frame_size;
-}
-
-} // namespace
-
-
-mpmd_xport_ctrl_udp::mpmd_xport_ctrl_udp(const uhd::device_addr_t& mb_args)
- : _mb_args(mb_args)
- , _recv_args(filter_args(mb_args, "recv"))
- , _send_args(filter_args(mb_args, "send"))
- , _available_addrs(get_addrs_from_mb_args(mb_args))
- , _mtu(MPMD_10GE_DATA_FRAME_MAX_SIZE)
-{
- const std::string mpm_discovery_port = _mb_args.get(
- mpmd_impl::MPM_DISCOVERY_PORT_KEY, std::to_string(mpmd_impl::MPM_DISCOVERY_PORT));
- auto discover_mtu_for_ip = [mpm_discovery_port](const std::string& ip_addr) {
- return discover_mtu(ip_addr,
- mpm_discovery_port,
- IP_PROTOCOL_MIN_MTU_SIZE - IP_PROTOCOL_UDP_PLUS_IP_HEADER,
- MPMD_10GE_DATA_FRAME_MAX_SIZE,
- MPMD_MTU_DISCOVERY_TIMEOUT);
- };
-
- for (const auto& ip_addr : _available_addrs) {
- _mtu = std::min(_mtu, discover_mtu_for_ip(ip_addr));
- }
-}
-
-uhd::both_xports_t mpmd_xport_ctrl_udp::make_transport(
- mpmd_xport_mgr::xport_info_t& xport_info,
- const usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args)
-{
-
- double link_speed = MAX_RATE_1GIGE;
- if (xport_info.count("link_speed") == 0) {
- UHD_LOG_WARNING("MPMD",
- "Could not determine link speed; using 1GibE max speed of "
- << MAX_RATE_1GIGE);
- } else {
- link_speed = xport_info.at("link_speed") == "10000" ? MAX_RATE_10GIGE
- : MAX_RATE_1GIGE;
- }
-
- // Constrain by this transport's MTU and the MTU in the xport_args
- const size_t send_mtu = std::min(get_mtu(uhd::TX_DIRECTION),
- xport_args.cast<size_t>("mtu", get_mtu(uhd::TX_DIRECTION)));
- const size_t recv_mtu = std::min(get_mtu(uhd::RX_DIRECTION),
- xport_args.cast<size_t>("mtu", get_mtu(uhd::RX_DIRECTION)));
-
- // Create actual UDP transport
- transport::zero_copy_xport_params default_buff_args;
- default_buff_args.num_send_frames = MPMD_UDP_DEFAULT_NUM_SEND_FRAMES;
- default_buff_args.num_recv_frames = MPMD_UDP_DEFAULT_NUM_RECV_FRAMES;
- default_buff_args.recv_frame_size = MPMD_UDP_MSG_FRAME_SIZE;
- default_buff_args.send_frame_size = MPMD_UDP_MSG_FRAME_SIZE;
- default_buff_args.recv_buff_size = link_speed * MPMD_BUFFER_DEPTH;
- default_buff_args.send_buff_size = link_speed * MPMD_BUFFER_DEPTH;
- if (xport_type == usrp::device3_impl::CTRL) {
- default_buff_args.num_recv_frames =
- uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
- } else if (xport_type == usrp::device3_impl::TX_DATA) {
- const size_t default_frame_size = (link_speed == MAX_RATE_10GIGE ?
- MPMD_UDP_10GE_DEFAULT_SEND_FRAME_SIZE :
- MPMD_UDP_1GE_DEFAULT_SEND_FRAME_SIZE);
- default_buff_args.send_frame_size =
- xport_args.cast<size_t>("send_frame_size",
- std::min(default_frame_size, send_mtu));
- default_buff_args.num_send_frames =
- xport_args.cast<size_t>("num_send_frames",
- default_buff_args.num_send_frames);
- default_buff_args.send_buff_size =
- xport_args.cast<size_t>("send_buff_size",
- default_buff_args.send_buff_size);
- } else if (xport_type == usrp::device3_impl::RX_DATA) {
- const size_t default_frame_size = (link_speed == MAX_RATE_10GIGE ?
- MPMD_UDP_10GE_DEFAULT_RECV_FRAME_SIZE :
- MPMD_UDP_1GE_DEFAULT_RECV_FRAME_SIZE);
- default_buff_args.recv_frame_size =
- xport_args.cast<size_t>("recv_frame_size",
- std::min(default_frame_size, recv_mtu));
- default_buff_args.num_recv_frames =
- xport_args.cast<size_t>("num_recv_frames",
- default_buff_args.num_recv_frames);
- default_buff_args.recv_buff_size =
- xport_args.cast<size_t>("recv_buff_size",
- default_buff_args.recv_buff_size);
- }
- transport::udp_zero_copy::buff_params buff_params;
- auto recv = transport::udp_zero_copy::make(xport_info["ipv4"],
- xport_info["port"],
- default_buff_args,
- buff_params);
- const uint16_t port = recv->get_local_port();
- const std::string src_ip_addr = recv->get_local_addr();
- xport_info["src_port"] = std::to_string(port);
- xport_info["src_ipv4"] = src_ip_addr;
-
- // Create both_xports_t object and finish:
- both_xports_t xports;
- xports.endianness = uhd::ENDIANNESS_BIG;
- xports.send_sid = sid_t(xport_info["send_sid"]);
- xports.recv_sid = xports.send_sid.reversed();
- xports.recv_buff_size = buff_params.recv_buff_size;
- xports.send_buff_size = buff_params.send_buff_size;
- xports.recv = recv; // Note: This is a type cast!
- xports.send = recv; // This too
- return xports;
-}
-
-bool mpmd_xport_ctrl_udp::is_valid(const mpmd_xport_mgr::xport_info_t& xport_info) const
-{
- return std::find(
- _available_addrs.cbegin(), _available_addrs.cend(), xport_info.at("ipv4"))
- != _available_addrs.cend();
-}
-
-size_t mpmd_xport_ctrl_udp::get_mtu(const uhd::direction_t /*dir*/) const
-{
- return _mtu;
-}
diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp
deleted file mode 100644
index 86301bb2a..000000000
--- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// Copyright 2017 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#ifndef INCLUDED_MPMD_XPORT_ctrl_udp_HPP
-#define INCLUDED_MPMD_XPORT_ctrl_udp_HPP
-
-#include "../device3/device3_impl.hpp"
-#include "mpmd_xport_ctrl_base.hpp"
-#include <uhd/types/device_addr.hpp>
-
-namespace uhd { namespace mpmd { namespace xport {
-
-/*! UDP transport manager
- *
- * Opens UDP sockets
- */
-class mpmd_xport_ctrl_udp : public mpmd_xport_ctrl_base
-{
-public:
- mpmd_xport_ctrl_udp(const uhd::device_addr_t& mb_args);
-
- both_xports_t make_transport(mpmd_xport_mgr::xport_info_t& xport_info,
- const usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args);
-
- bool is_valid(const mpmd_xport_mgr::xport_info_t& xport_info) const;
-
- size_t get_mtu(const uhd::direction_t dir) const;
-
-private:
- const uhd::device_addr_t _mb_args;
- const uhd::dict<std::string, std::string> _recv_args;
- const uhd::dict<std::string, std::string> _send_args;
- //! A list of IP addresses we can connect our CHDR connections to
- const std::vector<std::string> _available_addrs;
- //! MTU
- size_t _mtu;
-};
-
-}}} /* namespace uhd::mpmd::xport */
-
-#endif /* INCLUDED_MPMD_XPORT_ctrl_udp_HPP */
diff --git a/host/lib/usrp/mpmd/mpmd_xport_mgr.cpp b/host/lib/usrp/mpmd/mpmd_xport_mgr.cpp
deleted file mode 100644
index d3023e3af..000000000
--- a/host/lib/usrp/mpmd/mpmd_xport_mgr.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-//
-// Copyright 2017 Ettus Research, National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "mpmd_xport_mgr.hpp"
-#include "mpmd_impl.hpp"
-#include "mpmd_xport_ctrl_base.hpp"
-#include "mpmd_xport_ctrl_udp.hpp"
-#ifdef HAVE_LIBERIO
-# include "mpmd_xport_ctrl_liberio.hpp"
-#endif
-#ifdef HAVE_DPDK
-# include "mpmd_xport_ctrl_dpdk_udp.hpp"
-#endif
-
-uhd::dict<std::string, std::string> uhd::mpmd::xport::filter_args(
- const uhd::device_addr_t& args, const std::string& prefix)
-{
- uhd::dict<std::string, std::string> filtered_args;
- for (const std::string& key : args.keys()) {
- if (key.find(prefix) != std::string::npos) {
- filtered_args[key] = args[key];
- }
- }
-
- return filtered_args;
-}
-
-using namespace uhd::mpmd::xport;
-
-class mpmd_xport_mgr_impl : public mpmd_xport_mgr
-{
-public:
- mpmd_xport_mgr_impl(const uhd::device_addr_t& mb_args) : _mb_args(mb_args)
- {
- // nop
- }
-
- /**************************************************************************
- * API (see mpmd_xport_mgr.hpp)
- *************************************************************************/
- uhd::both_xports_t make_transport(const xport_info_list_t& xport_info_list,
- const uhd::usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& xport_args,
- xport_info_t& xport_info_out)
- {
- for (const auto& xport_info : xport_info_list) {
- require_xport_mgr(xport_info.at("type"));
- }
-
- // Run our incredibly smart selection algorithm
- xport_info_out = select_xport_option(xport_info_list);
- const std::string xport_medium = xport_info_out.at("type");
- UHD_LOG_TRACE("MPMD", __func__ << "(): xport medium is " << xport_medium);
-
- UHD_ASSERT_THROW(_xport_ctrls.count(xport_medium) > 0);
- UHD_ASSERT_THROW(_xport_ctrls.at(xport_medium));
- // When we've picked our preferred option, pass it to the transport
- // implementation for execution:
- return _xport_ctrls.at(xport_medium)
- ->make_transport(xport_info_out, xport_type, xport_args);
- }
-
- size_t get_mtu(const uhd::direction_t dir) const
- {
- if (_xport_ctrls.empty()) {
- UHD_LOG_WARNING("MPMD",
- "Cannot determine MTU, no transport controls have been "
- "established!");
- return 0;
- }
-
- size_t mtu = ~size_t(0);
- for (const auto& xport_ctrl_pair : _xport_ctrls) {
- mtu = std::min(mtu, xport_ctrl_pair.second->get_mtu(dir));
- }
-
- return mtu;
- }
-
-
-private:
- /**************************************************************************
- * Private methods / helpers
- *************************************************************************/
- /*! Picks a transport option based on available data
- *
- * \param xport_info_list List of available options, they all need to be
- * valid choices.
- *
- * \returns One element of \p xport_info_list based on a selection
- * algorithm.
- */
- xport_info_t select_xport_option(const xport_info_list_t& xport_info_list) const
- {
- for (const auto& xport_info : xport_info_list) {
- const std::string xport_medium = xport_info.at("type");
- if (_xport_ctrls.count(xport_medium) != 0 and _xport_ctrls.at(xport_medium)
- and _xport_ctrls.at(xport_medium)->is_valid(xport_info)) {
- return xport_info;
- }
- }
-
- throw uhd::runtime_error(
- "Could not select a transport option! "
- "Either a transport hint was not specified or the specified "
- "hint does not support communication with RFNoC blocks.");
- }
-
- //! Create an instance of an xport manager implementation
- //
- // \param xport_medium "UDP" or "liberio"
- // \param mb_args Device args
- mpmd_xport_ctrl_base::uptr make_mgr_impl(
- const std::string& xport_medium, const uhd::device_addr_t& mb_args) const
- {
- if (xport_medium == "UDP") {
-#ifdef HAVE_DPDK
- if (mb_args.has_key("use_dpdk")) {
- return mpmd_xport_ctrl_base::uptr(new mpmd_xport_ctrl_dpdk_udp(mb_args));
- }
-#endif
- return mpmd_xport_ctrl_base::uptr(new mpmd_xport_ctrl_udp(mb_args));
-#ifdef HAVE_LIBERIO
- } else if (xport_medium == "liberio") {
- return mpmd_xport_ctrl_base::uptr(new mpmd_xport_ctrl_liberio(mb_args));
-#endif
- } else {
- UHD_LOG_WARNING(
- "MPMD", "Cannot instantiate transport medium " << xport_medium);
- return nullptr;
- }
- }
-
- //! This will try to make _xport_ctrls contain a valid transport manager
- // for \p xport_medium
- //
- // When this function returns, it will be possible to access
- // this->_xport_ctrls[xport_medium].
- //
- // \param xport_medium Type of transport, e.g. "UDP", "liberio", ...
- //
- // \throws uhd::key_error if \p xport_medium is not known or registered
- void require_xport_mgr(const std::string& xport_medium)
- {
- if (_xport_ctrls.count(xport_medium) == 0) {
- UHD_LOG_TRACE(
- "MPMD", "Instantiating transport manager `" << xport_medium << "'");
- auto mgr_impl = make_mgr_impl(xport_medium, _mb_args);
- if (mgr_impl) {
- _xport_ctrls[xport_medium] = std::move(mgr_impl);
- }
- }
- }
-
- /**************************************************************************
- * Private attributes
- *************************************************************************/
- //! Cache available xport manager implementations
- //
- // Should only every be populated by require_xport_mgr()
- std::unordered_map<std::string, mpmd_xport_ctrl_base::uptr> _xport_ctrls;
-
- //! Motherboard args, can contain things like 'recv_buff_size'
- const uhd::device_addr_t _mb_args;
-};
-
-mpmd_xport_mgr::uptr mpmd_xport_mgr::make(const uhd::device_addr_t& mb_args)
-{
- return mpmd_xport_mgr::uptr(new mpmd_xport_mgr_impl(mb_args));
-}
diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt
index a19a6a4e8..1bd71dab4 100644
--- a/host/lib/usrp/x300/CMakeLists.txt
+++ b/host/lib/usrp/x300/CMakeLists.txt
@@ -16,23 +16,23 @@
if(ENABLE_X300)
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/x300_claim.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/x300_radio_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_radio_control.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_uart.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_dac_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_eth_mgr.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_eeprom_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_eeprom.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/x300_mboard_type.hpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_mboard_type.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_pcie_mgr.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_controller.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_prop_tree.cpp
${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c
)
endif(ENABLE_X300)
diff --git a/host/lib/usrp/x300/x300_conn_mgr.hpp b/host/lib/usrp/x300/x300_conn_mgr.hpp
index 8aca2eb06..9ad870dfd 100644
--- a/host/lib/usrp/x300/x300_conn_mgr.hpp
+++ b/host/lib/usrp/x300/x300_conn_mgr.hpp
@@ -8,9 +8,12 @@
#define INCLUDED_X300_CONN_MGR_HPP
#include <uhd/transport/if_addrs.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/types/direction.hpp>
#include <uhd/types/wb_iface.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhdlib/rfnoc/rfnoc_common.hpp>
+#include <uhdlib/transport/links.hpp>
#include <string>
namespace uhd { namespace usrp { namespace x300 {
@@ -28,6 +31,14 @@ public:
virtual uhd::wb_iface::sptr get_ctrl_iface() = 0;
virtual size_t get_mtu(uhd::direction_t dir) = 0;
+
+ virtual std::vector<uhd::rfnoc::device_id_t> get_local_device_ids() = 0;
+
+ virtual uhd::transport::both_links_t get_links(uhd::transport::link_type_t link_type,
+ const uhd::rfnoc::device_id_t local_device_id,
+ const uhd::rfnoc::sep_id_t& local_epid,
+ const uhd::rfnoc::sep_id_t& remote_epid,
+ const uhd::device_addr_t& link_args) = 0;
};
}}} // namespace uhd::usrp::x300
diff --git a/host/lib/usrp/x300/x300_defaults.hpp b/host/lib/usrp/x300/x300_defaults.hpp
index 752298aff..ae10bf243 100644
--- a/host/lib/usrp/x300/x300_defaults.hpp
+++ b/host/lib/usrp/x300/x300_defaults.hpp
@@ -7,7 +7,7 @@
#ifndef INCLUDED_X300_DEFAULTS_HPP
#define INCLUDED_X300_DEFAULTS_HPP
-#include "../device3/device3_impl.hpp"
+#include <uhd/transport/udp_simple.hpp> //mtu
#include <string>
#include <vector>
diff --git a/host/lib/usrp/x300/x300_eth_mgr.cpp b/host/lib/usrp/x300/x300_eth_mgr.cpp
index 3a71080ae..b1d9f40ee 100644
--- a/host/lib/usrp/x300/x300_eth_mgr.cpp
+++ b/host/lib/usrp/x300/x300_eth_mgr.cpp
@@ -6,21 +6,26 @@
#include "x300_eth_mgr.hpp"
#include "x300_claim.hpp"
+#include "x300_defaults.hpp"
#include "x300_fw_common.h"
#include "x300_mb_eeprom.hpp"
#include "x300_mb_eeprom_iface.hpp"
#include "x300_regs.hpp"
#include <uhd/exception.hpp>
+#include <uhd/rfnoc/defaults.hpp>
#include <uhd/transport/if_addrs.hpp>
#include <uhd/transport/udp_constants.hpp>
#include <uhd/transport/udp_simple.hpp>
#include <uhd/transport/udp_zero_copy.hpp>
-#include <uhd/transport/zero_copy_recv_offload.hpp>
-#ifdef HAVE_DPDK
-# include <uhdlib/transport/dpdk_simple.hpp>
-# include <uhdlib/transport/dpdk_zero_copy.hpp>
-#endif
+#include <uhd/utils/byteswap.hpp>
+#include <uhdlib/rfnoc/device_id.hpp>
+#include <uhdlib/transport/inline_io_service.hpp>
+#include <uhdlib/transport/udp_boost_asio_link.hpp>
#include <uhdlib/usrp/cores/i2c_core_100_wb32.hpp>
+//#ifdef HAVE_DPDK
+//# include <uhdlib/transport/dpdk_simple.hpp>
+//# include <uhdlib/transport/dpdk_zero_copy.hpp>
+//#endif
#include <boost/asio.hpp>
#include <string>
@@ -29,6 +34,7 @@ uhd::wb_iface::sptr x300_make_ctrl_iface_enet(
using namespace uhd;
using namespace uhd::usrp;
+using namespace uhd::rfnoc;
using namespace uhd::transport;
using namespace uhd::usrp::x300;
namespace asio = boost::asio;
@@ -47,12 +53,12 @@ constexpr size_t ETH_DATA_NUM_FRAMES = 32;
constexpr size_t ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; // bytes
constexpr size_t MAX_RATE_10GIGE = (size_t)( // bytes/s
10e9 / 8 * // wire speed multiplied by percentage of packets that is sample data
- (float(x300::DATA_FRAME_MAX_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN)
+ (float(x300::DATA_FRAME_MAX_SIZE - CHDR_MAX_LEN_HDR)
/ float(x300::DATA_FRAME_MAX_SIZE
+ 8 /* UDP header */ + 20 /* Ethernet header length */)));
constexpr size_t MAX_RATE_1GIGE = (size_t)( // bytes/s
10e9 / 8 * // wire speed multiplied by percentage of packets that is sample data
- (float(GE_DATA_FRAME_RECV_SIZE - uhd::usrp::DEVICE3_TX_MAX_HDR_LEN)
+ (float(GE_DATA_FRAME_RECV_SIZE - CHDR_MAX_LEN_HDR)
/ float(GE_DATA_FRAME_RECV_SIZE
+ 8 /* UDP header */ + 20 /* Ethernet header length */)));
@@ -67,15 +73,15 @@ eth_manager::udp_simple_factory_t eth_manager::x300_get_udp_factory(
{
udp_simple_factory_t udp_make_connected = udp_simple::make_connected;
if (args.has_key("use_dpdk")) {
-#ifdef HAVE_DPDK
- udp_make_connected = [](const std::string& addr, const std::string& port) {
- auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
- return dpdk_simple::make_connected(ctx, addr, port);
- };
-#else
+//#ifdef HAVE_DPDK
+// udp_make_connected = [](const std::string& addr, const std::string& port) {
+// auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
+// return dpdk_simple::make_connected(ctx, addr, port);
+// };
+//#else
UHD_LOG_WARNING(
"DPDK", "Detected use_dpdk argument, but DPDK support not built in.");
-#endif
+//#endif
}
return udp_make_connected;
}
@@ -86,14 +92,14 @@ device_addrs_t eth_manager::find(const device_addr_t& hint)
udp_simple_factory_t udp_make_connected = x300_get_udp_factory(hint);
#ifdef HAVE_DPDK
if (hint.has_key("use_dpdk")) {
- auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
- if (not dpdk_ctx.is_init_done()) {
- dpdk_ctx.init(hint);
- }
- udp_make_broadcast = [](const std::string& addr, const std::string& port) {
- auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
- return dpdk_simple::make_broadcast(ctx, addr, port);
- };
+// auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
+// if (not dpdk_ctx.is_init_done()) {
+// dpdk_ctx.init(hint);
+// }
+// udp_make_broadcast = [](const std::string& addr, const std::string& port) {
+// auto& ctx = uhd::transport::uhd_dpdk_ctx::get();
+// return dpdk_simple::make_broadcast(ctx, addr, port);
+// };
}
#endif
udp_simple::sptr comm =
@@ -190,7 +196,9 @@ eth_manager::eth_manager(const x300_device_args_t& args,
// In discover_eth(), we'll check and enable the other IP address, if given
x300_eth_conn_t init;
init.addr = args.get_first_addr();
- eth_conns.push_back(init);
+ auto device_id = allocate_device_id();
+ _local_device_ids.push_back(device_id);
+ eth_conns[device_id] = init;
_x300_make_udp_connected = x300_get_udp_factory(dev_addr);
@@ -198,182 +206,144 @@ eth_manager::eth_manager(const x300_device_args_t& args,
_tree = tree->subtree(root_path);
}
-both_xports_t eth_manager::make_transport(both_xports_t xports,
- const uhd::usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& args,
- const size_t send_mtu,
- const size_t recv_mtu,
- std::function<uhd::sid_t(uint32_t, uint32_t)>&& allocate_sid)
+both_links_t eth_manager::get_links(link_type_t link_type,
+ const device_id_t local_device_id,
+ const sep_id_t& /*local_epid*/,
+ const sep_id_t& /*remote_epid*/,
+ const device_addr_t& link_args)
{
- zero_copy_xport_params default_buff_args;
- xports.endianness = ENDIANNESS_BIG;
- xports.lossless = false;
- xports.recv = nullptr;
-
- size_t& next_src_addr = xport_type == uhd::usrp::device3_impl::TX_DATA
- ? _next_tx_src_addr
- : xport_type == uhd::usrp::device3_impl::RX_DATA
- ? _next_rx_src_addr
- : _next_src_addr;
-
- // Decide on the IP/Interface pair based on the endpoint index
- x300_eth_conn_t conn = eth_conns[next_src_addr];
- const uint32_t xbar_src_addr = next_src_addr == 0 ? x300::SRC_ADDR0 : x300::SRC_ADDR1;
- const uint32_t xbar_src_dst = conn.type == X300_IFACE_ETH0 ? x300::XB_DST_E0
- : x300::XB_DST_E1;
-
- // Do not increment src addr for tx_data by default, using dual ethernet
- // with the DMA FIFO causes sequence errors to DMA FIFO bandwidth
- // limitations.
- if (xport_type != uhd::usrp::device3_impl::TX_DATA
- || _args.get_enable_tx_dual_eth()) {
- next_src_addr = (next_src_addr + 1) % eth_conns.size();
+ if (std::find(_local_device_ids.cbegin(), _local_device_ids.cend(), local_device_id)
+ == _local_device_ids.cend()) {
+ throw uhd::runtime_error(
+ std::string("[X300] Cannot create Ethernet link through local device ID ")
+ + std::to_string(local_device_id)
+ + ", no such device associated with this motherboard!");
}
+ // FIXME: We now need to properly associate local_device_id with the right
+ // entry in eth_conn. We should probably do the load balancing elsewhere,
+ // and do something like this:
+ // However, we might also have to make sure that we don't do 2x TX through
+ // a DMA FIFO, which is a device-specific thing. So punt on that for now.
+
+ x300_eth_conn_t conn = eth_conns[local_device_id];
+ zero_copy_xport_params default_buff_args;
+
+ const size_t send_mtu = get_mtu(uhd::TX_DIRECTION);
+ const size_t recv_mtu = get_mtu(uhd::RX_DIRECTION);
- xports.send_sid = allocate_sid(xbar_src_addr, xbar_src_dst);
- xports.recv_sid = xports.send_sid.reversed();
// Set size and number of frames
default_buff_args.send_frame_size = std::min(send_mtu, ETH_MSG_FRAME_SIZE);
default_buff_args.recv_frame_size = std::min(recv_mtu, ETH_MSG_FRAME_SIZE);
if (_args.get_use_dpdk()) {
-#ifdef HAVE_DPDK
- auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
-
- default_buff_args.num_recv_frames = ETH_MSG_NUM_FRAMES;
- default_buff_args.num_send_frames = ETH_MSG_NUM_FRAMES;
- if (xport_type == uhd::usrp::device3_impl::CTRL) {
- // Increasing number of recv frames here because ctrl_iface uses it
- // to determine how many control packets can be in flight before it
- // must wait for an ACK
- default_buff_args.num_recv_frames =
- uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
- } else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
- size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
- ? GE_DATA_FRAME_SEND_SIZE
- : XGE_DATA_FRAME_SEND_SIZE;
- default_buff_args.send_frame_size = args.cast<size_t>(
- "send_frame_size", std::min(default_frame_size, send_mtu));
- default_buff_args.num_send_frames =
- args.cast<size_t>("num_send_frames", default_buff_args.num_send_frames);
- default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0);
- } else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
- size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
- ? GE_DATA_FRAME_RECV_SIZE
- : XGE_DATA_FRAME_RECV_SIZE;
- default_buff_args.recv_frame_size = args.cast<size_t>(
- "recv_frame_size", std::min(default_frame_size, recv_mtu));
- default_buff_args.num_recv_frames =
- args.cast<size_t>("num_recv_frames", default_buff_args.num_recv_frames);
- default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0);
- }
-
- int dpdk_port_id = dpdk_ctx.get_route(conn.addr);
- if (dpdk_port_id < 0) {
- throw uhd::runtime_error(
- "Could not find a DPDK port with route to " + conn.addr);
- }
- auto recv = transport::dpdk_zero_copy::make(dpdk_ctx,
- (const unsigned int)dpdk_port_id,
- conn.addr,
- BOOST_STRINGIZE(X300_VITA_UDP_PORT),
- "0",
- default_buff_args,
- uhd::device_addr_t());
-
- xports.recv = recv; // Note: This is a type cast!
- xports.send = xports.recv;
- xports.recv_buff_size =
- (default_buff_args.recv_frame_size - X300_UDP_RESERVED_FRAME_SIZE)
- * default_buff_args.num_recv_frames;
- xports.send_buff_size =
- (default_buff_args.send_frame_size - X300_UDP_RESERVED_FRAME_SIZE)
- * default_buff_args.num_send_frames;
- UHD_LOG_TRACE("BUFF",
- "num_recv_frames="
- << default_buff_args.num_recv_frames
- << ", num_send_frames=" << default_buff_args.num_send_frames
- << ", recv_frame_size=" << default_buff_args.recv_frame_size
- << ", send_frame_size=" << default_buff_args.send_frame_size);
-
-#else
+//#ifdef HAVE_DPDK
+ // auto& dpdk_ctx = uhd::transport::uhd_dpdk_ctx::get();
+
+ // default_buff_args.num_recv_frames = ETH_MSG_NUM_FRAMES;
+ // default_buff_args.num_send_frames = ETH_MSG_NUM_FRAMES;
+ // if (link_type == link_type_t::CTRL) {
+ //// Increasing number of recv frames here because ctrl_iface uses it
+ //// to determine how many control packets can be in flight before it
+ //// must wait for an ACK
+ // default_buff_args.num_recv_frames =
+ // uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
+ //} else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
+ // size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ //? GE_DATA_FRAME_SEND_SIZE
+ //: XGE_DATA_FRAME_SEND_SIZE;
+ // default_buff_args.send_frame_size = args.cast<size_t>(
+ //"send_frame_size", std::min(default_frame_size, send_mtu));
+ // default_buff_args.num_send_frames =
+ // args.cast<size_t>("num_send_frames", default_buff_args.num_send_frames);
+ // default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0);
+ //} else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
+ // size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ //? GE_DATA_FRAME_RECV_SIZE
+ //: XGE_DATA_FRAME_RECV_SIZE;
+ // default_buff_args.recv_frame_size = args.cast<size_t>(
+ //"recv_frame_size", std::min(default_frame_size, recv_mtu));
+ // default_buff_args.num_recv_frames =
+ // args.cast<size_t>("num_recv_frames", default_buff_args.num_recv_frames);
+ // default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0);
+ //}
+
+ // int dpdk_port_id = dpdk_ctx.get_route(conn.addr);
+ // if (dpdk_port_id < 0) {
+ // throw uhd::runtime_error(
+ //"Could not find a DPDK port with route to " + conn.addr);
+ //}
+ // auto recv = transport::dpdk_zero_copy::make(dpdk_ctx,
+ //(const unsigned int)dpdk_port_id,
+ // conn.addr,
+ // BOOST_STRINGIZE(X300_VITA_UDP_PORT),
+ //"0",
+ // default_buff_args,
+ // uhd::device_addr_t());
+
+//#else
UHD_LOG_WARNING("X300", "Cannot create DPDK transport, falling back to UDP");
-#endif
+//#endif
}
- if (!xports.recv) {
- // Buffering is done in the socket buffers, so size them relative to
- // the link rate
- default_buff_args.send_buff_size = conn.link_rate / 50; // 20ms
- default_buff_args.recv_buff_size = std::max(conn.link_rate / 50,
- ETH_MSG_NUM_FRAMES * ETH_MSG_FRAME_SIZE); // enough to hold greater of 20ms or
- // number of msg frames
- // There is no need for more than 1 send and recv frame since the
- // buffering is done in the socket buffers
- default_buff_args.num_send_frames = 1;
- default_buff_args.num_recv_frames = 1;
- if (xport_type == uhd::usrp::device3_impl::CTRL) {
- // Increasing number of recv frames here because ctrl_iface uses it
- // to determine how many control packets can be in flight before it
- // must wait for an ACK
- default_buff_args.num_recv_frames =
- uhd::rfnoc::CMD_FIFO_SIZE / uhd::rfnoc::MAX_CMD_PKT_SIZE;
- } else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
- size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
- ? GE_DATA_FRAME_SEND_SIZE
- : XGE_DATA_FRAME_SEND_SIZE;
- default_buff_args.send_frame_size = args.cast<size_t>(
- "send_frame_size", std::min(default_frame_size, send_mtu));
- default_buff_args.num_send_frames =
- args.cast<size_t>("num_send_frames", default_buff_args.num_send_frames);
- default_buff_args.send_buff_size =
- args.cast<size_t>("send_buff_size", default_buff_args.send_buff_size);
- } else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
- size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
- ? GE_DATA_FRAME_RECV_SIZE
- : XGE_DATA_FRAME_RECV_SIZE;
- default_buff_args.recv_frame_size = args.cast<size_t>(
- "recv_frame_size", std::min(default_frame_size, recv_mtu));
- // set some buffers so the offload thread actually offloads the
- // socket I/O
- default_buff_args.num_recv_frames = args.cast<size_t>("num_recv_frames", 2);
- default_buff_args.recv_buff_size =
- args.cast<size_t>("recv_buff_size", default_buff_args.recv_buff_size);
- }
- // make a new transport - fpga has no idea how to talk to us on this yet
- udp_zero_copy::buff_params buff_params;
- xports.recv = udp_zero_copy::make(conn.addr,
- BOOST_STRINGIZE(X300_VITA_UDP_PORT),
- default_buff_args,
- buff_params);
-
- // Create a threaded transport for the receive chain only
- if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
- xports.recv = zero_copy_recv_offload::make(
- xports.recv, x300::RECV_OFFLOAD_BUFFER_TIMEOUT);
- }
-
- xports.send = xports.recv;
-
- // For the UDP transport the buffer size is the size of the socket buffer
- // in the kernel
- xports.recv_buff_size = buff_params.recv_buff_size;
- xports.send_buff_size = buff_params.send_buff_size;
+ // Buffering is done in the socket buffers, so size them relative to
+ // the link rate
+ default_buff_args.send_buff_size = conn.link_rate / 50; // 20ms
+ default_buff_args.recv_buff_size = std::max(conn.link_rate / 50,
+ ETH_MSG_NUM_FRAMES * ETH_MSG_FRAME_SIZE); // enough to hold greater of 20ms or
+ // number of msg frames
+ // There is no need for more than 1 send and recv frame since the
+ // buffering is done in the socket buffers
+ default_buff_args.num_send_frames = 1; // or 2?
+ default_buff_args.num_recv_frames = 1;
+ if (link_type == link_type_t::CTRL) {
+ // Increasing number of recv frames here because ctrl_iface uses it
+ // to determine how many control packets can be in flight before it
+ // must wait for an ACK
+ // FIXME this is no longer true, find a good value
+ default_buff_args.num_recv_frames = 85; // 256/3
+ } else if (link_type == link_type_t::TX_DATA) {
+ size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_SEND_SIZE
+ : XGE_DATA_FRAME_SEND_SIZE;
+ default_buff_args.send_frame_size = link_args.cast<size_t>(
+ "send_frame_size", std::min(default_frame_size, send_mtu));
+ default_buff_args.num_send_frames = link_args.cast<size_t>(
+ "num_send_frames", default_buff_args.num_send_frames);
+ default_buff_args.send_buff_size = link_args.cast<size_t>(
+ "send_buff_size", default_buff_args.send_buff_size);
+ } else if (link_type == link_type_t::RX_DATA) {
+ size_t default_frame_size = conn.link_rate == MAX_RATE_1GIGE
+ ? GE_DATA_FRAME_RECV_SIZE
+ : XGE_DATA_FRAME_RECV_SIZE;
+ default_buff_args.recv_frame_size = link_args.cast<size_t>(
+ "recv_frame_size", std::min(default_frame_size, recv_mtu));
+ // set some buffers so the offload thread actually offloads the
+ // socket I/O
+ default_buff_args.num_recv_frames =
+ link_args.cast<size_t>("num_recv_frames", 2);
+ default_buff_args.recv_buff_size = link_args.cast<size_t>(
+ "recv_buff_size", default_buff_args.recv_buff_size);
}
- // send a mini packet with SID into the ZPU
- // ZPU will reprogram the ethernet framer
- UHD_LOGGER_DEBUG("X300") << "programming packet for new xport on " << conn.addr
- << " sid " << xports.send_sid;
- // YES, get a __send__ buffer from the __recv__ socket
- //-- this is the only way to program the framer for recv:
- managed_send_buffer::sptr buff = xports.recv->get_send_buff();
- buff->cast<uint32_t*>()[0] = 0; // eth dispatch looks for != 0
- buff->cast<uint32_t*>()[1] = uhd::htonx(xports.send_sid.get());
- buff->commit(8);
- buff.reset();
-
- return xports;
+ /* FIXME: Should have common infrastructure for creating I/O services */
+ auto io_srv = uhd::transport::inline_io_service::make();
+ link_params_t link_params;
+ link_params.num_recv_frames = default_buff_args.num_recv_frames;
+ link_params.num_send_frames = default_buff_args.num_send_frames;
+ link_params.recv_frame_size = default_buff_args.recv_frame_size;
+ link_params.send_frame_size = default_buff_args.send_frame_size;
+ link_params.recv_buff_size = default_buff_args.recv_buff_size;
+ link_params.send_buff_size = default_buff_args.send_buff_size;
+
+ size_t recv_buff_size, send_buff_size;
+ auto link = uhd::transport::udp_boost_asio_link::make(conn.addr,
+ BOOST_STRINGIZE(X300_VITA_UDP_PORT),
+ link_params,
+ recv_buff_size,
+ send_buff_size);
+ io_srv->attach_send_link(link);
+ io_srv->attach_recv_link(link);
+ return std::make_tuple(io_srv, link, send_buff_size, link, recv_buff_size, true);
}
/******************************************************************************
@@ -390,7 +360,7 @@ void eth_manager::init_link(
{
double link_max_rate = 0.0;
- // Discover ethernet interfaces
+ // Discover ethernet interfaces on the device
discover_eth(mb_eeprom, loaded_fpga_image);
/* This is an ETH connection. Figure out what the maximum supported frame
@@ -428,9 +398,9 @@ void eth_manager::init_link(
determine_max_frame_size(get_pri_eth().addr, req_max_frame_size);
_max_frame_sizes = pri_frame_sizes;
- if (eth_conns.size() > 1) {
+ if (_local_device_ids.size() > 1) {
frame_size_t sec_frame_sizes =
- determine_max_frame_size(eth_conns.at(1).addr, req_max_frame_size);
+ determine_max_frame_size(eth_conns.at(_local_device_ids.at(1)).addr, req_max_frame_size);
// Choose the minimum of the max frame sizes
// to ensure we don't exceed any one of the links' MTU
@@ -469,7 +439,8 @@ void eth_manager::init_link(
}
// Check frame sizes
- for (auto conn : eth_conns) {
+ for (auto conn_pair : eth_conns) {
+ auto conn = conn_pair.second;
link_max_rate += conn.link_rate;
size_t rec_send_frame_size = conn.link_rate == MAX_RATE_1GIGE
@@ -527,14 +498,14 @@ void eth_manager::discover_eth(
ip_addrs.push_back(_args.get_second_addr());
}
- // Clear any previous addresses added
- eth_conns.clear();
+ // Grab the device ID used during init
+ auto init_dev_id = _local_device_ids.at(0);
// Index the MB EEPROM addresses
std::vector<std::string> mb_eeprom_addrs;
const size_t num_mb_eeprom_addrs = 4;
for (size_t i = 0; i < num_mb_eeprom_addrs; i++) {
- const std::string key = "ip-addr" + boost::to_string(i);
+ const std::string key = "ip-addr" + std::to_string(i);
// Show a warning if there exists duplicate addresses in the mboard eeprom
if (std::find(mb_eeprom_addrs.begin(), mb_eeprom_addrs.end(), mb_eeprom[key])
@@ -630,13 +601,20 @@ void eth_manager::discover_eth(
str(boost::format("X300 Initialization Error: Invalid address %s")
% conn_iface.addr));
}
- eth_conns.push_back(conn_iface);
+ if (conn_iface.addr == eth_conns.at(init_dev_id).addr) {
+ eth_conns[init_dev_id] = conn_iface;
+ } else {
+ auto device_id = allocate_device_id();
+ _local_device_ids.push_back(device_id);
+ eth_conns[device_id] = conn_iface;
+ }
}
}
- if (eth_conns.size() == 0)
+ if (eth_conns.size() == 0) {
throw uhd::assertion_error(
- "X300 Initialization Error: No ethernet interfaces specified.");
+ "X300 Initialization Error: No valid Ethernet interfaces specified.");
+ }
}
eth_manager::frame_size_t eth_manager::determine_max_frame_size(
diff --git a/host/lib/usrp/x300/x300_eth_mgr.hpp b/host/lib/usrp/x300/x300_eth_mgr.hpp
index 022e14bbd..1f4013e17 100644
--- a/host/lib/usrp/x300/x300_eth_mgr.hpp
+++ b/host/lib/usrp/x300/x300_eth_mgr.hpp
@@ -10,13 +10,14 @@
#include "x300_conn_mgr.hpp"
#include "x300_device_args.hpp"
#include "x300_mboard_type.hpp"
+#include <uhd/property_tree.hpp>
#include <uhd/transport/if_addrs.hpp>
#include <uhd/transport/udp_constants.hpp>
#include <uhd/transport/udp_simple.hpp> //mtu
-#include <uhd/transport/udp_zero_copy.hpp>
#include <uhd/types/device_addr.hpp>
-#include <uhdlib/rfnoc/xports.hpp>
+#include <uhd/types/direction.hpp>
#include <functional>
+#include <map>
#include <vector>
namespace uhd { namespace usrp { namespace x300 {
@@ -52,12 +53,20 @@ public:
*/
void release_ctrl_iface(std::function<void(void)>&& release_fn);
- both_xports_t make_transport(both_xports_t xports,
- const uhd::usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& args,
- const size_t send_mtu,
- const size_t recv_mtu,
- std::function<uhd::sid_t(uint32_t, uint32_t)>&& allocate_sid);
+ /*! Return the list of local device IDs associated with this link
+ *
+ * Note: this will only be valid after init_link() is called.
+ */
+ std::vector<uhd::rfnoc::device_id_t> get_local_device_ids()
+ {
+ return _local_device_ids;
+ }
+
+ uhd::transport::both_links_t get_links(uhd::transport::link_type_t link_type,
+ const uhd::rfnoc::device_id_t local_device_id,
+ const uhd::rfnoc::sep_id_t& local_epid,
+ const uhd::rfnoc::sep_id_t& remote_epid,
+ const uhd::device_addr_t& link_args);
private:
//! Function to create a udp_simple::sptr (kernel-based or DPDK-based)
@@ -87,7 +96,7 @@ private:
// Get the primary ethernet connection
inline const x300_eth_conn_t& get_pri_eth() const
{
- return eth_conns[0];
+ return eth_conns.at(_local_device_ids.at(0));
}
static udp_simple_factory_t x300_get_udp_factory(const device_addr_t& args);
@@ -111,15 +120,14 @@ private:
udp_simple_factory_t _x300_make_udp_connected;
- std::vector<x300_eth_conn_t> eth_conns;
- size_t _next_src_addr = 0;
- size_t _next_tx_src_addr = 0;
- size_t _next_rx_src_addr = 0;
+ std::map<uhd::rfnoc::device_id_t, x300_eth_conn_t> eth_conns;
frame_size_t _max_frame_sizes;
uhd::device_addr_t recv_args;
uhd::device_addr_t send_args;
+
+ std::vector<uhd::rfnoc::device_id_t> _local_device_ids;
};
}}} // namespace uhd::usrp::x300
diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h
index 45301640a..a966bcd13 100644
--- a/host/lib/usrp/x300/x300_fw_common.h
+++ b/host/lib/usrp/x300/x300_fw_common.h
@@ -23,7 +23,7 @@ extern "C" {
#define X300_REVISION_MIN 2
#define X300_FW_COMPAT_MAJOR 6
#define X300_FW_COMPAT_MINOR 0
-#define X300_FPGA_COMPAT_MAJOR 0x24
+#define X300_FPGA_COMPAT_MAJOR 0x25
//shared memory sections - in between the stack and the program space
#define X300_FW_SHMEM_BASE 0x6000
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index 223b112ec..d1d7b43f7 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -10,19 +10,16 @@
#include "x300_claim.hpp"
#include "x300_eth_mgr.hpp"
#include "x300_mb_eeprom.hpp"
+#include "x300_mb_controller.hpp"
#include "x300_mb_eeprom_iface.hpp"
#include "x300_mboard_type.hpp"
#include "x300_pcie_mgr.hpp"
#include <uhd/transport/if_addrs.hpp>
-#include <uhd/types/sid.hpp>
-#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/utils/log.hpp>
-#include <uhd/utils/math.hpp>
#include <uhd/utils/paths.hpp>
#include <uhd/utils/safe_call.hpp>
#include <uhd/utils/static.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/make_shared.hpp>
+#include <uhdlib/rfnoc/device_id.hpp>
#include <chrono>
#include <fstream>
#include <thread>
@@ -35,6 +32,17 @@ using namespace uhd::rfnoc;
using namespace uhd::usrp::x300;
namespace asio = boost::asio;
+namespace uhd { namespace usrp { namespace x300 {
+
+void init_prop_tree(
+ const size_t mb_idx, uhd::rfnoc::x300_mb_controller* mbc, property_tree::sptr pt);
+
+}}} // namespace uhd::usrp::x300
+
+
+const uhd::rfnoc::chdr::chdr_packet_factory x300_impl::_pkt_factory(
+ CHDR_W_64, ENDIANNESS_BIG);
+
/***********************************************************************
* Discovery over the udp and pcie transport
@@ -168,10 +176,10 @@ static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string& file_nam
UHD_LOGGER_INFO("X300") << "Firmware loaded!";
}
-x300_impl::x300_impl(const uhd::device_addr_t& dev_addr) : device3_impl(), _sid_framer(0)
+x300_impl::x300_impl(const uhd::device_addr_t& dev_addr)
+ : rfnoc_device()
{
UHD_LOGGER_INFO("X300") << "X300 initialization sequence...";
- _tree->create<std::string>("/name").set("X-Series Device");
const device_addrs_t device_args = separate_device_addr(dev_addr);
_mb.resize(device_args.size());
@@ -216,6 +224,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
mb.send_args[key] = dev_addr[key];
}
+ mb.device_id = allocate_device_id();
+ UHD_LOG_DEBUG(
+ "X300", "Motherboard " << mb_i << " has remote device ID: " << mb.device_id);
+
UHD_LOGGER_DEBUG("X300") << "Setting up basic communication...";
if (mb.xport_path == xport_path_t::NIRIO) {
mb.conn_mgr = std::make_shared<pcie_manager>(mb.args, _tree, mb_path);
@@ -243,9 +255,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
this->check_fpga_compat(mb_path, mb);
this->check_fw_compat(mb_path, mb);
- mb.fw_regmap = boost::make_shared<fw_regmap_t>();
- mb.fw_regmap->initialize(*mb.zpu_ctrl.get(), true);
-
// store which FPGA image is loaded
mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl);
@@ -256,28 +265,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
mb.zpu_i2c->set_clock_rate(x300::BUS_CLOCK_RATE / 2);
////////////////////////////////////////////////////////////////////
- // print network routes mapping
- ////////////////////////////////////////////////////////////////////
- /*
- const uint32_t routes_addr = mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE,
- X300_FW_SHMEM_ROUTE_MAP_ADDR)); const uint32_t routes_len =
- mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_ROUTE_MAP_LEN));
- UHD_VAR(routes_len);
- for (size_t i = 0; i < routes_len; i+=1)
- {
- const uint32_t node_addr = mb.zpu_ctrl->peek32(SR_ADDR(routes_addr, i*2+0));
- const uint32_t nbor_addr = mb.zpu_ctrl->peek32(SR_ADDR(routes_addr, i*2+1));
- if (node_addr != 0 and nbor_addr != 0)
- {
- UHD_LOGGER_INFO("X300") << boost::format("%u: %s -> %s")
- % i
- % asio::ip::address_v4(node_addr).to_string()
- % asio::ip::address_v4(nbor_addr).to_string();
- }
- }
- */
-
- ////////////////////////////////////////////////////////////////////
// setup the mboard eeprom
////////////////////////////////////////////////////////////////////
UHD_LOGGER_DEBUG("X300") << "Loading values from EEPROM...";
@@ -322,8 +309,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
"reprogramming.");
}
}
- _tree->create<std::string>(mb_path / "name").set(product_name);
- _tree->create<std::string>(mb_path / "codename").set("Yetti");
////////////////////////////////////////////////////////////////////
// discover interfaces, frame sizes, and link rates
@@ -344,190 +329,56 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
// create clock control objects
////////////////////////////////////////////////////////////////////
UHD_LOGGER_DEBUG("X300") << "Setting up RF frontend clocking...";
-
- // Initialize clock control registers. NOTE: This does not configure the LMK yet.
+ // Initialize clock control registers.
+ // NOTE: This does not configure the LMK yet.
mb.clock = x300_clock_ctrl::make(mb.zpu_spi,
1 /*slaveno*/,
mb.hw_rev,
mb.args.get_master_clock_rate(),
mb.args.get_dboard_clock_rate(),
mb.args.get_system_ref_rate());
- mb.fw_regmap->ref_freq_reg.write(
- fw_regmap_t::ref_freq_reg_t::REF_FREQ, uint32_t(mb.args.get_system_ref_rate()));
-
- // Initialize clock source to use internal reference and generate
- // a valid radio clock. This may change after configuration is done.
- // This will configure the LMK and wait for lock
- update_clock_source(mb, mb.args.get_clock_source());
////////////////////////////////////////////////////////////////////
- // create clock properties
+ // create motherboard controller
////////////////////////////////////////////////////////////////////
- _tree->create<double>(mb_path / "master_clock_rate").set_publisher([&mb]() {
- return mb.clock->get_master_clock_rate();
- });
+ // Now we have all the peripherals, create the MB controller. It will also
+ // initialize the clock source, and the time source.
+ auto mb_ctrl = std::make_shared<x300_mb_controller>(
+ mb.hw_rev, product_name, mb.zpu_i2c, mb.zpu_ctrl, mb.clock, mb_eeprom, mb.args);
+ register_mb_controller(mb_i, mb_ctrl);
+ // Clock should be up now!
UHD_LOGGER_INFO("X300") << "Radio 1x clock: "
<< (mb.clock->get_master_clock_rate() / 1e6) << " MHz";
////////////////////////////////////////////////////////////////////
- // Create the GPSDO control
- ////////////////////////////////////////////////////////////////////
- static constexpr uint32_t dont_look_for_gpsdo = 0x1234abcdul;
-
- // otherwise if not disabled, look for the internal GPSDO
- if (mb.zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS))
- != dont_look_for_gpsdo) {
- UHD_LOG_DEBUG("X300", "Detecting internal GPSDO....");
- try {
- // gps_ctrl will print its own log statements if a GPSDO was found
- mb.gps = gps_ctrl::make(x300_make_uart_iface(mb.zpu_ctrl));
- } catch (std::exception& e) {
- UHD_LOGGER_ERROR("X300")
- << "An error occurred making GPSDO control: " << e.what();
- }
- if (mb.gps and mb.gps->gps_detected()) {
- for (const std::string& name : mb.gps->get_sensors()) {
- _tree->create<sensor_value_t>(mb_path / "sensors" / name)
- .set_publisher([&mb, name]() { return mb.gps->get_sensor(name); });
- }
- } else {
- mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS),
- dont_look_for_gpsdo);
- }
- }
-
- ////////////////////////////////////////////////////////////////////
- // setup time sources and properties
- ////////////////////////////////////////////////////////////////////
- _tree->create<std::string>(mb_path / "time_source" / "value")
- .set(mb.args.get_time_source())
- .add_coerced_subscriber([this, &mb](const std::string& time_source) {
- this->update_time_source(mb, time_source);
- });
- _tree->create<std::vector<std::string>>(mb_path / "time_source" / "options")
- .set(TIME_SOURCE_OPTIONS);
-
- // setup the time output, default to ON
- _tree->create<bool>(mb_path / "time_source" / "output")
- .add_coerced_subscriber([this, &mb](const bool time_output) {
- this->set_time_source_out(mb, time_output);
- })
- .set(true);
-
- ////////////////////////////////////////////////////////////////////
- // setup clock sources and properties
+ // setup properties
////////////////////////////////////////////////////////////////////
- _tree->create<std::string>(mb_path / "clock_source" / "value")
- .set(mb.args.get_clock_source())
- .add_coerced_subscriber([this, &mb](const std::string& clock_source) {
- this->update_clock_source(mb, clock_source);
- });
- _tree->create<std::vector<std::string>>(mb_path / "clock_source" / "options")
- .set(CLOCK_SOURCE_OPTIONS);
-
- // setup external reference options. default to 10 MHz input reference
- _tree->create<std::string>(mb_path / "clock_source" / "external");
- _tree
- ->create<std::vector<double>>(
- mb_path / "clock_source" / "external" / "freq" / "options")
- .set(x300::EXTERNAL_FREQ_OPTIONS);
- _tree->create<double>(mb_path / "clock_source" / "external" / "value")
- .set(mb.clock->get_sysref_clock_rate());
- // FIXME the external clock source settings need to be more robust
-
- // setup the clock output, default to ON
- _tree->create<bool>(mb_path / "clock_source" / "output")
- .add_coerced_subscriber(
- [&mb](const bool clock_output) { mb.clock->set_ref_out(clock_output); });
-
- // Initialize tick rate (must be done before setting time)
- // Note: The master tick rate can't be changed at runtime!
- const double master_clock_rate = mb.clock->get_master_clock_rate();
- _tree->create<double>(mb_path / "tick_rate")
- .set_coercer([master_clock_rate](const double rate) {
- // The contract of multi_usrp::set_master_clock_rate() is to coerce
- // and not throw, so we'll follow that behaviour here.
- if (!uhd::math::frequencies_are_equal(rate, master_clock_rate)) {
- UHD_LOGGER_WARNING("X300")
- << "Cannot update master clock rate! X300 Series does not "
- "allow changing the clock rate during runtime.";
- }
- return master_clock_rate;
- })
- .add_coerced_subscriber([this](const double) { this->update_tx_streamers(); })
- .add_coerced_subscriber([this](const double) { this->update_rx_streamers(); })
- .set(master_clock_rate);
+ init_prop_tree(mb_i, mb_ctrl.get(), _tree);
////////////////////////////////////////////////////////////////////
- // and do the misc mboard sensors
+ // RFNoC Stuff
////////////////////////////////////////////////////////////////////
- _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked")
- .set_publisher([this, &mb]() { return this->get_ref_locked(mb); });
-
- //////////////// RFNOC /////////////////
- const size_t n_rfnoc_blocks = mb.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_NUM_CE));
- enumerate_rfnoc_blocks(mb_i,
- n_rfnoc_blocks,
- x300::XB_DST_PCI + 1, /* base port */
- uhd::sid_t(x300::SRC_ADDR0, 0, x300::DST_ADDR + mb_i, 0),
- dev_addr);
- //////////////// RFNOC /////////////////
-
- // If we have a radio, we must configure its codec control:
- const std::string radio_blockid_hint = str(boost::format("%d/Radio") % mb_i);
- std::vector<rfnoc::block_id_t> radio_ids =
- find_blocks<rfnoc::x300_radio_ctrl_impl>(radio_blockid_hint);
- if (not radio_ids.empty()) {
- if (radio_ids.size() > 2) {
- UHD_LOGGER_WARNING("X300")
- << "Too many Radio Blocks found. Using only the first two.";
- radio_ids.resize(2);
- }
-
- for (const rfnoc::block_id_t& id : radio_ids) {
- rfnoc::x300_radio_ctrl_impl::sptr radio(
- get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id));
- mb.radios.push_back(radio);
- radio->setup_radio(mb.zpu_i2c,
- mb.clock,
- mb.args.get_ignore_cal_file(),
- mb.args.get_self_cal_adc_delay());
- }
-
- ////////////////////////////////////////////////////////////////////
- // ADC test and cal
- ////////////////////////////////////////////////////////////////////
- if (mb.args.get_self_cal_adc_delay()) {
- rfnoc::x300_radio_ctrl_impl::self_cal_adc_xfer_delay(mb.radios,
- mb.clock,
- [this, &mb](const double timeout) {
- return this->wait_for_clk_locked(
- mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout);
- },
- true /* Apply ADC delay */);
- }
- if (mb.args.get_ext_adc_self_test()) {
- rfnoc::x300_radio_ctrl_impl::extended_adc_test(
- mb.radios, mb.args.get_ext_adc_self_test_duration());
- } else {
- for (size_t i = 0; i < mb.radios.size(); i++) {
- mb.radios.at(i)->self_test_adc();
- }
+ // Set the remote device ID
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), mb.device_id);
+ // Configure the CHDR port number in the dispatcher
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0 + 8 + 3)), X300_VITA_UDP_PORT);
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT1 + 8 + 3)), X300_VITA_UDP_PORT);
+ // Peek to finish transaction
+ mb.zpu_ctrl->peek32(0);
+
+ { // Need to lock access to _mb_ifaces, so we can run setup_mb() in
+ // parallel
+ std::lock_guard<std::mutex> l(_mb_iface_mutex);
+ _mb_ifaces.insert({mb_i,
+ x300_mb_iface(mb.conn_mgr, mb.clock->get_master_clock_rate(), mb.device_id)});
+ UHD_LOG_DEBUG("X300", "Motherboard " << mb_i << " has local device IDs: ");
+ for (const auto local_dev_id : _mb_ifaces.at(mb_i).get_local_device_ids()) {
+ UHD_LOG_DEBUG("X300", "* " << local_dev_id);
}
+ } // End of locked section
- ////////////////////////////////////////////////////////////////////
- // Synchronize times (dboard initialization can desynchronize them)
- ////////////////////////////////////////////////////////////////////
- if (radio_ids.size() == 2) {
- this->sync_times(mb, mb.radios[0]->get_time_now());
- }
-
- } else {
- UHD_LOGGER_INFO("X300") << "No Radio Block found. Assuming radio-less operation.";
- } /* end of radio block(s) initialization */
-
- mb.initialization_done = true;
+ mb_ctrl->set_initialization_done();
}
x300_impl::~x300_impl(void)
@@ -548,283 +399,6 @@ x300_impl::~x300_impl(void)
}
}
-uhd::both_xports_t x300_impl::make_transport(const uhd::sid_t& address,
- const xport_type_t xport_type,
- const uhd::device_addr_t& args)
-{
- const size_t mb_index = address.get_dst_addr() - x300::DST_ADDR;
- mboard_members_t& mb = _mb[mb_index];
- both_xports_t xports;
-
- // Calculate MTU based on MTU in args and device limitations
- const size_t send_mtu = args.cast<size_t>("mtu",
- get_mtu(mb_index, uhd::TX_DIRECTION));
- const size_t recv_mtu = args.cast<size_t>("mtu",
- get_mtu(mb_index, uhd::RX_DIRECTION));
-
- if (mb.xport_path == xport_path_t::NIRIO) {
- xports.send_sid =
- this->allocate_sid(mb, address, x300::SRC_ADDR0, x300::XB_DST_PCI);
- xports.recv_sid = xports.send_sid.reversed();
- return std::dynamic_pointer_cast<pcie_manager>(mb.conn_mgr)
- ->make_transport(xports, xport_type, args, send_mtu, recv_mtu);
- } else if (mb.xport_path == xport_path_t::ETH) {
- xports = std::dynamic_pointer_cast<eth_manager>(mb.conn_mgr)
- ->make_transport(xports,
- xport_type,
- args,
- send_mtu,
- recv_mtu,
- [this, &mb, address](
- const uint32_t src_addr, const uint32_t src_dst) {
- return this->allocate_sid(mb, address, src_addr, src_dst);
- });
-
- // reprogram the ethernet dispatcher's udp port (should be safe to always set)
- UHD_LOGGER_TRACE("X300")
- << "reprogram the ethernet dispatcher's udp port to " << X300_VITA_UDP_PORT;
- mb.zpu_ctrl->poke32(
- SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT0 + 8 + 3)), X300_VITA_UDP_PORT);
- mb.zpu_ctrl->poke32(
- SR_ADDR(SET0_BASE, (ZPU_SR_ETHINT1 + 8 + 3)), X300_VITA_UDP_PORT);
-
- // Do a peek to an arbitrary address to guarantee that the
- // ethernet framer has been programmed before we return.
- mb.zpu_ctrl->peek32(0);
-
- return xports;
- }
- UHD_THROW_INVALID_CODE_PATH();
-}
-
-
-uhd::sid_t x300_impl::allocate_sid(mboard_members_t& mb,
- const uhd::sid_t& address,
- const uint32_t src_addr,
- const uint32_t src_dst)
-{
- uhd::sid_t sid = address;
- sid.set_src_addr(src_addr);
- sid.set_src_endpoint(_sid_framer++); // increment for next setup
-
- // TODO Move all of this setup_mb()
- // Program the X300 to recognise it's own local address.
- mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), address.get_dst_addr());
- // Program CAM entry for outgoing packets matching a X300 resource (for example a
- // Radio) This type of packet matches the XB_LOCAL address and is looked up in the
- // upper half of the CAM
- mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 256 + address.get_dst_endpoint()),
- address.get_dst_xbarport());
- // Program CAM entry for returning packets to us (for example GR host via Eth0)
- // This type of packet does not match the XB_LOCAL address and is looked up in the
- // lower half of the CAM
- mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 0 + src_addr), src_dst);
-
- UHD_LOGGER_TRACE("X300") << "done router config for sid " << sid;
-
- return sid;
-}
-
-/***********************************************************************
- * clock and time control logic
- **********************************************************************/
-void x300_impl::set_time_source_out(mboard_members_t& mb, const bool enb)
-{
- mb.fw_regmap->clock_ctrl_reg.write(
- fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb ? 1 : 0);
-}
-
-void x300_impl::update_clock_source(mboard_members_t& mb, const std::string& source)
-{
- // Optimize for the case when the current source is internal and we are trying
- // to set it to internal. This is the only case where we are guaranteed that
- // the clock has not gone away so we can skip setting the MUX and reseting the LMK.
- const bool reconfigure_clks = (mb.current_refclk_src != "internal")
- or (source != "internal");
- if (reconfigure_clks) {
- // Update the clock MUX on the motherboard to select the requested source
- if (source == "internal") {
- mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
- fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL);
- mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 1);
- } else if (source == "external") {
- mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
- fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL);
- mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);
- } else if (source == "gpsdo") {
- mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
- fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO);
- mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);
- } else {
- throw uhd::key_error("update_clock_source: unknown source: " + source);
- }
- mb.fw_regmap->clock_ctrl_reg.flush();
-
- // Reset the LMK to make sure it re-locks to the new reference
- mb.clock->reset_clocks();
- }
-
- // Wait for the LMK to lock (always, as a sanity check that the clock is useable)
- //* Currently the LMK can take as long as 30 seconds to lock to a reference but we
- // don't
- //* want to wait that long during initialization.
- // TODO: Need to verify timeout and settings to make sure lock can be achieved in
- // < 1.0 seconds
- double timeout = mb.initialization_done ? 30.0 : 1.0;
-
- // The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may
- // lead to locking issues. So, disable the ref-locked check for older (unsupported)
- // boards.
- if (mb.hw_rev > 4) {
- if (not wait_for_clk_locked(
- mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout)) {
- // failed to lock on reference
- if (mb.initialization_done) {
- throw uhd::runtime_error(
- (boost::format("Reference Clock PLL failed to lock to %s source.")
- % source)
- .str());
- } else {
- // TODO: Re-enable this warning when we figure out a reliable lock time
- // UHD_LOGGER_WARNING("X300") << "Reference clock failed to lock to " +
- // source + " during device initialization. " <<
- // "Check for the lock before operation or ignore this warning if using
- // another clock source." ;
- }
- }
- }
-
- if (reconfigure_clks) {
- // Reset the radio clock PLL in the FPGA
- mb.zpu_ctrl->poke32(
- SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL);
- mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
-
- // Wait for radio clock PLL to lock
- if (not wait_for_clk_locked(
- mb, fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK, 0.01)) {
- throw uhd::runtime_error(
- (boost::format("Reference Clock PLL in FPGA failed to lock to %s source.")
- % source)
- .str());
- }
-
- // Reset the IDELAYCTRL used to calibrate the data interface delays
- mb.zpu_ctrl->poke32(
- SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL);
- mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
-
- // Wait for the ADC IDELAYCTRL to be ready
- if (not wait_for_clk_locked(
- mb, fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK, 0.01)) {
- throw uhd::runtime_error(
- (boost::format(
- "ADC Calibration Clock in FPGA failed to lock to %s source.")
- % source)
- .str());
- }
-
- // Reset ADCs and DACs
- for (rfnoc::x300_radio_ctrl_impl::sptr r : mb.radios) {
- r->reset_codec();
- }
- }
-
- // Update cache value
- mb.current_refclk_src = source;
-}
-
-void x300_impl::update_time_source(mboard_members_t& mb, const std::string& source)
-{
- if (source == "internal") {
- mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
- fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL);
- } else if (source == "external") {
- mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
- fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL);
- } else if (source == "gpsdo") {
- mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
- fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO);
- } else {
- throw uhd::key_error("update_time_source: unknown source: " + source);
- }
-
- /* TODO - Implement intelligent PPS detection
- //check for valid pps
- if (!is_pps_present(mb)) {
- throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please
- check the PPS source and try again.") % source).str());
- }
- */
-}
-
-void x300_impl::sync_times(mboard_members_t& mb, const uhd::time_spec_t& t)
-{
- std::vector<rfnoc::block_id_t> radio_ids =
- find_blocks<rfnoc::x300_radio_ctrl_impl>("Radio");
- for (const rfnoc::block_id_t& id : radio_ids) {
- get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id)->set_time_sync(t);
- }
-
- mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0);
- mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 1);
- mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0);
-}
-
-bool x300_impl::wait_for_clk_locked(mboard_members_t& mb, uint32_t which, double timeout)
-{
- const auto timeout_time = std::chrono::steady_clock::now()
- + std::chrono::milliseconds(int64_t(timeout * 1000));
- do {
- if (mb.fw_regmap->clock_status_reg.read(which) == 1) {
- return true;
- }
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
- } while (std::chrono::steady_clock::now() < timeout_time);
-
- // Check one last time
- return (mb.fw_regmap->clock_status_reg.read(which) == 1);
-}
-
-sensor_value_t x300_impl::get_ref_locked(mboard_members_t& mb)
-{
- mb.fw_regmap->clock_status_reg.refresh();
- const bool lock =
- (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::LMK_LOCK) == 1)
- && (mb.fw_regmap->clock_status_reg.get(
- fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK)
- == 1)
- && (mb.fw_regmap->clock_status_reg.get(
- fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK)
- == 1);
- return sensor_value_t("Ref", lock, "locked", "unlocked");
-}
-
-bool x300_impl::is_pps_present(mboard_members_t& mb)
-{
- // The ZPU_RB_CLK_STATUS_PPS_DETECT bit toggles with each rising edge of the PPS.
- // We monitor it for up to 1.5 seconds looking for it to toggle.
- uint32_t pps_detect =
- mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT);
- for (int i = 0; i < 15; i++) {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- if (pps_detect
- != mb.fw_regmap->clock_status_reg.read(
- fw_regmap_t::clk_status_reg_t::PPS_DETECT))
- return true;
- }
- return false;
-}
-
-/***********************************************************************
- * Frame size detection
- **********************************************************************/
-size_t x300_impl::get_mtu(const size_t mb_index, const uhd::direction_t dir)
-{
- auto& mb = _mb.at(mb_index);
- return mb.conn_mgr->get_mtu(dir);
-}
-
/***********************************************************************
* compat checks
**********************************************************************/
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index 2ae586a5d..600d224a5 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -14,41 +14,64 @@
#include "x300_defaults.hpp"
#include "x300_device_args.hpp"
#include "x300_fw_common.h"
+#include "x300_mb_controller.hpp"
#include "x300_mboard_type.hpp"
-#include "x300_radio_ctrl_impl.hpp"
#include "x300_regs.hpp"
+#include <uhd/property_tree.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/types/sensors.hpp>
#include <uhd/types/wb_iface.hpp>
-#include <uhd/usrp/gps_ctrl.hpp>
#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhdlib/rfnoc/chdr_types.hpp>
+#include <uhdlib/rfnoc/clock_iface.hpp>
+#include <uhdlib/rfnoc/mb_iface.hpp>
+#include <uhdlib/rfnoc/mgmt_portal.hpp>
+#include <uhdlib/rfnoc/rfnoc_common.hpp>
+#include <uhdlib/rfnoc/rfnoc_device.hpp>
+#include <uhdlib/transport/links.hpp>
#include <uhdlib/usrp/cores/i2c_core_100_wb32.hpp>
+#include <uhdlib/usrp/cores/spi_core_3000.hpp>
#include <atomic>
+#include <functional>
#include <memory>
+#include <mutex>
+
uhd::device_addrs_t x300_find(const uhd::device_addr_t& hint_);
-class x300_impl : public uhd::usrp::device3_impl
+class x300_impl : public uhd::rfnoc::detail::rfnoc_device
{
public:
x300_impl(const uhd::device_addr_t&);
void setup_mb(const size_t which, const uhd::device_addr_t&);
~x300_impl(void);
-protected:
- void subdev_to_blockid(const uhd::usrp::subdev_spec_pair_t& spec,
- const size_t mb_i,
- uhd::rfnoc::block_id_t& block_id,
- uhd::device_addr_t& block_args);
- uhd::usrp::subdev_spec_pair_t blockid_to_subdev(
- const uhd::rfnoc::block_id_t& blockid, const uhd::device_addr_t& block_args);
+ /**************************************************************************
+ * rfnoc_device API
+ *************************************************************************/
+ virtual uhd::rfnoc::mb_iface& get_mb_iface(const size_t mb_idx)
+ {
+ if (mb_idx >= _mb_ifaces.size()) {
+ throw uhd::index_error(
+ std::string("Cannot get mb_iface, invalid motherboard index: ")
+ + std::to_string(mb_idx));
+ }
+ return _mb_ifaces.at(mb_idx);
+ }
private:
+ /**************************************************************************
+ * Types
+ *************************************************************************/
// vector of member objects per motherboard
struct mboard_members_t
{
uhd::usrp::x300::x300_device_args_t args;
+ //! Remote Device ID for this motherboard
+ uhd::rfnoc::device_id_t device_id;
+
bool initialization_done = false;
uhd::task::sptr claimer_task;
uhd::usrp::x300::xport_path_t xport_path;
@@ -62,9 +85,6 @@ private:
// other perifs on mboard
x300_clock_ctrl::sptr clock;
- uhd::gps_ctrl::sptr gps;
-
- uhd::usrp::x300::fw_regmap_t::sptr fw_regmap;
// which FPGA image is loaded
std::string loaded_fpga_image;
@@ -72,46 +92,66 @@ private:
size_t hw_rev;
std::string current_refclk_src;
- std::vector<uhd::rfnoc::x300_radio_ctrl_impl::sptr> radios;
-
uhd::usrp::x300::conn_manager::sptr conn_mgr;
};
- std::vector<mboard_members_t> _mb;
-
- std::atomic<size_t> _sid_framer;
-
- uhd::sid_t allocate_sid(mboard_members_t& mb,
- const uhd::sid_t& address,
- const uint32_t src_addr,
- const uint32_t src_dst);
- uhd::both_xports_t make_transport(const uhd::sid_t& address,
- const xport_type_t xport_type,
- const uhd::device_addr_t& args);
- //! get mtu
- size_t get_mtu(const size_t, const uhd::direction_t);
-
- bool _ignore_cal_file;
-
- void update_clock_control(mboard_members_t&);
- void initialize_clock_control(mboard_members_t& mb);
- void set_time_source_out(mboard_members_t&, const bool);
- void update_clock_source(mboard_members_t&, const std::string&);
- void update_time_source(mboard_members_t&, const std::string&);
- void sync_times(mboard_members_t&, const uhd::time_spec_t&);
-
- uhd::sensor_value_t get_ref_locked(mboard_members_t& mb);
- bool wait_for_clk_locked(mboard_members_t& mb, uint32_t which, double timeout);
- bool is_pps_present(mboard_members_t& mb);
+ //! X300-Specific Implementation of rfnoc::mb_iface
+ class x300_mb_iface : public uhd::rfnoc::mb_iface
+ {
+ public:
+ x300_mb_iface(uhd::usrp::x300::conn_manager::sptr conn_mgr,
+ const double radio_clk_freq,
+ const uhd::rfnoc::device_id_t remote_dev_id);
+ ~x300_mb_iface();
+ uint16_t get_proto_ver();
+ uhd::rfnoc::chdr_w_t get_chdr_w();
+ uhd::endianness_t get_endianness(const uhd::rfnoc::device_id_t local_device_id);
+ uhd::rfnoc::device_id_t get_remote_device_id();
+ std::vector<uhd::rfnoc::device_id_t> get_local_device_ids();
+ uhd::transport::adapter_id_t get_adapter_id(const uhd::rfnoc::device_id_t local_device_id);
+ void reset_network();
+ uhd::rfnoc::clock_iface::sptr get_clock_iface(const std::string& clock_name);
+ uhd::rfnoc::chdr_ctrl_xport::sptr make_ctrl_transport(
+ uhd::rfnoc::device_id_t local_device_id,
+ const uhd::rfnoc::sep_id_t& local_epid);
+ uhd::rfnoc::chdr_rx_data_xport::uptr make_rx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args);
+ uhd::rfnoc::chdr_tx_data_xport::uptr make_tx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args);
+
+ private:
+ const uhd::rfnoc::device_id_t _remote_dev_id;
+ std::unordered_map<uhd::rfnoc::device_id_t, uhd::transport::adapter_id_t> _adapter_map;
+ uhd::rfnoc::clock_iface::sptr _bus_clk;
+ uhd::rfnoc::clock_iface::sptr _radio_clk;
+ uhd::usrp::x300::conn_manager::sptr _conn_mgr;
+ };
+ /**************************************************************************
+ * Private Methods
+ *************************************************************************/
void check_fw_compat(const uhd::fs_path& mb_path, const mboard_members_t& members);
void check_fpga_compat(const uhd::fs_path& mb_path, const mboard_members_t& members);
- /// More IO stuff
- uhd::device_addr_t get_tx_hints(size_t mb_index);
- uhd::device_addr_t get_rx_hints(size_t mb_index);
+ /**************************************************************************
+ * Private Attributes
+ *************************************************************************/
+ std::vector<mboard_members_t> _mb;
+
+ std::mutex _mb_iface_mutex;
+ std::unordered_map<size_t, x300_mb_iface> _mb_ifaces;
- void post_streamer_hooks(uhd::direction_t dir);
+ static const uhd::rfnoc::chdr::chdr_packet_factory _pkt_factory;
};
#endif /* INCLUDED_X300_IMPL_HPP */
diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp
deleted file mode 100644
index 07e93173a..000000000
--- a/host/lib/usrp/x300/x300_io_impl.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// Copyright 2013-2014 Ettus Research LLC
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "x300_impl.hpp"
-#include "x300_regs.hpp"
-
-using namespace uhd;
-using namespace uhd::usrp;
-
-/***********************************************************************
- * Hooks for get_tx_stream() and get_rx_stream()
- **********************************************************************/
-device_addr_t x300_impl::get_rx_hints(size_t mb_index)
-{
- device_addr_t rx_hints = _mb[mb_index].recv_args;
- return rx_hints;
-}
-
-
-device_addr_t x300_impl::get_tx_hints(size_t mb_index)
-{
- device_addr_t tx_hints = _mb[mb_index].send_args;
- return tx_hints;
-}
-
-void x300_impl::post_streamer_hooks(direction_t dir)
-{
- if (dir != TX_DIRECTION) {
- return;
- }
-
- // Loop through all tx streamers. Find all radios connected to one
- // streamer. Sync those.
- for (const boost::weak_ptr<uhd::tx_streamer>& streamer_w : _tx_streamers.vals()) {
- const boost::shared_ptr<device3_send_packet_streamer> streamer =
- boost::dynamic_pointer_cast<device3_send_packet_streamer>(streamer_w.lock());
- if (not streamer) {
- continue;
- }
-
- std::vector<rfnoc::x300_radio_ctrl_impl::sptr> radio_ctrl_blks =
- streamer->get_terminator()
- ->find_downstream_node<rfnoc::x300_radio_ctrl_impl>();
- try {
- // UHD_LOGGER_INFO("X300") << "[X300] syncing " << radio_ctrl_blks.size() << "
- // radios " ;
- rfnoc::x300_radio_ctrl_impl::synchronize_dacs(radio_ctrl_blks);
- } catch (const uhd::io_error& ex) {
- throw uhd::io_error(
- str(boost::format("Failed to sync DACs! %s ") % ex.what()));
- }
- }
-}
-
-// vim: sw=4 expandtab:
diff --git a/host/lib/usrp/x300/x300_mb_controller.cpp b/host/lib/usrp/x300/x300_mb_controller.cpp
index 9762f486d..fd70526ab 100644
--- a/host/lib/usrp/x300/x300_mb_controller.cpp
+++ b/host/lib/usrp/x300/x300_mb_controller.cpp
@@ -5,34 +5,751 @@
//
#include "x300_mb_controller.hpp"
+#include "x300_fw_common.h"
+#include "x300_regs.hpp"
+#include <uhd/exception.hpp>
+#include <uhdlib/utils/narrow.hpp>
+#include <chrono>
+#include <thread>
+uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
+
+using namespace uhd;
using namespace uhd::rfnoc;
+using namespace uhd::usrp::x300;
+using namespace std::chrono_literals;
+
+namespace {
+constexpr uint32_t DONT_LOOK_FOR_GPSDO = 0x1234abcdul;
+
+constexpr uint32_t ADC_SELF_TEST_DURATION = 100; // ms
+
+// When these regs are fixed, there is another fixme below to actually init the
+// timekeepers
+constexpr uint32_t TK_NUM_TIMEKEEPERS = 12; //Read-only
+constexpr uint32_t TK_REG_BASE = 100;
+constexpr uint32_t TK_REG_OFFSET = 48;
+constexpr uint32_t TK_REG_TICKS_NOW_LO = 0x00; // Read-only
+constexpr uint32_t TK_REG_TICKS_NOW_HI = 0x04; // Read-only
+constexpr uint32_t TK_REG_TICKS_EVENT_LO = 0x08; // Write-only
+constexpr uint32_t TK_REG_TICKS_EVENT_HI = 0x0C; // Write-only
+constexpr uint32_t TK_REG_TICKS_CTRL = 0x10; // Write-only
+constexpr uint32_t TK_REG_TICKS_PPS_LO = 0x14; // Read-only
+constexpr uint32_t TK_REG_TICKS_PPS_HI = 0x18; // Read-only
+constexpr uint32_t TK_REG_TICKS_PERIOD_LO = 0x1C; // Read-Write
+constexpr uint32_t TK_REG_TICKS_PERIOD_HI = 0x20; // Read-Write
+
+constexpr char LOG_ID[] = "X300::MB_CTRL";
+
+} // namespace
+
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+x300_mb_controller::x300_mb_controller(const size_t hw_rev,
+ const std::string product_name,
+ uhd::i2c_iface::sptr zpu_i2c,
+ uhd::wb_iface::sptr zpu_ctrl,
+ x300_clock_ctrl::sptr clock_ctrl,
+ uhd::usrp::mboard_eeprom_t mb_eeprom,
+ x300_device_args_t args)
+ : _hw_rev(hw_rev)
+ , _product_name(product_name)
+ , _zpu_i2c(zpu_i2c)
+ , _zpu_ctrl(zpu_ctrl)
+ , _clock_ctrl(clock_ctrl)
+ , _mb_eeprom(mb_eeprom)
+ , _args(args)
+{
+ _fw_regmap = std::make_shared<fw_regmap_t>();
+ _fw_regmap->initialize(*_zpu_ctrl.get(), true);
+ _fw_regmap->ref_freq_reg.write(
+ fw_regmap_t::ref_freq_reg_t::REF_FREQ, uint32_t(args.get_system_ref_rate()));
+ // Initialize clock source to generate a valid radio clock. This may change
+ // after configuration is done.
+ // This will configure the LMK and wait for lock
+ x300_mb_controller::set_clock_source(args.get_clock_source());
+ x300_mb_controller::set_time_source(args.get_time_source());
+ const size_t num_tks = _zpu_ctrl->peek32(SR_ADDR(SET0_BASE, TK_NUM_TIMEKEEPERS));
+ for (size_t i = 0; i < num_tks; i++) {
+ register_timekeeper(i, std::make_shared<x300_timekeeper>(i, _zpu_ctrl, clock_ctrl->get_master_clock_rate()));
+ }
+
+ init_gps();
+ _radio_refs.reserve(2);
+}
+
+x300_mb_controller::~x300_mb_controller() {}
+
+/******************************************************************************
+ * Timekeeper APIs
+ *****************************************************************************/
uint64_t x300_mb_controller::x300_timekeeper::get_ticks_now()
{
- // tbw
- return 0;
+ uint32_t ticks_lo = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_NOW_LO));
+ uint32_t ticks_hi = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_NOW_HI));
+ return uint64_t(ticks_lo) | (uint64_t(ticks_hi) << 32);
}
uint64_t x300_mb_controller::x300_timekeeper::get_ticks_last_pps()
{
- // tbw
- return 0;
+ uint32_t ticks_lo = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_PPS_LO));
+ uint32_t ticks_hi = _zpu_ctrl->peek32(get_tk_addr(TK_REG_TICKS_PPS_HI));
+ return uint64_t(ticks_lo) | (uint64_t(ticks_hi) << 32);
}
void x300_mb_controller::x300_timekeeper::set_ticks_now(const uint64_t ticks)
{
- // tbw
+ _zpu_ctrl->poke32(
+ get_tk_addr(TK_REG_TICKS_EVENT_LO), narrow_cast<uint32_t>(ticks & 0xFFFFFFFF));
+ _zpu_ctrl->poke32(
+ get_tk_addr(TK_REG_TICKS_EVENT_HI), narrow_cast<uint32_t>(ticks >> 32));
+ _zpu_ctrl->poke32(
+ get_tk_addr(TK_REG_TICKS_CTRL), narrow_cast<uint32_t>(0x1));
}
void x300_mb_controller::x300_timekeeper::set_ticks_next_pps(const uint64_t ticks)
{
- // tbw
+ _zpu_ctrl->poke32(
+ get_tk_addr(TK_REG_TICKS_EVENT_LO), narrow_cast<uint32_t>(ticks & 0xFFFFFFFF));
+ _zpu_ctrl->poke32(
+ get_tk_addr(TK_REG_TICKS_EVENT_HI), narrow_cast<uint32_t>(ticks >> 32));
+ _zpu_ctrl->poke32(
+ get_tk_addr(TK_REG_TICKS_CTRL), narrow_cast<uint32_t>(0x2));
}
void x300_mb_controller::x300_timekeeper::set_period(const uint64_t period_ns)
{
- // tbw
+ _zpu_ctrl->poke32(get_tk_addr(TK_REG_TICKS_PERIOD_LO),
+ narrow_cast<uint32_t>(period_ns & 0xFFFFFFFF));
+ _zpu_ctrl->poke32(get_tk_addr(TK_REG_TICKS_PERIOD_HI),
+ narrow_cast<uint32_t>(period_ns >> 32));
+}
+
+uint32_t x300_mb_controller::x300_timekeeper::get_tk_addr(const uint32_t tk_addr)
+{
+ return SR_ADDR(SET0_BASE, TK_REG_BASE + TK_REG_OFFSET * _tk_idx + tk_addr);
+}
+
+/******************************************************************************
+ * Motherboard Control API (see mb_controller.hpp)
+ *****************************************************************************/
+void x300_mb_controller::init()
+{
+ if (_radio_refs.empty()) {
+ UHD_LOG_WARNING(LOG_ID, "No radio registered! Skipping ADC checks.");
+ return;
+ }
+ // Check ADCs
+ if (_args.get_ext_adc_self_test()) {
+ extended_adc_test(_args.get_ext_adc_self_test_duration() / _radio_refs.size());
+ } else if (_args.get_self_cal_adc_delay()) {
+ constexpr bool apply_delay = true;
+ self_cal_adc_xfer_delay(apply_delay);
+ } else {
+ for (auto& radio : _radio_refs) {
+ radio->self_test_adc(ADC_SELF_TEST_DURATION);
+ }
+ }
+}
+
+std::string x300_mb_controller::get_mboard_name() const
+{
+ return _product_name;
+}
+
+void x300_mb_controller::set_time_source(const std::string& source)
+{
+ if (source == "internal") {
+ _fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
+ fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL);
+ } else if (source == "external") {
+ _fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
+ fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL);
+ } else if (source == "gpsdo") {
+ _fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT,
+ fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO);
+ } else {
+ throw uhd::key_error("update_time_source: unknown source: " + source);
+ }
+
+ /* TODO - Implement intelligent PPS detection
+ //check for valid pps
+ if (!is_pps_present(mb)) {
+ throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please
+ check the PPS source and try again.") % source).str());
+ }
+ */
+}
+
+std::string x300_mb_controller::get_time_source() const
+{
+ return _current_time_src;
+}
+
+std::vector<std::string> x300_mb_controller::get_time_sources() const
+{
+ return {"internal", "external", "gpsdo"};
+}
+
+void x300_mb_controller::set_clock_source(const std::string& source)
+{
+ UHD_LOG_TRACE("X300::MB_CTRL", "Setting clock source to " << source);
+ // Optimize for the case when the current source is internal and we are trying
+ // to set it to internal. This is the only case where we are guaranteed that
+ // the clock has not gone away so we can skip setting the MUX and reseting the LMK.
+ const bool reconfigure_clks = (_current_refclk_src != "internal")
+ or (source != "internal");
+ if (reconfigure_clks) {
+ // Update the clock MUX on the motherboard to select the requested source
+ if (source == "internal") {
+ _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
+ fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL);
+ _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 1);
+ } else if (source == "external") {
+ _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
+ fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL);
+ _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);
+ } else if (source == "gpsdo") {
+ _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE,
+ fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO);
+ _fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0);
+ } else {
+ throw uhd::key_error("set_clock_source: unknown source: " + source);
+ }
+ _fw_regmap->clock_ctrl_reg.flush();
+
+ // Reset the LMK to make sure it re-locks to the new reference
+ _clock_ctrl->reset_clocks();
+ }
+
+ // Wait for the LMK to lock (always, as a sanity check that the clock is useable)
+ //* Currently the LMK can take as long as 30 seconds to lock to a reference but we
+ // don't
+ //* want to wait that long during initialization.
+ // TODO: Need to verify timeout and settings to make sure lock can be achieved in
+ // < 1.0 seconds
+ double timeout = _initialization_done ? 30.0 : 1.0;
+
+ // The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may
+ // lead to locking issues. So, disable the ref-locked check for older (unsupported)
+ // boards.
+ if (_hw_rev > 4) {
+ if (not wait_for_clk_locked(fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout)) {
+ // failed to lock on reference
+ if (_initialization_done) {
+ throw uhd::runtime_error(
+ (boost::format("Reference Clock PLL failed to lock to %s source.")
+ % source)
+ .str());
+ } else {
+ // TODO: Re-enable this warning when we figure out a reliable lock time
+ // UHD_LOGGER_WARNING("X300::MB_CTRL") << "Reference clock failed to lock to " +
+ // source + " during device initialization. " <<
+ // "Check for the lock before operation or ignore this warning if using
+ // another clock source." ;
+ }
+ }
+ }
+
+ if (reconfigure_clks) {
+ // Reset the radio clock PLL in the FPGA
+ _zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL);
+ _zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
+
+ // Wait for radio clock PLL to lock
+ if (not wait_for_clk_locked(
+ fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK, 0.01)) {
+ throw uhd::runtime_error(
+ (boost::format("Reference Clock PLL in FPGA failed to lock to %s source.")
+ % source)
+ .str());
+ }
+
+ // Reset the IDELAYCTRL used to calibrate the data interface delays
+ _zpu_ctrl->poke32(
+ SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL);
+ _zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
+
+ // Wait for the ADC IDELAYCTRL to be ready
+ if (not wait_for_clk_locked(
+ fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK, 0.01)) {
+ throw uhd::runtime_error(
+ (boost::format(
+ "ADC Calibration Clock in FPGA failed to lock to %s source.")
+ % source)
+ .str());
+ }
+
+ // Reset ADCs and DACs
+ reset_codecs();
+ }
+
+ // Update cache value
+ _current_refclk_src = source;
+}
+
+std::string x300_mb_controller::get_clock_source() const
+{
+ return _current_refclk_src;
+}
+
+std::vector<std::string> x300_mb_controller::get_clock_sources() const
+{
+ return {"internal", "external", "gpsdo"};
+}
+
+void x300_mb_controller::set_sync_source(
+ const std::string& clock_source, const std::string& time_source)
+{
+ device_addr_t sync_args;
+ sync_args["clock_source"] = clock_source;
+ sync_args["time_source"] = time_source;
+ set_sync_source(sync_args);
+}
+
+void x300_mb_controller::set_sync_source(const device_addr_t& sync_source) {
+ if (sync_source.has_key("clock_source")) {
+ set_clock_source(sync_source["clock_source"]);
+ }
+ if (sync_source.has_key("time_source")) {
+ set_time_source(sync_source["time_source"]);
+ }
+}
+
+device_addr_t x300_mb_controller::get_sync_source() const
+{
+ const std::string clock_source = get_clock_source();
+ const std::string time_source = get_time_source();
+ device_addr_t sync_source;
+ sync_source["clock_source"] = clock_source;
+ sync_source["time_source"] = time_source;
+ return sync_source;
+}
+
+std::vector<device_addr_t> x300_mb_controller::get_sync_sources()
+{
+ const std::vector<std::pair<std::string, std::string>> clock_time_src_pairs = {
+ // Clock source, Time source
+ {"internal", "internal"},
+ {"external", "internal"},
+ {"external", "external"},
+ {"gpsdo", "gpsdo"},
+ {"gpsdo", "internal"}
+ };
+
+ // Now convert to vector of device_addr_t
+ std::vector<device_addr_t> sync_sources;
+ for (const auto& ct_pair : clock_time_src_pairs) {
+ device_addr_t sync_source;
+ sync_source["clock_source"] = ct_pair.first;
+ sync_source["time_source"] = ct_pair.second;
+ sync_sources.push_back(sync_source);
+ }
+ return sync_sources;
+}
+
+void x300_mb_controller::set_clock_source_out(const bool enb)
+{
+ _clock_ctrl->set_ref_out(enb);
}
+void x300_mb_controller::set_time_source_out(const bool enb)
+{
+ _fw_regmap->clock_ctrl_reg.write(
+ fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb ? 1 : 0);
+}
+
+sensor_value_t x300_mb_controller::get_sensor(const std::string& name)
+{
+ if (name == "ref_locked") {
+ return sensor_value_t("Ref", get_ref_locked(), "locked", "unlocked");
+ }
+ // There are only GPS sensors and ref_locked, so we can take a shortcut here
+ // and directly ask the GPS for its sensor value:
+ if (_sensors.count(name)) {
+ return _gps->get_sensor(name);
+ }
+ throw uhd::key_error(std::string("Invalid sensor name: ") + name);
+}
+
+std::vector<std::string> x300_mb_controller::get_sensor_names()
+{
+ return std::vector<std::string>(_sensors.cbegin(), _sensors.cend());
+}
+
+uhd::usrp::mboard_eeprom_t x300_mb_controller::get_eeprom()
+{
+ return _mb_eeprom;
+}
+
+bool x300_mb_controller::synchronize(std::vector<mb_controller::sptr>& mb_controllers,
+ const uhd::time_spec_t& time_spec,
+ const bool quiet)
+{
+ if (!mb_controller::synchronize(mb_controllers, time_spec, quiet)) {
+ return false;
+ }
+
+ std::vector<std::shared_ptr<x300_mb_controller>> mb_controller_copy;
+ mb_controller_copy.reserve(mb_controllers.size());
+ for (auto mb_controller : mb_controllers) {
+ if (std::dynamic_pointer_cast<x300_mb_controller>(mb_controller)) {
+ mb_controller_copy.push_back(
+ std::dynamic_pointer_cast<x300_mb_controller>(mb_controller));
+ }
+ }
+ // Now, mb_controller_copy contains only references of mb_controllers that
+ // are actually x300_mb_controllers
+ mb_controllers.clear();
+ for (auto mb_controller : mb_controller_copy) {
+ mb_controllers.push_back(mb_controller);
+ }
+
+ // Now we have the housekeeping out of the way, we can actually start
+ // synchronizing. The X300 needs to sync its DACs. First, we get a reference
+ // to all the radios (and thus to the DACs).
+ std::vector<uhd::usrp::x300::x300_radio_mbc_iface*> radios;
+ radios.reserve(2 * mb_controller_copy.size());
+ for (auto& mbc : mb_controller_copy) {
+ for (auto radio_ref : mbc->_radio_refs) {
+ radios.push_back(radio_ref);
+ }
+ }
+
+ UHD_LOG_TRACE(LOG_ID, "Running DAC sync on " << radios.size() << " radios.");
+
+ // **PRECONDITION**
+ // This function assumes that all the VITA times for "radios" are
+ // synchronized to a common reference, which we did earlier.
+
+ // Get a rough estimate of the cumulative command latency
+ auto t_start = std::chrono::steady_clock::now();
+ for (auto radio : radios) {
+ radio->get_adc_rx_word(); // Discard value. We are just timing the call
+ }
+ auto t_elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::steady_clock::now() - t_start);
+ // Add 100% of headroom + uncertainty to the command time
+ uint64_t t_sync_us = (t_elapsed.count() * 2) + 16000 /* Scheduler latency */;
+
+ const double radio_clk_rate = _clock_ctrl->get_master_clock_rate();
+ std::string err_str;
+ // Try to sync 3 times before giving up
+ constexpr size_t MAX_ATTEMPTS = 3;
+ for (size_t attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
+ try {
+ // Reinitialize and resync all DACs
+ for (auto radio : radios) {
+ radio->sync_dac();
+ }
+
+ // Make sure FRAMEP/N is 0
+ for (auto radio : radios) {
+ radio->set_dac_sync(false);
+ }
+
+ // Pick radios[0] as the time reference.
+ uhd::time_spec_t sync_time =
+ mb_controller_copy.front()->get_timekeeper(0)->get_time_now()
+ + uhd::time_spec_t(((double)t_sync_us) / 1e6);
+
+ // Send the sync command
+ for (auto radio : radios) {
+ // Arm FRAMEP/N sync pulse by asserting a rising edge
+ radio->set_dac_sync(true, sync_time);
+ }
+
+ // Reset FRAMEP/N to 0 after 2 clock cycles, and reset command time
+ for (auto radio : radios) {
+ radio->set_dac_sync(false, sync_time + (2.0 / radio_clk_rate));
+ }
+
+ // Wait and check status
+ std::this_thread::sleep_for(std::chrono::microseconds(t_sync_us));
+ for (auto radio : radios) {
+ radio->dac_verify_sync();
+ }
+
+ UHD_LOG_TRACE(LOG_ID, "DAC sync passed on attempt " << attempt);
+ return true;
+ } catch (const uhd::runtime_error& e) {
+ err_str = e.what();
+ RFNOC_LOG_DEBUG("Retrying DAC synchronization: " << err_str);
+ }
+ }
+ throw uhd::runtime_error(err_str);
+}
+
+/******************************************************************************
+ * Private Methods
+ *****************************************************************************/
+std::string x300_mb_controller::get_unique_id()
+{
+ return std::string("X300::MB_CTRL") + ""; // FIXME
+}
+
+void x300_mb_controller::init_gps()
+{
+ // otherwise if not disabled, look for the internal GPSDO
+ if (_zpu_ctrl->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS))
+ != DONT_LOOK_FOR_GPSDO) {
+ UHD_LOG_TRACE("X300::MB_CTRL", "Detecting internal GPSDO....");
+ try {
+ // gps_ctrl will print its own log statements if a GPSDO was found
+ _gps = gps_ctrl::make(x300_make_uart_iface(_zpu_ctrl));
+ } catch (std::exception& e) {
+ UHD_LOGGER_WARNING("X300::MB_CTRL")
+ << "An error occurred making GPSDO control: " << e.what()
+ << " Continuing without GPS.";
+ }
+ if (_gps and _gps->gps_detected()) {
+ auto sensors = _gps->get_sensors();
+ _sensors.insert(sensors.cbegin(), sensors.cend());
+ } else {
+ UHD_LOG_TRACE("X300::MB_CTRL",
+ "No GPS found, setting register to save time on next run.");
+ _zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_GPSDO_STATUS),
+ DONT_LOOK_FOR_GPSDO);
+ }
+ } else {
+ UHD_LOG_TRACE("X300::MB_CTRL",
+ "Not detecting internal GPSDO, previous run already failed to find it.");
+ }
+}
+
+void x300_mb_controller::reset_codecs()
+{
+ for (auto& callback : _reset_cbs) {
+ UHD_LOG_TRACE("X300::MB_CTRL", "Calling DAC/ADC reset callback");
+ callback();
+ }
+}
+
+bool x300_mb_controller::wait_for_clk_locked(uint32_t which, double timeout)
+{
+ const auto timeout_time = std::chrono::steady_clock::now()
+ + std::chrono::milliseconds(int64_t(timeout * 1000));
+ do {
+ if (_fw_regmap->clock_status_reg.read(which) == 1) {
+ return true;
+ }
+ std::this_thread::sleep_for(5ms);
+ } while (std::chrono::steady_clock::now() < timeout_time);
+
+ // Check one last time
+ return (_fw_regmap->clock_status_reg.read(which) == 1);
+}
+
+bool x300_mb_controller::is_pps_present()
+{
+ // The ZPU_RB_CLK_STATUS_PPS_DETECT bit toggles with each rising edge of the PPS.
+ // We monitor it for up to 1.5 seconds looking for it to toggle.
+ uint32_t pps_detect =
+ _fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT);
+ const auto timeout_time = std::chrono::steady_clock::now() + 1500ms;
+ while (std::chrono::steady_clock::now() < timeout_time) {
+ std::this_thread::sleep_for(100ms);
+ if (pps_detect
+ != _fw_regmap->clock_status_reg.read(
+ fw_regmap_t::clk_status_reg_t::PPS_DETECT))
+ return true;
+ }
+ return false;
+}
+
+bool x300_mb_controller::get_ref_locked()
+{
+ _fw_regmap->clock_status_reg.refresh();
+ return (_fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::LMK_LOCK)
+ == 1)
+ && (_fw_regmap->clock_status_reg.get(
+ fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK)
+ == 1)
+ && (_fw_regmap->clock_status_reg.get(
+ fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK)
+ == 1);
+}
+
+void x300_mb_controller::self_cal_adc_xfer_delay(bool apply_delay)
+{
+ UHD_LOG_INFO("X300", "Running ADC transfer delay self-cal: ");
+
+ // Effective resolution of the self-cal.
+ constexpr size_t NUM_DELAY_STEPS = 100;
+
+ double master_clk_period = (1.0e9 / _clock_ctrl->get_master_clock_rate()); // in ns
+ double delay_start = 0.0;
+ double delay_range = 2 * master_clk_period;
+ double delay_incr = delay_range / NUM_DELAY_STEPS;
+
+ double cached_clk_delay = _clock_ctrl->get_clock_delay(X300_CLOCK_WHICH_ADC0);
+ double fpga_clk_delay = _clock_ctrl->get_clock_delay(X300_CLOCK_WHICH_FPGA);
+
+ // Iterate through several values of delays and measure ADC data integrity
+ std::vector<std::pair<double, bool>> results;
+ for (size_t i = 0; i < NUM_DELAY_STEPS; i++) {
+ // Delay the ADC clock (will set both Ch0 and Ch1 delays)
+ double delay = _clock_ctrl->set_clock_delay(
+ X300_CLOCK_WHICH_ADC0, delay_incr * i + delay_start);
+ wait_for_clk_locked(fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1);
+
+ uint32_t err_code = 0;
+ for (auto& radio : _radio_refs) {
+ // Test each channel (I and Q) individually so as to not accidentally
+ // trigger on the data from the other channel if there is a swap
+
+ // -- Test I Channel --
+ // Put ADC in ramp test mode. Tie the other channel to all ones.
+ radio->set_adc_test_word("ramp", "ones");
+ // Turn on the pattern checker in the FPGA. It will lock when it sees a
+ // zero and count deviations from the expected value
+ radio->set_adc_checker_enabled(false);
+ radio->set_adc_checker_enabled(true);
+ // 50ms @ 200MHz = 10 million samples
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ if (radio->get_adc_checker_locked(true /* I */)) {
+ err_code += radio->get_adc_checker_error_code(true /* I */);
+ } else {
+ err_code += 100; // Increment error code by 100 to indicate no lock
+ }
+
+ // -- Test Q Channel --
+ // Put ADC in ramp test mode. Tie the other channel to all ones.
+ radio->set_adc_test_word("ones", "ramp");
+ // Turn on the pattern checker in the FPGA. It will lock when it sees a
+ // zero and count deviations from the expected value
+ radio->set_adc_checker_enabled(false);
+ radio->set_adc_checker_enabled(true);
+ // 50ms @ 200MHz = 10 million samples
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ if (radio->get_adc_checker_locked(false /* Q */)) {
+ err_code += radio->get_adc_checker_error_code(false /* Q */);
+ } else {
+ err_code += 100; // Increment error code by 100 to indicate no lock
+ }
+ }
+ UHD_LOG_TRACE(
+ LOG_ID, boost::format("XferDelay=%fns, Error=%d") % delay % err_code);
+ results.push_back(std::pair<double, bool>(delay, err_code == 0));
+ }
+
+ // Calculate the valid window
+ // When done win_start_idx will have the first delay value index that caused
+ // no errors, and win_stop_idx will have the last valid delay value index
+ int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1;
+ for (size_t i = 0; i < results.size(); i++) {
+ std::pair<double, bool>& item = results[i];
+ if (item.second) { // If data is stable
+ if (cur_start_idx == -1) { // This is the first window
+ cur_start_idx = i;
+ cur_stop_idx = i;
+ } else { // We are extending the window
+ cur_stop_idx = i;
+ }
+ } else {
+ if (cur_start_idx == -1) { // We haven't yet seen valid data
+ // Do nothing
+ } else if (win_start_idx == -1) { // We passed the first valid window
+ win_start_idx = cur_start_idx;
+ win_stop_idx = cur_stop_idx;
+ } else { // Update cached window if current window is larger
+ double cur_win_len =
+ results[cur_stop_idx].first - results[cur_start_idx].first;
+ double cached_win_len =
+ results[win_stop_idx].first - results[win_start_idx].first;
+ if (cur_win_len > cached_win_len) {
+ win_start_idx = cur_start_idx;
+ win_stop_idx = cur_stop_idx;
+ }
+ }
+ // Reset current window
+ cur_start_idx = -1;
+ cur_stop_idx = -1;
+ }
+ }
+ if (win_start_idx == -1) {
+ throw uhd::runtime_error(
+ "self_cal_adc_xfer_delay: Self calibration failed. Convergence error.");
+ }
+
+ double win_center =
+ (results[win_stop_idx].first + results[win_start_idx].first) / 2.0;
+ const double win_length = results[win_stop_idx].first - results[win_start_idx].first;
+ if (win_length < master_clk_period / 4) {
+ throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. "
+ "Valid window too narrow.");
+ }
+
+ // Cycle slip the relative delay by a clock cycle to prevent sample misalignment
+ // fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need
+ bool cycle_slip = (win_center - fpga_clk_delay >= master_clk_period);
+ if (cycle_slip) {
+ win_center -= master_clk_period;
+ }
+
+ if (apply_delay) {
+ // Apply delay
+ win_center = _clock_ctrl->set_clock_delay(
+ X300_CLOCK_WHICH_ADC0, win_center); // Sets ADC0 and ADC1
+ wait_for_clk_locked(fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1);
+ // Validate
+ for (auto radio_ref : _radio_refs) {
+ radio_ref->self_test_adc(2000);
+ }
+ } else {
+ // Restore delay
+ _clock_ctrl->set_clock_delay(
+ X300_CLOCK_WHICH_ADC0, cached_clk_delay); // Sets ADC0 and ADC1
+ }
+
+ // Teardown
+ for (auto& radio : _radio_refs) {
+ radio->set_adc_test_word("normal", "normal");
+ radio->set_adc_checker_enabled(false);
+ }
+ UHD_LOGGER_INFO(LOG_ID)
+ << (boost::format("ADC transfer delay self-cal done (FPGA->ADC=%.3fns%s, "
+ "Window=%.3fns)")
+ % (win_center - fpga_clk_delay) % (cycle_slip ? " +cyc" : "")
+ % win_length);
+}
+
+void x300_mb_controller::extended_adc_test(double duration_s)
+{
+ static const size_t SECS_PER_ITER = 5;
+ RFNOC_LOG_INFO(
+ boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...")
+ % duration_s % SECS_PER_ITER);
+
+ size_t num_iters = static_cast<size_t>(ceil(duration_s / SECS_PER_ITER));
+ size_t num_failures = 0;
+ for (size_t iter = 0; iter < num_iters; iter++) {
+ // Run self-test
+ RFNOC_LOG_INFO(
+ boost::format("Extended ADC Self-Test Iteration %06d... ") % (iter + 1));
+ try {
+ for (auto& radio : _radio_refs) {
+ radio->self_test_adc(SECS_PER_ITER * 1000);
+ }
+ RFNOC_LOG_INFO(boost::format("Extended ADC Self-Test Iteration %06d passed ")
+ % (iter + 1));
+ } catch (std::exception& e) {
+ num_failures++;
+ RFNOC_LOG_ERROR(e.what());
+ }
+ }
+ if (num_failures == 0) {
+ RFNOC_LOG_INFO("Extended ADC Self-Test PASSED");
+ } else {
+ const std::string err_msg =
+ (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)")
+ % num_failures % num_iters)
+ .str();
+ RFNOC_LOG_ERROR(err_msg);
+ throw uhd::runtime_error(err_msg);
+ }
+}
diff --git a/host/lib/usrp/x300/x300_mb_controller.hpp b/host/lib/usrp/x300/x300_mb_controller.hpp
index 9a220ab00..53f166a0e 100644
--- a/host/lib/usrp/x300/x300_mb_controller.hpp
+++ b/host/lib/usrp/x300/x300_mb_controller.hpp
@@ -8,26 +8,45 @@
#define INCLUDED_LIBUHD_X300_MB_CONTROLLER_HPP
#include "x300_clock_ctrl.hpp"
+#include "x300_device_args.hpp"
+#include "x300_radio_mbc_iface.hpp"
+#include "x300_regs.hpp"
#include <uhd/rfnoc/mb_controller.hpp>
+#include <uhd/types/sensors.hpp>
#include <uhd/types/wb_iface.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <unordered_set>
namespace uhd { namespace rfnoc {
/*! X300-Specific version of the mb_controller
*
* Reminder: There is one of these per motherboard.
+ *
+ * The X300 motherboard controller is responsible for:
+ * - Controlling the timekeeper
+ * - Controlling all time- and clock-related settings
+ * - Initialize and hold the GPS control
*/
class x300_mb_controller : public mb_controller
{
public:
- x300_mb_controller(uhd::i2c_iface::sptr zpu_i2c,
+ /**************************************************************************
+ * Structors
+ *************************************************************************/
+ x300_mb_controller(const size_t hw_rev,
+ const std::string product_name,
+ uhd::i2c_iface::sptr zpu_i2c,
uhd::wb_iface::sptr zpu_ctrl,
- x300_clock_ctrl::sptr clock_ctrl)
- : _zpu_i2c(zpu_i2c), _zpu_ctrl(zpu_ctrl), _clock_ctrl(clock_ctrl)
- {
- // nop
- }
+ x300_clock_ctrl::sptr clock_ctrl,
+ uhd::usrp::mboard_eeprom_t mb_eeprom,
+ uhd::usrp::x300::x300_device_args_t args);
+
+ ~x300_mb_controller();
+ /**************************************************************************
+ * X300-Specific APIs
+ *************************************************************************/
//! Return reference to the ZPU-owned I2C controller
uhd::i2c_iface::sptr get_zpu_i2c()
{
@@ -40,30 +59,107 @@ public:
//! Return reference to LMK clock controller
x300_clock_ctrl::sptr get_clock_ctrl() { return _clock_ctrl; }
+ void register_reset_codec_cb(std::function<void(void)>&& reset_cb)
+ {
+ _reset_cbs.push_back(std::move(reset_cb));
+ }
+
+ void set_initialization_done() { _initialization_done = true; }
+
+ void register_radio(uhd::usrp::x300::x300_radio_mbc_iface* radio)
+ {
+ _radio_refs.push_back(radio);
+ }
+
+ /**************************************************************************
+ * Timekeeper API
+ *************************************************************************/
//! X300-specific version of the timekeeper controls
+ //
+ // The X300 controls timekeepers via the ZPU
class x300_timekeeper : public mb_controller::timekeeper
{
public:
- x300_timekeeper(uhd::wb_iface::sptr zpu_ctrl) : _zpu_ctrl(zpu_ctrl) {}
-
+ x300_timekeeper(const size_t tk_idx, uhd::wb_iface::sptr zpu_ctrl, const double tick_rate)
+ : _tk_idx(tk_idx), _zpu_ctrl(zpu_ctrl)
+ {
+ set_tick_rate(tick_rate);
+ }
uint64_t get_ticks_now();
-
uint64_t get_ticks_last_pps();
-
void set_ticks_now(const uint64_t ticks);
-
void set_ticks_next_pps(const uint64_t ticks);
-
void set_period(const uint64_t period_ns);
private:
+ uint32_t get_tk_addr(const uint32_t tk_addr);
+ const size_t _tk_idx;
uhd::wb_iface::sptr _zpu_ctrl;
- };
+ }; /* x300_timekeeper */
+
+ /**************************************************************************
+ * Motherboard Control API (see mb_controller.hpp)
+ *************************************************************************/
+ void init();
+ std::string get_mboard_name() const;
+ void set_time_source(const std::string& source);
+ std::string get_time_source() const;
+ std::vector<std::string> get_time_sources() const;
+ void set_clock_source(const std::string& source);
+ std::string get_clock_source() const;
+ std::vector<std::string> get_clock_sources() const;
+ void set_sync_source(const std::string& clock_source, const std::string& time_source);
+ void set_sync_source(const device_addr_t& sync_source);
+ device_addr_t get_sync_source() const;
+ std::vector<device_addr_t> get_sync_sources();
+ void set_clock_source_out(const bool enb);
+ void set_time_source_out(const bool enb);
+ sensor_value_t get_sensor(const std::string& name);
+ std::vector<std::string> get_sensor_names();
+ uhd::usrp::mboard_eeprom_t get_eeprom();
+ bool synchronize(std::vector<mb_controller::sptr>& mb_controllers,
+ const uhd::time_spec_t& time_spec = uhd::time_spec_t(0.0),
+ const bool quiet = false);
private:
+ //! Return a string X300::MB_CTRL#N
+ std::string get_unique_id();
+
+ //! Init GPS
+ void init_gps();
+
+ //! Reset all registered DACs and ADCs
+ void reset_codecs();
+
+ //! Wait until reference clock locks, or a timeout occurs
+ //
+ // \returns lock status
+ bool wait_for_clk_locked(uint32_t which, double timeout);
+
+ //! Returns true if a PPS signal is detected
+ bool is_pps_present();
+
+ //! Return LMK lock status
+ bool get_ref_locked();
+
+ /*! Calibrate the ADC transfer delay
+ *
+ * This will try various clock delay settings to the ADC, and pick the one
+ * with the best BER performance.
+ */
+ void self_cal_adc_xfer_delay(const bool apply_delay);
+
+ void extended_adc_test(double duration_s);
+
/**************************************************************************
* Attributes
*************************************************************************/
+ //! Hardware revision
+ const size_t _hw_rev;
+
+ //! Product name (X310, X300)
+ const std::string _product_name;
+
//! Reference to the ZPU-owned I2C controller
uhd::i2c_iface::sptr _zpu_i2c;
@@ -72,6 +168,37 @@ private:
//! Reference to LMK clock controller
x300_clock_ctrl::sptr _clock_ctrl;
+
+ //! State of the MB EEPROM
+ uhd::usrp::mboard_eeprom_t _mb_eeprom;
+
+ //! Copy of the device args
+ uhd::usrp::x300::x300_device_args_t _args;
+
+ //! Reference to clock control register
+ uhd::usrp::x300::fw_regmap_t::sptr _fw_regmap;
+
+ //! Reference to GPS control
+ uhd::gps_ctrl::sptr _gps;
+
+ //! Reference to all callbacks to reset the ADCs/DACs
+ std::vector<std::function<void(void)>> _reset_cbs;
+
+ //! Current clock source (external, internal, gpsdo)
+ std::string _current_refclk_src;
+
+ //! Current time source (external, internal, gpsdo)
+ std::string _current_time_src;
+
+ //! Reference to radios on this motherboard
+ std::vector<uhd::usrp::x300::x300_radio_mbc_iface*> _radio_refs;
+
+ //! List of available sensors
+ std::unordered_set<std::string> _sensors{"ref_locked"};
+
+ //! Flag to tell us if initialization is complete. Some functions behave
+ // differently after initialization.
+ bool _initialization_done = false;
};
}} // namespace uhd::rfnoc
diff --git a/host/lib/usrp/x300/x300_mb_iface.cpp b/host/lib/usrp/x300/x300_mb_iface.cpp
new file mode 100644
index 000000000..2c053080d
--- /dev/null
+++ b/host/lib/usrp/x300/x300_mb_iface.cpp
@@ -0,0 +1,226 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "x300_impl.hpp"
+#include <uhdlib/rfnoc/device_id.hpp>
+
+using namespace uhd::rfnoc;
+using uhd::transport::link_type_t;
+
+
+x300_impl::x300_mb_iface::x300_mb_iface(uhd::usrp::x300::conn_manager::sptr conn_mgr,
+ const double radio_clk_freq,
+ const uhd::rfnoc::device_id_t remote_dev_id)
+ : _remote_dev_id(remote_dev_id)
+ , _bus_clk(std::make_shared<uhd::rfnoc::clock_iface>(
+ "bus_clk", uhd::usrp::x300::BUS_CLOCK_RATE, false))
+ , _radio_clk(
+ std::make_shared<uhd::rfnoc::clock_iface>("radio_clk", radio_clk_freq, false))
+ , _conn_mgr(conn_mgr)
+{
+ UHD_ASSERT_THROW(_conn_mgr);
+ _bus_clk->set_running(true);
+ _radio_clk->set_running(true);
+}
+
+x300_impl::x300_mb_iface::~x300_mb_iface() = default;
+
+uint16_t x300_impl::x300_mb_iface::get_proto_ver()
+{
+ // TODO: Get from from a hardware register
+ return 0x0100;
+}
+
+uhd::rfnoc::chdr_w_t x300_impl::x300_mb_iface::get_chdr_w()
+{
+ // TODO: Get from from a hardware register
+ return uhd::rfnoc::CHDR_W_64;
+}
+
+uhd::endianness_t x300_impl::x300_mb_iface::get_endianness(
+ const uhd::rfnoc::device_id_t /*local_device_id*/)
+{
+ // FIXME
+ return uhd::ENDIANNESS_BIG;
+}
+
+uhd::rfnoc::device_id_t x300_impl::x300_mb_iface::get_remote_device_id()
+{
+ return _remote_dev_id;
+}
+
+std::vector<uhd::rfnoc::device_id_t> x300_impl::x300_mb_iface::get_local_device_ids()
+{
+ return _conn_mgr->get_local_device_ids();
+}
+
+uhd::transport::adapter_id_t x300_impl::x300_mb_iface::get_adapter_id(
+ const uhd::rfnoc::device_id_t local_device_id)
+{
+ return _adapter_map[local_device_id];
+}
+
+void x300_impl::x300_mb_iface::reset_network()
+{
+ // FIXME
+}
+
+uhd::rfnoc::clock_iface::sptr x300_impl::x300_mb_iface::get_clock_iface(
+ const std::string& clock_name)
+{
+ if (clock_name == "radio_clk") {
+ return _radio_clk;
+ }
+ if (clock_name == "bus_clk") {
+ return _bus_clk;
+ }
+ UHD_LOG_ERROR("X300", "Invalid timebase clock name: " + clock_name);
+ throw uhd::key_error("[X300] Invalid timebase clock name: " + clock_name);
+}
+
+uhd::rfnoc::chdr_ctrl_xport::sptr x300_impl::x300_mb_iface::make_ctrl_transport(
+ uhd::rfnoc::device_id_t local_device_id, const uhd::rfnoc::sep_id_t& local_epid)
+{
+ uhd::transport::io_service::sptr io_srv;
+ uhd::transport::send_link_if::sptr send_link;
+ uhd::transport::recv_link_if::sptr recv_link;
+ std::tie(io_srv, send_link, std::ignore, recv_link, std::ignore, std::ignore) =
+ _conn_mgr->get_links(link_type_t::CTRL,
+ local_device_id,
+ local_epid,
+ uhd::rfnoc::sep_id_t(),
+ uhd::device_addr_t());
+
+ /* Associate local device ID with the adapter */
+ _adapter_map[local_device_id] = send_link->get_send_adapter_id();
+
+ auto xport = uhd::rfnoc::chdr_ctrl_xport::make(io_srv,
+ send_link,
+ recv_link,
+ _pkt_factory,
+ local_epid,
+ send_link->get_num_send_frames(),
+ recv_link->get_num_recv_frames());
+ return xport;
+}
+
+uhd::rfnoc::chdr_rx_data_xport::uptr x300_impl::x300_mb_iface::make_rx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args)
+{
+ const uhd::rfnoc::sep_addr_t local_sep_addr = addrs.second;
+ const uhd::rfnoc::sep_id_t remote_epid = epids.first;
+ const uhd::rfnoc::sep_id_t local_epid = epids.second;
+
+ uhd::transport::io_service::sptr io_srv;
+ uhd::transport::send_link_if::sptr send_link;
+ uhd::transport::recv_link_if::sptr recv_link;
+ size_t recv_buff_size;
+ bool lossy_xport;
+ std::tie(io_srv, send_link, std::ignore, recv_link, recv_buff_size, lossy_xport) =
+ _conn_mgr->get_links(link_type_t::RX_DATA,
+ local_sep_addr.first,
+ local_epid,
+ remote_epid,
+ xport_args);
+
+ /* Associate local device ID with the adapter */
+ _adapter_map[local_sep_addr.first] = send_link->get_send_adapter_id();
+
+ // TODO: configure this based on the transport type
+ const uhd::rfnoc::stream_buff_params_t recv_capacity = {
+ recv_buff_size, uhd::rfnoc::MAX_FC_CAPACITY_PKTS};
+
+ const double ratio = 1.0 / 32;
+
+ // Configure flow control frequency to use bytes only for UDP
+ uhd::rfnoc::stream_buff_params_t fc_freq = {
+ static_cast<uint64_t>(std::ceil(double(recv_buff_size) * ratio)),
+ uhd::rfnoc::MAX_FC_FREQ_PKTS};
+
+ uhd::rfnoc::stream_buff_params_t fc_headroom = {0, 0};
+
+ // Create the data transport
+ auto fc_params = uhd::rfnoc::chdr_rx_data_xport::configure_sep(io_srv,
+ recv_link,
+ send_link,
+ _pkt_factory,
+ mgmt_portal,
+ epids,
+ pyld_buff_fmt,
+ mdata_buff_fmt,
+ recv_capacity,
+ fc_freq,
+ fc_headroom,
+ lossy_xport);
+
+ auto rx_xport = std::make_unique<uhd::rfnoc::chdr_rx_data_xport>(io_srv,
+ recv_link,
+ send_link,
+ _pkt_factory,
+ epids,
+ recv_link->get_num_recv_frames(),
+ fc_params);
+
+ return rx_xport;
+}
+
+uhd::rfnoc::chdr_tx_data_xport::uptr x300_impl::x300_mb_iface::make_tx_data_transport(
+ uhd::rfnoc::mgmt::mgmt_portal& mgmt_portal,
+ const uhd::rfnoc::sep_addr_pair_t& addrs,
+ const uhd::rfnoc::sep_id_pair_t& epids,
+ const uhd::rfnoc::sw_buff_t pyld_buff_fmt,
+ const uhd::rfnoc::sw_buff_t mdata_buff_fmt,
+ const uhd::device_addr_t& xport_args)
+{
+ const uhd::rfnoc::sep_addr_t local_sep_addr = addrs.first;
+ const uhd::rfnoc::sep_id_t remote_epid = epids.second;
+ const uhd::rfnoc::sep_id_t local_epid = epids.first;
+
+ uhd::transport::io_service::sptr io_srv;
+ uhd::transport::send_link_if::sptr send_link;
+ uhd::transport::recv_link_if::sptr recv_link;
+ bool lossy_xport;
+ std::tie(io_srv, send_link, std::ignore, recv_link, std::ignore, lossy_xport) =
+ _conn_mgr->get_links(link_type_t::TX_DATA,
+ local_sep_addr.first,
+ local_epid,
+ remote_epid,
+ xport_args);
+
+ /* Associate local device ID with the adapter */
+ _adapter_map[local_sep_addr.first] = send_link->get_send_adapter_id();
+
+ // TODO: configure this based on the transport type
+ const double fc_freq_ratio = 1.0 / 8;
+ const double fc_headroom_ratio = 0;
+
+ const auto buff_capacity = chdr_tx_data_xport::configure_sep(io_srv,
+ recv_link,
+ send_link,
+ _pkt_factory,
+ mgmt_portal,
+ epids,
+ pyld_buff_fmt,
+ mdata_buff_fmt,
+ fc_freq_ratio,
+ fc_headroom_ratio);
+
+ // Create the data transport
+ auto tx_xport = std::make_unique<chdr_tx_data_xport>(io_srv,
+ recv_link,
+ send_link,
+ _pkt_factory,
+ epids,
+ send_link->get_num_send_frames(),
+ buff_capacity);
+
+ return tx_xport;
+}
diff --git a/host/lib/usrp/x300/x300_pcie_mgr.cpp b/host/lib/usrp/x300/x300_pcie_mgr.cpp
index 47095b370..220a96530 100644
--- a/host/lib/usrp/x300/x300_pcie_mgr.cpp
+++ b/host/lib/usrp/x300/x300_pcie_mgr.cpp
@@ -12,22 +12,21 @@
#include "x300_mboard_type.hpp"
#include "x300_regs.hpp"
#include "x310_lvbitx.hpp"
-#include <uhd/transport/nirio_zero_copy.hpp>
-#include <uhd/transport/zero_copy.hpp>
#include <uhd/types/device_addr.hpp>
-#include <uhd/utils/byteswap.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/static.hpp>
+#include <uhdlib/rfnoc/device_id.hpp>
+#include <uhdlib/transport/nirio_link.hpp>
#include <uhdlib/usrp/cores/i2c_core_100_wb32.hpp>
#include <unordered_map>
#include <mutex>
namespace {
-uint32_t extract_sid_from_pkt(void* pkt, size_t)
-{
- return uhd::sid_t(uhd::wtohx(static_cast<const uint32_t*>(pkt)[1])).get_dst();
-}
+//uint32_t extract_sid_from_pkt(void* pkt, size_t)
+//{
+ //return uhd::sid_t(uhd::wtohx(static_cast<const uint32_t*>(pkt)[1])).get_dst();
+//}
constexpr uint32_t RADIO_DEST_PREFIX_TX = 0;
@@ -198,9 +197,8 @@ device_addrs_t pcie_manager::find(const device_addr_t& hint, bool explicit_query
/******************************************************************************
* Structors
*****************************************************************************/
-pcie_manager::pcie_manager(const x300_device_args_t& args,
- uhd::property_tree::sptr tree,
- const uhd::fs_path& root_path)
+pcie_manager::pcie_manager(
+ const x300_device_args_t& args, uhd::property_tree::sptr, const uhd::fs_path&)
: _args(args), _resource(args.get_resource())
{
nirio_status status = 0;
@@ -237,9 +235,7 @@ pcie_manager::pcie_manager(const x300_device_args_t& args,
_rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(
tx_data_fifos, 2);
- tree->create<size_t>(root_path / "mtu/recv").set(PCIE_RX_DATA_FRAME_SIZE);
- tree->create<size_t>(root_path / "mtu/send").set(PCIE_TX_DATA_FRAME_SIZE);
- tree->create<double>(root_path / "link_max_rate").set(MAX_RATE_PCIE);
+ _local_device_id = rfnoc::allocate_device_id();
}
/******************************************************************************
@@ -276,115 +272,124 @@ void pcie_manager::release_ctrl_iface(std::function<void(void)>&& release_fn)
}
uint32_t pcie_manager::allocate_pcie_dma_chan(
- const uhd::sid_t& tx_sid, const uhd::usrp::device3_impl::xport_type_t xport_type)
+ const rfnoc::sep_id_t& /*remote_epid*/, const link_type_t /*link_type*/)
{
- constexpr uint32_t CTRL_CHANNEL = 0;
- constexpr uint32_t ASYNC_MSG_CHANNEL = 1;
- constexpr uint32_t FIRST_DATA_CHANNEL = 2;
- if (xport_type == uhd::usrp::device3_impl::CTRL) {
- return CTRL_CHANNEL;
- } else if (xport_type == uhd::usrp::device3_impl::ASYNC_MSG) {
- return ASYNC_MSG_CHANNEL;
- } else {
- // sid_t has no comparison defined, so we need to convert it uint32_t
- uint32_t raw_sid = tx_sid.get();
-
- if (_dma_chan_pool.count(raw_sid) == 0) {
- size_t channel = _dma_chan_pool.size() + FIRST_DATA_CHANNEL;
- if (channel > PCIE_MAX_CHANNELS) {
- throw uhd::runtime_error(
- "Trying to allocate more DMA channels than are available");
- }
- _dma_chan_pool[raw_sid] = channel;
- UHD_LOGGER_DEBUG("X300")
- << "Assigning PCIe DMA channel " << _dma_chan_pool[raw_sid] << " to SID "
- << tx_sid.to_pp_string_hex();
- }
-
- return _dma_chan_pool[raw_sid];
- }
+ throw uhd::not_implemented_error("allocate_pcie_dma_chan()");
+ //constexpr uint32_t CTRL_CHANNEL = 0;
+ //constexpr uint32_t ASYNC_MSG_CHANNEL = 1;
+ //constexpr uint32_t FIRST_DATA_CHANNEL = 2;
+ //if (link_type == uhd::usrp::device3_impl::CTRL) {
+ //return CTRL_CHANNEL;
+ //} else if (link_type == uhd::usrp::device3_impl::ASYNC_MSG) {
+ //return ASYNC_MSG_CHANNEL;
+ //} else {
+ //// sid_t has no comparison defined, so we need to convert it uint32_t
+ //uint32_t raw_sid = tx_sid.get();
+
+ //if (_dma_chan_pool.count(raw_sid) == 0) {
+ //size_t channel = _dma_chan_pool.size() + FIRST_DATA_CHANNEL;
+ //if (channel > PCIE_MAX_CHANNELS) {
+ //throw uhd::runtime_error(
+ //"Trying to allocate more DMA channels than are available");
+ //}
+ //_dma_chan_pool[raw_sid] = channel;
+ //UHD_LOGGER_DEBUG("X300")
+ //<< "Assigning PCIe DMA channel " << _dma_chan_pool[raw_sid] << " to SID "
+ //<< tx_sid.to_pp_string_hex();
+ //}
+
+ //return _dma_chan_pool[raw_sid];
+ //}
}
muxed_zero_copy_if::sptr pcie_manager::make_muxed_pcie_msg_xport(
uint32_t dma_channel_num, size_t max_muxed_ports)
{
- zero_copy_xport_params buff_args;
- buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE;
- buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE;
- buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES;
- buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES;
-
- zero_copy_if::sptr base_xport = nirio_zero_copy::make(
- _rio_fpga_interface, dma_channel_num, buff_args, uhd::device_addr_t());
- return muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, max_muxed_ports);
+ throw uhd::not_implemented_error("NI-RIO links not yet implemented!");
+ //zero_copy_xport_params buff_args;
+ //buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE;
+ //buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE;
+ //buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES;
+ //buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES;
+
+ //zero_copy_if::sptr base_xport = nirio_zero_copy::make(
+ //_rio_fpga_interface, dma_channel_num, buff_args, uhd::device_addr_t());
+ //return muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, max_muxed_ports);
}
-both_xports_t pcie_manager::make_transport(both_xports_t xports,
- const uhd::usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& args,
- const size_t send_mtu,
- const size_t recv_mtu)
+both_links_t pcie_manager::get_links(link_type_t /*link_type*/,
+ const rfnoc::device_id_t local_device_id,
+ const rfnoc::sep_id_t& /*local_epid*/,
+ const rfnoc::sep_id_t& /*remote_epid*/,
+ const device_addr_t& /*link_args*/)
{
- zero_copy_xport_params default_buff_args;
- xports.endianness = ENDIANNESS_LITTLE;
- xports.lossless = true;
- const uint32_t dma_channel_num = allocate_pcie_dma_chan(xports.send_sid, xport_type);
- if (xport_type == uhd::usrp::device3_impl::CTRL) {
- // Transport for control stream
- if (not _ctrl_dma_xport) {
- // One underlying DMA channel will handle
- // all control traffic
- _ctrl_dma_xport =
- make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_CTRL_XPORTS);
- }
- // Create a virtual control transport
- xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst());
- } else if (xport_type == uhd::usrp::device3_impl::ASYNC_MSG) {
- // Transport for async message stream
- if (not _async_msg_dma_xport) {
- // One underlying DMA channel will handle
- // all async message traffic
- _async_msg_dma_xport =
- make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_ASYNC_XPORTS);
- }
- // Create a virtual async message transport
- xports.recv = _async_msg_dma_xport->make_stream(xports.recv_sid.get_dst());
- } else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
- default_buff_args.send_frame_size = args.cast<size_t>(
- "send_frame_size", std::min(send_mtu, PCIE_TX_DATA_FRAME_SIZE));
- default_buff_args.num_send_frames =
- args.cast<size_t>("num_send_frames", PCIE_TX_DATA_NUM_FRAMES);
- default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0);
- default_buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE;
- default_buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES;
- xports.recv = nirio_zero_copy::make(
- _rio_fpga_interface, dma_channel_num, default_buff_args);
- } else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
- default_buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE;
- default_buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES;
- default_buff_args.recv_frame_size = args.cast<size_t>(
- "recv_frame_size", std::min(recv_mtu, PCIE_RX_DATA_FRAME_SIZE));
- default_buff_args.num_recv_frames =
- args.cast<size_t>("num_recv_frames", PCIE_RX_DATA_NUM_FRAMES);
- default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0);
- xports.recv = nirio_zero_copy::make(
- _rio_fpga_interface, dma_channel_num, default_buff_args);
+ throw uhd::not_implemented_error("NI-RIO links not yet implemented!");
+ if (local_device_id != _local_device_id) {
+ throw uhd::runtime_error(
+ std::string("[X300] Cannot create NI-RIO link through local device ID ")
+ + std::to_string(local_device_id)
+ + ", no such device associated with this motherboard!");
}
-
- xports.send = xports.recv;
-
- // Router config word is:
- // - Upper 16 bits: Destination address (e.g. 0.0)
- // - Lower 16 bits: DMA channel
- uint32_t router_config_word = (xports.recv_sid.get_dst() << 16) | dma_channel_num;
- _rio_fpga_interface->get_kernel_proxy()->poke(PCIE_ROUTER_REG(0), router_config_word);
-
- // For the nirio transport, buffer size is depends on the frame size and num
- // frames
- xports.recv_buff_size =
- xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size();
- xports.send_buff_size =
- xports.send->get_num_send_frames() * xports.send->get_send_frame_size();
-
- return xports;
+ //zero_copy_xport_params default_buff_args;
+ //xports.endianness = ENDIANNESS_LITTLE;
+ //xports.lossless = true;
+ //const uint32_t dma_channel_num = allocate_pcie_dma_chan(xports.send_sid, xport_type);
+ //if (xport_type == uhd::usrp::device3_impl::CTRL) {
+ //// Transport for control stream
+ //if (not _ctrl_dma_xport) {
+ //// One underlying DMA channel will handle
+ //// all control traffic
+ //_ctrl_dma_xport =
+ //make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_CTRL_XPORTS);
+ //}
+ //// Create a virtual control transport
+ //xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst());
+ //} else if (xport_type == uhd::usrp::device3_impl::ASYNC_MSG) {
+ //// Transport for async message stream
+ //if (not _async_msg_dma_xport) {
+ //// One underlying DMA channel will handle
+ //// all async message traffic
+ //_async_msg_dma_xport =
+ //make_muxed_pcie_msg_xport(dma_channel_num, PCIE_MAX_MUXED_ASYNC_XPORTS);
+ //}
+ //// Create a virtual async message transport
+ //xports.recv = _async_msg_dma_xport->make_stream(xports.recv_sid.get_dst());
+ //} else if (xport_type == uhd::usrp::device3_impl::TX_DATA) {
+ //default_buff_args.send_frame_size = args.cast<size_t>(
+ //"send_frame_size", std::min(send_mtu, PCIE_TX_DATA_FRAME_SIZE));
+ //default_buff_args.num_send_frames =
+ //args.cast<size_t>("num_send_frames", PCIE_TX_DATA_NUM_FRAMES);
+ //default_buff_args.send_buff_size = args.cast<size_t>("send_buff_size", 0);
+ //default_buff_args.recv_frame_size = PCIE_MSG_FRAME_SIZE;
+ //default_buff_args.num_recv_frames = PCIE_MSG_NUM_FRAMES;
+ //xports.recv = nirio_zero_copy::make(
+ //_rio_fpga_interface, dma_channel_num, default_buff_args);
+ //} else if (xport_type == uhd::usrp::device3_impl::RX_DATA) {
+ //default_buff_args.send_frame_size = PCIE_MSG_FRAME_SIZE;
+ //default_buff_args.num_send_frames = PCIE_MSG_NUM_FRAMES;
+ //default_buff_args.recv_frame_size = args.cast<size_t>(
+ //"recv_frame_size", std::min(recv_mtu, PCIE_RX_DATA_FRAME_SIZE));
+ //default_buff_args.num_recv_frames =
+ //args.cast<size_t>("num_recv_frames", PCIE_RX_DATA_NUM_FRAMES);
+ //default_buff_args.recv_buff_size = args.cast<size_t>("recv_buff_size", 0);
+ //xports.recv = nirio_zero_copy::make(
+ //_rio_fpga_interface, dma_channel_num, default_buff_args);
+ //}
+
+ //xports.send = xports.recv;
+
+ //// Router config word is:
+ //// - Upper 16 bits: Destination address (e.g. 0.0)
+ //// - Lower 16 bits: DMA channel
+ //uint32_t router_config_word = (xports.recv_sid.get_dst() << 16) | dma_channel_num;
+ //_rio_fpga_interface->get_kernel_proxy()->poke(PCIE_ROUTER_REG(0), router_config_word);
+
+ //// For the nirio transport, buffer size is depends on the frame size and num
+ //// frames
+ //xports.recv_buff_size =
+ //xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size();
+ //xports.send_buff_size =
+ //xports.send->get_num_send_frames() * xports.send->get_send_frame_size();
+
+ //return xports;
}
diff --git a/host/lib/usrp/x300/x300_pcie_mgr.hpp b/host/lib/usrp/x300/x300_pcie_mgr.hpp
index c884c8b5f..146a2ff93 100644
--- a/host/lib/usrp/x300/x300_pcie_mgr.hpp
+++ b/host/lib/usrp/x300/x300_pcie_mgr.hpp
@@ -7,13 +7,15 @@
#ifndef INCLUDED_X300_PCI_MGR_HPP
#define INCLUDED_X300_PCI_MGR_HPP
-#include "../device3/device3_impl.hpp"
#include "x300_conn_mgr.hpp"
#include "x300_device_args.hpp"
#include "x300_mboard_type.hpp"
+#include <uhd/property_tree.hpp>
#include <uhd/transport/muxed_zero_copy_if.hpp>
#include <uhd/transport/nirio/niusrprio_session.h>
-#include <uhdlib/rfnoc/xports.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhdlib/rfnoc/rfnoc_common.hpp>
+#include <uhdlib/transport/links.hpp>
namespace uhd { namespace usrp { namespace x300 {
@@ -47,11 +49,18 @@ public:
*/
void release_ctrl_iface(std::function<void(void)>&& release_fn);
- both_xports_t make_transport(both_xports_t xports,
- const uhd::usrp::device3_impl::xport_type_t xport_type,
- const uhd::device_addr_t& args,
- const size_t send_mtu,
- const size_t recv_mtu);
+ /*! Return list of local device IDs associated with this link
+ */
+ std::vector<uhd::rfnoc::device_id_t> get_local_device_ids()
+ {
+ return {_local_device_id};
+ }
+
+ uhd::transport::both_links_t get_links(uhd::transport::link_type_t link_type,
+ const uhd::rfnoc::device_id_t local_device_id,
+ const uhd::rfnoc::sep_id_t& local_epid,
+ const uhd::rfnoc::sep_id_t& remote_epid,
+ const uhd::device_addr_t& link_args);
private:
/*! Allocate or return a previously allocated PCIe channel pair
@@ -59,7 +68,7 @@ private:
* Note the SID is always the transmit SID (i.e. from host to device).
*/
uint32_t allocate_pcie_dma_chan(
- const uhd::sid_t& tx_sid, const uhd::usrp::device3_impl::xport_type_t xport_type);
+ const uhd::rfnoc::sep_id_t& remote_epid, const uhd::transport::link_type_t link_type);
uhd::transport::muxed_zero_copy_if::sptr make_muxed_pcie_msg_xport(
uint32_t dma_channel_num, size_t max_muxed_ports);
@@ -69,13 +78,15 @@ private:
uhd::niusrprio::niusrprio_session::sptr _rio_fpga_interface;
- //! Maps SID -> DMA channel
- std::map<uint32_t, uint32_t> _dma_chan_pool;
+ //! Maps Remote EPID -> DMA channel
+ std::map<uhd::rfnoc::sep_id_t, uint32_t> _dma_chan_pool;
//! Control transport for one PCIe connection
uhd::transport::muxed_zero_copy_if::sptr _ctrl_dma_xport;
//! Async message transport
uhd::transport::muxed_zero_copy_if::sptr _async_msg_dma_xport;
+
+ uhd::rfnoc::device_id_t _local_device_id;
};
}}} // namespace uhd::usrp::x300
diff --git a/host/lib/usrp/x300/x300_prop_tree.cpp b/host/lib/usrp/x300/x300_prop_tree.cpp
new file mode 100644
index 000000000..21597beea
--- /dev/null
+++ b/host/lib/usrp/x300/x300_prop_tree.cpp
@@ -0,0 +1,117 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "x300_defaults.hpp"
+#include "x300_mb_controller.hpp"
+#include <uhd/property_tree.hpp>
+#include <uhd/utils/math.hpp>
+
+using namespace uhd;
+using namespace uhd::rfnoc;
+
+namespace uhd { namespace usrp { namespace x300 {
+
+void init_prop_tree(
+ const size_t mb_idx, x300_mb_controller* mbc, property_tree::sptr pt)
+{
+ const fs_path mb_path = fs_path("/mboards") / mb_idx;
+ try {
+ pt->create<std::string>("/name").set("X-Series Device");
+ } catch (const uhd::runtime_error&) {
+ // property_tree lacks an atomic check to only create a new node if it
+ // doesn't exist, so we simply try and create it and when it fails, we
+ // assume that another device has already created this node and we move
+ // on. If we did "if exists" before creating, there's a non-zero chance
+ // that a concurrent device init would still throw.
+ }
+ pt->create<std::string>(mb_path / "name").set(mbc->get_mboard_name());
+ pt->create<std::string>(mb_path / "codename").set("Yetti");
+
+ ////////////////////////////////////////////////////////////////////
+ // create clock properties
+ ////////////////////////////////////////////////////////////////////
+ pt->create<double>(mb_path / "master_clock_rate").set_publisher([mbc]() {
+ return mbc->get_clock_ctrl()->get_master_clock_rate();
+ });
+
+ ////////////////////////////////////////////////////////////////////
+ // setup time sources and properties
+ ////////////////////////////////////////////////////////////////////
+ pt->create<std::string>(mb_path / "time_source" / "value")
+ .set(mbc->get_time_source())
+ .add_coerced_subscriber([mbc](const std::string& time_source) {
+ mbc->set_time_source(time_source);
+ });
+ pt->create<std::vector<std::string>>(mb_path / "time_source" / "options")
+ .set(mbc->get_time_sources());
+
+ // setup the time output, default to ON
+ pt->create<bool>(mb_path / "time_source" / "output")
+ .add_coerced_subscriber([mbc](const bool time_output) {
+ mbc->set_time_source_out(time_output);
+ })
+ .set(true);
+
+ ////////////////////////////////////////////////////////////////////
+ // setup clock sources and properties
+ ////////////////////////////////////////////////////////////////////
+ pt->create<std::string>(mb_path / "clock_source" / "value")
+ .set(mbc->get_clock_source())
+ .add_coerced_subscriber([mbc](const std::string& clock_source) {
+ mbc->set_clock_source(clock_source);
+ })
+ .set_publisher([mbc]() { return mbc->get_clock_source(); });
+ pt->create<std::vector<std::string>>(mb_path / "clock_source" / "options")
+ .set(mbc->get_clock_sources());
+
+ // setup external reference options. default to 10 MHz input reference
+ pt->create<std::string>(mb_path / "clock_source" / "external");
+ pt
+ ->create<std::vector<double>>(
+ mb_path / "clock_source" / "external" / "freq" / "options")
+ .set(EXTERNAL_FREQ_OPTIONS);
+ pt->create<double>(mb_path / "clock_source" / "external" / "value")
+ .set(mbc->get_clock_ctrl()->get_sysref_clock_rate())
+ .set_coercer([current_rate = mbc->get_clock_ctrl()->get_sysref_clock_rate()](
+ const double clock_rate) {
+ if (!uhd::math::frequencies_are_equal(clock_rate, current_rate)) {
+ UHD_LOG_WARNING(
+ "X300", "Cannot change the sysref clock rate at runtime!");
+ }
+ return clock_rate;
+ });
+
+ // setup the clock output, default to ON
+ pt->create<bool>(mb_path / "clock_source" / "output")
+ .add_coerced_subscriber(
+ [mbc](const bool clock_output) { mbc->set_clock_source_out(clock_output); });
+
+ // Initialize tick rate (must be done before setting time)
+ // Note: The master tick rate can't be changed at runtime!
+ const double master_clock_rate = mbc->get_clock_ctrl()->get_master_clock_rate();
+ pt->create<double>(mb_path / "tick_rate")
+ .set_coercer([master_clock_rate](const double rate) {
+ // The contract of multi_usrp::set_master_clock_rate() is to coerce
+ // and not throw, so we'll follow that behaviour here.
+ if (!uhd::math::frequencies_are_equal(rate, master_clock_rate)) {
+ UHD_LOGGER_WARNING("X300")
+ << "Cannot update master clock rate! X300 Series does not "
+ "allow changing the clock rate during runtime.";
+ }
+ return master_clock_rate;
+ })
+ .set(master_clock_rate);
+
+ ////////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////////
+ for (const std::string& sensor_name : mbc->get_sensor_names()) {
+ pt->create<sensor_value_t>(mb_path / "sensors" / sensor_name)
+ .set_publisher([mbc, sensor_name]() { return mbc->get_sensor(sensor_name); });
+ }
+}
+
+}}} // namespace uhd::usrp::x300
diff --git a/host/lib/usrp/x300/x300_radio_control.cpp b/host/lib/usrp/x300/x300_radio_control.cpp
new file mode 100644
index 000000000..6cee57827
--- /dev/null
+++ b/host/lib/usrp/x300/x300_radio_control.cpp
@@ -0,0 +1,1906 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "x300_adc_ctrl.hpp"
+#include "x300_dac_ctrl.hpp"
+#include "x300_dboard_iface.hpp"
+#include "x300_device_args.hpp"
+#include "x300_mb_controller.hpp"
+#include "x300_radio_mbc_iface.hpp"
+#include "x300_regs.hpp"
+#include <uhd/rfnoc/registry.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/utils/gain_group.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhd/utils/soft_register.hpp>
+#include <uhdlib/rfnoc/radio_control_impl.hpp>
+#include <uhdlib/rfnoc/reg_iface_adapter.hpp>
+#include <uhdlib/usrp/common/apply_corrections.hpp>
+#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
+#include <uhdlib/usrp/cores/rx_frontend_core_3000.hpp>
+#include <uhdlib/usrp/cores/spi_core_3000.hpp>
+#include <uhdlib/usrp/cores/tx_frontend_core_200.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/make_shared.hpp>
+#include <algorithm>
+#include <chrono>
+#include <functional>
+#include <iostream>
+#include <thread>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+namespace {
+
+std::vector<uint8_t> str_to_bytes(std::string str)
+{
+ return std::vector<uint8_t>(str.cbegin(), str.cend());
+}
+
+std::string bytes_to_str(std::vector<uint8_t> str_b)
+{
+ return std::string(str_b.cbegin(), str_b.cend());
+}
+
+gain_fcns_t make_gain_fcns_from_subtree(property_tree::sptr subtree)
+{
+ gain_fcns_t gain_fcns;
+ gain_fcns.get_range = [subtree]() {
+ return subtree->access<meta_range_t>("range").get();
+ };
+ gain_fcns.get_value = [subtree]() { return subtree->access<double>("value").get(); };
+ gain_fcns.set_value = [subtree](const double gain) {
+ subtree->access<double>("value").set(gain);
+ };
+ return gain_fcns;
+}
+
+template <typename map_type>
+size_t _get_chan_from_map(std::unordered_map<size_t, map_type> map, const std::string& fe)
+{
+ for (auto it = map.begin(); it != map.end(); ++it) {
+ if (it->second.db_fe_name == fe) {
+ return it->first;
+ }
+ }
+ throw uhd::lookup_error(
+ str(boost::format("Invalid daughterboard frontend name: %s") % fe));
+}
+
+constexpr double DEFAULT_RATE = 200e6;
+
+} // namespace
+
+namespace x300_regs {
+
+static constexpr uint32_t PERIPH_BASE = 0x80000;
+static constexpr uint32_t PERIPH_REG_OFFSET = 8;
+
+// db_control registers
+static constexpr uint32_t SR_MISC_OUTS = PERIPH_BASE + 160 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_SPI = PERIPH_BASE + 168 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_LEDS = PERIPH_BASE + 176 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_FP_GPIO = PERIPH_BASE + 184 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_DB_GPIO = PERIPH_BASE + 192 * PERIPH_REG_OFFSET;
+
+static constexpr uint32_t RB_MISC_IO = PERIPH_BASE + 16 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_SPI = PERIPH_BASE + 17 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_LEDS = PERIPH_BASE + 18 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_DB_GPIO = PERIPH_BASE + 19 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_FP_GPIO = PERIPH_BASE + 20 * PERIPH_REG_OFFSET;
+
+
+//! Delta between frontend offsets for channel 0 and 1
+constexpr uint32_t SR_FE_CHAN_OFFSET = 16 * PERIPH_REG_OFFSET;
+constexpr uint32_t SR_TX_FE_BASE = PERIPH_BASE + 208 * PERIPH_REG_OFFSET;
+constexpr uint32_t SR_RX_FE_BASE = PERIPH_BASE + 224 * PERIPH_REG_OFFSET;
+
+} // namespace x300_regs
+
+class x300_radio_control_impl : public radio_control_impl,
+ public uhd::usrp::x300::x300_radio_mbc_iface
+{
+public:
+ RFNOC_RADIO_CONSTRUCTOR(x300_radio_control)
+ , _radio_type(get_block_id().get_block_count() == 0 ? PRIMARY : SECONDARY)
+ {
+ RFNOC_LOG_TRACE("Initializing x300_radio_control, slot "
+ << x300_radio_control_impl::get_slot_name());
+ UHD_ASSERT_THROW(get_mb_controller());
+ _x300_mb_control =
+ std::dynamic_pointer_cast<x300_mb_controller>(get_mb_controller());
+ UHD_ASSERT_THROW(_x300_mb_control);
+ _x300_mb_control->register_radio(this);
+ // MCR is locked for this session
+ _master_clock_rate = _x300_mb_control->get_clock_ctrl()->get_master_clock_rate();
+ UHD_ASSERT_THROW(get_tick_rate() == _master_clock_rate);
+ radio_control_impl::set_rate(_master_clock_rate);
+
+ ////////////////////////////////////////////////////////////////
+ // Setup peripherals
+ ////////////////////////////////////////////////////////////////
+ // The X300 only requires a single timed_wb_iface, even for TwinRX
+ _wb_iface = RFNOC_MAKE_WB_IFACE(0, 0);
+
+ RFNOC_LOG_TRACE("Creating SPI interface...");
+ _spi = spi_core_3000::make(
+ [this](const uint32_t addr, const uint32_t data) {
+ regs().poke32(addr, data, get_command_time(0));
+ },
+ [this](
+ const uint32_t addr) { return regs().peek32(addr, get_command_time(0)); },
+ x300_regs::SR_SPI,
+ 8,
+ x300_regs::RB_SPI);
+ // DAC/ADC
+ RFNOC_LOG_TRACE("Running init_codec...");
+ // Note: ADC calibration and DAC sync happen in x300_mb_controller
+ _init_codecs();
+ _x300_mb_control->register_reset_codec_cb([this]() { this->reset_codec(); });
+ // FP-GPIO
+ if (_radio_type == PRIMARY) {
+ RFNOC_LOG_TRACE("Creating FP-GPIO interface...");
+ _fp_gpio = gpio_atr::gpio_atr_3000::make(_wb_iface,
+ x300_regs::SR_FP_GPIO,
+ x300_regs::RB_FP_GPIO,
+ x300_regs::PERIPH_REG_OFFSET);
+ // Create the GPIO banks and attributes, and populate them with some default
+ // values
+ // TODO: Do we need this section? Since the _fp_gpio handles state now, we
+ // don't need to stash values here. We only need this if we want to set
+ // anything to a default value.
+ for (const gpio_atr::gpio_attr_map_t::value_type attr :
+ gpio_atr::gpio_attr_map) {
+ // TODO: Default values?
+ if (attr.first == usrp::gpio_atr::GPIO_SRC) {
+ // Don't set the SRC
+ // TODO: Remove from the map??
+ continue;
+ }
+ set_gpio_attr("FP0", usrp::gpio_atr::gpio_attr_map.at(attr.first), 0);
+ }
+ }
+ // DB Initialization
+ _init_db(); // This does not init the dboards themselves!
+
+ // LEDs are technically valid for both RX and TX, but let's put them
+ // here
+ _leds = gpio_atr::gpio_atr_3000::make_write_only(
+ _wb_iface, x300_regs::SR_LEDS, x300_regs::PERIPH_REG_OFFSET);
+ _leds->set_atr_mode(
+ usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ // We always want to initialize at least one frontend core for both TX and RX
+ // RX periphs
+ for (size_t i = 0; i < std::max<size_t>(get_num_output_ports(), 1); i++) {
+ _rx_fe_map[i].core = rx_frontend_core_3000::make(_wb_iface,
+ x300_regs::SR_RX_FE_BASE + i * x300_regs::SR_FE_CHAN_OFFSET,
+ x300_regs::PERIPH_REG_OFFSET);
+ _rx_fe_map[i].core->set_adc_rate(
+ _x300_mb_control->get_clock_ctrl()->get_master_clock_rate());
+ _rx_fe_map[i].core->set_dc_offset(
+ rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE);
+ _rx_fe_map[i].core->set_dc_offset_auto(
+ rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE);
+ _rx_fe_map[i].core->populate_subtree(
+ get_tree()->subtree(FE_PATH / "rx_fe_corrections" / i));
+ }
+ // TX Periphs
+ for (size_t i = 0; i < std::max<size_t>(get_num_input_ports(), 1); i++) {
+ _tx_fe_map[i].core = tx_frontend_core_200::make(_wb_iface,
+ x300_regs::SR_TX_FE_BASE + i * x300_regs::SR_FE_CHAN_OFFSET,
+ x300_regs::PERIPH_REG_OFFSET);
+ _tx_fe_map[i].core->set_dc_offset(
+ tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ _tx_fe_map[i].core->set_iq_balance(
+ tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
+ _tx_fe_map[i].core->populate_subtree(
+ get_tree()->subtree(FE_PATH / "tx_fe_corrections" / i));
+ }
+
+ // Dboards
+ _init_dboards();
+
+ // Properties
+ for (auto& samp_rate_prop : _samp_rate_in) {
+ samp_rate_prop.set(get_rate());
+ }
+ for (auto& samp_rate_prop : _samp_rate_out) {
+ samp_rate_prop.set(get_rate());
+ }
+ } /* ctor */
+
+ ~x300_radio_control_impl()
+ {
+ // nop
+ }
+
+ /**************************************************************************
+ * Radio API calls
+ *************************************************************************/
+ double set_rate(double rate)
+ {
+ // On X3x0, tick rate can't actually be changed at runtime
+ const double actual_rate = get_rate();
+ if (not uhd::math::frequencies_are_equal(rate, actual_rate)) {
+ RFNOC_LOG_WARNING("Requesting invalid sampling rate from device: "
+ << (rate / 1e6) << " MHz. Actual rate is: "
+ << (actual_rate / 1e6) << " MHz.");
+ }
+ return actual_rate;
+ }
+
+ void set_tx_antenna(const std::string& ant, const size_t chan)
+ {
+ get_tree()
+ ->access<std::string>(get_db_path("tx", chan) / "antenna" / "value")
+ .set(ant);
+ }
+
+ std::string get_tx_antenna(const size_t chan) const
+ {
+ return get_tree()
+ ->access<std::string>(get_db_path("tx", chan) / "antenna" / "value")
+ .get();
+ }
+
+ std::vector<std::string> get_tx_antennas(size_t chan) const
+ {
+ return get_tree()
+ ->access<std::vector<std::string>>(
+ get_db_path("tx", chan) / "antenna" / "options")
+ .get();
+ }
+
+ void set_rx_antenna(const std::string& ant, const size_t chan)
+ {
+ get_tree()
+ ->access<std::string>(get_db_path("rx", chan) / "antenna" / "value")
+ .set(ant);
+ }
+
+ std::string get_rx_antenna(const size_t chan) const
+ {
+ return get_tree()
+ ->access<std::string>(get_db_path("rx", chan) / "antenna" / "value")
+ .get();
+ }
+
+ std::vector<std::string> get_rx_antennas(size_t chan) const
+ {
+ return get_tree()
+ ->access<std::vector<std::string>>(
+ get_db_path("rx", chan) / "antenna" / "options")
+ .get();
+ }
+
+ double set_tx_frequency(const double freq, const size_t chan)
+ {
+ return get_tree()
+ ->access<double>(get_db_path("tx", chan) / "freq" / "value")
+ .set(freq)
+ .get();
+ }
+
+ void set_tx_tune_args(const uhd::device_addr_t& tune_args, const size_t chan)
+ {
+ if (get_tree()->exists(get_db_path("tx", chan) / "tune_args")) {
+ get_tree()
+ ->access<uhd::device_addr_t>(get_db_path("tx", chan) / "tune_args")
+ .set(tune_args);
+ }
+ }
+
+ double get_tx_frequency(const size_t chan)
+ {
+ return get_tree()
+ ->access<double>(get_db_path("tx", chan) / "freq" / "value")
+ .get();
+ }
+
+ double set_rx_frequency(const double freq, const size_t chan)
+ {
+ RFNOC_LOG_TRACE(
+ "set_rx_frequency(freq=" << (freq / 1e6) << " MHz, chan=" << chan << ")");
+ return get_tree()
+ ->access<double>(get_db_path("rx", chan) / "freq" / "value")
+ .set(freq)
+ .get();
+ }
+
+ void set_rx_tune_args(const uhd::device_addr_t& tune_args, const size_t chan)
+ {
+ if (get_tree()->exists(get_db_path("rx", chan) / "tune_args")) {
+ get_tree()
+ ->access<uhd::device_addr_t>(get_db_path("rx", chan) / "tune_args")
+ .set(tune_args);
+ }
+ }
+
+ double get_rx_frequency(const size_t chan)
+ {
+ return get_tree()
+ ->access<double>(get_db_path("rx", chan) / "freq" / "value")
+ .get();
+ }
+
+ uhd::freq_range_t get_tx_frequency_range(const size_t chan) const
+ {
+ return get_tree()
+ ->access<uhd::freq_range_t>(get_db_path("tx", chan) / "freq" / "range")
+ .get();
+ }
+
+ uhd::freq_range_t get_rx_frequency_range(const size_t chan) const
+ {
+ return get_tree()
+ ->access<uhd::meta_range_t>(get_db_path("rx", chan) / "freq" / "range")
+ .get();
+ }
+
+ /*** Bandwidth-Related APIs************************************************/
+ double set_rx_bandwidth(const double bandwidth, const size_t chan)
+ {
+ return get_tree()
+ ->access<double>(get_db_path("rx", chan) / "bandwidth" / "value")
+ .set(bandwidth)
+ .get();
+ }
+
+ double get_rx_bandwidth(const size_t chan)
+ {
+ return get_tree()
+ ->access<double>(get_db_path("rx", chan) / "bandwidth" / "value")
+ .get();
+ }
+
+ uhd::meta_range_t get_rx_bandwidth_range(size_t chan) const
+ {
+ return get_tree()
+ ->access<uhd::meta_range_t>(get_db_path("rx", chan) / "bandwidth" / "range")
+ .get();
+ }
+
+ double set_tx_bandwidth(const double bandwidth, const size_t chan)
+ {
+ return get_tree()
+ ->access<double>(get_db_path("tx", chan) / "bandwidth" / "value")
+ .set(bandwidth)
+ .get();
+ }
+
+ double get_tx_bandwidth(const size_t chan)
+ {
+ return get_tree()
+ ->access<double>(get_db_path("tx", chan) / "bandwidth" / "value")
+ .get();
+ }
+
+ uhd::meta_range_t get_tx_bandwidth_range(size_t chan) const
+ {
+ return get_tree()
+ ->access<uhd::meta_range_t>(get_db_path("tx", chan) / "bandwidth" / "range")
+ .get();
+ }
+
+ /*** Gain-Related APIs ***************************************************/
+ double set_tx_gain(const double gain, const size_t chan)
+ {
+ return set_tx_gain(gain, ALL_GAINS, chan);
+ }
+
+ double set_tx_gain(const double gain, const std::string& name, const size_t chan)
+ {
+ if (_tx_gain_groups.count(chan)) {
+ auto& gg = _tx_gain_groups.at(chan);
+ gg->set_value(gain, name);
+ return radio_control_impl::set_tx_gain(gg->get_value(name), chan);
+ }
+ return radio_control_impl::set_tx_gain(0.0, chan);
+ }
+
+ double set_rx_gain(const double gain, const size_t chan)
+ {
+ return set_rx_gain(gain, ALL_GAINS, chan);
+ }
+
+ double set_rx_gain(const double gain, const std::string& name, const size_t chan)
+ {
+ auto& gg = _rx_gain_groups.at(chan);
+ gg->set_value(gain, name);
+ return radio_control_impl::set_rx_gain(gg->get_value(name), chan);
+ }
+
+ double get_rx_gain(const size_t chan)
+ {
+ return get_rx_gain(ALL_GAINS, chan);
+ }
+
+ double get_rx_gain(const std::string& name, const size_t chan)
+ {
+ return _rx_gain_groups.at(chan)->get_value(name);
+ }
+
+ double get_tx_gain(const size_t chan)
+ {
+ return get_tx_gain(ALL_GAINS, chan);
+ }
+
+ double get_tx_gain(const std::string& name, const size_t chan)
+ {
+ return _tx_gain_groups.at(chan)->get_value(name);
+ }
+
+ std::vector<std::string> get_tx_gain_names(const size_t chan) const
+ {
+ return _tx_gain_groups.at(chan)->get_names();
+ }
+
+ std::vector<std::string> get_rx_gain_names(const size_t chan) const
+ {
+ return _rx_gain_groups.at(chan)->get_names();
+ }
+
+ uhd::gain_range_t get_tx_gain_range(const size_t chan) const
+ {
+ return get_tx_gain_range(ALL_GAINS, chan);
+ }
+
+ uhd::gain_range_t get_tx_gain_range(const std::string& name, const size_t chan) const
+ {
+ if (!_tx_gain_groups.count(chan)) {
+ throw uhd::index_error(
+ "Trying to access invalid TX gain group: " + std::to_string(chan));
+ }
+ return _tx_gain_groups.at(chan)->get_range(name);
+ }
+
+ uhd::gain_range_t get_rx_gain_range(const size_t chan) const
+ {
+ return get_rx_gain_range(ALL_GAINS, chan);
+ }
+
+ uhd::gain_range_t get_rx_gain_range(const std::string& name, const size_t chan) const
+ {
+ if (!_rx_gain_groups.count(chan)) {
+ throw uhd::index_error(
+ "Trying to access invalid RX gain group: " + std::to_string(chan));
+ }
+ return _rx_gain_groups.at(chan)->get_range(name);
+ }
+
+ std::vector<std::string> get_tx_gain_profile_names(const size_t chan) const
+ {
+ return get_tree()
+ ->access<std::vector<std::string>>(
+ get_db_path("tx", chan) / "gains/all/profile/options")
+ .get();
+ }
+
+ std::vector<std::string> get_rx_gain_profile_names(const size_t chan) const
+ {
+ return get_tree()
+ ->access<std::vector<std::string>>(
+ get_db_path("rx", chan) / "gains/all/profile/options")
+ .get();
+ }
+
+
+ void set_tx_gain_profile(const std::string& profile, const size_t chan)
+ {
+ get_tree()
+ ->access<std::string>(get_db_path("tx", chan) / "gains/all/profile/value")
+ .set(profile);
+ }
+
+ void set_rx_gain_profile(const std::string& profile, const size_t chan)
+ {
+ get_tree()
+ ->access<std::string>(get_db_path("rx", chan) / "gains/all/profile/value")
+ .set(profile);
+ }
+
+
+ std::string get_tx_gain_profile(const size_t chan) const
+ {
+ return get_tree()
+ ->access<std::string>(get_db_path("tx", chan) / "gains/all/profile/value")
+ .get();
+ }
+
+ std::string get_rx_gain_profile(const size_t chan) const
+ {
+ return get_tree()
+ ->access<std::string>(get_db_path("rx", chan) / "gains/all/profile/value")
+ .get();
+ }
+
+ /**************************************************************************
+ * LO controls
+ *************************************************************************/
+ std::vector<std::string> get_rx_lo_names(const size_t chan) const
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+ std::vector<std::string> lo_names;
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ for (const std::string& name : get_tree()->list(rx_fe_fe_root / "los")) {
+ lo_names.push_back(name);
+ }
+ }
+ return lo_names;
+ }
+
+ std::vector<std::string> get_rx_lo_sources(
+ const std::string& name, const size_t chan) const
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ if (get_tree()->exists(rx_fe_fe_root / "los" / ALL_LOS)) {
+ // Special value ALL_LOS support atomically sets the source for all
+ // LOs
+ return get_tree()
+ ->access<std::vector<std::string>>(
+ rx_fe_fe_root / "los" / ALL_LOS / "source" / "options")
+ .get();
+ } else {
+ return std::vector<std::string>();
+ }
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ return get_tree()
+ ->access<std::vector<std::string>>(
+ rx_fe_fe_root / "los" / name / "source" / "options")
+ .get();
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ // If the daughterboard doesn't expose it's LO(s) then it can only be internal
+ return std::vector<std::string>(1, "internal");
+ }
+ }
+
+ void set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan)
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ if (get_tree()->exists(rx_fe_fe_root / "los" / ALL_LOS)) {
+ // Special value ALL_LOS support atomically sets the source for all
+ // LOs
+ get_tree()
+ ->access<std::string>(
+ rx_fe_fe_root / "los" / ALL_LOS / "source" / "value")
+ .set(src);
+ } else {
+ for (const std::string& n : get_tree()->list(rx_fe_fe_root / "los")) {
+ this->set_rx_lo_source(src, n, chan);
+ }
+ }
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ get_tree()
+ ->access<std::string>(
+ rx_fe_fe_root / "los" / name / "source" / "value")
+ .set(src);
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ throw uhd::runtime_error(
+ "This device does not support manual configuration of LOs");
+ }
+ }
+
+ const std::string get_rx_lo_source(const std::string& name, const size_t chan)
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ // Special value ALL_LOS support atomically sets the source for all LOs
+ return get_tree()
+ ->access<std::string>(
+ rx_fe_fe_root / "los" / ALL_LOS / "source" / "value")
+ .get();
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ return get_tree()
+ ->access<std::string>(
+ rx_fe_fe_root / "los" / name / "source" / "value")
+ .get();
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ // If the daughterboard doesn't expose it's LO(s) then it can only be internal
+ return "internal";
+ }
+ }
+
+ void set_rx_lo_export_enabled(
+ bool enabled, const std::string& name, const size_t chan)
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ if (get_tree()->exists(rx_fe_fe_root / "los" / ALL_LOS)) {
+ // Special value ALL_LOS support atomically sets the source for all
+ // LOs
+ get_tree()
+ ->access<bool>(rx_fe_fe_root / "los" / ALL_LOS / "export")
+ .set(enabled);
+ } else {
+ for (const std::string& n : get_tree()->list(rx_fe_fe_root / "los")) {
+ this->set_rx_lo_export_enabled(enabled, n, chan);
+ }
+ }
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ get_tree()
+ ->access<bool>(rx_fe_fe_root / "los" / name / "export")
+ .set(enabled);
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ throw uhd::runtime_error(
+ "This device does not support manual configuration of LOs");
+ }
+ }
+
+ bool get_rx_lo_export_enabled(const std::string& name, const size_t chan) const
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ // Special value ALL_LOS support atomically sets the source for all LOs
+ return get_tree()
+ ->access<bool>(rx_fe_fe_root / "los" / ALL_LOS / "export")
+ .get();
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ return get_tree()
+ ->access<bool>(rx_fe_fe_root / "los" / name / "export")
+ .get();
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ // If the daughterboard doesn't expose it's LO(s), assume it cannot export
+ return false;
+ }
+ }
+
+ double set_rx_lo_freq(double freq, const std::string& name, const size_t chan)
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO frequency must be set for each stage individually");
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ get_tree()
+ ->access<double>(rx_fe_fe_root / "los" / name / "freq" / "value")
+ .set(freq);
+ return get_tree()
+ ->access<double>(rx_fe_fe_root / "los" / name / "freq" / "value")
+ .get();
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ throw uhd::runtime_error(
+ "This device does not support manual configuration of LOs");
+ }
+ }
+
+ double get_rx_lo_freq(const std::string& name, const size_t chan)
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO frequency must be retrieved for each stage individually");
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ return get_tree()
+ ->access<double>(rx_fe_fe_root / "los" / name / "freq" / "value")
+ .get();
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ // Return actual RF frequency if the daughterboard doesn't expose its LO(s)
+ return get_tree()->access<double>(rx_fe_fe_root / "freq" / " value").get();
+ }
+ }
+
+ freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan) const
+ {
+ fs_path rx_fe_fe_root = get_db_path("rx", chan);
+
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO frequency range must be retrieved for each stage individually");
+ } else {
+ if (get_tree()->exists(rx_fe_fe_root / "los")) {
+ return get_tree()
+ ->access<freq_range_t>(
+ rx_fe_fe_root / "los" / name / "freq" / "range")
+ .get();
+ } else {
+ throw uhd::runtime_error("Could not find LO stage " + name);
+ }
+ }
+ } else {
+ // Return the actual RF range if the daughterboard doesn't expose its LO(s)
+ return get_tree()
+ ->access<meta_range_t>(rx_fe_fe_root / "freq" / "range")
+ .get();
+ }
+ }
+
+ /*** Calibration API *****************************************************/
+ void set_tx_dc_offset(const std::complex<double>& offset, size_t chan)
+ {
+ const fs_path dc_offset_path = get_fe_path("tx", chan) / "dc_offset" / "value";
+ if (get_tree()->exists(dc_offset_path)) {
+ get_tree()->access<std::complex<double>>(dc_offset_path).set(offset);
+ } else {
+ RFNOC_LOG_WARNING("Setting TX DC offset is not possible on this device.");
+ }
+ }
+
+ meta_range_t get_tx_dc_offset_range(size_t chan) const
+ {
+ const fs_path range_path = get_fe_path("tx", chan) / "dc_offset" / "range";
+ if (get_tree()->exists(range_path)) {
+ return get_tree()->access<uhd::meta_range_t>(range_path).get();
+ } else {
+ RFNOC_LOG_WARNING(
+ "This device does not support querying the TX DC offset range.");
+ return meta_range_t(0, 0);
+ }
+ }
+
+ void set_tx_iq_balance(const std::complex<double>& correction, size_t chan)
+ {
+ const fs_path iq_balance_path = get_fe_path("tx", chan) / "iq_balance" / "value";
+ if (get_tree()->exists(iq_balance_path)) {
+ get_tree()->access<std::complex<double>>(iq_balance_path).set(correction);
+ } else {
+ RFNOC_LOG_WARNING("Setting TX IQ Balance is not possible on this device.");
+ }
+ }
+
+ void set_rx_dc_offset(const bool enb, size_t chan)
+ {
+ const fs_path dc_offset_path = get_fe_path("rx", chan) / "dc_offset" / "enable";
+ if (get_tree()->exists(dc_offset_path)) {
+ get_tree()->access<bool>(dc_offset_path).set(enb);
+ } else {
+ RFNOC_LOG_WARNING(
+ "Setting DC offset compensation is not possible on this device.");
+ }
+ }
+
+ void set_rx_dc_offset(const std::complex<double>& offset, size_t chan)
+ {
+ const fs_path dc_offset_path = get_fe_path("rx", chan) / "dc_offset" / "value";
+ if (get_tree()->exists(dc_offset_path)) {
+ get_tree()->access<std::complex<double>>(dc_offset_path).set(offset);
+ } else {
+ RFNOC_LOG_WARNING("Setting RX DC offset is not possible on this device.");
+ }
+ }
+
+ meta_range_t get_rx_dc_offset_range(size_t chan) const
+ {
+ const fs_path range_path = get_fe_path("rx", chan) / "dc_offset" / "range";
+ if (get_tree()->exists(range_path)) {
+ return get_tree()->access<uhd::meta_range_t>(range_path).get();
+ } else {
+ RFNOC_LOG_WARNING(
+ "This device does not support querying the rx DC offset range.");
+ return meta_range_t(0, 0);
+ }
+ }
+
+ void set_rx_iq_balance(const bool enb, size_t chan)
+ {
+ const fs_path iq_balance_path = get_fe_path("rx", chan) / "iq_balance" / "enable";
+ if (get_tree()->exists(iq_balance_path)) {
+ get_tree()->access<bool>(iq_balance_path).set(enb);
+ } else {
+ RFNOC_LOG_WARNING("Setting RX IQ Balance is not possible on this device.");
+ }
+ }
+
+ void set_rx_iq_balance(const std::complex<double>& correction, size_t chan)
+ {
+ const fs_path iq_balance_path = get_fe_path("rx", chan) / "iq_balance" / "value";
+ if (get_tree()->exists(iq_balance_path)) {
+ get_tree()->access<std::complex<double>>(iq_balance_path).set(correction);
+ } else {
+ RFNOC_LOG_WARNING("Setting RX IQ Balance is not possible on this device.");
+ }
+ }
+
+ /*** GPIO API ************************************************************/
+ std::vector<std::string> get_gpio_banks() const
+ {
+ std::vector<std::string> banks{"RX", "TX"};
+ if (_fp_gpio) {
+ banks.push_back("FP0");
+ }
+ return banks;
+ }
+
+ void set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value)
+ {
+ if (bank == "FP0" and _fp_gpio) {
+ _fp_gpio->set_gpio_attr(usrp::gpio_atr::gpio_attr_rev_map.at(attr), value);
+ return;
+ }
+ if (bank.size() >= 2 and bank[1] == 'X') {
+ const std::string name = bank.substr(2);
+ const dboard_iface::unit_t unit = (bank[0] == 'R') ? dboard_iface::UNIT_RX
+ : dboard_iface::UNIT_TX;
+ constexpr uint16_t mask = 0xFFFF;
+ if (attr == "CTRL") {
+ _db_iface->set_pin_ctrl(unit, value, mask);
+ }
+ else if (attr == "DDR") {
+ _db_iface->set_gpio_ddr(unit, value, mask);
+ }
+ else if (attr == "OUT") {
+ _db_iface->set_gpio_out(unit, value, mask);
+ }
+ else if (attr == "ATR_0X") {
+ _db_iface->set_atr_reg(unit, gpio_atr::ATR_REG_IDLE, value, mask);
+ }
+ else if (attr == "ATR_RX") {
+ _db_iface->set_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY, value, mask);
+ }
+ else if (attr == "ATR_TX") {
+ _db_iface->set_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY, value, mask);
+ }
+ else if (attr == "ATR_XX") {
+ _db_iface->set_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX, value, mask);
+ }
+ else {
+ RFNOC_LOG_ERROR("Invalid GPIO attribute name: " << attr);
+ throw uhd::key_error(std::string("Invalid GPIO attribute name: ") + attr);
+ }
+ return;
+ }
+ RFNOC_LOG_WARNING(
+ "Invalid GPIO bank name: `"
+ << bank
+ << "'. Ignoring call to set_gpio_attr() to retain backward compatibility.");
+ }
+
+ uint32_t get_gpio_attr(const std::string& bank, const std::string& attr)
+ {
+ if (bank == "FP0" and _fp_gpio) {
+ return _fp_gpio->get_attr_reg(usrp::gpio_atr::gpio_attr_rev_map.at(attr));
+ }
+ if (bank.size() >= 2 and bank[1] == 'X') {
+ const std::string name = bank.substr(2);
+ const dboard_iface::unit_t unit = (bank[0] == 'R') ? dboard_iface::UNIT_RX
+ : dboard_iface::UNIT_TX;
+ if (attr == "CTRL")
+ return _db_iface->get_pin_ctrl(unit);
+ if (attr == "DDR")
+ return _db_iface->get_gpio_ddr(unit);
+ if (attr == "OUT")
+ return _db_iface->get_gpio_out(unit);
+ if (attr == "ATR_0X")
+ return _db_iface->get_atr_reg(unit, gpio_atr::ATR_REG_IDLE);
+ if (attr == "ATR_RX")
+ return _db_iface->get_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY);
+ if (attr == "ATR_TX")
+ return _db_iface->get_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY);
+ if (attr == "ATR_XX")
+ return _db_iface->get_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX);
+ if (attr == "READBACK")
+ return _db_iface->read_gpio(unit);
+ RFNOC_LOG_ERROR("Invalid GPIO attribute name: " << attr);
+ throw uhd::key_error(std::string("Invalid GPIO attribute name: ") + attr);
+ }
+ RFNOC_LOG_WARNING(
+ "Invalid GPIO bank name: `"
+ << bank
+ << "'. get_gpio_attr() will return 0 to retain backward compatibility.");
+ return 0;
+ }
+
+ /**************************************************************************
+ * Sensor API
+ *************************************************************************/
+ std::vector<std::string> get_rx_sensor_names(size_t chan) const
+ {
+ const fs_path sensor_path = get_db_path("rx", chan) / "sensors";
+ if (get_tree()->exists(sensor_path)) {
+ return get_tree()->list(sensor_path);
+ }
+ return {};
+ }
+
+ uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan)
+ {
+ return get_tree()
+ ->access<uhd::sensor_value_t>(get_db_path("rx", chan) / "sensors" / name)
+ .get();
+ }
+
+ std::vector<std::string> get_tx_sensor_names(size_t chan) const
+ {
+ const fs_path sensor_path = get_db_path("tx", chan) / "sensors";
+ if (get_tree()->exists(sensor_path)) {
+ return get_tree()->list(sensor_path);
+ }
+ return {};
+ }
+
+ uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan)
+ {
+ return get_tree()
+ ->access<uhd::sensor_value_t>(get_db_path("tx", chan) / "sensors" / name)
+ .get();
+ }
+
+ /**************************************************************************
+ * EEPROM API
+ *************************************************************************/
+ void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom)
+ {
+ const std::string key_prefix = db_eeprom.count("rx_id") ? "rx_" : "tx_";
+ const std::string id_key = key_prefix + "id";
+ const std::string serial_key = key_prefix + "serial";
+ const std::string rev_key = key_prefix + "rev";
+ if (!(db_eeprom.count(id_key) && db_eeprom.count(serial_key)
+ && db_eeprom.count(rev_key))) {
+ RFNOC_LOG_ERROR("set_db_eeprom() requires id, serial, and rev keys!");
+ throw uhd::key_error(
+ "[X300] set_db_eeprom() requires id, serial, and rev keys!");
+ }
+
+ dboard_eeprom_t eeprom;
+ eeprom.id.from_string(bytes_to_str(db_eeprom.at(id_key)));
+ eeprom.serial = bytes_to_str(db_eeprom.at(serial_key));
+ eeprom.revision = bytes_to_str(db_eeprom.at(rev_key));
+ if (get_tree()->exists(DB_PATH / (key_prefix + "eeprom"))) {
+ get_tree()
+ ->access<dboard_eeprom_t>(DB_PATH / (key_prefix + "eeprom"))
+ .set(eeprom);
+ } else {
+ RFNOC_LOG_WARNING("Cannot set EEPROM, tree path does not exist.");
+ }
+ }
+
+
+ uhd::eeprom_map_t get_db_eeprom()
+ {
+ uhd::eeprom_map_t result;
+ if (get_tree()->exists(DB_PATH / "rx_eeprom")) {
+ const auto rx_eeprom =
+ get_tree()->access<dboard_eeprom_t>(DB_PATH / "rx_eeprom").get();
+ result["rx_id"] = str_to_bytes(rx_eeprom.id.to_pp_string());
+ result["rx_serial"] = str_to_bytes(rx_eeprom.serial);
+ result["rx_rev"] = str_to_bytes(rx_eeprom.revision);
+ }
+ if (get_tree()->exists(DB_PATH / "tx_eeprom")) {
+ const auto rx_eeprom =
+ get_tree()->access<dboard_eeprom_t>(DB_PATH / "rx_eeprom").get();
+ result["tx_id"] = str_to_bytes(rx_eeprom.id.to_pp_string());
+ result["tx_serial"] = str_to_bytes(rx_eeprom.serial);
+ result["tx_rev"] = str_to_bytes(rx_eeprom.revision);
+ }
+ return result;
+ }
+
+ /**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+ std::string get_slot_name() const
+ {
+ return _radio_type == PRIMARY ? "A" : "B";
+ }
+
+ size_t get_chan_from_dboard_fe(
+ const std::string& fe, const direction_t direction) const
+ {
+ switch (direction) {
+ case uhd::TX_DIRECTION:
+ return _get_chan_from_map(_tx_fe_map, fe);
+ case uhd::RX_DIRECTION:
+ return _get_chan_from_map(_rx_fe_map, fe);
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ std::string get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t direction) const
+ {
+ switch (direction) {
+ case uhd::TX_DIRECTION:
+ return _tx_fe_map.at(chan).db_fe_name;
+ case uhd::RX_DIRECTION:
+ return _rx_fe_map.at(chan).db_fe_name;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ std::string get_fe_name(const size_t chan, const uhd::direction_t direction) const
+ {
+ fs_path name_path =
+ get_db_path(direction == uhd::RX_DIRECTION ? "rx" : "tx", chan) / "name";
+ if (!get_tree()->exists(name_path)) {
+ return get_dboard_fe_from_chan(chan, direction);
+ }
+
+ return get_tree()->access<std::string>(name_path).get();
+ }
+
+
+ virtual void set_command_time(uhd::time_spec_t time, const size_t chan)
+ {
+ node_t::set_command_time(time, chan);
+ // This is for TwinRX only:
+ fs_path cmd_time_path = get_db_path("rx", chan) / "time" / "cmd";
+ if (get_tree()->exists(cmd_time_path)) {
+ get_tree()->access<time_spec_t>(cmd_time_path).set(time);
+ }
+ }
+
+ /**************************************************************************
+ * MB Interface API Calls
+ *************************************************************************/
+ uint32_t get_adc_rx_word()
+ {
+ return regs().peek32(regmap::RADIO_BASE_ADDR + regmap::REG_RX_DATA);
+ }
+
+ void set_adc_test_word(const std::string& patterna, const std::string& patternb)
+ {
+ _adc->set_test_word(patterna, patternb);
+ }
+
+ void set_adc_checker_enabled(const bool enb)
+ {
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, enb ? 1 : 0);
+ }
+
+ bool get_adc_checker_locked(const bool I)
+ {
+ return bool(_regs->misc_ins_reg.read(
+ I ? radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED
+ : radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED));
+ }
+
+ uint32_t get_adc_checker_error_code(const bool I)
+ {
+ return _regs->misc_ins_reg.get(
+ I ? radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR
+ : radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR);
+ }
+
+ // Documented in x300_radio_mbc_iface.hpp
+ void self_test_adc(const uint32_t ramp_time_ms)
+ {
+ RFNOC_LOG_DEBUG("Running ADC self-cal...");
+ // Bypass all front-end corrections
+ for (size_t i = 0; i < get_num_output_ports(); i++) {
+ _rx_fe_map[i].core->bypass_all(true);
+ }
+
+ // Test basic patterns
+ _adc->set_test_word("ones", "ones");
+ _check_adc(0xfffcfffc);
+ _adc->set_test_word("zeros", "zeros");
+ _check_adc(0x00000000);
+ _adc->set_test_word("ones", "zeros");
+ _check_adc(0xfffc0000);
+ _adc->set_test_word("zeros", "ones");
+ _check_adc(0x0000fffc);
+ for (size_t k = 0; k < 14; k++) {
+ _adc->set_test_word("zeros", "custom", 1 << k);
+ _check_adc(1 << (k + 2));
+ }
+ for (size_t k = 0; k < 14; k++) {
+ _adc->set_test_word("custom", "zeros", 1 << k);
+ _check_adc(1 << (k + 18));
+ }
+
+ // Turn on ramp pattern test
+ _adc->set_test_word("ramp", "ramp");
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0);
+ // Sleep added for SPI transactions to finish and ramp to start before checker is
+ // enabled.
+ std::this_thread::sleep_for(std::chrono::microseconds(1000));
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(ramp_time_ms));
+ _regs->misc_ins_reg.refresh();
+
+ std::string i_status, q_status;
+ if (_regs->misc_ins_reg.get(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED))
+ if (_regs->misc_ins_reg.get(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR))
+ i_status = "Bit Errors!";
+ else
+ i_status = "Good";
+ else
+ i_status = "Not Locked!";
+
+ if (_regs->misc_ins_reg.get(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED))
+ if (_regs->misc_ins_reg.get(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR))
+ q_status = "Bit Errors!";
+ else
+ q_status = "Good";
+ else
+ q_status = "Not Locked!";
+
+ // Return to normal mode
+ _adc->set_test_word("normal", "normal");
+
+ if ((i_status != "Good") or (q_status != "Good")) {
+ throw uhd::runtime_error(
+ (boost::format("ADC self-test failed for %s. Ramp checker status: "
+ "{ADC_A=%s, ADC_B=%s}")
+ % get_unique_id() % i_status % q_status)
+ .str());
+ }
+
+ // Restore front-end corrections
+ for (size_t i = 0; i < get_num_output_ports(); i++) {
+ _rx_fe_map[i].core->bypass_all(false);
+ }
+ }
+
+ void sync_dac()
+ {
+ _dac->sync();
+ }
+
+ void set_dac_sync(const bool enb, const uhd::time_spec_t& time)
+ {
+ if (time != uhd::time_spec_t(0.0)) {
+ set_command_time(time, 0);
+ }
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::DAC_SYNC, enb ? 1 : 0);
+ if (!enb && time != uhd::time_spec_t(0.0)) {
+ set_command_time(uhd::time_spec_t(0.0), 0);
+ }
+ }
+
+ void dac_verify_sync()
+ {
+ _dac->verify_sync();
+ }
+
+private:
+ /**************************************************************************
+ * ADC Control
+ *************************************************************************/
+ //! Create the ADC/DAC objects, reset them, run ADC cal
+ void _init_codecs()
+ {
+ _regs = std::make_unique<radio_regmap_t>(get_block_id().get_block_count());
+ _regs->initialize(*_wb_iface, true);
+ // Only Radio0 has the ADC/DAC reset bits
+ if (_radio_type == PRIMARY) {
+ RFNOC_LOG_TRACE("Resetting DAC and ADCs...");
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1);
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0);
+ _regs->misc_outs_reg.flush();
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0);
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1);
+ _regs->misc_outs_reg.flush();
+ }
+ _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1);
+
+ RFNOC_LOG_TRACE("Creating ADC interface...");
+ _adc = x300_adc_ctrl::make(_spi, DB_ADC_SEN);
+ RFNOC_LOG_TRACE("Creating DAC interface...");
+ _dac = x300_dac_ctrl::make(_spi, DB_DAC_SEN, _master_clock_rate);
+ _self_cal_adc_capture_delay();
+
+ ////////////////////////////////////////////////////////////////
+ // create legacy codec control objects
+ ////////////////////////////////////////////////////////////////
+ // DAC has no gains
+ get_tree()->create<int>("tx_codec/gains");
+ get_tree()->create<std::string>("tx_codec/name").set("ad9146");
+ get_tree()->create<std::string>("rx_codec/name").set("ads62p48");
+ get_tree()
+ ->create<meta_range_t>("rx_codec/gains/digital/range")
+ .set(meta_range_t(0, 6.0, 0.5));
+ get_tree()
+ ->create<double>("rx_codec/gains/digital/value")
+ .add_coerced_subscriber([this](const double gain) { _adc->set_gain(gain); })
+ .set(0);
+ }
+
+ //! Calibrate delays on the ADC. This needs to happen before every session.
+ void _self_cal_adc_capture_delay()
+ {
+ RFNOC_LOG_TRACE("Running ADC capture delay self-cal...");
+ constexpr uint32_t NUM_DELAY_STEPS = 32; // The IDELAYE2 element has 32 steps
+ // Retry self-cal if it fails in warmup situations
+ constexpr uint32_t NUM_RETRIES = 2;
+ constexpr int32_t MIN_WINDOW_LEN = 4;
+
+ int32_t win_start = -1, win_stop = -1;
+ uint32_t iter = 0;
+ while (iter++ < NUM_RETRIES) {
+ for (uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) {
+ // Apply delay
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, dly_tap);
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1);
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0);
+
+ uint32_t err_code = 0;
+
+ // -- Test I Channel --
+ // Put ADC in ramp test mode. Tie the other channel to all ones.
+ _adc->set_test_word("ramp", "ones");
+ // Turn on the pattern checker in the FPGA. It will lock when it sees a
+ // zero and count deviations from the expected value
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0);
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);
+ // 5ms @ 200MHz = 1 million samples
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ if (_regs->misc_ins_reg.read(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_LOCKED)) {
+ err_code += _regs->misc_ins_reg.get(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_ERROR);
+ } else {
+ err_code += 100; // Increment error code by 100 to indicate no lock
+ }
+
+ // -- Test Q Channel --
+ // Put ADC in ramp test mode. Tie the other channel to all ones.
+ _adc->set_test_word("ones", "ramp");
+ // Turn on the pattern checker in the FPGA. It will lock when it sees a
+ // zero and count deviations from the expected value
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0);
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1);
+ // 5ms @ 200MHz = 1 million samples
+ std::this_thread::sleep_for(std::chrono::milliseconds(5));
+ if (_regs->misc_ins_reg.read(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_LOCKED)) {
+ err_code += _regs->misc_ins_reg.get(
+ radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_ERROR);
+ } else {
+ err_code += 100; // Increment error code by 100 to indicate no lock
+ }
+
+ if (err_code == 0) {
+ if (win_start == -1) { // This is the first window
+ win_start = dly_tap;
+ win_stop = dly_tap;
+ } else { // We are extending the window
+ win_stop = dly_tap;
+ }
+ } else {
+ if (win_start != -1) { // A valid window turned invalid
+ if (win_stop - win_start >= MIN_WINDOW_LEN) {
+ break; // Valid window found
+ } else {
+ win_start = -1; // Reset window
+ }
+ }
+ }
+ // UHD_LOGGER_INFO("X300 RADIO") << (boost::format("CapTap=%d, Error=%d")
+ // % dly_tap % err_code);
+ }
+
+ // Retry the self-cal if it fails
+ if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN)
+ && iter < NUM_RETRIES /*not last iteration*/) {
+ win_start = -1;
+ win_stop = -1;
+ std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+ } else {
+ break;
+ }
+ }
+ _adc->set_test_word("normal", "normal");
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0);
+
+ if (win_start == -1) {
+ throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration "
+ "failed. Convergence error.");
+ }
+
+ if (win_stop - win_start < MIN_WINDOW_LEN) {
+ throw uhd::runtime_error(
+ "self_cal_adc_capture_delay: Self calibration failed. "
+ "Valid window too narrow.");
+ }
+
+ uint32_t ideal_tap = (win_stop + win_start) / 2;
+ _regs->misc_outs_reg.write(
+ radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, ideal_tap);
+ _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1);
+ _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0);
+
+ double tap_delay = (1.0e12 / 200e6) / (2 * 32); // in ps
+ RFNOC_LOG_DEBUG(
+ boost::format("ADC capture delay self-cal done (Tap=%d, Window=%d, "
+ "TapDelay=%.3fps, Iter=%d)")
+ % ideal_tap % (win_stop - win_start) % tap_delay % iter);
+ }
+
+ //! Verify that the output of the ADC matches an expected \p val
+ void _check_adc(const uint32_t val)
+ {
+ // Wait for previous control transaction to flush
+ get_adc_rx_word();
+ // Wait for ADC test pattern to propagate
+ std::this_thread::sleep_for(std::chrono::microseconds(5));
+ // Read value of RX readback register and verify, adapt for I inversion
+ // in FPGA
+ const uint32_t adc_rb = get_adc_rx_word() ^ 0xfffc0000;
+ if (val != adc_rb) {
+ RFNOC_LOG_ERROR(boost::format("ADC self-test failed! (Exp=0x%x, Got=0x%x)")
+ % val % adc_rb);
+ throw uhd::runtime_error("ADC self-test failed!");
+ }
+ }
+
+ void reset_codec()
+ {
+ RFNOC_LOG_TRACE("Start reset_codec");
+ if (_radio_type == PRIMARY) { // ADC/DAC reset lines only exist in Radio0
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1);
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0);
+ _regs->misc_outs_reg.flush();
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0);
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1);
+ _regs->misc_outs_reg.flush();
+ }
+ _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1);
+ UHD_ASSERT_THROW(bool(_adc));
+ UHD_ASSERT_THROW(bool(_dac));
+ _adc->reset();
+ _dac->reset();
+ RFNOC_LOG_TRACE("Done reset_codec");
+ }
+
+ /**************************************************************************
+ * DBoard
+ *************************************************************************/
+ fs_path get_db_path(const std::string& dir, const size_t chan) const
+ {
+ UHD_ASSERT_THROW(dir == "rx" || dir == "tx");
+ if (dir == "rx" && chan >= get_num_output_ports()) {
+ throw uhd::key_error("Invalid RX channel: " + std::to_string(chan));
+ }
+ if (dir == "tx" && chan >= get_num_input_ports()) {
+ throw uhd::key_error("Invalid TX channel: " + std::to_string(chan));
+ }
+ return DB_PATH / (dir + "_frontends")
+ / ((dir == "rx") ? _rx_fe_map.at(chan).db_fe_name
+ : _tx_fe_map.at(chan).db_fe_name);
+ }
+
+ fs_path get_fe_path(const std::string& dir, const size_t chan) const
+ {
+ UHD_ASSERT_THROW(dir == "rx" || dir == "tx");
+ if (dir == "rx" && chan >= get_num_output_ports()) {
+ throw uhd::key_error("Invalid RX channel: " + std::to_string(chan));
+ }
+ if (dir == "tx" && chan >= get_num_input_ports()) {
+ throw uhd::key_error("Invalid TX channel: " + std::to_string(chan));
+ }
+ return FE_PATH / (dir + "_fe_corrections")
+ / ((dir == "rx") ? _rx_fe_map.at(chan).db_fe_name
+ : _tx_fe_map.at(chan).db_fe_name);
+ }
+
+ void _init_db()
+ {
+ constexpr size_t BASE_ADDR = 0x50;
+ constexpr size_t RX_EEPROM_ADDR = 0x5;
+ constexpr size_t TX_EEPROM_ADDR = 0x4;
+ constexpr size_t GDB_EEPROM_ADDR = 0x1;
+ static const std::vector<size_t> EEPROM_ADDRS{
+ RX_EEPROM_ADDR, TX_EEPROM_ADDR, GDB_EEPROM_ADDR};
+ static const std::vector<std::string> EEPROM_PATHS{
+ "rx_eeprom", "tx_eeprom", "gdb_eeprom"};
+ const size_t DB_OFFSET = (_radio_type == PRIMARY) ? 0x0 : 0x2;
+ auto zpu_i2c = _x300_mb_control->get_zpu_i2c();
+ auto clock = _x300_mb_control->get_clock_ctrl();
+ for (size_t i = 0; i < EEPROM_ADDRS.size(); i++) {
+ const size_t addr = EEPROM_ADDRS[i] + DB_OFFSET;
+ // Load EEPROM
+ _db_eeproms[addr].load(*zpu_i2c, BASE_ADDR | addr);
+ // Add to tree
+ get_tree()
+ ->create<dboard_eeprom_t>(DB_PATH / EEPROM_PATHS[i])
+ .set(_db_eeproms[addr])
+ .add_coerced_subscriber([this, zpu_i2c, BASE_ADDR, addr](
+ const uhd::usrp::dboard_eeprom_t& db_eeprom) {
+ _set_db_eeprom(zpu_i2c, BASE_ADDR | addr, db_eeprom);
+ });
+ }
+
+ // create a new dboard interface
+ x300_dboard_iface_config_t db_config;
+ db_config.gpio = gpio_atr::db_gpio_atr_3000::make(_wb_iface,
+ x300_regs::SR_DB_GPIO,
+ x300_regs::RB_DB_GPIO,
+ x300_regs::PERIPH_REG_OFFSET);
+ db_config.spi = _spi;
+ db_config.rx_spi_slaveno = DB_RX_SEN;
+ db_config.tx_spi_slaveno = DB_TX_SEN;
+ db_config.i2c = zpu_i2c;
+ db_config.clock = clock;
+ db_config.which_rx_clk = (_radio_type == PRIMARY) ? X300_CLOCK_WHICH_DB0_RX
+ : X300_CLOCK_WHICH_DB1_RX;
+ db_config.which_tx_clk = (_radio_type == PRIMARY) ? X300_CLOCK_WHICH_DB0_TX
+ : X300_CLOCK_WHICH_DB1_TX;
+ db_config.dboard_slot = (_radio_type == PRIMARY) ? 0 : 1;
+ db_config.cmd_time_ctrl = _wb_iface;
+
+ // create a new dboard manager
+ RFNOC_LOG_TRACE("Creating DB interface...");
+ _db_iface = boost::make_shared<x300_dboard_iface>(db_config);
+ RFNOC_LOG_TRACE("Creating DB manager...");
+ _db_manager = dboard_manager::make(_db_eeproms[RX_EEPROM_ADDR + DB_OFFSET],
+ _db_eeproms[TX_EEPROM_ADDR + DB_OFFSET],
+ _db_eeproms[GDB_EEPROM_ADDR + DB_OFFSET],
+ _db_iface,
+ get_tree()->subtree(DB_PATH),
+ true // defer daughterboard initialization
+ );
+ RFNOC_LOG_TRACE("DB Manager Initialization complete.");
+
+ // The X3x0 radio block defaults to two ports, but most daughterboards
+ // only have one frontend. So we now reduce the number of actual ports
+ // based on what is connected.
+ // Note: The Basic and LF boards pretend they have four frontends,
+ // which a hack from the past. However, they actually only have one
+ // frontend, and we select the AB/BA/A/B setting through the antenna.
+ // The easiest way to identify those boards is because they're the only
+ // ones with four frontends.
+ // For all other cases, we reduce the number of frontends to one.
+ const size_t num_tx_frontends = _db_manager->get_tx_frontends().size();
+ const size_t num_rx_frontends = _db_manager->get_rx_frontends().size();
+ if (num_tx_frontends == 4) {
+ RFNOC_LOG_TRACE("Found four frontends, inferring BasicTX or LFTX.");
+ set_num_input_ports(1);
+ } else if (num_tx_frontends == 2 || num_tx_frontends == 1) {
+ set_num_input_ports(num_tx_frontends);
+ } else {
+ throw uhd::runtime_error("Unexpected number of TX frontends!");
+ }
+ if (num_rx_frontends == 4) {
+ RFNOC_LOG_TRACE("Found four frontends, inferring BasicRX or LFRX.");
+ set_num_output_ports(1);
+ } else if (num_rx_frontends == 2 || num_rx_frontends == 1) {
+ set_num_output_ports(num_rx_frontends);
+ } else {
+ throw uhd::runtime_error("Unexpected number of RX frontends!");
+ }
+ // This is specific to TwinRX. Due to driver legacy, we think we have a
+ // Tx frontend even though we don't. We thus hard-code that knowledge
+ // here.
+ if (num_rx_frontends == 2
+ && boost::starts_with(
+ get_tree()->access<std::string>(DB_PATH / "rx_frontends/0/name").get(),
+ "TwinRX")) {
+ set_num_input_ports(0);
+ }
+ RFNOC_LOG_TRACE("Num Active Frontends: RX: " << get_num_output_ports()
+ << " TX: " << get_num_input_ports());
+ }
+
+ void _init_dboards()
+ {
+ size_t rx_chan = 0;
+ size_t tx_chan = 0;
+ for (const std::string& fe : _db_manager->get_rx_frontends()) {
+ if (rx_chan >= get_num_output_ports()) {
+ break;
+ }
+ _rx_fe_map[rx_chan].db_fe_name = fe;
+ _db_iface->add_rx_fe(fe, _rx_fe_map[rx_chan].core);
+ const fs_path fe_path(DB_PATH / "rx_frontends" / fe);
+ const std::string conn =
+ get_tree()->access<std::string>(fe_path / "connection").get();
+ const double if_freq =
+ (get_tree()->exists(fe_path / "if_freq/value"))
+ ? get_tree()->access<double>(fe_path / "if_freq/value").get()
+ : 0.0;
+ _rx_fe_map[rx_chan].core->set_fe_connection(
+ usrp::fe_connection_t(conn, if_freq));
+ rx_chan++;
+ }
+ for (const std::string& fe : _db_manager->get_tx_frontends()) {
+ if (tx_chan >= get_num_input_ports()) {
+ break;
+ }
+ _tx_fe_map[tx_chan].db_fe_name = fe;
+ const fs_path fe_path(DB_PATH / "tx_frontends" / fe);
+ const std::string conn =
+ get_tree()->access<std::string>(fe_path / "connection").get();
+ _tx_fe_map[tx_chan].core->set_mux(conn);
+ tx_chan++;
+ }
+ UHD_ASSERT_THROW(rx_chan or tx_chan);
+ const double actual_rate = rx_chan ? _rx_fe_map.at(0).core->get_output_rate()
+ : get_rate();
+ RFNOC_LOG_DEBUG("Actual sample rate: " << (actual_rate / 1e6) << " Msps.");
+ radio_control_impl::set_rate(actual_rate);
+
+ // Initialize the daughterboards now that frontend cores and connections exist
+ _db_manager->initialize_dboards();
+
+ // now that dboard is created -- register into rx antenna event
+ if (not _rx_fe_map.empty()) {
+ for (size_t i = 0; i < get_num_output_ports(); i++) {
+ if (get_tree()->exists(get_db_path("rx", i) / "antenna" / "value")) {
+ // We need a desired subscriber for antenna/value because the experts
+ // don't coerce that property.
+ get_tree()
+ ->access<std::string>(get_db_path("rx", i) / "antenna" / "value")
+ .add_desired_subscriber([this, i](const std::string& led) {
+ _update_atr_leds(led, i);
+ })
+ .update();
+ } else {
+ _update_atr_leds("", i); // init anyway, even if never called
+ }
+ }
+ }
+
+ // bind frontend corrections to the dboard freq props
+ if (not _tx_fe_map.empty()) {
+ for (size_t i = 0; i < get_num_input_ports(); i++) {
+ if (get_tree()->exists(get_db_path("tx", i) / "freq" / "value")) {
+ get_tree()
+ ->access<double>(get_db_path("tx", i) / "freq" / "value")
+ .add_coerced_subscriber([this, i](const double freq) {
+ set_tx_fe_corrections(freq, i);
+ });
+ }
+ }
+ }
+ if (not _rx_fe_map.empty()) {
+ for (size_t i = 0; i < get_num_output_ports(); i++) {
+ if (get_tree()->exists(get_db_path("rx", i) / "freq" / "value")) {
+ get_tree()
+ ->access<double>(get_db_path("rx", i) / "freq" / "value")
+ .add_coerced_subscriber([this, i](const double freq) {
+ set_rx_fe_corrections(freq, i);
+ });
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Set gain groups
+ // Note: The actual gain control comes from the daughterboard drivers, thus,
+ // we need to call into the prop tree at the appropriate location in order
+ // to modify the gains.
+ ////////////////////////////////////////////////////////////////
+ // TX
+ for (size_t chan = 0; chan < get_num_input_ports(); chan++) {
+ fs_path rf_gains_path(get_db_path("tx", chan) / "gains");
+ if (!get_tree()->exists(rf_gains_path)) {
+ _tx_gain_groups[chan] = gain_group::make_zero();
+ continue;
+ }
+
+ std::vector<std::string> gain_stages = get_tree()->list(rf_gains_path);
+ if (gain_stages.empty()) {
+ _tx_gain_groups[chan] = gain_group::make_zero();
+ continue;
+ }
+
+ // DAC does not have a gain path
+ auto gg = gain_group::make();
+ for (const auto& name : gain_stages) {
+ gg->register_fcns(name,
+ make_gain_fcns_from_subtree(
+ get_tree()->subtree(rf_gains_path / name)),
+ 1 /* high prio */);
+ }
+ _tx_gain_groups[chan] = gg;
+ }
+ // RX
+ for (size_t chan = 0; chan < get_num_output_ports(); chan++) {
+ fs_path rf_gains_path(get_db_path("rx", chan) / "gains");
+ fs_path adc_gains_path("rx_codec/gains");
+
+ auto gg = gain_group::make();
+ // ADC also has a gain path
+ for (const auto& name : get_tree()->list(adc_gains_path)) {
+ gg->register_fcns("ADC-" + name,
+ make_gain_fcns_from_subtree(
+ get_tree()->subtree(adc_gains_path / name)),
+ 0 /* low prio */);
+ }
+ if (get_tree()->exists(rf_gains_path)) {
+ for (const auto& name : get_tree()->list(rf_gains_path)) {
+ gg->register_fcns(name,
+ make_gain_fcns_from_subtree(
+ get_tree()->subtree(rf_gains_path / name)),
+ 1 /* high prio */);
+ }
+ }
+ _rx_gain_groups[chan] = gg;
+ }
+ } /* _init_dboards */
+
+ void _set_db_eeprom(i2c_iface::sptr i2c,
+ const size_t addr,
+ const uhd::usrp::dboard_eeprom_t& db_eeprom)
+ {
+ db_eeprom.store(*i2c, addr);
+ _db_eeproms[addr] = db_eeprom;
+ }
+
+ void _update_atr_leds(const std::string& rx_ant, const size_t /*chan*/)
+ {
+ // The "RX1" port is used by TwinRX and the "TX/RX" port is used by all
+ // other full-duplex dboards. We need to handle both here.
+ const bool is_txrx = (rx_ant == "TX/RX" or rx_ant == "RX1");
+ const int TXRX_RX = (1 << 0);
+ const int TXRX_TX = (1 << 1);
+ const int RX2_RX = (1 << 2);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_IDLE, 0);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, is_txrx ? TXRX_RX : RX2_RX);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, TXRX_TX);
+ _leds->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, RX2_RX | TXRX_TX);
+ }
+
+ void set_rx_fe_corrections(const double lo_freq, const size_t chan)
+ {
+ if (not _ignore_cal_file) {
+ apply_rx_fe_corrections(get_tree(),
+ get_tree()->access<dboard_eeprom_t>(DB_PATH / "rx_eeprom").get().serial,
+ get_fe_path("rx", chan),
+ lo_freq);
+ }
+ }
+
+ void set_tx_fe_corrections(const double lo_freq, const size_t chan)
+ {
+ if (not _ignore_cal_file) {
+ apply_tx_fe_corrections(get_tree(),
+ get_tree()->access<dboard_eeprom_t>(DB_PATH / "tx_eeprom").get().serial,
+ get_fe_path("tx", chan),
+ lo_freq);
+ }
+ }
+
+ /**************************************************************************
+ * noc_block_base API
+ *************************************************************************/
+ //! Safely shut down all peripherals
+ //
+ // Reminder: After this is called, no peeks and pokes are allowed!
+ void deinit()
+ {
+ RFNOC_LOG_TRACE("deinit()");
+ // Reset daughterboard
+ _db_manager.reset();
+ _db_iface.reset();
+ // Reset codecs
+ if (_radio_type == PRIMARY) {
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1);
+ _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0);
+ }
+ _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0);
+ _regs->misc_outs_reg.flush();
+ _adc.reset();
+ _dac.reset();
+ // Destroy all other periph controls
+ _spi.reset();
+ _fp_gpio.reset();
+ _leds.reset();
+ _rx_fe_map.clear();
+ _tx_fe_map.clear();
+ }
+
+ bool check_topology(const std::vector<size_t>& connected_inputs,
+ const std::vector<size_t>& connected_outputs)
+ {
+ RFNOC_LOG_TRACE("check_topology()");
+ if (!node_t::check_topology(connected_inputs, connected_outputs)) {
+ return false;
+ }
+
+ for (size_t chan = 0; chan < get_num_input_ports(); chan++) {
+ const auto fe_enable_path = get_db_path("tx", chan) / "enabled";
+ if (get_tree()->exists(fe_enable_path)) {
+ const bool chan_active = std::any_of(connected_inputs.cbegin(),
+ connected_inputs.cend(),
+ [chan](const size_t input) { return input == chan; });
+ RFNOC_LOG_TRACE(
+ "Enabling TX chan " << chan << ": " << (chan_active ? "Yes" : "No"));
+ get_tree()->access<bool>(fe_enable_path).set(chan_active);
+ }
+ }
+
+ for (size_t chan = 0; chan < get_num_output_ports(); chan++) {
+ const auto fe_enable_path = get_db_path("rx", chan) / "enabled";
+ if (get_tree()->exists(fe_enable_path)) {
+ const bool chan_active = std::any_of(connected_outputs.cbegin(),
+ connected_outputs.cend(),
+ [chan](const size_t output) { return output == chan; });
+ RFNOC_LOG_TRACE(
+ "Enabling RX chan " << chan << ": " << (chan_active ? "Yes" : "No"));
+ get_tree()->access<bool>(fe_enable_path).set(chan_active);
+ }
+ }
+
+ return true;
+ }
+
+
+ /**************************************************************************
+ * Attributes
+ *************************************************************************/
+ //! Register space for the ADC and DAC
+ class radio_regmap_t : public uhd::soft_regmap_t
+ {
+ public:
+ class misc_outs_reg_t : public uhd::soft_reg32_wo_t
+ {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET, /*width*/ 1, /*shift*/ 2); //[2]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB, /*width*/ 1, /*shift*/ 3); //[3]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL, /*width*/ 5, /*shift*/ 4); //[8:4]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER_ENABLED, /*width*/ 1, /*shift*/ 9); //[9]
+ UHD_DEFINE_SOFT_REG_FIELD(DAC_SYNC, /*width*/ 1, /*shift*/ 10); //[10]
+
+ misc_outs_reg_t() : uhd::soft_reg32_wo_t(x300_regs::SR_MISC_OUTS)
+ {
+ // Initial values
+ set(DAC_ENABLED, 0);
+ set(DAC_RESET_N, 0);
+ set(ADC_RESET, 0);
+ set(ADC_DATA_DLY_STB, 0);
+ set(ADC_DATA_DLY_VAL, 16);
+ set(ADC_CHECKER_ENABLED, 0);
+ set(DAC_SYNC, 0);
+ }
+ } misc_outs_reg;
+
+ class misc_ins_reg_t : public uhd::soft_reg64_ro_t
+ {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 32); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 33); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 34); //[2]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 35); //[3]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER0_Q_ERROR, /*width*/ 1, /*shift*/ 36); //[4]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER0_I_ERROR, /*width*/ 1, /*shift*/ 37); //[5]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER1_Q_ERROR, /*width*/ 1, /*shift*/ 38); //[6]
+ UHD_DEFINE_SOFT_REG_FIELD(
+ ADC_CHECKER1_I_ERROR, /*width*/ 1, /*shift*/ 39); //[7]
+
+ misc_ins_reg_t() : uhd::soft_reg64_ro_t(x300_regs::RB_MISC_IO) {}
+ } misc_ins_reg;
+
+ radio_regmap_t(int radio_num)
+ : soft_regmap_t("radio" + std::to_string(radio_num) + "_regmap")
+ {
+ add_to_map(misc_outs_reg, "misc_outs_reg", PRIVATE);
+ add_to_map(misc_ins_reg, "misc_ins_reg", PRIVATE);
+ }
+ }; /* class radio_regmap_t */
+ //! wb_iface Instance for _regs
+ uhd::timed_wb_iface::sptr _wb_iface;
+ //! Instantiation of regs object for ADC and DAC (MISC_OUT register)
+ std::unique_ptr<radio_regmap_t> _regs;
+ //! Reference to the MB controller, typecast
+ std::shared_ptr<x300_mb_controller> _x300_mb_control;
+
+ //! Reference to the DBoard SPI core (also controls ADC/DAC)
+ spi_core_3000::sptr _spi;
+ //! Reference to the ADC controller
+ x300_adc_ctrl::sptr _adc;
+ //! Reference to the DAC controller
+ x300_dac_ctrl::sptr _dac;
+ //! Front-panel GPIO
+ usrp::gpio_atr::gpio_atr_3000::sptr _fp_gpio;
+ //! LEDs
+ usrp::gpio_atr::gpio_atr_3000::sptr _leds;
+
+ struct rx_fe_perif
+ {
+ std::string name;
+ std::string db_fe_name;
+ rx_frontend_core_3000::sptr core;
+ };
+ struct tx_fe_perif
+ {
+ std::string name;
+ std::string db_fe_name;
+ tx_frontend_core_200::sptr core;
+ };
+
+ std::unordered_map<size_t, rx_fe_perif> _rx_fe_map;
+ std::unordered_map<size_t, tx_fe_perif> _tx_fe_map;
+
+ //! Cache of EEPROM info (one per channel)
+ std::unordered_map<size_t, usrp::dboard_eeprom_t> _db_eeproms;
+ //! Reference to DB manager
+ usrp::dboard_manager::sptr _db_manager;
+ //! Reference to DB iface
+ boost::shared_ptr<x300_dboard_iface> _db_iface;
+
+ enum radio_connection_t { PRIMARY, SECONDARY };
+ radio_connection_t _radio_type;
+
+ bool _ignore_cal_file = false;
+
+ std::unordered_map<size_t, uhd::gain_group::sptr> _tx_gain_groups;
+ std::unordered_map<size_t, uhd::gain_group::sptr> _rx_gain_groups;
+
+ double _master_clock_rate = DEFAULT_RATE;
+};
+
+UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
+ x300_radio_control, RADIO_BLOCK, X300, "Radio", true, "radio_clk", "radio_clk")
diff --git a/host/lib/usrp/x300/x300_radio_mbc_iface.hpp b/host/lib/usrp/x300/x300_radio_mbc_iface.hpp
new file mode 100644
index 000000000..cb0d99306
--- /dev/null
+++ b/host/lib/usrp/x300/x300_radio_mbc_iface.hpp
@@ -0,0 +1,64 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_X300_MBC_IFACE_HPP
+#define INCLUDED_LIBUHD_X300_MBC_IFACE_HPP
+
+#include <uhd/types/time_spec.hpp>
+#include <cstddef>
+#include <string>
+
+namespace uhd { namespace usrp { namespace x300 {
+
+class x300_radio_mbc_iface
+{
+public:
+ virtual ~x300_radio_mbc_iface() {}
+
+ //! Return the current output of the ADC via a register
+ virtual uint32_t get_adc_rx_word() = 0;
+
+ //! Set ADC test word (see x300_adc_ctrl::set_test_word())
+ virtual void set_adc_test_word(
+ const std::string& patterna, const std::string& patternb) = 0;
+
+ //! Enable or disable the ADC checker
+ virtual void set_adc_checker_enabled(const bool enb) = 0;
+
+ //! Query ADC checker lock bit
+ virtual bool get_adc_checker_locked(const bool I) = 0;
+
+ //! Return current ADC error status
+ virtual uint32_t get_adc_checker_error_code(const bool I) = 0;
+
+ /*! Runs some ADC self tests
+ *
+ * - First, the ADC gets set to produce a constant value and we see if it
+ * reaches the FPGA
+ * - Then, the ADC is put into ramp mode, and we see if we read the ramp
+ * with no errors
+ *
+ * \param ramp_time_ms The duration of the ramp test. Increasing the test
+ * will increase the probability of a bit error.
+ * \throws uhd::runtime_error if one or more bit errors occurred
+ */
+ virtual void self_test_adc(const uint32_t ramp_time_ms) = 0;
+
+ //! Call sync() on the DAC object
+ virtual void sync_dac() = 0;
+
+ //! Set the FRAMEP/N sync pulse. If time is not zero, it will do so at the
+ // given time.
+ virtual void set_dac_sync(
+ const bool enb, const uhd::time_spec_t& time = uhd::time_spec_t(0.0)) = 0;
+
+ //! Call verify_sync() on the DAC object
+ virtual void dac_verify_sync() = 0;
+};
+
+}}} // namespace uhd::usrp::x300
+
+#endif /* INCLUDED_LIBUHD_X300_MBC_IFACE_HPP */
diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp
index d2677c05e..64ff63d77 100644
--- a/host/lib/usrp/x300/x300_regs.hpp
+++ b/host/lib/usrp/x300/x300_regs.hpp
@@ -11,6 +11,7 @@
#include <uhd/config.hpp>
#include <uhd/utils/soft_register.hpp>
#include <stdint.h>
+#include <memory>
static const int BL_ADDRESS = 0;
static const int BL_DATA = 1;
@@ -171,7 +172,7 @@ namespace uhd { namespace usrp { namespace x300 {
class fw_regmap_t : public uhd::soft_regmap_t
{
public:
- typedef boost::shared_ptr<fw_regmap_t> sptr;
+ using sptr = std::shared_ptr<fw_regmap_t>;
class clk_ctrl_reg_t : public uhd::soft_reg32_wo_t
{
diff --git a/host/tests/devtest/devtest_e3xx.py b/host/tests/devtest/devtest_e3xx.py
index a93c15ea5..9d21f8849 100755
--- a/host/tests/devtest/devtest_e3xx.py
+++ b/host/tests/devtest/devtest_e3xx.py
@@ -1,12 +1,16 @@
#
# Copyright 2015 Ettus Research LLC
# Copyright 2018 Ettus Research, a National Instruments Company
+# Copyright 2019 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
-Run device tests for the E3XX series.
+Run device tests for the E31X series.
"""
+
+# pylint: disable=wrong-import-position
+# pylint: disable=unused-import
from usrp_probe_test import uhd_usrp_probe_test
from benchmark_rate_test import uhd_benchmark_rate_test
uhd_benchmark_rate_test.tests = {
@@ -35,10 +39,15 @@ uhd_benchmark_rate_test.tests = {
from rx_samples_to_file_test import rx_samples_to_file_test
rx_samples_to_file_test.tests = {
- 'default': {
+ 'chan0': {
'duration': 1,
'subdev': 'A:0',
- 'rate': 5e6,
+ 'rate': 1e6,
+ },
+ 'chan1': {
+ 'duration': 1,
+ 'subdev': 'A:1',
+ 'rate': 1e6,
},
}
@@ -48,7 +57,7 @@ uhd_tx_waveforms_test.tests = {
'chan': '0',
},
'chan1': {
- 'chan': '0',
+ 'chan': '1',
},
'both_chans': {
'chan': '0,1',
@@ -57,4 +66,3 @@ uhd_tx_waveforms_test.tests = {
from tx_bursts_test import uhd_tx_bursts_test
from test_pps_test import uhd_test_pps_test
-
diff --git a/mpm/python/usrp_mpm/CMakeLists.txt b/mpm/python/usrp_mpm/CMakeLists.txt
index 6b6d99a30..e4332a617 100644
--- a/mpm/python/usrp_mpm/CMakeLists.txt
+++ b/mpm/python/usrp_mpm/CMakeLists.txt
@@ -16,7 +16,7 @@ set(USRP_MPM_TOP_FILES
${CMAKE_CURRENT_SOURCE_DIR}/discovery.py
${CMAKE_CURRENT_SOURCE_DIR}/eeprom.py
${CMAKE_CURRENT_SOURCE_DIR}/e31x_legacy_eeprom.py
- ${CMAKE_CURRENT_SOURCE_DIR}/ethtable.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/ethdispatch.py
${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.py
${CMAKE_CURRENT_SOURCE_DIR}/liberiotable.py
${CMAKE_CURRENT_SOURCE_DIR}/mpmlog.py
diff --git a/mpm/python/usrp_mpm/dboard_manager/neon.py b/mpm/python/usrp_mpm/dboard_manager/neon.py
index 00438503d..7f9abd5ce 100644
--- a/mpm/python/usrp_mpm/dboard_manager/neon.py
+++ b/mpm/python/usrp_mpm/dboard_manager/neon.py
@@ -286,8 +286,8 @@ class Neon(DboardManagerBase):
return {
'name': 'ad9361_lock',
'type': 'BOOLEAN',
- 'unit': 'locked' if lo_locked else 'unlocked',
- 'value': str(lo_locked).lower(),
+ 'unit': 'locked' if lo_locked else 'unlocked',
+ 'value': str(lo_locked).lower(),
}
def get_catalina_temp_sensor(self, _):
diff --git a/mpm/python/usrp_mpm/dboard_manager/rhodium.py b/mpm/python/usrp_mpm/dboard_manager/rhodium.py
index b53b7f5bb..85f7b2b60 100644
--- a/mpm/python/usrp_mpm/dboard_manager/rhodium.py
+++ b/mpm/python/usrp_mpm/dboard_manager/rhodium.py
@@ -20,7 +20,6 @@ from usrp_mpm.dboard_manager.adc_rh import AD9695Rh
from usrp_mpm.dboard_manager.dac_rh import DAC37J82Rh
from usrp_mpm.mpmlog import get_logger
from usrp_mpm.sys_utils.uio import open_uio
-from usrp_mpm.sys_utils.udev import get_eeprom_paths
from usrp_mpm.user_eeprom import BfrfsEEPROM
diff --git a/mpm/python/usrp_mpm/ethdispatch.py b/mpm/python/usrp_mpm/ethdispatch.py
new file mode 100644
index 000000000..7c7437225
--- /dev/null
+++ b/mpm/python/usrp_mpm/ethdispatch.py
@@ -0,0 +1,102 @@
+#
+# Copyright 2017-2018 Ettus Research, a National Instruments Company
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+Ethernet dispatcher table control
+"""
+
+from builtins import str
+from builtins import object
+import netaddr
+from usrp_mpm.mpmlog import get_logger
+from usrp_mpm.sys_utils.uio import UIO
+
+
+class EthDispatcherCtrl(object):
+ """
+ Controls an Ethernet dispatcher.
+ """
+ DEFAULT_VITA_PORT = (49153, 49154)
+ # Address offsets:
+ ETH_IP_OFFSET = 0x0000
+ ETH_PORT_OFFSET = 0x0004
+ FORWARD_ETH_BCAST_OFFSET = 0x0008
+ BRIDGE_MAC_LO_OFFSET = 0x0010
+ BRIDGE_MAC_HI_OFFSET = 0x0014
+ BRIDGE_IP_OFFSET = 0x0018
+ BRIDGE_PORT_OFFSET = 0x001c
+ BRIDGE_ENABLE_OFFSET = 0x0020
+
+
+ def __init__(self, label):
+ self.log = get_logger(label)
+ self._regs = UIO(label=label, read_only=False)
+ self.poke32 = self._regs.poke32
+ self.peek32 = self._regs.peek32
+
+ def set_bridge_mode(self, bridge_mode):
+ " Enable/Disable Bridge Mode "
+ self.log.trace("Bridge Mode {}".format(
+ "Enabled" if bridge_mode else "Disabled"
+ ))
+ self.poke32(self.BRIDGE_ENABLE_OFFSET, int(bridge_mode))
+
+ def set_bridge_mac_addr(self, mac_addr):
+ """
+ Set the bridge MAC address for this Ethernet dispatcher.
+ Outgoing packets will have this MAC address.
+ """
+ self.log.debug("Setting bridge MAC address to `{}'".format(mac_addr))
+ mac_addr_int = int(netaddr.EUI(mac_addr))
+ self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
+ self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF
+ ))
+ self.poke32(self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF)
+ self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
+ self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32
+ ))
+ self.poke32(self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32)
+
+ def set_ipv4_addr(self, ip_addr, bridge_en=False):
+ """
+ Set the own IPv4 address for this Ethernet dispatcher.
+ Outgoing packets will have this IP address.
+ """
+ if bridge_en:
+ own_ip_offset = self.BRIDGE_IP_OFFSET
+ else:
+ own_ip_offset = self.ETH_IP_OFFSET
+ self.log.debug("Setting my own IP address to `{}'".format(ip_addr))
+ ip_addr_int = int(netaddr.IPAddress(ip_addr))
+ with self._regs:
+ self.poke32(own_ip_offset, ip_addr_int)
+
+ def set_vita_port(self, port_value=None, port_idx=None, bridge_en=False):
+ """
+ Set the port that is used for incoming VITA traffic. This is used to
+ distinguish traffic that goes to the FPGA from that going to the ARM.
+ """
+ port_idx = port_idx or 0
+ port_value = port_value or self.DEFAULT_VITA_PORT[port_idx]
+ assert port_idx in (0,) #FIXME: Fix port_idx = 1
+ if bridge_en:
+ port_reg_addr = self.BRIDGE_PORT_OFFSET
+ else:
+ port_reg_addr = self.ETH_PORT_OFFSET
+ with self._regs:
+ self.poke32(port_reg_addr, port_value)
+ self.log.debug("Setting RFNOC UDP port to `{}'".format(port_value))
+
+ def set_forward_policy(self, forward_eth, forward_bcast):
+ """
+ Forward Ethernet packet not matching OWN_IP to CROSSOVER
+ Forward broadcast packet to CPU and CROSSOVER
+ """
+ reg_value = int(bool(forward_eth) << 1) | int(bool(forward_bcast))
+ self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
+ self.FORWARD_ETH_BCAST_OFFSET, reg_value
+ ))
+ with self._regs:
+ self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)
diff --git a/mpm/python/usrp_mpm/ethtable.py b/mpm/python/usrp_mpm/ethtable.py
deleted file mode 100644
index 4a6566987..000000000
--- a/mpm/python/usrp_mpm/ethtable.py
+++ /dev/null
@@ -1,187 +0,0 @@
-#
-# Copyright 2017-2018 Ettus Research, a National Instruments Company
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-"""
-Ethernet dispatcher table control
-"""
-
-from builtins import str
-from builtins import object
-import netaddr
-from usrp_mpm.mpmlog import get_logger
-from usrp_mpm.sys_utils.uio import UIO
-from usrp_mpm.sys_utils.net import get_mac_addr
-
-
-class EthDispatcherTable(object):
- """
- Controls an Ethernet dispatcher table.
- """
- DEFAULT_VITA_PORT = (49153, 49154)
- # Address offsets:
- ETH_IP_OFFSET = 0x0000
- ETH_PORT_OFFSET = 0x0004
- FORWARD_ETH_BCAST_OFFSET = 0x0008
- BRIDGE_MAC_LO_OFFSET = 0x0010
- BRIDGE_MAC_HI_OFFSET = 0x0014
- BRIDGE_IP_OFFSET = 0x0018
- BRIDGE_PORT_OFFSET = 0x001c
- BRIDGE_ENABLE_OFFSET = 0x0020
- SID_IP_OFFSET = 0x1000
- SID_PORT_MAC_HI_OFFSET = 0x1400
- SID_MAC_LO_OFFSET = 0x1800
- LADDR_IP_OFFSET = 0x1D00
- LADDR_PORT_MAC_HI_OFFSET = 0x1E00
- LADDR_MAC_LO_OFFSET = 0x1F00
-
-
- def __init__(self, label):
- self.log = get_logger(label)
- self._regs = UIO(label=label, read_only=False)
- self.poke32 = self._regs.poke32
- self.peek32 = self._regs.peek32
-
- def set_bridge_mode(self, bridge_mode):
- " Enable/Disable Bridge Mode "
- self.log.trace("Bridge Mode {}".format(
- "Enabled" if bridge_mode else "Disabled"
- ))
- self.poke32(self.BRIDGE_ENABLE_OFFSET, int(bridge_mode))
-
- def set_bridge_mac_addr(self, mac_addr):
- """
- Set the bridge MAC address for this Ethernet dispatcher.
- Outgoing packets will have this MAC address.
- """
- self.log.debug("Setting bridge MAC address to `{}'".format(mac_addr))
- mac_addr_int = int(netaddr.EUI(mac_addr))
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF
- ))
- self.poke32(self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF)
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32
- ))
- self.poke32(self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32)
-
- def set_ipv4_addr(self, ip_addr, bridge_en=False):
- """
- Set the own IPv4 address for this Ethernet dispatcher.
- Outgoing packets will have this IP address.
- """
- if bridge_en:
- own_ip_offset = self.BRIDGE_IP_OFFSET
- else:
- own_ip_offset = self.ETH_IP_OFFSET
- self.log.debug("Setting my own IP address to `{}'".format(ip_addr))
- ip_addr_int = int(netaddr.IPAddress(ip_addr))
- with self._regs:
- self.poke32(own_ip_offset, ip_addr_int)
-
- def set_vita_port(self, port_value=None, port_idx=None, bridge_en=False):
- """
- Set the port that is used for incoming VITA traffic. This is used to
- distinguish traffic that goes to the FPGA from that going to the ARM.
- """
- port_idx = port_idx or 0
- port_value = port_value or self.DEFAULT_VITA_PORT[port_idx]
- assert port_idx in (0) #FIXME: Fix port_idx = 1
- if bridge_en:
- port_reg_addr = self.BRIDGE_PORT_OFFSET
- else:
- port_reg_addr = self.ETH_PORT_OFFSET
- with self._regs:
- self.poke32(port_reg_addr, port_value)
-
- def set_route(self, sid, ip_addr, udp_port, mac_addr=None):
- """
- Sets up routing in the Ethernet dispatcher. From sid, only the
- destination part is important. After this call, any CHDR packet
- reaching this Ethernet dispatcher will get routed to `ip_addr' and
- `udp_port'.
-
- It automatically looks up the MAC address of the destination unless a
- MAC address is given.
-
- sid -- Full SID, but only destination part matters.
- ip_addr -- IPv4 destination address. String format ("1.2.3.4").
- udp_addr -- Destination UDP port.
- mac_addr -- If given, this will replace an ARP lookup for the MAC
- address. String format, ("aa:bb:cc:dd:ee:ff"), case does
- not matter.
- """
- udp_port = int(udp_port)
- if mac_addr is None:
- mac_addr = get_mac_addr(ip_addr)
- if mac_addr is None:
- self.log.error(
- "Could not resolve a MAC address for IP address `{}'".format(ip_addr)
- )
- if sid.dst_addr == 0 or sid.dst_addr == 1:
- table_addr = sid.dst_ep
- ip_base_offset = self.SID_IP_OFFSET
- port_mac_hi_base_offset = self.SID_PORT_MAC_HI_OFFSET
- mac_lo_base_offset = self.SID_MAC_LO_OFFSET
- self.log.debug(
- "Routing SID `{sid}' (endpoint `{ep}') to IP address `{ip}', " \
- "MAC address `{mac}', port `{port}'".format(
- sid=str(sid),
- ep=table_addr,
- ip=ip_addr,
- mac=mac_addr,
- port=udp_port
- )
- )
- else: #populate local addr eth dispatch table
- table_addr = sid.dst_addr
- ip_base_offset = self.LADDR_IP_OFFSET
- port_mac_hi_base_offset = self.LADDR_PORT_MAC_HI_OFFSET
- mac_lo_base_offset = self.LADDR_MAC_LO_OFFSET
- self.log.debug(
- "Routing SID `{sid}' (local addr`{addr}') to IP address `{ip}', " \
- "MAC address `{mac}', port `{port}'".format(
- sid=str(sid),
- addr=table_addr,
- ip=ip_addr,
- mac=mac_addr,
- port=udp_port
- )
- )
- ip_addr_int = int(netaddr.IPAddress(ip_addr))
- mac_addr_int = int(netaddr.EUI(mac_addr))
- dst_offset = 4 * table_addr
-
- def poke_and_trace(addr, data):
- " Do a poke32() and log.trace() "
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- addr, data
- ))
- self.poke32(addr, data)
-
- with self._regs:
- poke_and_trace(
- ip_base_offset + dst_offset,
- ip_addr_int
- )
- poke_and_trace(
- mac_lo_base_offset + dst_offset,
- mac_addr_int & 0xFFFFFFFF,
- )
- poke_and_trace(
- port_mac_hi_base_offset + dst_offset,
- (udp_port << 16) | (mac_addr_int >> 32)
- )
-
- def set_forward_policy(self, forward_eth, forward_bcast):
- """
- Forward Ethernet packet not matching OWN_IP to CROSSOVER
- Forward broadcast packet to CPU and CROSSOVER
- """
- reg_value = int(bool(forward_eth) << 1) | int(bool(forward_bcast))
- self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
- self.FORWARD_ETH_BCAST_OFFSET, reg_value
- ))
- with self._regs:
- self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)
diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py
index be7600333..4ae30778a 100644
--- a/mpm/python/usrp_mpm/periph_manager/base.py
+++ b/mpm/python/usrp_mpm/periph_manager/base.py
@@ -177,7 +177,7 @@ class PeriphManagerBase(object):
###########################################################################
def __init__(self):
# Note: args is a dictionary.
- assert len(self.pids) > 0
+ assert self.pids
assert self.mboard_eeprom_magic is not None
self.dboards = []
self._default_args = ""
@@ -241,14 +241,17 @@ class PeriphManagerBase(object):
If no EEPROM is defined, returns empty values.
"""
- if len(self.mboard_eeprom_addr):
+ if self.mboard_eeprom_addr:
self.log.trace("Reading EEPROM from address `{}'..."
.format(self.mboard_eeprom_addr))
- if (not get_eeprom_paths(self.mboard_eeprom_addr)):
- raise RuntimeError("No EEPROM found at address `{}'"
- .format(self.mboard_eeprom_addr))
+ eeprom_paths = get_eeprom_paths(self.mboard_eeprom_addr)
+ if not eeprom_paths:
+ self.log.error("Could not identify EEPROM paths for %s!",
+ self.mboard_eeprom_addr)
+ return {}, b''
+ self.log.trace("Found mboard EEPROM path: %s", eeprom_paths[0])
(eeprom_head, eeprom_rawdata) = eeprom.read_eeprom(
- get_eeprom_paths(self.mboard_eeprom_addr)[0],
+ eeprom_paths[0],
self.mboard_eeprom_offset,
eeprom.MboardEEPROM.eeprom_header_format,
eeprom.MboardEEPROM.eeprom_header_keys,
@@ -512,6 +515,22 @@ class PeriphManagerBase(object):
###########################################################################
# Misc device status controls and indicators
###########################################################################
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ raise NotImplementedError("set_device_id() not implemented.")
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ raise NotImplementedError("get_device_id() not implemented.")
+
def get_init_status(self):
"""
Returns the status of the device after its initialization (that happens
@@ -592,6 +611,20 @@ class PeriphManagerBase(object):
"""
return [dboard.device_info for dboard in self.dboards]
+ @no_claim
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ raise NotImplementedError("get_proto_ver() not implemented.")
+
+ @no_claim
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ raise NotImplementedError("get_chdr_width() not implemented.")
+
###########################################################################
# Component updating
###########################################################################
@@ -670,64 +703,10 @@ class PeriphManagerBase(object):
self.log.trace("Component info: {}".format(metadata))
# Convert all values to str
return dict([a, str(x)] for a, x in metadata.items())
- else:
- self.log.trace("Component not found in updateable components: {}"
- .format(component_name))
- return {}
-
- ###########################################################################
- # Crossbar control
- ###########################################################################
- @no_claim
- def get_num_xbars(self):
- """
- Returns the number of crossbars instantiated in the current design
- """
- return 1 # FIXME
-
- @no_claim
- def get_num_blocks(self, xbar_index):
- """
- Returns the number of blocks connected to crossbar with index
- xbar_index.
-
- xbar_index -- The index of the crossbar that's being queried.
- docstring for get_num_blocks"""
- # FIXME udev lookup
- xbar_sysfs_path = '/sys/class/rfnoc_crossbar/crossbar{}/nports'.format(
- xbar_index
- )
- return int(open(xbar_sysfs_path).read().strip()) - \
- self.get_base_port(xbar_index)
-
- @no_claim
- def get_base_port(self, xbar_index):
- """
- Returns the index of the first port which is connected to an RFNoC
- block. Example: Assume there are two SFPs connected to the crossbar, and
- one DMA engine for CHDR traffic. The convention would be to connect
- those to ports 0, 1, and 2, respectively. This makes port 3 the first
- block to be connected to an RFNoC block.
-
- xbar_index -- The index of the crossbar that's being queried
- """
- return self.crossbar_base_port
-
- def set_xbar_local_addr(self, xbar_index, local_addr):
- """
- Program crossbar xbar_index to have the local address local_addr.
- """
- # FIXME udev lookup
- xbar_sysfs_path = '/sys/class/rfnoc_crossbar/crossbar{}/local_addr'.format(
- xbar_index
- )
- laddr_value = "0x{:X}".format(local_addr)
- self.log.trace("Setting local address for xbar {} to {}.".format(
- xbar_sysfs_path, laddr_value
- ))
- with open(xbar_sysfs_path, "w") as xbar_file:
- xbar_file.write(laddr_value)
- return True
+ # else:
+ self.log.trace("Component not found in updateable components: {}"
+ .format(component_name))
+ return {}
##########################################################################
# Mboard Sensors
@@ -823,86 +802,44 @@ class PeriphManagerBase(object):
#######################################################################
# Transport API
#######################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type,
- ):
- """
- When setting up a CHDR connection, this is the first call to be
- made. This function will return a list of dictionaries, each
- describing a way to open an CHDR connection. The list of dictionaries
- is sorted by preference, meaning that the caller should use the first
- option, if possible.
- All transports requested are bidirectional.
-
- The callee must maintain a lock on the available CHDR xports. After
- calling request_xport(), the caller needs to pick one of the
- dictionaries, possibly amend data (e.g., if the connection is an
- Ethernet connection, then we need to know the source port, but more
- details on that in commit_xport()'s documentation).
- One way to implement a lock is to simply lock a mutex here and
- unlock it in commit_xport(), even though there are probably more
- nuanced solutions.
+ def get_chdr_link_types(self):
+ """
+ Return a list of ways how the UHD session can connect to this device to
+ initiate CHDR traffic.
- Arguments:
- dst_sid -- The destination part of the connection, i.e., which
- RFNoC block are we connecting to. Example: 0x0230
- suggested_src_sid -- The source part of the connection, i.e.,
- what's the source address of packets going to
- the destination at dst_sid. This is a
- suggestion, MPM can override this. Example:
- 0x0001.
- xport_type -- One of the following strings: CTRL, ASYNC_MSG,
- TX_DATA, RX_DATA. See also xports_type_t in UHD.
-
- The return value is a list of dictionaries. Every dictionary has
- the following key/value pairs:
- - type: Type of transport, e.g., "UDP", "liberio".
- - ipv4 (UDP only): IPv4 address to connect to.
- - port (UDP only): IP port to connect to.
- - send_sid: String version of the SID used for this transport. This is
- the definitive version of the SID, the suggested_src_address
- can be ignored at this point.
- - allocation: This is an integer value which represents a score of
- how much bandwidth is used. Note: Currently does not
- have any unit, is just a counter. Higher numbers mean
- higher utilization. RX means device to UHD, for
- example, committing an RX streamer would increase this
- value.
- This key is optional, MPM does not have to provide it.
- If the allocation affects the preference, it will be
- factored into the order of the results, meaning the
- caller does not strictly have to check its value even if
- the transport option with the smallest allocation is
- preferred.
-
- Note: The dictionary may include other keys which should be ignored,
- or at the very least, kept intact. commit_xport() might be requiring
- them.
- """
- raise NotImplementedError("request_xport() not implemented.")
-
- def commit_xport(self, xport_info):
- """
- When setting up a CHDR connection, this is the second call to be
- made.
+ The return value is a list of strings. Every string is a key for a
+ transport type. Values include:
+ - "udp": Means this device can be reached via UDP
+ - "liberio": Means this device can be reached via Liberio (local DMA)
- Arguments:
- xport_info -- A dictionary (string -> string). The dictionary must
- have been originally created by request_xport(), but
- additional key/value pairs need to be added.
+ The list is filtered based on what the device knows about where the UHD
+ session is. For example, on an N310, it will only either return "UDP"
+ or "Liberio", depending on if we're remotely launching UHD, or locally.
- All transports need to also provide:
- - rx_mtu: In bytes, the max number of bytes going from device to UHD
- - tx_mtu: In bytes, the max number of bytes going from UHD to device
+ In order to get further information about how to connect to the device,
+ the keys returned from this function can be used with
+ get_chdr_link_options().
+ """
+ raise NotImplementedError("get_chdr_link_types() not implemented.")
- UDP transports need to also provide:
- - src_ipv4: IPv4 address the connection is coming from.
- - src_port: IP port the connection is coming from.
+ def get_chdr_link_options(self, xport_type):
"""
- raise NotImplementedError("commit_xport() not implemented.")
+ Returns a list of dictionaries. Every dictionary contains information
+ about one way to connect to this device in order to initiate CHDR
+ traffic.
+
+ The interpretation of the return value is very highly dependant on the
+ transport type (xport_type).
+ For UDP, the every entry of the list has the following keys:
+ - ipv4 (IP Address)
+ - port (UDP port)
+ - link_rate (bps of the link, e.g. 10e9 for 10GigE)
+
+ For Liberio, every entry has the following keys:
+ - tx_dev: TX device (/dev/tx-dma*)
+ - rx_dev: RX device (/dev/rx-dma*)
+ """
+ raise NotImplementedError("get_chdr_link_options() not implemented.")
#######################################################################
# Claimer API
@@ -927,3 +864,52 @@ class PeriphManagerBase(object):
"""
self.log.debug("Device was unclaimed. No actions defined.")
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ raise NotImplementedError("get_num_timekeepers() not implemented.")
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ raise NotImplementedError(
+ "get_ticks_now({}, {}) not implemented.".format(tk_idx, last_pps))
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ raise NotImplementedError(
+ "set_ticks_last_pps({}, {}, {}) not implemented."
+ .format(tk_idx, ticks, next_pps))
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ raise NotImplementedError(
+ "set_tick_period({}) not implemented.".format(tk_idx, period_ns))
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ raise NotImplementedError("get_clocks() not implemented.")
diff --git a/mpm/python/usrp_mpm/periph_manager/e31x.py b/mpm/python/usrp_mpm/periph_manager/e31x.py
index 722f8d7a9..e93cbef25 100644
--- a/mpm/python/usrp_mpm/periph_manager/e31x.py
+++ b/mpm/python/usrp_mpm/periph_manager/e31x.py
@@ -30,7 +30,7 @@ from usrp_mpm import e31x_legacy_eeprom
E310_DEFAULT_CLOCK_SOURCE = 'internal'
E310_DEFAULT_TIME_SOURCE = 'internal'
E310_DEFAULT_ENABLE_FPGPIO = True
-E310_FPGA_COMPAT = (1,0)
+E310_FPGA_COMPAT = (4,0)
E310_DBOARD_SLOT_IDX = 0
###############################################################################
@@ -39,9 +39,7 @@ E310_DBOARD_SLOT_IDX = 0
class E310XportMgrLiberio(XportMgrLiberio):
" E310-specific Liberio configuration "
- max_chan = 4
- xbar_dev = "/dev/crossbar0"
- xbar_port = 0
+ max_chan = 5
###############################################################################
# Main Class
@@ -178,7 +176,6 @@ class e31x(ZynqComponents, PeriphManagerBase):
self._tear_down = False
self._clock_source = None
self._time_source = None
- self._available_endpoints = list(range(256))
self.dboard = self.dboards[E310_DBOARD_SLOT_IDX]
try:
self._init_peripherals(self.args_cached)
@@ -277,9 +274,6 @@ class e31x(ZynqComponents, PeriphManagerBase):
self.mboard_regs_control.get_build_timestamp()
self._check_fpga_compat()
self._update_fpga_type()
- self.crossbar_base_port = self.mboard_regs_control.get_xbar_baseport()
- self.log.debug("crossbar base port: {}".format(self.crossbar_base_port))
-
# Init clocking
self._init_ref_clock_and_time(args)
# Init CHDR transports
@@ -443,7 +437,6 @@ class e31x(ZynqComponents, PeriphManagerBase):
for xport_mgr in itervalues(self._xport_mgrs):
xport_mgr.deinit()
self.log.trace("Resetting SID pool...")
- self._available_endpoints = list(range(256))
if not self._do_not_reload:
self.tear_down()
# Reset back to value from _default_args (mpm.conf)
@@ -480,56 +473,22 @@ class e31x(ZynqComponents, PeriphManagerBase):
self.log.trace("Found idle overlay: %s", idle_overlay)
return is_idle
+
###########################################################################
# Transport API
###########################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type
- ):
- """
- See PeriphManagerBase.request_xport() for docs.
- """
- # Try suggested address first, then just pick the first available one:
- src_address = suggested_src_address
- if src_address not in self._available_endpoints:
- if not self._available_endpoints:
- raise RuntimeError(
- "Depleted pool of SID endpoints for this device!")
- else:
- src_address = self._available_endpoints[0]
- sid = SID(src_address << 16 | dst_address)
- # Note: This SID may change its source address!
- self.log.trace(
- "request_xport(dst=0x%04X, suggested_src_address=0x%04X, xport_type=%s): " \
- "operating on temporary SID: %s",
- dst_address, suggested_src_address, str(xport_type), str(sid))
- assert self.mboard_info['rpc_connection'] in ('local')
- if self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].request_xport(
- sid,
- xport_type,
- )
-
- def commit_xport(self, xport_info):
+ def get_chdr_link_types(self):
"""
- See PeriphManagerBase.commit_xport() for docs.
+ See PeriphManagerBase.get_chdr_link_types() for docs.
+ """
+ return ['liberio']
- Reminder: All connections are incoming, i.e. "send" or "TX" means
- remote device to local device, and "receive" or "RX" means this local
- device to remote device. "Remote device" can be, for example, a UHD
- session.
+ def get_chdr_link_options(self, xport_type):
+ """
+ See PeriphManagerBase.get_chdr_link_options() for docs.
"""
- ## Go, go, go
- assert self.mboard_info['rpc_connection'] in ('local')
- sid = SID(xport_info['send_sid'])
- self._available_endpoints.remove(sid.src_ep)
- self.log.debug("Committing transport for SID %s, xport info: %s",
- str(sid), str(xport_info))
- if self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].commit_xport(sid, xport_info)
+ if xport_type == 'liberio':
+ return self._xport_mgrs['liberio'].get_chdr_link_options()
###########################################################################
# Device info
@@ -550,6 +509,39 @@ class e31x(ZynqComponents, PeriphManagerBase):
})
return device_info
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ self.log.debug("Setting device ID to `{}'".format(device_id))
+ self.mboard_regs_control.set_device_id(device_id)
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ return self.mboard_regs_control.get_device_id()
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ proto_ver = self.mboard_regs_control.get_proto_ver()
+ self.log.debug("RFNoC protocol version supported by this device is {}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ chdr_width = self.mboard_regs_control.get_chdr_width()
+ self.log.debug("CHDR width supported by the device is {}".format(chdr_width))
+ return chdr_width
+
###########################################################################
# Clock/Time API
###########################################################################
@@ -742,3 +734,59 @@ class e31x(ZynqComponents, PeriphManagerBase):
fpga_type = self.mboard_regs_control.get_fpga_type()
self.log.debug("Updating mboard FPGA type info to {}".format(fpga_type))
self.updateable_components['fpga']['type'] = fpga_type
+
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ return self.mboard_regs_control.get_num_timekeepers()
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ return self.mboard_regs_control.get_timekeeper_time(tk_idx, last_pps)
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ self.mboard_regs_control.set_timekeeper_time(tk_idx, ticks, next_pps)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ self.mboard_regs_control.set_tick_period(tk_idx, period_ns)
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ return [
+ {
+ 'name': 'radio_clk',
+ 'freq': str(self.dboard.get_master_clock_rate()),
+ 'mutable': 'true'
+ },
+ {
+ 'name': 'bus_clk',
+ 'freq': str(100e6),
+ }
+ ]
diff --git a/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py
index 0b166e5bc..330cd830e 100644
--- a/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/e31x_periphs.py
@@ -33,23 +33,40 @@ class MboardRegsControl(object):
Control the FPGA Motherboard registers
"""
# Motherboard registers
- MB_COMPAT_NUM = 0x0000
- MB_DATESTAMP = 0x0004
- MB_GIT_HASH = 0x0008
- MB_SCRATCH = 0x000C
- MB_NUM_CE = 0x0010
- MB_NUM_IO_CE = 0x0014
- MB_CLOCK_CTRL = 0x0018
- MB_XADC_RB = 0x001C
- MB_BUS_CLK_RATE = 0x0020
- MB_BUS_COUNTER = 0x0024
- MB_GPIO_MASTER = 0x0030
- MB_GPIO_RADIO_SRC = 0x0034
- MB_GPS_CTRL = 0x0038
- MB_GPS_STATUS = 0x003C
- MB_DBOARD_CTRL = 0x0040
- MB_DBOARD_STATUS = 0x0044
- MB_XBAR_BASEPORT = 0x0048
+ MB_COMPAT_NUM = 0x0000
+ MB_DATESTAMP = 0x0004
+ MB_GIT_HASH = 0x0008
+ MB_SCRATCH = 0x000C
+ MB_DEVICE_ID = 0x0010
+ MB_RFNOC_INFO = 0x0014
+ MB_CLOCK_CTRL = 0x0018
+ MB_XADC_RB = 0x001C
+ MB_BUS_CLK_RATE = 0x0020
+ MB_BUS_COUNTER = 0x0024
+ MB_SFP_PORT_INFO = 0x0028
+ MB_GPIO_CTRL = 0x002C
+ MB_GPIO_MASTER = 0x0030
+ MB_GPIO_RADIO_SRC = 0x0034
+ MB_GPS_CTRL = 0x0038
+ MB_GPS_STATUS = 0x003C
+ MB_DBOARD_CTRL = 0x0040
+ MB_DBOARD_STATUS = 0x0044
+ MB_NUM_TIMEKEEPERS = 0x0048
+ # Timekeeper registers
+ MB_TIME_NOW_LO = 0x1000
+ MB_TIME_NOW_HI = 0x1004
+ MB_TIME_EVENT_LO = 0x1008
+ MB_TIME_EVENT_HI = 0x100C
+ MB_TIME_CTRL = 0x1010
+ MB_TIME_LAST_PPS_LO = 0x1014
+ MB_TIME_LAST_PPS_HI = 0x1018
+ MB_TIME_BASE_PERIOD_LO = 0x101C
+ MB_TIME_BASE_PERIOD_HI = 0x1020
+ MB_TIMEKEEPER_OFFSET = 12
+
+ # Bitfield locations for the MB_RFNOC_INFO register.
+ MB_RFNOC_INFO_PROTO_VER = 0
+ MB_RFNOC_INFO_CHDR_WIDTH = 16
# Bitfield locations for the MB_CLOCK_CTRL register.
MB_CLOCK_CTRL_PPS_SEL_INT = 0
@@ -100,6 +117,26 @@ class MboardRegsControl(object):
major = (compat_number>>16) & 0xff
return (major, minor)
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ proto_ver = (reg_val & 0x0000ffff) >> self.MB_RFNOC_INFO_PROTO_VER
+ self.log.trace("Read RFNOC_PROTO_VER 0x{:08X}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ chdr_width = (reg_val & 0xffff0000) >> self.MB_RFNOC_INFO_CHDR_WIDTH
+ self.log.trace("Read RFNOC_CHDR_WIDTH 0x{:08X}".format(chdr_width))
+ return chdr_width
+
def set_fp_gpio_master(self, value):
"""set driver for front panel GPIO
Arguments:
@@ -136,6 +173,24 @@ class MboardRegsControl(object):
with self.regs:
return self.peek32(self.MB_GPIO_RADIO_SRC) & 0xffffff
+ def set_device_id(self, device_id):
+ """
+ Set device ID
+ """
+ with self.regs:
+ self.log.trace("Writing MB_DEVICE_ID with 0x{:08X}".format(device_id))
+ return self.poke32(self.MB_DEVICE_ID, device_id)
+
+ def get_device_id(self):
+ """
+ Get device ID
+ """
+ with self.regs:
+ regs_val = self.peek32(self.MB_DEVICE_ID)
+ device_id = regs_val & 0x0000ffff
+ self.log.trace("Read MB_DEVICE_ID 0x{:08X}".format(device_id))
+ return device_id
+
def get_build_timestamp(self):
"""
Returns the build date/time for the FPGA image.
@@ -206,6 +261,71 @@ class MboardRegsControl(object):
else:
assert False, "Cannot set to invalid clock source: {}".format(clock_source)
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ with self.regs:
+ return self.peek32(self.MB_NUM_TIMEKEEPERS)
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ addr_lo = \
+ (self.MB_TIME_LAST_PPS_LO if last_pps else self.MB_TIME_NOW_LO) + \
+ tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ with self.regs:
+ time_lo = self.peek32(addr_lo)
+ time_hi = self.peek32(addr_hi)
+ return time_hi << 32 | time_lo
+
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ addr_lo = \
+ self.MB_TIME_EVENT_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ addr_ctrl = \
+ self.MB_TIME_CTRL + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ time_lo = ticks & 0xFFFFFFFF
+ time_hi = (ticks > 32) & 0xFFFFFFFF
+ time_ctrl = 0x2 if next_pps else 0x1
+ self.log.trace("Setting time on timekeeper %d to %d %s", tk_idx, ticks,
+ ("on next pps" if next_pps else "now"))
+ with self.regs:
+ self.poke32(addr_lo, time_lo)
+ self.poke32(addr_hi, time_hi)
+ self.poke32(addr_ctrl, time_ctrl)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ addr_lo = self.MB_TIME_BASE_PERIOD_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ period_lo = period_ns & 0xFFFFFFFF
+ period_hi = (period_ns > 32) & 0xFFFFFFFF
+ with self.regs:
+ self.poke32(addr_lo, period_lo)
+ self.poke32(addr_hi, period_hi)
+
def get_fpga_type(self):
"""
Reads the type of the FPGA image currently loaded
@@ -248,7 +368,8 @@ class MboardRegsControl(object):
reg_val = self.peek32(self.MB_DBOARD_CTRL)
if channel_mode == "MIMO":
reg_val = (0b1 << self.MB_DBOARD_CTRL_MIMO)
- self.log.trace("Setting channel mode in AD9361 interface: {}".format("2R2T" if channel_mode == 2 else "1R1T"))
+ self.log.trace("Setting channel mode in AD9361 interface: %s",
+ "2R2T" if channel_mode == 2 else "1R1T")
else:
# Warn if user tries to set either tx0/tx1 in mimo mode
# as both will be set automatically
@@ -269,7 +390,7 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_TX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("TX RF PLL reporting unlocked. ")
@@ -283,15 +404,10 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_RX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("RX RF PLL reporting unlocked. ")
else:
self.log.trace("RX RF PLL locked")
return locked
-
- def get_xbar_baseport(self):
- "Get the RFNoC crossbar base port"
- with self.regs:
- return self.peek32(self.MB_XBAR_BASEPORT)
diff --git a/mpm/python/usrp_mpm/periph_manager/e320.py b/mpm/python/usrp_mpm/periph_manager/e320.py
index f76e6ac3e..04999e505 100644
--- a/mpm/python/usrp_mpm/periph_manager/e320.py
+++ b/mpm/python/usrp_mpm/periph_manager/e320.py
@@ -32,7 +32,7 @@ E320_DEFAULT_CLOCK_SOURCE = 'internal'
E320_DEFAULT_TIME_SOURCE = 'internal'
E320_DEFAULT_ENABLE_GPS = True
E320_DEFAULT_ENABLE_FPGPIO = True
-E320_FPGA_COMPAT = (3, 1)
+E320_FPGA_COMPAT = (4, 0)
E320_MONITOR_THREAD_INTERVAL = 1.0 # seconds
E320_DBOARD_SLOT_IDX = 0
@@ -42,21 +42,15 @@ E320_DBOARD_SLOT_IDX = 0
###############################################################################
class E320XportMgrUDP(XportMgrUDP):
"E320-specific UDP configuration"
- xbar_dev = "/dev/crossbar0"
iface_config = {
'sfp0': {
'label': 'misc-enet-regs',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
}
}
class E320XportMgrLiberio(XportMgrLiberio):
" E320-specific Liberio configuration "
max_chan = 6
- xbar_dev = "/dev/crossbar0"
- xbar_port = 1
###############################################################################
# Main Class
@@ -139,7 +133,6 @@ class e320(ZynqComponents, PeriphManagerBase):
self._ext_clock_freq = E320_DEFAULT_EXT_CLOCK_FREQ
self._clock_source = None
self._time_source = None
- self._available_endpoints = list(range(256))
self._gpsd = None
self.dboard = self.dboards[E320_DBOARD_SLOT_IDX]
from functools import partial
@@ -266,7 +259,6 @@ class e320(ZynqComponents, PeriphManagerBase):
self.mboard_regs_control.get_build_timestamp()
self._check_fpga_compat()
self._update_fpga_type()
- self.crossbar_base_port = self.mboard_regs_control.get_xbar_baseport()
# Init peripherals
self.enable_gps(
enable=str2bool(
@@ -274,10 +266,7 @@ class e320(ZynqComponents, PeriphManagerBase):
)
)
self.enable_fp_gpio(
- enable=args.get(
- 'enable_fp_gpio',
- E320_DEFAULT_ENABLE_FPGPIO
- )
+ enable=args.get('enable_fp_gpio', E320_DEFAULT_ENABLE_FPGPIO)
)
# Init clocking
self._init_ref_clock_and_time(args)
@@ -285,8 +274,8 @@ class e320(ZynqComponents, PeriphManagerBase):
self._init_gps_sensors()
# Init CHDR transports
self._xport_mgrs = {
- 'udp': E320XportMgrUDP(self.log.getChild('UDP'), args),
- 'liberio': E320XportMgrLiberio(self.log.getChild('liberio')),
+ 'udp': E320XportMgrUDP(self.log, args),
+ 'liberio': E320XportMgrLiberio(self.log),
}
# Spawn status monitoring thread
self.log.trace("Spawning status monitor thread...")
@@ -347,8 +336,6 @@ class e320(ZynqComponents, PeriphManagerBase):
super(e320, self).deinit()
for xport_mgr in itervalues(self._xport_mgrs):
xport_mgr.deinit()
- self.log.trace("Resetting SID pool...")
- self._available_endpoints = list(range(256))
def tear_down(self):
"""
@@ -372,61 +359,37 @@ class e320(ZynqComponents, PeriphManagerBase):
###########################################################################
# Transport API
###########################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type
- ):
- """
- See PeriphManagerBase.request_xport() for docs.
- """
- # Try suggested address first, then just pick the first available one:
- src_address = suggested_src_address
- if src_address not in self._available_endpoints:
- if not self._available_endpoints:
- raise RuntimeError(
- "Depleted pool of SID endpoints for this device!")
- else:
- src_address = self._available_endpoints[0]
- sid = SID(src_address << 16 | dst_address)
- # Note: This SID may change its source address!
- self.log.trace(
- "request_xport(dst=0x%04X, suggested_src_address=0x%04X, xport_type=%s): " \
- "operating on temporary SID: %s",
- dst_address, suggested_src_address, str(xport_type), str(sid))
- # FIXME token!
+ def get_chdr_link_types(self):
+ """
+ This will only ever return a single item (udp or liberio).
+ """
assert self.mboard_info['rpc_connection'] in ('remote', 'local')
if self.mboard_info['rpc_connection'] == 'remote':
- return self._xport_mgrs['udp'].request_xport(
- sid,
- xport_type,
- )
- elif self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].request_xport(
- sid,
- xport_type,
- )
+ return ["udp"]
+ # else:
+ return ["liberio"]
- def commit_xport(self, xport_info):
+ def get_chdr_link_options(self, xport_type):
"""
- See PeriphManagerBase.commit_xport() for docs.
+ Returns a list of dictionaries. Every dictionary contains information
+ about one way to connect to this device in order to initiate CHDR
+ traffic.
+
+ The interpretation of the return value is very highly dependant on the
+ transport type (xport_type).
+ For UDP, the every entry of the list has the following keys:
+ - ipv4 (IP Address)
+ - port (UDP port)
+ - link_rate (bps of the link, e.g. 10e9 for 10GigE)
- Reminder: All connections are incoming, i.e. "send" or "TX" means
- remote device to local device, and "receive" or "RX" means this local
- device to remote device. "Remote device" can be, for example, a UHD
- session.
+ For Liberio, every entry has the following keys:
+ - tx_dev: TX device (/dev/tx-dma*)
+ - rx_dev: RX device (/dev/rx-dma*)
"""
- ## Go, go, go
- assert self.mboard_info['rpc_connection'] in ('remote', 'local')
- sid = SID(xport_info['send_sid'])
- self._available_endpoints.remove(sid.src_ep)
- self.log.debug("Committing transport for SID %s, xport info: %s",
- str(sid), str(xport_info))
- if self.mboard_info['rpc_connection'] == 'remote':
- return self._xport_mgrs['udp'].commit_xport(sid, xport_info)
- elif self.mboard_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].commit_xport(sid, xport_info)
+ if xport_type not in self._xport_mgrs:
+ self.log.warning("Can't get link options for unknown link type: `{}'.")
+ return []
+ return self._xport_mgrs[xport_type].get_chdr_link_options()
###########################################################################
# Device info
@@ -443,10 +406,43 @@ class e320(ZynqComponents, PeriphManagerBase):
*self.mboard_regs_control.get_compat_number()),
'fpga_version_hash': "{:x}.{}".format(
*self.mboard_regs_control.get_git_hash()),
- 'fpga': self.updateable_components.get('fpga', {}).get('type',""),
+ 'fpga': self.updateable_components.get('fpga', {}).get('type', ""),
})
return device_info
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ self.log.debug("Setting device ID to `{}'".format(device_id))
+ self.mboard_regs_control.set_device_id(device_id)
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ return self.mboard_regs_control.get_device_id()
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ proto_ver = self.mboard_regs_control.get_proto_ver()
+ self.log.debug("RFNoC protocol version supported by this device is {}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ chdr_width = self.mboard_regs_control.get_chdr_width()
+ self.log.debug("CHDR width supported by the device is {}".format(chdr_width))
+ return chdr_width
+
###########################################################################
# Clock/Time API
###########################################################################
@@ -637,7 +633,7 @@ class e320(ZynqComponents, PeriphManagerBase):
except ValueError:
self.log.warning("Error when converting temperature value")
except KeyError:
- self.log.warning("Can't read temp on thermal_zone".format(sensor))
+ self.log.warning("Can't read temp on thermal_zone {}".format(sensor))
return {
'name': sensor_name,
'type': 'REALNUM',
@@ -753,3 +749,59 @@ class e320(ZynqComponents, PeriphManagerBase):
fpga_type = self.mboard_regs_control.get_fpga_type()
self.log.debug("Updating mboard FPGA type info to {}".format(fpga_type))
self.updateable_components['fpga']['type'] = fpga_type
+
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ return self.mboard_regs_control.get_num_timekeepers()
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ return self.mboard_regs_control.get_timekeeper_time(tk_idx, last_pps)
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ self.mboard_regs_control.set_timekeeper_time(tk_idx, ticks, next_pps)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ self.mboard_regs_control.set_tick_period(tk_idx, period_ns)
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ return [
+ {
+ 'name': 'radio_clk',
+ 'freq': str(self.dboard.get_master_clock_rate()),
+ 'mutable': 'true'
+ },
+ {
+ 'name': 'bus_clk',
+ 'freq': str(200e6),
+ }
+ ]
diff --git a/mpm/python/usrp_mpm/periph_manager/e320_periphs.py b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
index 0cf7f59c6..cad5c39ad 100644
--- a/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/e320_periphs.py
@@ -48,25 +48,40 @@ class MboardRegsControl(object):
Control the FPGA Motherboard registers
"""
# Motherboard registers
- MB_COMPAT_NUM = 0x0000
- MB_DATESTAMP = 0x0004
- MB_GIT_HASH = 0x0008
- MB_SCRATCH = 0x000C
- MB_NUM_CE = 0x0010
- MB_NUM_IO_CE = 0x0014
- MB_CLOCK_CTRL = 0x0018
- MB_XADC_RB = 0x001C
- MB_BUS_CLK_RATE = 0x0020
- MB_BUS_COUNTER = 0x0024
- MB_SFP_PORT_INFO = 0x0028
- MB_GPIO_CTRL = 0x002C
- MB_GPIO_MASTER = 0x0030
- MB_GPIO_RADIO_SRC = 0x0034
- MB_GPS_CTRL = 0x0038
- MB_GPS_STATUS = 0x003C
- MB_DBOARD_CTRL = 0x0040
- MB_DBOARD_STATUS = 0x0044
- MB_XBAR_BASEPORT = 0x0048
+ MB_COMPAT_NUM = 0x0000
+ MB_DATESTAMP = 0x0004
+ MB_GIT_HASH = 0x0008
+ MB_SCRATCH = 0x000C
+ MB_DEVICE_ID = 0x0010
+ MB_RFNOC_INFO = 0x0014
+ MB_CLOCK_CTRL = 0x0018
+ MB_XADC_RB = 0x001C
+ MB_BUS_CLK_RATE = 0x0020
+ MB_BUS_COUNTER = 0x0024
+ MB_SFP_PORT_INFO = 0x0028
+ MB_GPIO_CTRL = 0x002C
+ MB_GPIO_MASTER = 0x0030
+ MB_GPIO_RADIO_SRC = 0x0034
+ MB_GPS_CTRL = 0x0038
+ MB_GPS_STATUS = 0x003C
+ MB_DBOARD_CTRL = 0x0040
+ MB_DBOARD_STATUS = 0x0044
+ MB_NUM_TIMEKEEPERS = 0x0048
+ # Timekeeper registers
+ MB_TIME_NOW_LO = 0x1000
+ MB_TIME_NOW_HI = 0x1004
+ MB_TIME_EVENT_LO = 0x1008
+ MB_TIME_EVENT_HI = 0x100C
+ MB_TIME_CTRL = 0x1010
+ MB_TIME_LAST_PPS_LO = 0x1014
+ MB_TIME_LAST_PPS_HI = 0x1018
+ MB_TIME_BASE_PERIOD_LO = 0x101C
+ MB_TIME_BASE_PERIOD_HI = 0x1020
+ MB_TIMEKEEPER_OFFSET = 12
+
+ # Bitfield locations for the MB_RFNOC_INFO register.
+ MB_RFNOC_INFO_PROTO_VER = 0
+ MB_RFNOC_INFO_CHDR_WIDTH = 16
# Bitfield locations for the MB_CLOCK_CTRL register.
MB_CLOCK_CTRL_PPS_SEL_INT = 0
@@ -122,6 +137,44 @@ class MboardRegsControl(object):
major = (compat_number>>16) & 0xff
return (major, minor)
+ def set_device_id(self, device_id):
+ """
+ Set device ID
+ """
+ with self.regs:
+ self.log.trace("Writing MB_DEVICE_ID with 0x{:08X}".format(device_id))
+ return self.poke32(self.MB_DEVICE_ID, device_id)
+
+ def get_device_id(self):
+ """
+ Get device ID
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_DEVICE_ID)
+ device_id = reg_val & 0x0000ffff
+ self.log.trace("Read MB_DEVICE_ID 0x{:08X}".format(device_id))
+ return device_id
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ proto_ver = (reg_val & 0x0000ffff) >> self.MB_RFNOC_INFO_PROTO_VER
+ self.log.trace("Read RFNOC_PROTO_VER 0x{:08X}".format(proto_ver))
+ return proto_ver;
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ chdr_width = (reg_val & 0xffff0000) >> self.MB_RFNOC_INFO_CHDR_WIDTH
+ self.log.trace("Read RFNOC_CHDR_WIDTH 0x{:08X}".format(chdr_width))
+ return chdr_width
+
def enable_fp_gpio(self, enable):
""" Enable front panel GPIO buffers and power supply
and set voltage 3.3 V
@@ -372,7 +425,8 @@ class MboardRegsControl(object):
reg_val = self.peek32(self.MB_DBOARD_CTRL)
if channel_mode == "MIMO":
reg_val = (0b1 << self.MB_DBOARD_CTRL_MIMO)
- self.log.trace("Setting channel mode in AD9361 interface: {}".format("2R2T" if channel_mode == 2 else "1R1T"))
+ self.log.trace("Setting channel mode in AD9361 interface: %s",
+ "2R2T" if channel_mode == 2 else "1R1T")
else:
# Warn if user tries to set either tx0/tx1 in mimo mode
# as both will be set automatically
@@ -393,7 +447,7 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_TX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("TX RF PLL reporting unlocked. ")
@@ -407,7 +461,7 @@ class MboardRegsControl(object):
"""
mask = 0b1 << self.MB_DBOARD_STATUS_RX_LOCK
with self.regs:
- reg_val = self.peek32(self.MB_DBOARD_STATUS)
+ reg_val = self.peek32(self.MB_DBOARD_STATUS)
locked = (reg_val & mask) > 0
if not locked:
self.log.warning("RX RF PLL reporting unlocked. ")
@@ -415,7 +469,67 @@ class MboardRegsControl(object):
self.log.trace("RX RF PLL locked")
return locked
- def get_xbar_baseport(self):
- "Get the RFNoC crossbar base port"
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ with self.regs:
+ return self.peek32(self.MB_NUM_TIMEKEEPERS)
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ addr_lo = \
+ (self.MB_TIME_LAST_PPS_LO if last_pps else self.MB_TIME_NOW_LO) + \
+ tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ with self.regs:
+ time_lo = self.peek32(addr_lo)
+ time_hi = self.peek32(addr_hi)
+ return time_hi << 32 | time_lo
+
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ addr_lo = \
+ self.MB_TIME_EVENT_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ addr_ctrl = \
+ self.MB_TIME_CTRL + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ time_lo = ticks & 0xFFFFFFFF
+ time_hi = (ticks > 32) & 0xFFFFFFFF
+ time_ctrl = 0x2 if next_pps else 0x1
+ self.log.trace("Setting time on timekeeper %d to %d %s", tk_idx, ticks,
+ ("on next pps" if next_pps else "now"))
+ with self.regs:
+ self.poke32(addr_lo, time_lo)
+ self.poke32(addr_hi, time_hi)
+ self.poke32(addr_ctrl, time_ctrl)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ addr_lo = self.MB_TIME_BASE_PERIOD_LO + tk_idx * self.MB_TIMEKEEPER_OFFSET
+ addr_hi = addr_lo + 4
+ period_lo = period_ns & 0xFFFFFFFF
+ period_hi = (period_ns > 32) & 0xFFFFFFFF
with self.regs:
- return self.peek32(self.MB_XBAR_BASEPORT)
+ self.poke32(addr_lo, period_lo)
+ self.poke32(addr_hi, period_hi)
diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py
index 2695dbfba..8d5f83569 100644
--- a/mpm/python/usrp_mpm/periph_manager/n3xx.py
+++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py
@@ -41,7 +41,7 @@ N3XX_DEFAULT_ENABLE_PPS_EXPORT = True
N32X_DEFAULT_QSFP_RATE_PRESET = 'Ethernet'
N32X_DEFAULT_QSFP_DRIVER_PRESET = 'Optical'
N32X_QSFP_I2C_LABEL = 'qsfp-i2c'
-N3XX_FPGA_COMPAT = (5, 3)
+N3XX_FPGA_COMPAT = (6, 0)
N3XX_MONITOR_THREAD_INTERVAL = 1.0 # seconds
# Import daughterboard PIDs from their respective classes
@@ -52,39 +52,24 @@ RHODIUM_PID = Rhodium.pids[0]
###############################################################################
# Transport managers
###############################################################################
+# pylint: disable=too-few-public-methods
class N3xxXportMgrUDP(XportMgrUDP):
" N3xx-specific UDP configuration "
- xbar_dev = "/dev/crossbar0"
iface_config = {
'bridge0': {
'label': 'misc-enet-regs0',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
},
'sfp0': {
'label': 'misc-enet-regs0',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
},
'sfp1': {
'label': 'misc-enet-regs1',
- 'xbar': 0,
- 'xbar_port': 1,
- 'ctrl_src_addr': 1,
},
'eth1': {
'label': 'misc-enet-regs0',
- 'xbar': 0,
- 'xbar_port': 0,
- 'ctrl_src_addr': 0,
},
'eth2': {
'label': 'misc-enet-regs1',
- 'xbar': 0,
- 'xbar_port': 1,
- 'ctrl_src_addr': 1,
},
}
bridges = {'bridge0': ['sfp0', 'sfp1', 'bridge0']}
@@ -92,8 +77,7 @@ class N3xxXportMgrUDP(XportMgrUDP):
class N3xxXportMgrLiberio(XportMgrLiberio):
" N3xx-specific Liberio configuration "
max_chan = 10
- xbar_dev = "/dev/crossbar0"
- xbar_port = 2
+# pylint: enable=too-few-public-methods
###############################################################################
# Main Class
@@ -216,7 +200,6 @@ class n3xx(ZynqComponents, PeriphManagerBase):
self._ext_clock_freq = None
self._clock_source = None
self._time_source = None
- self._available_endpoints = list(range(256))
self._bp_leds = None
self._gpsd = None
self._qsfp_retimer = None
@@ -352,7 +335,6 @@ class n3xx(ZynqComponents, PeriphManagerBase):
self.mboard_regs_control.get_git_hash()
self.mboard_regs_control.get_build_timestamp()
self._check_fpga_compat()
- self.crossbar_base_port = self.mboard_regs_control.get_xbar_baseport()
# Init clocking
self.enable_ref_clock(enable=True)
self._ext_clock_freq = None
@@ -463,8 +445,6 @@ class n3xx(ZynqComponents, PeriphManagerBase):
super(n3xx, self).deinit()
for xport_mgr in itervalues(self._xport_mgrs):
xport_mgr.deinit()
- self.log.trace("Resetting SID pool...")
- self._available_endpoints = list(range(256))
def tear_down(self):
"""
@@ -489,66 +469,37 @@ class n3xx(ZynqComponents, PeriphManagerBase):
###########################################################################
# Transport API
###########################################################################
- def request_xport(
- self,
- dst_address,
- suggested_src_address,
- xport_type
- ):
- """
- See PeriphManagerBase.request_xport() for docs.
- """
- # Try suggested address first, then just pick the first available one:
- src_address = suggested_src_address
- if src_address not in self._available_endpoints:
- if len(self._available_endpoints) == 0:
- raise RuntimeError(
- "Depleted pool of SID endpoints for this device!")
- else:
- src_address = self._available_endpoints[0]
- sid = SID(src_address << 16 | dst_address)
- # Note: This SID may change its source address!
- self.log.trace(
- "request_xport(dst=0x%04X, suggested_src_address=0x%04X, xport_type=%s): " \
- "operating on temporary SID: %s",
- dst_address, suggested_src_address, str(xport_type), str(sid))
- # FIXME token!
- assert self.device_info['rpc_connection'] in ('remote', 'local')
- if self.device_info['rpc_connection'] == 'remote':
- if self.device_info['product'] == 'n320':
- alloc_limit = 1
- else:
- alloc_limit = 2
- return self._xport_mgrs['udp'].request_xport(
- sid,
- xport_type,
- alloc_limit
- )
- elif self.device_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].request_xport(
- sid,
- xport_type,
- )
+ def get_chdr_link_types(self):
+ """
+ This will only ever return a single item (udp or liberio).
+ """
+ assert self.mboard_info['rpc_connection'] in ('remote', 'local')
+ if self.mboard_info['rpc_connection'] == 'remote':
+ return ["udp"]
+ # else:
+ return ["liberio"]
- def commit_xport(self, xport_info):
+ def get_chdr_link_options(self, xport_type):
"""
- See PeriphManagerBase.commit_xport() for docs.
+ Returns a list of dictionaries. Every dictionary contains information
+ about one way to connect to this device in order to initiate CHDR
+ traffic.
+
+ The interpretation of the return value is very highly dependant on the
+ transport type (xport_type).
+ For UDP, the every entry of the list has the following keys:
+ - ipv4 (IP Address)
+ - port (UDP port)
+ - link_rate (bps of the link, e.g. 10e9 for 10GigE)
- Reminder: All connections are incoming, i.e. "send" or "TX" means
- remote device to local device, and "receive" or "RX" means this local
- device to remote device. "Remote device" can be, for example, a UHD
- session.
+ For Liberio, every entry has the following keys:
+ - tx_dev: TX device (/dev/tx-dma*)
+ - rx_dev: RX device (/dev/rx-dma*)
"""
- ## Go, go, go
- assert self.device_info['rpc_connection'] in ('remote', 'local')
- sid = SID(xport_info['send_sid'])
- self._available_endpoints.remove(sid.src_ep)
- self.log.debug("Committing transport for SID %s, xport info: %s",
- str(sid), str(xport_info))
- if self.device_info['rpc_connection'] == 'remote':
- return self._xport_mgrs['udp'].commit_xport(sid, xport_info)
- elif self.device_info['rpc_connection'] == 'local':
- return self._xport_mgrs['liberio'].commit_xport(sid, xport_info)
+ if xport_type not in self._xport_mgrs:
+ self.log.warning("Can't get link options for unknown link type: `{}'.")
+ return []
+ return self._xport_mgrs[xport_type].get_chdr_link_options()
###########################################################################
# Device info
@@ -569,6 +520,39 @@ class n3xx(ZynqComponents, PeriphManagerBase):
})
return device_info
+ def set_device_id(self, device_id):
+ """
+ Sets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ self.log.debug("Setting device ID to `{}'".format(device_id))
+ self.mboard_regs_control.set_device_id(device_id)
+
+ def get_device_id(self):
+ """
+ Gets the device ID for this motherboard.
+ The device ID is used to identify the RFNoC components associated with
+ this motherboard.
+ """
+ return self.mboard_regs_control.get_device_id()
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ proto_ver = self.mboard_regs_control.get_proto_ver()
+ self.log.debug("RFNoC protocol version supported by this device is {}".format(proto_ver))
+ return proto_ver
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ chdr_width = self.mboard_regs_control.get_chdr_width()
+ self.log.debug("CHDR width supported by the device is {}".format(chdr_width))
+ return chdr_width
+
###########################################################################
# Clock/Time API
###########################################################################
@@ -1065,3 +1049,60 @@ class n3xx(ZynqComponents, PeriphManagerBase):
if self._bp_leds is not None:
# Turn off LINK
self._bp_leds.set(self._bp_leds.LED_LINK, 0)
+
+ #######################################################################
+ # Timekeeper API
+ #######################################################################
+ def get_num_timekeepers(self):
+ """
+ Return the number of timekeepers
+ """
+ return self.mboard_regs_control.get_num_timekeepers()
+
+ def get_timekeeper_time(self, tk_idx, last_pps):
+ """
+ Get the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ next_pps: If True, get time at last PPS. Otherwise, get time now.
+ """
+ return self.mboard_regs_control.get_timekeeper_time(tk_idx, last_pps)
+
+ def set_timekeeper_time(self, tk_idx, ticks, next_pps):
+ """
+ Set the time in ticks
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ ticks: Time in ticks
+ next_pps: If True, set time at next PPS. Otherwise, set time now.
+ """
+ self.mboard_regs_control.set_timekeeper_time(tk_idx, ticks, next_pps)
+
+ def set_tick_period(self, tk_idx, period_ns):
+ """
+ Set the time per tick in nanoseconds (tick period)
+
+ Arguments:
+ tk_idx: Index of timekeeper
+ period_ns: Period in nanoseconds
+ """
+ self.mboard_regs_control.set_tick_period(tk_idx, period_ns)
+
+ def get_clocks(self):
+ """
+ Gets the RFNoC-related clocks present in the FPGA design
+ """
+ return [
+ {
+ 'name': 'radio_clk',
+ 'freq': str(self.dboards[0].get_master_clock_rate()),
+ 'mutable': 'true'
+ },
+ {
+ 'name': 'bus_clk',
+ 'freq': str(200e6),
+ }
+ ]
+
diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
index cb6c237c2..fe005a716 100644
--- a/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
+++ b/mpm/python/usrp_mpm/periph_manager/n3xx_periphs.py
@@ -165,12 +165,12 @@ class MboardRegsControl(object):
Control the FPGA Motherboard registers
"""
# Motherboard registers
- M_COMPAT_NUM = 0x0000
+ MB_COMPAT_NUM = 0x0000
MB_DATESTAMP = 0x0004
MB_GIT_HASH = 0x0008
MB_SCRATCH = 0x000C
- MB_NUM_CE = 0x0010
- MB_NUM_IO_CE = 0x0014
+ MB_DEVICE_ID = 0x0010
+ MB_RFNOC_INFO = 0x0014
MB_CLOCK_CTRL = 0x0018
MB_XADC_RB = 0x001C
MB_BUS_CLK_RATE = 0x0020
@@ -179,7 +179,22 @@ class MboardRegsControl(object):
MB_SFP1_INFO = 0x002C
MB_GPIO_MASTER = 0x0030
MB_GPIO_RADIO_SRC = 0x0034
- MB_XBAR_BASEPORT = 0x0038
+ MB_NUM_TIMEKEEPERS = 0x0048
+ # Timekeeper registers
+ MB_TIME_NOW_LO = 0x1000
+ MB_TIME_NOW_HI = 0x1004
+ MB_TIME_EVENT_LO = 0x1008
+ MB_TIME_EVENT_HI = 0x100C
+ MB_TIME_CTRL = 0x1010
+ MB_TIME_LAST_PPS_LO = 0x1014
+ MB_TIME_LAST_PPS_HI = 0x1018
+ MB_TIME_BASE_PERIOD_LO = 0x101C
+ MB_TIME_BASE_PERIOD_HI = 0x1020
+ MB_TIMEKEEPER_OFFSET = 12
+
+ # Bitfield locations for the MB_RFNOC_INFO register.
+ MB_RFNOC_INFO_PROTO_VER = 0
+ MB_RFNOC_INFO_CHDR_WIDTH = 16
# Bitfield locations for the MB_CLOCK_CTRL register.
MB_CLOCK_CTRL_PPS_SEL_INT_10 = 0 # pps_sel is one-hot encoded!
@@ -210,11 +225,49 @@ class MboardRegsControl(object):
2 numbers: (major compat number, minor compat number )
"""
with self.regs:
- compat_number = self.peek32(self.M_COMPAT_NUM)
+ compat_number = self.peek32(self.MB_COMPAT_NUM)
minor = compat_number & 0xff
major = (compat_number>>16) & 0xff
return (major, minor)
+ def set_device_id(self, device_id):
+ """
+ Set device ID
+ """
+ with self.regs:
+ self.log.trace("Writing MB_DEVICE_ID with 0x{:08X}".format(device_id))
+ return self.poke32(self.MB_DEVICE_ID, device_id)
+
+ def get_device_id(self):
+ """
+ Get device ID
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_DEVICE_ID)
+ device_id = reg_val & 0x0000ffff
+ self.log.trace("Read MB_DEVICE_ID 0x{:08X}".format(device_id))
+ return device_id
+
+ def get_proto_ver(self):
+ """
+ Return RFNoC protocol version
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ proto_ver = (reg_val & 0x0000ffff) >> self.MB_RFNOC_INFO_PROTO_VER
+ self.log.trace("Read RFNOC_PROTO_VER 0x{:08X}".format(proto_ver))
+ return proto_ver;
+
+ def get_chdr_width(self):
+ """
+ Return RFNoC CHDR width
+ """
+ with self.regs:
+ reg_val = self.peek32(self.MB_RFNOC_INFO)
+ chdr_width = (reg_val & 0xffff0000) >> self.MB_RFNOC_INFO_CHDR_WIDTH
+ self.log.trace("Read RFNOC_CHDR_WIDTH 0x{:08X}".format(chdr_width))
+ return chdr_width
+
def set_fp_gpio_master(self, value):
"""set driver for front panel GPIO
Arguments:
@@ -416,10 +469,6 @@ class MboardRegsControl(object):
.format(sfp0_type, sfp1_type))
return ""
- def get_xbar_baseport(self):
- "Get the RFNoC crossbar base port"
- with self.regs:
- return self.peek32(self.MB_XBAR_BASEPORT)
class RetimerQSFP(DS125DF410):
# (deemphasis, swing)
diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py
index 2745ba59d..17179f04d 100644
--- a/mpm/python/usrp_mpm/rpc_server.py
+++ b/mpm/python/usrp_mpm/rpc_server.py
@@ -32,7 +32,7 @@ from usrp_mpm.sys_utils import net
TIMEOUT_INTERVAL = 5.0 # Seconds before claim expires (default value)
TOKEN_LEN = 16 # Length of the token string
# Compatibility number for MPM
-MPM_COMPAT_NUM = (1, 2)
+MPM_COMPAT_NUM = (2, 0)
def no_claim(func):
" Decorator for functions that require no token check "
diff --git a/mpm/python/usrp_mpm/xports/xportmgr_liberio.py b/mpm/python/usrp_mpm/xports/xportmgr_liberio.py
index c9c5ee0f1..f7c1861af 100644
--- a/mpm/python/usrp_mpm/xports/xportmgr_liberio.py
+++ b/mpm/python/usrp_mpm/xports/xportmgr_liberio.py
@@ -8,8 +8,6 @@ Liberio Transport manager
"""
from builtins import object
-from usrp_mpm.liberiotable import LiberioDispatcherTable
-from usrp_mpm import lib
class XportMgrLiberio(object):
"""
@@ -19,14 +17,9 @@ class XportMgrLiberio(object):
liberio_label = 'liberio'
# Number of available DMA channels
max_chan = 4
- # Crossbar to which the Liberio DMA engine is connected
- xbar_dev = "/dev/crossbar0"
- xbar_port = 2
def __init__(self, log):
- self.log = log
- self._dma_dispatcher = LiberioDispatcherTable(self.liberio_label)
- self._data_chan_ctr = 0
+ self.log = log.getChild('Liberio')
def init(self, args):
"""
@@ -36,7 +29,7 @@ class XportMgrLiberio(object):
def deinit(self):
" Clean up after a session terminates "
- self._data_chan_ctr = 0
+ pass
def get_xport_info(self):
"""
@@ -48,50 +41,22 @@ class XportMgrLiberio(object):
In this case, returns an empty dict.
"""
+ assert hasattr(self, 'log')
return {}
- def request_xport(
- self,
- sid,
- xport_type,
- ):
- """
- Return liberio xport info
+ def get_chdr_link_options(self):
"""
- assert xport_type in ('CTRL', 'ASYNC_MSG', 'TX_DATA', 'RX_DATA')
- if xport_type == 'CTRL':
- chan = 0
- elif xport_type == 'ASYNC_MSG':
- chan = 1
- else:
- if self.max_chan > 4:
- chan = 2 + self._data_chan_ctr
- self._data_chan_ctr += 1
- else:
- if xport_type == 'RX_DATA':
- chan = 2
- else:
- chan = 3
- xport_info = {
- 'type': 'liberio',
- 'send_sid': str(sid),
- 'muxed': str(xport_type in ('CTRL', 'ASYNC_MSG')) if (self.max_chan > 4) else 'True',
- 'dma_chan': str(chan),
- 'tx_dev': "/dev/tx-dma{}".format(chan),
- 'rx_dev': "/dev/rx-dma{}".format(chan),
- }
- self.log.trace("Liberio: Chan: {} TX Device: {} RX Device: {}".format(
- chan, xport_info['tx_dev'], xport_info['rx_dev']))
- self.log.trace("Liberio channel is muxed: %s",
- "Yes" if xport_info['muxed'] else "No")
- return [xport_info]
-
- def commit_xport(self, sid, xport_info):
- " Commit liberio transport "
- chan = int(xport_info['dma_chan'])
- xbar_iface = lib.xbar.xbar(self.xbar_dev)
- xbar_iface.set_route(sid.src_addr, self.xbar_port)
- self._dma_dispatcher.set_route(sid.reversed(), chan)
- self.log.trace("Liberio transport successfully committed!")
- return True
+ Returns a list of dictionaries for returning by
+ PeriphManagerBase.get_chdr_link_options().
+ Note: This requires a claim, which means that init() was called, and
+ deinit() was not yet called.
+ """
+ return [
+ {
+ 'tx_dev': "/dev/tx-dma{}".format(chan),
+ 'rx_dev': "/dev/rx-dma{}".format(chan),
+ 'dma_chan': str(chan),
+ }
+ for chan in range(self.max_chan)
+ ]
diff --git a/mpm/python/usrp_mpm/xports/xportmgr_udp.py b/mpm/python/usrp_mpm/xports/xportmgr_udp.py
index 17762bb76..761db4a08 100644
--- a/mpm/python/usrp_mpm/xports/xportmgr_udp.py
+++ b/mpm/python/usrp_mpm/xports/xportmgr_udp.py
@@ -1,5 +1,6 @@
#
# Copyright 2017 Ettus Research, a National Instruments Company
+# Copyright 2019 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
@@ -9,10 +10,8 @@ UDP Transport manager
from builtins import object
from six import iteritems, itervalues
-from usrp_mpm.ethtable import EthDispatcherTable
+from usrp_mpm.ethdispatch import EthDispatcherCtrl
from usrp_mpm.sys_utils import net
-from usrp_mpm.mpmtypes import SID
-from usrp_mpm import lib
DEFAULT_BRIDGE_MODE = False
@@ -25,40 +24,38 @@ class XportMgrUDP(object):
# iface_config = {
# 'eth1': { # Add key for every Ethernet iface connected to the FPGA
# 'label': 'misc-enet-regs0', # UIO label for the Eth table
- # 'xbar': 0, # Which crossbar? 0 -> /dev/crossbar0
- # 'xbar_port': 0, # Which port on the crossbar it is connected to
# },
# }
iface_config = {}
bridges = {}
- # The control addresses are typically addresses bound to the controlling
- # UHD session. When the requested source address is below or equal to this
- # number, we override requested SID source addresses based on other logic.
- max_ctrl_addr = 1
def __init__(self, log, args):
- assert len(self.iface_config)
+ assert self.iface_config
assert all((
- all((key in x for key in ('label', 'xbar', 'xbar_port')))
+ all((key in x for key in ('label',)))
for x in itervalues(self.iface_config)
))
- self.log = log
+ self.log = log.getChild('UDP')
self.log.trace("Initializing UDP xport manager...")
self._possible_chdr_ifaces = self.iface_config.keys()
self.log.trace("Identifying available network interfaces...")
- self.chdr_port = EthDispatcherTable.DEFAULT_VITA_PORT[0]
- self._chdr_ifaces = \
- self._init_interfaces(self._possible_chdr_ifaces)
+ self.chdr_port = EthDispatcherCtrl.DEFAULT_VITA_PORT[0]
+ self._chdr_ifaces = self._init_interfaces(self._possible_chdr_ifaces)
self._bridge_mode = args.get('bridge_mode', DEFAULT_BRIDGE_MODE)
self._eth_dispatchers = {}
- self._allocations = {}
- self._previous_block_ep = {}
def _init_interfaces(self, possible_ifaces):
"""
Enumerate all network interfaces that are currently able to stream CHDR
Returns a dictionary iface name -> iface info, where iface info is the
return value of get_iface_info().
+
+ Arguments:
+ - possible_ifaces: A list of strings containing iface names, e.g.
+ ["sfp0", "sfp1"]
+
+ Return Value:
+ A list of dictionaries. The keys are determined by net.get_iface_info().
"""
self.log.trace("Testing available interfaces out of `{}'".format(
list(possible_ifaces)
@@ -83,7 +80,7 @@ class XportMgrUDP(object):
"{} to {}."
.format(len(valid_iface_infos), len(valid_iface_infos_filtered))
)
- if len(valid_iface_infos_filtered):
+ if valid_iface_infos_filtered:
self.log.debug(
"Found CHDR interfaces: `{}'"
.format(", ".join(list(valid_iface_infos.keys())))
@@ -104,10 +101,11 @@ class XportMgrUDP(object):
if len(self._chdr_ifaces) != 1 or bridge_iface != list(self.bridges.keys())[0]:
self.log.error("No Bridge Interfaces found")
raise RuntimeError("No Bridge Interfaces found")
- self.log.info("Updated dispatchers in bridge mode with bridge interface {}".format(
- bridge_iface))
+ self.log.info(
+ "Updated dispatchers in bridge mode with bridge interface {}"
+ .format(bridge_iface))
self._eth_dispatchers = {
- x: EthDispatcherTable(self.iface_config[x]['label'])
+ x: EthDispatcherCtrl(self.iface_config[x]['label'])
for x in self.bridges[bridge_iface]
}
for dispatcher, table in iteritems(self._eth_dispatchers):
@@ -130,7 +128,7 @@ class XportMgrUDP(object):
for iface in self._chdr_ifaces:
if iface not in self._eth_dispatchers:
self._eth_dispatchers[iface] = \
- EthDispatcherTable(self.iface_config[iface]['label'])
+ EthDispatcherCtrl(self.iface_config[iface]['label'])
self._eth_dispatchers[iface].set_ipv4_addr(
self._chdr_ifaces[iface]['ip_addr']
)
@@ -139,8 +137,7 @@ class XportMgrUDP(object):
"""
Call this when the user calls 'init' on the periph manager
"""
- self._chdr_ifaces = \
- self._init_interfaces(self._possible_chdr_ifaces)
+ self._chdr_ifaces = self._init_interfaces(self._possible_chdr_ifaces)
if "bridge_mode" in args:
self._bridge_mode = args.get("bridge_mode")
self._update_dispatchers()
@@ -153,16 +150,10 @@ class XportMgrUDP(object):
args.get('forward_eth', False),
args.get('forward_bcast', False)
)
- if 'preload_ethtables' in args:
- self._preload_ethtables(
- self._eth_dispatchers,
- args['preload_ethtables']
- )
-
def deinit(self):
" Clean up after a session terminates "
- self._allocations = {}
+ pass
def get_xport_info(self):
"""
@@ -174,174 +165,25 @@ class XportMgrUDP(object):
In this case, returns the available IP addresses.
"""
- available_interfaces = \
- self._init_interfaces(self._possible_chdr_ifaces)
+ available_interfaces = self._init_interfaces(self._possible_chdr_ifaces)
return dict(zip(
("addr", "second_addr", "third_addr", "fourth_addr"),
(x['ip_addr'] for x in itervalues(available_interfaces))
))
- def _preload_ethtables(self, eth_dispatchers, table_file):
- """
- Populates the ethernet tables from a JSON file
+ def get_chdr_link_options(self):
"""
- import json
- try:
- eth_table_data = json.load(open(table_file))
- except ValueError as ex:
- self.log.warning(
- "Bad values in preloading table file: %s",
- str(ex)
- )
- return
- self.log.info(
- "Preloading Ethernet dispatch tables from JSON file `%s'.",
- table_file
- )
- for eth_iface, data in iteritems(eth_table_data):
- if eth_iface not in eth_dispatchers:
- self.log.warning(
- "Request to preload eth dispatcher table for "
- "iface `{}', but no such interface is "
- "registered. Known interfaces: {}".format(
- str(eth_iface),
- ",".join(eth_dispatchers.keys())
- )
- )
- continue
- eth_dispatcher = eth_dispatchers[eth_iface]
- self.log.debug("Preloading {} dispatch table".format(eth_iface))
- try:
- for dst_addr, udp_data in iteritems(data):
- sid = SID()
- sid.set_dst_addr(int(dst_addr))
- eth_dispatcher.set_route(
- sid,
- udp_data['ip_addr'],
- udp_data['port'],
- udp_data.get('mac_addr', None)
- )
- except ValueError as ex:
- self.log.warning(
- "Bad values in preloading table file: %s",
- str(ex)
- )
+ Returns a list of dictionaries for returning by
+ PeriphManagerBase.get_chdr_link_options().
- def get_xbar_dev(self, iface):
- """
- Given an Ethernet interface (e.g., 'eth1') returns the crossbar device
- it is connected to.
+ Note: This requires a claim, which means that init() was called, and
+ deinit() was not yet called.
"""
- xbar_idx = self.iface_config[iface]['xbar']
- return "/dev/crossbar{}".format(xbar_idx)
-
- def request_xport(
- self,
- sid,
- xport_type,
- alloc_limit=2
- ):
- """
- Return UDP xport info
- """
- def fixup_sid(sid, iface_name):
- " Modify the source SID (e.g. the UHD SID) "
- if sid.src_addr <= self.max_ctrl_addr:
- sid.src_addr = self.iface_config[iface_name]['ctrl_src_addr']
- return sid
-
- def sort_xport_info(xport):
- """
- We sort xport_info (which is a list of xport) as follows:
- 1. Look at current allocation of xport src_addr (which is the addr
- of host). If the allocation too large, in this case larger or
- equal to 2 (since 2*125 = 250MS/s = max bandwidth of SFP+ port),
- we will use the allocation for sorting.
- 2. Else, we need to look at the destination block. The priority will
- yield to the xport that has the previous destination block
- that is the same as this coming destination block.
- Note: smaller number return is the higher chance to be picked
- """
- sid = SID(xport['send_sid'])
- src_addr = sid.src_addr
- prev_block = -1
- if src_addr in self._previous_block_ep:
- prev_block = self._previous_block_ep[src_addr]
- allocation = int(xport['allocation'])
- if allocation >= alloc_limit:
- return allocation
- else:
- return allocation if prev_block != sid.get_dst_block() else -1;
-
- assert xport_type in ('CTRL', 'ASYNC_MSG', 'TX_DATA', 'RX_DATA')
- allocation_getter = lambda iface: {
- 'CTRL': 0,
- 'ASYNC_MSG': 0,
- 'RX_DATA': self._allocations.get(iface, {}).get('rx', 0),
- 'TX_DATA': self._allocations.get(iface, {}).get('tx', 0),
- }[xport_type]
- xport_info = sorted([
+ return [
{
- 'type': 'UDP',
'ipv4': str(iface_info['ip_addr']),
'port': str(self.chdr_port),
- 'send_sid': str(fixup_sid(sid, iface_name)),
- 'allocation': str(allocation_getter(iface_name)),
- 'xport_type': xport_type,
- 'link_speed': str(iface_info['link_speed'])
+ 'link_rate': str(int(iface_info['link_speed'] * 1e9 / 8))
}
for iface_name, iface_info in iteritems(self._chdr_ifaces)
]
- , key=lambda x: sort_xport_info(x)
- , reverse=False)
- return xport_info
-
- def commit_xport(self, sid, xport_info):
- """
- Commit transport
-
- Saves the transport configuration to the device.
- Returns the status of the commit.
- """
- self.log.trace("Sanity checking xport_info %s...", str(xport_info))
- assert xport_info['type'] == 'UDP'
- assert any([xport_info['ipv4'] == x['ip_addr']
- for x in itervalues(self._chdr_ifaces)])
- assert xport_info['port'] == str(self.chdr_port)
- assert len(xport_info.get('src_ipv4')) > 5
- assert int(xport_info.get('src_port')) > 0
- sender_addr = xport_info['src_ipv4']
- sender_port = int(xport_info['src_port'])
- self.log.trace("Incoming connection is coming from %s:%d",
- sender_addr, sender_port)
- mac_addr = net.get_mac_addr(sender_addr)
- if mac_addr is None:
- raise RuntimeError(
- "Could not find MAC address for IP address {}".format(
- sender_addr))
- self.log.trace("Incoming connection is coming from %s",
- mac_addr)
- eth_iface = net.ip_addr_to_iface(xport_info['ipv4'], self._chdr_ifaces)
- xbar_port = self.iface_config[eth_iface]['xbar_port']
- self.log.trace("Using Ethernet interface %s, crossbar port %d",
- eth_iface, xbar_port)
- xbar_iface = lib.xbar.xbar(self.get_xbar_dev(eth_iface))
- xbar_iface.set_route(sid.src_addr, xbar_port)
- self._eth_dispatchers[eth_iface].set_route(
- sid.reversed(), sender_addr, sender_port)
- self.log.trace("UDP transport successfully committed!")
- self._previous_block_ep[sid.src_addr] = sid.get_dst_block()
- if xport_info.get('xport_type') == 'TX_DATA':
- self._allocations[eth_iface] = \
- {'tx': self._allocations.get(eth_iface, {}).get('tx', 0) + 1}
- if xport_info.get('xport_type') == 'RX_DATA':
- self._allocations[eth_iface] = \
- {'rx': self._allocations.get(eth_iface, {}).get('rx', 0) + 1}
- self.log.trace(
- "New link allocations for %s: TX: %d RX: %d",
- eth_iface,
- self._allocations.get(eth_iface, {}).get('tx', 0),
- self._allocations.get(eth_iface, {}).get('rx', 0),
- )
- return True
-