aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/include/uhdlib
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2018-01-31 20:20:14 +0100
committerMartin Braun <martin.braun@ettus.com>2018-03-14 15:17:44 -0700
commit6652eb4a033b38bd952563f3544eb11e98f27327 (patch)
treec1b0af72cbaceaa1df462f18194f4063fb13ae17 /host/lib/include/uhdlib
parent86b95486ed6d68e2772d79f20feddbef5439981b (diff)
downloaduhd-6652eb4a033b38bd952563f3544eb11e98f27327.tar.gz
uhd-6652eb4a033b38bd952563f3544eb11e98f27327.tar.bz2
uhd-6652eb4a033b38bd952563f3544eb11e98f27327.zip
uhd: Move internal headers to uhdlib/
To avoid the proliferation of additional include directories and multiple ways of including project-local headers, we now default to moving all headers that are used across UHD into the uhdlib/ subdirectory. Some #include statements were also reordered as they were modified for closer compliance with the coding guidelines. Internal cpp source files should now include files like this: #include <uhdlib/rfnoc/ctrl_iface.hpp> Reviewed-by: Ashish Chaudhari <ashish.chaudhari@ettus.com>
Diffstat (limited to 'host/lib/include/uhdlib')
-rw-r--r--host/lib/include/uhdlib/experts/expert_container.hpp192
-rw-r--r--host/lib/include/uhdlib/experts/expert_factory.hpp327
-rw-r--r--host/lib/include/uhdlib/experts/expert_nodes.hpp472
-rw-r--r--host/lib/include/uhdlib/rfnoc/ctrl_iface.hpp46
-rw-r--r--host/lib/include/uhdlib/rfnoc/graph_impl.hpp80
-rw-r--r--host/lib/include/uhdlib/rfnoc/legacy_compat.hpp49
-rw-r--r--host/lib/include/uhdlib/rfnoc/radio_ctrl_impl.hpp247
-rw-r--r--host/lib/include/uhdlib/rfnoc/rpc_block_ctrl.hpp41
-rw-r--r--host/lib/include/uhdlib/rfnoc/rx_stream_terminator.hpp79
-rw-r--r--host/lib/include/uhdlib/rfnoc/tx_stream_terminator.hpp80
-rw-r--r--host/lib/include/uhdlib/rfnoc/utils.hpp67
-rw-r--r--host/lib/include/uhdlib/rfnoc/wb_iface_adapter.hpp60
-rw-r--r--host/lib/include/uhdlib/rfnoc/xports.hpp29
-rw-r--r--host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp161
-rw-r--r--host/lib/include/uhdlib/usrp/common/ad936x_manager.hpp122
-rw-r--r--host/lib/include/uhdlib/usrp/common/adf4001_ctrl.hpp131
-rw-r--r--host/lib/include/uhdlib/usrp/common/adf435x.hpp422
-rw-r--r--host/lib/include/uhdlib/usrp/common/adf535x.hpp477
-rw-r--r--host/lib/include/uhdlib/usrp/common/apply_corrections.hpp43
-rw-r--r--host/lib/include/uhdlib/usrp/common/async_packet_handler.hpp74
-rw-r--r--host/lib/include/uhdlib/usrp/common/fifo_ctrl_excelsior.hpp51
-rw-r--r--host/lib/include/uhdlib/usrp/common/fx2_ctrl.hpp138
-rw-r--r--host/lib/include/uhdlib/usrp/common/max287x.hpp935
-rw-r--r--host/lib/include/uhdlib/usrp/common/recv_packet_demuxer.hpp33
-rw-r--r--host/lib/include/uhdlib/usrp/common/recv_packet_demuxer_3000.hpp145
-rw-r--r--host/lib/include/uhdlib/usrp/common/validate_subdev_spec.hpp28
-rw-r--r--host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp76
-rw-r--r--host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp23
-rw-r--r--host/lib/include/uhdlib/usrp/cores/gpio_atr_3000.hpp178
-rw-r--r--host/lib/include/uhdlib/usrp/cores/gpio_core_200.hpp77
-rw-r--r--host/lib/include/uhdlib/usrp/cores/i2c_core_100_wb32.hpp29
-rw-r--r--host/lib/include/uhdlib/usrp/cores/i2c_core_200.hpp27
-rw-r--r--host/lib/include/uhdlib/usrp/cores/radio_ctrl_core_3000.hpp54
-rw-r--r--host/lib/include/uhdlib/usrp/cores/rx_dsp_core_200.hpp59
-rw-r--r--host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp58
-rw-r--r--host/lib/include/uhdlib/usrp/cores/rx_frontend_core_200.hpp43
-rw-r--r--host/lib/include/uhdlib/usrp/cores/rx_frontend_core_3000.hpp59
-rw-r--r--host/lib/include/uhdlib/usrp/cores/rx_vita_core_3000.hpp51
-rw-r--r--host/lib/include/uhdlib/usrp/cores/spi_core_3000.hpp38
-rw-r--r--host/lib/include/uhdlib/usrp/cores/time64_core_200.hpp55
-rw-r--r--host/lib/include/uhdlib/usrp/cores/time_core_3000.hpp52
-rw-r--r--host/lib/include/uhdlib/usrp/cores/tx_dsp_core_200.hpp51
-rw-r--r--host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp52
-rw-r--r--host/lib/include/uhdlib/usrp/cores/tx_frontend_core_200.hpp40
-rw-r--r--host/lib/include/uhdlib/usrp/cores/tx_vita_core_3000.hpp51
-rw-r--r--host/lib/include/uhdlib/usrp/cores/user_settings_core_200.hpp28
-rw-r--r--host/lib/include/uhdlib/usrp/cores/user_settings_core_3000.hpp25
-rw-r--r--host/lib/include/uhdlib/utils/eeprom_utils.hpp20
-rw-r--r--host/lib/include/uhdlib/utils/ihex.hpp69
-rw-r--r--host/lib/include/uhdlib/utils/rpc.hpp191
50 files changed, 5935 insertions, 0 deletions
diff --git a/host/lib/include/uhdlib/experts/expert_container.hpp b/host/lib/include/uhdlib/experts/expert_container.hpp
new file mode 100644
index 000000000..c50e45847
--- /dev/null
+++ b/host/lib/include/uhdlib/experts/expert_container.hpp
@@ -0,0 +1,192 @@
+//
+// Copyright 2016 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_UHD_EXPERTS_EXPERT_CONTAINER_HPP
+#define INCLUDED_UHD_EXPERTS_EXPERT_CONTAINER_HPP
+
+#include <uhdlib/experts/expert_nodes.hpp>
+#include <uhd/config.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+
+namespace uhd { namespace experts {
+
+ enum auto_resolve_mode_t {
+ AUTO_RESOLVE_OFF,
+ AUTO_RESOLVE_ON_READ,
+ AUTO_RESOLVE_ON_WRITE,
+ AUTO_RESOLVE_ON_READ_WRITE
+ };
+
+ class UHD_API expert_container : private boost::noncopyable, public node_retriever_t {
+ public: //Methods
+ typedef boost::shared_ptr<expert_container> sptr;
+
+ virtual ~expert_container() {};
+
+ /*!
+ * Return the name of this container
+ */
+ virtual const std::string& get_name() const = 0;
+
+ /*!
+ * Resolves all the nodes in this expert graph.
+ *
+ * Dependency analysis is performed on the graph and nodes
+ * are resolved in a topologically sorted order to ensure
+ * that no nodes receive stale data.
+ * Nodes and their dependencies are resolved only if they are
+ * dirty i.e. their contained values have changed since the
+ * last resolve.
+ * This call requires an acyclic expert graph.
+ *
+ * \param force If true then ignore dirty state and resolve all nodes
+ * \throws uhd::runtime_error if graph cannot be resolved
+ */
+ virtual void resolve_all(bool force = false) = 0;
+
+ /*!
+ * Resolves all the nodes that depend on the specified node.
+ *
+ * Dependency analysis is performed on the graph and nodes
+ * are resolved in a topologically sorted order to ensure
+ * that no nodes receive stale data.
+ * Nodes and their dependencies are resolved only if they are
+ * dirty i.e. their contained values have changed since the
+ * last resolve.
+ * This call requires an acyclic expert graph.
+ *
+ * \param node_name Name of the node to start resolving from
+ * \throws uhd::lookup_error if node_name not in container
+ * \throws uhd::runtime_error if graph cannot be resolved
+ *
+ */
+ virtual void resolve_from(const std::string& node_name) = 0;
+
+ /*!
+ * Resolves all the specified node and all of its dependencies.
+ *
+ * Dependency analysis is performed on the graph and nodes
+ * are resolved in a topologically sorted order to ensure
+ * that no nodes receive stale data.
+ * Nodes and their dependencies are resolved only if they are
+ * dirty i.e. their contained values have changed since the
+ * last resolve.
+ * This call requires an acyclic expert graph.
+ *
+ * \param node_name Name of the node to resolve
+ * \throws uhd::lookup_error if node_name not in container
+ * \throws uhd::runtime_error if graph cannot be resolved
+ *
+ */
+ virtual void resolve_to(const std::string& node_name) = 0;
+
+ /*!
+ * Return a node retriever object for this container
+ */
+ virtual const node_retriever_t& node_retriever() const = 0;
+
+ /*!
+ * Returns a DOT (graph description language) representation
+ * of the expert graph. The output has labels for the node
+ * name, node type (data or worker) and the underlying
+ * data type for each node.
+ *
+ */
+ virtual std::string to_dot() const = 0;
+
+ /*!
+ * Runs several sanity checks on the underlying graph to
+ * flag dependency issues. Outputs of the checks are
+ * logged to the console so UHD_EXPERTS_VERBOSE_LOGGING
+ * must be enabled to see the results
+ *
+ */
+ virtual void debug_audit() const = 0;
+
+ private:
+ /*!
+ * Lookup a node with the specified name in the contained graph
+ *
+ * If the node is found, a reference to the node is returned.
+ * If the node is not found, uhd::lookup_error is thrown
+ * lookup can return a data or a worker node
+ * \implements uhd::experts::node_retriever_t
+ *
+ * \param name Name of the node to find
+ *
+ */
+ virtual const dag_vertex_t& lookup(const std::string& name) const = 0;
+ virtual dag_vertex_t& retrieve(const std::string& name) const = 0;
+
+ /*!
+ * expert_factory is a friend of expert_container and
+ * handles all operations that change the structure of
+ * the underlying dependency graph.
+ * The expert_container instance owns all data and worker
+ * nodes and is responsible for release storage on destruction.
+ * However, the expert_factory allocates storage for the
+ * node and passes them into the expert_container using the
+ * following "protected" API calls.
+ *
+ */
+ friend class expert_factory;
+
+ /*!
+ * Creates an empty instance of expert_container with the
+ * specified name.
+ *
+ * \param name Name of the container
+ */
+ static sptr make(const std::string& name);
+
+ /*!
+ * Returns a reference to the resolver mutex.
+ *
+ * The resolver mutex guarantees that external operations
+ * to data-nodes are serialized with resolves of this
+ * container.
+ *
+ */
+ virtual boost::recursive_mutex& resolve_mutex() = 0;
+
+ /*!
+ * Add a data node to the expert graph
+ *
+ * \param data_node Pointer to a fully constructed data node object
+ * \resolve_mode Auto resolve options: Choose from "disabled" and resolve on "read", "write" or "both"
+ * \throws uhd::runtime_error if node already exists or is of a wrong type (recoverable)
+ * \throws uhd::assertion_error for other failures (unrecoverable. will clear the graph)
+ *
+ */
+ virtual void add_data_node(dag_vertex_t* data_node, auto_resolve_mode_t resolve_mode = AUTO_RESOLVE_OFF) = 0;
+
+ /*!
+ * Add a worker node to the expert graph
+ *
+ * \param worker Pointer to a fully constructed worker object
+ * \throws uhd::runtime_error if worker already exists or is of a wrong type (recoverable)
+ * \throws uhd::assertion_error for other failures (unrecoverable. will clear the graph)
+ *
+ */
+ virtual void add_worker(worker_node_t* worker) = 0;
+
+ /*!
+ * Release all storage for this object. This will delete all contained
+ * data and worker nodes and remove all dependency relationship and
+ * resolve callbacks.
+ *
+ * The object will be restored to its newly constructed state. Will not
+ * throw.
+ */
+ virtual void clear() = 0;
+ };
+
+}}
+
+#endif /* INCLUDED_UHD_EXPERTS_EXPERT_CONTAINER_HPP */
diff --git a/host/lib/include/uhdlib/experts/expert_factory.hpp b/host/lib/include/uhdlib/experts/expert_factory.hpp
new file mode 100644
index 000000000..72798eccc
--- /dev/null
+++ b/host/lib/include/uhdlib/experts/expert_factory.hpp
@@ -0,0 +1,327 @@
+//
+// Copyright 2016 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_UHD_EXPERTS_EXPERT_FACTORY_HPP
+#define INCLUDED_UHD_EXPERTS_EXPERT_FACTORY_HPP
+
+#include <uhdlib/experts/expert_container.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/config.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/bind.hpp>
+#include <memory>
+
+namespace uhd { namespace experts {
+
+ /*!
+ * expert_factory is a friend of expert_container and
+ * handles all operations to create and change the structure of
+ * the an expert container.
+ * The expert_factory allocates storage for the nodes in the
+ * expert_container and passes allocated objects to the container
+ * using private APIs. The expert_container instance owns all
+ * data and workernodes and is responsible for releasing their
+ * storage on destruction.
+ *
+ */
+ class UHD_API expert_factory : public boost::noncopyable {
+ public:
+
+ /*!
+ * Creates an empty instance of expert_container with the
+ * specified name.
+ *
+ * \param name Name of the container
+ */
+ static expert_container::sptr create_container(
+ const std::string& name
+ );
+
+ /*!
+ * Add a data node to the expert graph.
+ *
+ * \param container A shared pointer to the container to add the node to
+ * \param name The name of the data node
+ * \param init_val The initial value of the data node
+ * \param mode The auto resolve mode
+ *
+ * Requirements for data_t
+ * - Must have a default constructor
+ * - Must have a copy constructor
+ * - Must have an assignment operator (=)
+ * - Must have an equality operator (==)
+ */
+ template<typename data_t>
+ inline static void add_data_node(
+ expert_container::sptr container,
+ const std::string& name,
+ const data_t& init_val,
+ const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF
+ ) {
+ container->add_data_node(new data_node_t<data_t>(name, init_val), mode);
+ }
+
+ /*!
+ * Add a expert property to a property tree AND an expert graph
+ *
+ * \param container A shared pointer to the expert container to add the node to
+ * \param subtree A shared pointer to subtree to add the property to
+ * \param path The path of the property in the subtree
+ * \param name The name of the data node in the expert graph
+ * \param init_val The initial value of the data node
+ * \param mode The auto resolve mode
+ *
+ * Requirements for data_t
+ * - Must have a default constructor
+ * - Must have a copy constructor
+ * - Must have an assignment operator (=)
+ * - Must have an equality operator (==)
+ */
+ template<typename data_t>
+ inline static property<data_t>& add_prop_node(
+ expert_container::sptr container,
+ property_tree::sptr subtree,
+ const fs_path &path,
+ const std::string& name,
+ const data_t& init_val,
+ const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF
+ ) {
+ property<data_t>& prop = subtree->create<data_t>(path, property_tree::MANUAL_COERCE);
+ data_node_t<data_t>* node_ptr =
+ new data_node_t<data_t>(name, init_val, &container->resolve_mutex());
+ prop.set(init_val);
+ prop.add_desired_subscriber(boost::bind(&data_node_t<data_t>::commit, node_ptr, _1));
+ prop.set_publisher(boost::bind(&data_node_t<data_t>::retrieve, node_ptr));
+ container->add_data_node(node_ptr, mode);
+ return prop;
+ }
+
+ /*!
+ * Add a expert property to a property tree AND an expert graph.
+ * The property is registered with the path as the identifier for
+ * both the property subtree and the expert container
+ *
+ * \param container A shared pointer to the expert container to add the node to
+ * \param subtree A shared pointer to subtree to add the property to
+ * \param path The path of the property in the subtree
+ * \param init_val The initial value of the data node
+ * \param mode The auto resolve mode
+ *
+ */
+ template<typename data_t>
+ inline static property<data_t>& add_prop_node(
+ expert_container::sptr container,
+ property_tree::sptr subtree,
+ const fs_path &path,
+ const data_t& init_val,
+ const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF
+ ) {
+ return add_prop_node(container, subtree, path, path, init_val, mode);
+ }
+
+ /*!
+ * Add a dual expert property to a property tree AND an expert graph.
+ * A dual property is a desired and coerced value pair
+ *
+ * \param container A shared pointer to the expert container to add the node to
+ * \param subtree A shared pointer to subtree to add the property to
+ * \param path The path of the property in the subtree
+ * \param desired_name The name of the desired data node in the expert graph
+ * \param desired_name The name of the coerced data node in the expert graph
+ * \param init_val The initial value of both the data nodes
+ * \param mode The auto resolve mode
+ *
+ * Requirements for data_t
+ * - Must have a default constructor
+ * - Must have a copy constructor
+ * - Must have an assignment operator (=)
+ * - Must have an equality operator (==)
+ */
+ template<typename data_t>
+ inline static property<data_t>& add_dual_prop_node(
+ expert_container::sptr container,
+ property_tree::sptr subtree,
+ const fs_path &path,
+ const std::string& desired_name,
+ const std::string& coerced_name,
+ const data_t& init_val,
+ const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF
+ ) {
+ bool auto_resolve_desired = (mode==AUTO_RESOLVE_ON_WRITE or mode==AUTO_RESOLVE_ON_READ_WRITE);
+ bool auto_resolve_coerced = (mode==AUTO_RESOLVE_ON_READ or mode==AUTO_RESOLVE_ON_READ_WRITE);
+
+ property<data_t>& prop = subtree->create<data_t>(path, property_tree::MANUAL_COERCE);
+ data_node_t<data_t>* desired_node_ptr =
+ new data_node_t<data_t>(desired_name, init_val, &container->resolve_mutex());
+ data_node_t<data_t>* coerced_node_ptr =
+ new data_node_t<data_t>(coerced_name, init_val, &container->resolve_mutex());
+ prop.set(init_val);
+ prop.set_coerced(init_val);
+ prop.add_desired_subscriber(boost::bind(&data_node_t<data_t>::commit, desired_node_ptr, _1));
+ prop.set_publisher(boost::bind(&data_node_t<data_t>::retrieve, coerced_node_ptr));
+
+ container->add_data_node(desired_node_ptr,
+ auto_resolve_desired ? AUTO_RESOLVE_ON_WRITE : AUTO_RESOLVE_OFF);
+ container->add_data_node(coerced_node_ptr,
+ auto_resolve_coerced ? AUTO_RESOLVE_ON_READ : AUTO_RESOLVE_OFF);
+ return prop;
+ }
+
+ /*!
+ * Add a dual expert property to a property tree AND an expert graph.
+ * A dual property is a desired and coerced value pair
+ * The property is registered with path/desired as the desired node
+ * name and path/coerced as the coerced node name
+ *
+ * \param container A shared pointer to the expert container to add the node to
+ * \param subtree A shared pointer to subtree to add the property to
+ * \param path The path of the property in the subtree
+ * \param init_val The initial value of both the data nodes
+ * \param mode The auto resolve mode
+ *
+ */
+ template<typename data_t>
+ inline static property<data_t>& add_dual_prop_node(
+ expert_container::sptr container,
+ property_tree::sptr subtree,
+ const fs_path &path,
+ const data_t& init_val,
+ const auto_resolve_mode_t mode = AUTO_RESOLVE_OFF
+ ) {
+ return add_dual_prop_node(container, subtree, path, path + "/desired", path + "/coerced", init_val, mode);
+ }
+
+ /*!
+ * Add a worker node to the expert graph.
+ * The expert_container owns and manages storage for the worker
+ *
+ * \tparam worker_t Data type of the worker class
+ *
+ * \param container A shared pointer to the container to add the node to
+ *
+ */
+ template<typename worker_t>
+ inline static void add_worker_node(
+ expert_container::sptr container
+ ) {
+ container->add_worker(new worker_t());
+ }
+
+ /*!
+ * Add a worker node to the expert graph.
+ * The expert_container owns and manages storage for the worker
+ *
+ * \tparam worker_t Data type of the worker class
+ * \tparam arg1_t Data type of the first argument to the constructor
+ * \tparam ...
+ * \tparam argN_t Data type of the Nth argument to the constructor
+ *
+ * \param container A shared pointer to the container to add the node to
+ * \param arg1 First arg to ctor
+ * \param ...
+ * \param argN Nth arg to ctor
+ *
+ */
+ template<typename worker_t, typename arg1_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1
+ ) {
+ container->add_worker(new worker_t(arg1));
+ }
+
+ template<typename worker_t, typename arg1_t, typename arg2_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1,
+ arg2_t const & arg2
+ ) {
+ container->add_worker(new worker_t(arg1, arg2));
+ }
+
+ template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1,
+ arg2_t const & arg2,
+ arg3_t const & arg3
+ ) {
+ container->add_worker(new worker_t(arg1, arg2, arg3));
+ }
+
+ template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1,
+ arg2_t const & arg2,
+ arg3_t const & arg3,
+ arg4_t const & arg4
+ ) {
+ container->add_worker(new worker_t(arg1, arg2, arg3, arg4));
+ }
+
+ template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t,
+ typename arg5_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1,
+ arg2_t const & arg2,
+ arg3_t const & arg3,
+ arg4_t const & arg4,
+ arg5_t const & arg5
+ ) {
+ container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5));
+ }
+
+ template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t,
+ typename arg5_t, typename arg6_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1,
+ arg2_t const & arg2,
+ arg3_t const & arg3,
+ arg4_t const & arg4,
+ arg5_t const & arg5,
+ arg6_t const & arg6
+ ) {
+ container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6));
+ }
+
+ template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t,
+ typename arg5_t, typename arg6_t, typename arg7_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1,
+ arg2_t const & arg2,
+ arg3_t const & arg3,
+ arg4_t const & arg4,
+ arg5_t const & arg5,
+ arg6_t const & arg6,
+ arg7_t const & arg7
+ ) {
+ container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7));
+ }
+
+ template<typename worker_t, typename arg1_t, typename arg2_t, typename arg3_t, typename arg4_t,
+ typename arg5_t, typename arg6_t, typename arg7_t, typename arg8_t>
+ inline static void add_worker_node(
+ expert_container::sptr container,
+ arg1_t const & arg1,
+ arg2_t const & arg2,
+ arg3_t const & arg3,
+ arg4_t const & arg4,
+ arg5_t const & arg5,
+ arg6_t const & arg6,
+ arg7_t const & arg7,
+ arg7_t const & arg8
+ ) {
+ container->add_worker(new worker_t(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
+ }
+ };
+}}
+
+#endif /* INCLUDED_UHD_EXPERTS_EXPERT_FACTORY_HPP */
diff --git a/host/lib/include/uhdlib/experts/expert_nodes.hpp b/host/lib/include/uhdlib/experts/expert_nodes.hpp
new file mode 100644
index 000000000..697ca19c3
--- /dev/null
+++ b/host/lib/include/uhdlib/experts/expert_nodes.hpp
@@ -0,0 +1,472 @@
+//
+// Copyright 2016 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_UHD_EXPERTS_EXPERT_NODES_HPP
+#define INCLUDED_UHD_EXPERTS_EXPERT_NODES_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/dirty_tracked.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <boost/function.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+#include <boost/thread.hpp>
+#include <boost/units/detail/utility.hpp>
+#include <memory>
+#include <list>
+#include <stdint.h>
+
+namespace uhd { namespace experts {
+
+ enum node_class_t { CLASS_WORKER, CLASS_DATA, CLASS_PROPERTY };
+ enum node_access_t { ACCESS_READER, ACCESS_WRITER };
+ enum node_author_t { AUTHOR_NONE, AUTHOR_USER, AUTHOR_EXPERT };
+
+ /*!---------------------------------------------------------
+ * class dag_vertex_t
+ *
+ * This serves as the base class for all nodes in the expert
+ * graph. Data nodes and workers are derived from this class.
+ * ---------------------------------------------------------
+ */
+ class dag_vertex_t : private boost::noncopyable {
+ public:
+ typedef boost::function<void(std::string)> callback_func_t;
+
+ virtual ~dag_vertex_t() {}
+
+ // Getters for basic info about the node
+ inline node_class_t get_class() const {
+ return _class;
+ }
+
+ inline const std::string& get_name() const {
+ return _name;
+ }
+
+ virtual const std::string& get_dtype() const = 0;
+
+ virtual std::string to_string() const = 0;
+
+ // Graph resolution specific
+ virtual bool is_dirty() const = 0;
+ virtual void mark_clean() = 0;
+ virtual void resolve() = 0;
+
+ // External callbacks
+ virtual void set_write_callback(const callback_func_t& func) = 0;
+ virtual bool has_write_callback() const = 0;
+ virtual void clear_write_callback() = 0;
+ virtual void set_read_callback(const callback_func_t& func) = 0;
+ virtual bool has_read_callback() const = 0;
+ virtual void clear_read_callback() = 0;
+
+ protected:
+ dag_vertex_t(const node_class_t c, const std::string& n):
+ _class(c), _name(n) {}
+
+ private:
+ const node_class_t _class;
+ const std::string _name;
+ };
+
+ class data_node_printer {
+ public:
+ //Generic implementation
+ template<typename data_t>
+ static std::string print(const data_t& val) {
+ std::ostringstream os;
+ os << val;
+ return os.str();
+ }
+
+ static std::string print(const uint8_t& val) {
+ std::ostringstream os;
+ os << int(val);
+ return os.str();
+ }
+
+ static std::string print(const time_spec_t time) {
+ std::ostringstream os;
+ os << time.get_real_secs();
+ return os.str();
+ }
+ };
+
+ /*!---------------------------------------------------------
+ * class data_node_t
+ *
+ * The data node class hold a passive piece of data in the
+ * expert graph. A data node is clean if its underlying data
+ * is clean. Access to the underlying data is provided using
+ * two methods:
+ * 1. Special accessor classes (for R/W enforcement)
+ * 2. External clients (via commit and retrieve). This access
+ * is protected by the callback mutex.
+ *
+ * Requirements for data_t
+ * - Must have a default constructor
+ * - Must have a copy constructor
+ * - Must have an assignment operator (=)
+ * - Must have an equality operator (==)
+ * ---------------------------------------------------------
+ */
+ template<typename data_t>
+ class data_node_t : public dag_vertex_t {
+ public:
+ // A data_node_t instance can have a type of CLASS_DATA or CLASS_PROPERTY
+ // In general a data node is a property if it can be accessed and modified
+ // from the outside world (of experts) using read and write callbacks. We
+ // assume that if a callback mutex is passed into the data node that it will
+ // be accessed from the outside and tag the data node as a PROPERTY.
+ data_node_t(const std::string& name, boost::recursive_mutex* mutex = NULL) :
+ dag_vertex_t(mutex?CLASS_PROPERTY:CLASS_DATA, name), _callback_mutex(mutex), _data(), _author(AUTHOR_NONE) {}
+
+ data_node_t(const std::string& name, const data_t& value, boost::recursive_mutex* mutex = NULL) :
+ dag_vertex_t(mutex?CLASS_PROPERTY:CLASS_DATA, name), _callback_mutex(mutex), _data(value), _author(AUTHOR_NONE) {}
+
+ // Basic info
+ virtual const std::string& get_dtype() const {
+ static const std::string dtype(
+ boost::units::detail::demangle(typeid(data_t).name()));
+ return dtype;
+ }
+
+ virtual std::string to_string() const {
+ return data_node_printer::print(get());
+ }
+
+ inline node_author_t get_author() const {
+ return _author;
+ }
+
+ // Graph resolution specific
+ virtual bool is_dirty() const {
+ return _data.is_dirty();
+ }
+
+ virtual void mark_clean() {
+ _data.mark_clean();
+ }
+
+ void resolve() {
+ //NOP
+ }
+
+ // Data node specific setters and getters (for the framework)
+ void set(const data_t& value) {
+ _data = value;
+ _author = AUTHOR_EXPERT;
+ }
+
+ const data_t& get() const {
+ return _data;
+ }
+
+ // Data node specific setters and getters (for external entities)
+ void commit(const data_t& value) {
+ if (_callback_mutex == NULL) throw uhd::assertion_error("node " + get_name() + " is missing the callback mutex");
+ boost::lock_guard<boost::recursive_mutex> lock(*_callback_mutex);
+ set(value);
+ _author = AUTHOR_USER;
+ if (is_dirty() and has_write_callback()) {
+ _wr_callback(std::string(get_name())); //Put the name on the stack before calling
+ }
+ }
+
+ const data_t retrieve() const {
+ if (_callback_mutex == NULL) throw uhd::assertion_error("node " + get_name() + " is missing the callback mutex");
+ boost::lock_guard<boost::recursive_mutex> lock(*_callback_mutex);
+ if (has_read_callback()) {
+ _rd_callback(std::string(get_name()));
+ }
+ return get();
+ }
+
+ private:
+ // External callbacks
+ virtual void set_write_callback(const callback_func_t& func) {
+ _wr_callback = func;
+ }
+
+ virtual bool has_write_callback() const {
+ return not _wr_callback.empty();
+ }
+
+ virtual void clear_write_callback() {
+ _wr_callback.clear();
+ }
+
+ virtual void set_read_callback(const callback_func_t& func) {
+ _rd_callback = func;
+ }
+
+ virtual bool has_read_callback() const {
+ return not _rd_callback.empty();
+ }
+
+ virtual void clear_read_callback() {
+ _rd_callback.clear();
+ }
+
+ boost::recursive_mutex* _callback_mutex;
+ callback_func_t _rd_callback;
+ callback_func_t _wr_callback;
+ dirty_tracked<data_t> _data;
+ node_author_t _author;
+ };
+
+ /*!---------------------------------------------------------
+ * class node_retriever_t
+ *
+ * Node storage is managed by a framework class so we need
+ * and interface to find and retrieve data nodes to associate
+ * with accessors.
+ * ---------------------------------------------------------
+ */
+ class node_retriever_t {
+ public:
+ virtual ~node_retriever_t() {}
+ virtual const dag_vertex_t& lookup(const std::string& name) const = 0;
+ private:
+ friend class data_accessor_t;
+ virtual dag_vertex_t& retrieve(const std::string& name) const = 0;
+ };
+
+ /*!---------------------------------------------------------
+ * class data_accessor_t
+ *
+ * Accessors provide protected access to data nodes and help
+ * establish dependency relationships.
+ * ---------------------------------------------------------
+ */
+ class data_accessor_t {
+ public:
+ virtual ~data_accessor_t() {}
+
+ virtual bool is_reader() const = 0;
+ virtual bool is_writer() const = 0;
+ virtual dag_vertex_t& node() const = 0;
+ protected:
+ data_accessor_t(const node_retriever_t& r, const std::string& n):
+ _vertex(r.retrieve(n)) {}
+ dag_vertex_t& _vertex;
+ };
+
+ template<typename data_t>
+ class data_accessor_base : public data_accessor_t {
+ public:
+ virtual ~data_accessor_base() {}
+
+ virtual bool is_reader() const {
+ return _access == ACCESS_READER;
+ }
+
+ virtual bool is_writer() const {
+ return _access == ACCESS_WRITER;
+ }
+
+ inline bool is_dirty() const {
+ return _datanode->is_dirty();
+ }
+
+ inline node_class_t get_class() const {
+ return _datanode->get_class();
+ }
+
+ inline node_author_t get_author() const {
+ return _datanode->get_author();
+ }
+
+ protected:
+ data_accessor_base(
+ const node_retriever_t& r, const std::string& n, const node_access_t a) :
+ data_accessor_t(r, n), _datanode(NULL), _access(a)
+ {
+ _datanode = dynamic_cast< data_node_t<data_t>* >(&node());
+ if (_datanode == NULL) {
+ throw uhd::type_error("Expected data type for node " + n +
+ " was " + boost::units::detail::demangle(typeid(data_t).name()) +
+ " but got " + node().get_dtype());
+ }
+ }
+
+ data_node_t<data_t>* _datanode;
+ const node_access_t _access;
+
+ private:
+ virtual dag_vertex_t& node() const {
+ return _vertex;
+ }
+ };
+
+ /*!---------------------------------------------------------
+ * class data_reader_t
+ *
+ * Accessor to read the value of a data node and to establish
+ * a data node => worker node dependency
+ * ---------------------------------------------------------
+ */
+ template<typename data_t>
+ class data_reader_t : public data_accessor_base<data_t> {
+ public:
+ data_reader_t(const node_retriever_t& retriever, const std::string& node) :
+ data_accessor_base<data_t>(
+ retriever, node, ACCESS_READER) {}
+
+ inline const data_t& get() const {
+ return data_accessor_base<data_t>::_datanode->get();
+ }
+
+ inline operator const data_t&() const {
+ return get();
+ }
+
+ inline bool operator==(const data_t& rhs) {
+ return get() == rhs;
+ }
+
+ inline bool operator!=(const data_t& rhs) {
+ return !(get() == rhs);
+ }
+ };
+
+ /*!---------------------------------------------------------
+ * class data_reader_t
+ *
+ * Accessor to read and write the value of a data node and
+ * to establish a worker node => data node dependency
+ * ---------------------------------------------------------
+ */
+ template<typename data_t>
+ class data_writer_t : public data_accessor_base<data_t> {
+ public:
+ data_writer_t(const node_retriever_t& retriever, const std::string& node) :
+ data_accessor_base<data_t>(
+ retriever, node, ACCESS_WRITER) {}
+
+ inline const data_t& get() const {
+ return data_accessor_base<data_t>::_datanode->get();
+ }
+
+ inline operator const data_t&() const {
+ return get();
+ }
+
+ inline bool operator==(const data_t& rhs) {
+ return get() == rhs;
+ }
+
+ inline bool operator!=(const data_t& rhs) {
+ return !(get() == rhs);
+ }
+
+ inline void set(const data_t& value) {
+ data_accessor_base<data_t>::_datanode->set(value);
+ }
+
+ inline data_writer_t<data_t>& operator=(const data_t& value) {
+ set(value);
+ return *this;
+ }
+
+ inline data_writer_t<data_t>& operator=(const data_writer_t<data_t>& value) {
+ set(value.get());
+ return *this;
+ }
+};
+
+ /*!---------------------------------------------------------
+ * class worker_node_t
+ *
+ * A node class to implement a function that consumes
+ * zero or more input data nodes and emits zero or more output
+ * data nodes. The worker can also operate on other non-expert
+ * interfaces because worker_node_t is abstract and the client
+ * is required to implement the "resolve" method in a subclass.
+ * ---------------------------------------------------------
+ */
+ class worker_node_t : public dag_vertex_t {
+ public:
+ worker_node_t(const std::string& name) :
+ dag_vertex_t(CLASS_WORKER, name) {}
+
+ // Worker node specific
+ std::list<std::string> get_inputs() const {
+ std::list<std::string> retval;
+ for(data_accessor_t* acc: _inputs) {
+ retval.push_back(acc->node().get_name());
+ }
+ return retval;
+ }
+
+ std::list<std::string> get_outputs() const {
+ std::list<std::string> retval;
+ for(data_accessor_t* acc: _outputs) {
+ retval.push_back(acc->node().get_name());
+ }
+ return retval;
+ }
+
+ protected:
+ // This function is used to bind data accessors
+ // to this worker. Accessors can be read/write
+ // and the binding will ensure proper dependency
+ // handling.
+ void bind_accessor(data_accessor_t& accessor) {
+ if (accessor.is_reader()) {
+ _inputs.push_back(&accessor);
+ } else if (accessor.is_writer()) {
+ _outputs.push_back(&accessor);
+ } else {
+ throw uhd::assertion_error("Invalid accessor type");
+ }
+ }
+
+ private:
+ // Graph resolution specific
+ virtual bool is_dirty() const {
+ bool inputs_dirty = false;
+ for(data_accessor_t* acc: _inputs) {
+ inputs_dirty |= acc->node().is_dirty();
+ }
+ return inputs_dirty;
+ }
+
+ virtual void mark_clean() {
+ for(data_accessor_t* acc: _inputs) {
+ acc->node().mark_clean();
+ }
+ }
+
+ virtual void resolve() = 0;
+
+ // Basic type info
+ virtual const std::string& get_dtype() const {
+ static const std::string dtype = "<worker>";
+ return dtype;
+ }
+
+ virtual std::string to_string() const {
+ return "<worker>";
+ }
+
+ // Workers don't have callbacks so implement stubs
+ virtual void set_write_callback(const callback_func_t&) {}
+ virtual bool has_write_callback() const { return false; }
+ virtual void clear_write_callback() {}
+ virtual void set_read_callback(const callback_func_t&) {}
+ virtual bool has_read_callback() const { return false; }
+ virtual void clear_read_callback() {}
+
+ std::list<data_accessor_t*> _inputs;
+ std::list<data_accessor_t*> _outputs;
+ };
+
+}}
+
+#endif /* INCLUDED_UHD_EXPERTS_EXPERT_NODE_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/ctrl_iface.hpp b/host/lib/include/uhdlib/rfnoc/ctrl_iface.hpp
new file mode 100644
index 000000000..2ef50a1a1
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/ctrl_iface.hpp
@@ -0,0 +1,46 @@
+//
+// Copyright 2012-2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_CTRL_IFACE_HPP
+#define INCLUDED_LIBUHD_RFNOC_CTRL_IFACE_HPP
+
+#include <uhd/utils/msg_task.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <string>
+
+namespace uhd { namespace rfnoc {
+
+/*!
+ * Provide access to peek, poke for the radio ctrl module
+ */
+class ctrl_iface : public uhd::timed_wb_iface
+{
+public:
+ typedef boost::shared_ptr<ctrl_iface> sptr;
+
+ virtual ~ctrl_iface(void) = 0;
+
+ //! Make a new control object
+ static sptr make(
+ const bool big_endian,
+ uhd::transport::zero_copy_if::sptr ctrl_xport,
+ uhd::transport::zero_copy_if::sptr resp_xport,
+ const uint32_t sid,
+ const std::string &name = "0"
+ );
+
+ //! Set the tick rate (converting time into ticks)
+ virtual void set_tick_rate(const double rate) = 0;
+};
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_CTRL_IFACE_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/graph_impl.hpp b/host/lib/include/uhdlib/rfnoc/graph_impl.hpp
new file mode 100644
index 000000000..182befbf4
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/graph_impl.hpp
@@ -0,0 +1,80 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_GRAPH_IMPL_HPP
+#define INCLUDED_LIBUHD_RFNOC_GRAPH_IMPL_HPP
+
+#include <uhd/rfnoc/graph.hpp>
+#include <uhd/device3.hpp>
+
+namespace uhd { namespace rfnoc {
+
+class graph_impl : public graph
+{
+public:
+ /*!
+ * \param name An optional name to describe this graph
+ * \param device_ptr Weak pointer to the originating device3
+ * \param msg_handler Pointer to the async message handler
+ */
+ graph_impl(
+ const std::string &name,
+ boost::weak_ptr<uhd::device3> device_ptr
+ //async_msg_handler::sptr msg_handler
+ );
+ virtual ~graph_impl() {};
+
+ /************************************************************************
+ * Connection API
+ ***********************************************************************/
+ void connect(
+ const block_id_t &src_block,
+ size_t src_block_port,
+ const block_id_t &dst_block,
+ size_t dst_block_port,
+ const size_t pkt_size = 0
+ );
+
+ void connect(
+ const block_id_t &src_block,
+ const block_id_t &dst_block
+ );
+
+ void connect_src(
+ const block_id_t &src_block,
+ const size_t src_block_port,
+ const uhd::sid_t dst_sid,
+ const size_t buf_size_dst_bytes,
+ const size_t pkt_size_
+ );
+
+ void connect_sink(
+ const block_id_t &sink_block,
+ const size_t dst_block_port,
+ const size_t pkts_per_ack
+ );
+
+ /************************************************************************
+ * Utilities
+ ***********************************************************************/
+ std::string get_name() const { return _name; }
+
+
+private:
+
+ //! Optional: A string to describe this graph
+ const std::string _name;
+
+ //! Reference to the generating device object
+ const boost::weak_ptr<uhd::device3> _device_ptr;
+
+};
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_GRAPH_IMPL_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/include/uhdlib/rfnoc/legacy_compat.hpp b/host/lib/include/uhdlib/rfnoc/legacy_compat.hpp
new file mode 100644
index 000000000..185b89f82
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/legacy_compat.hpp
@@ -0,0 +1,49 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_RFNOC_LEGACY_COMPAT_HPP
+#define INCLUDED_RFNOC_LEGACY_COMPAT_HPP
+
+#include <uhd/device3.hpp>
+#include <uhd/stream.hpp>
+
+namespace uhd { namespace rfnoc {
+
+ /*! Legacy compatibility layer class.
+ */
+ class legacy_compat
+ {
+ public:
+ typedef boost::shared_ptr<legacy_compat> sptr;
+
+ virtual uhd::fs_path rx_dsp_root(const size_t mboard_idx, const size_t chan) = 0;
+
+ virtual uhd::fs_path tx_dsp_root(const size_t mboard_idx, const size_t chan) = 0;
+
+ virtual uhd::fs_path rx_fe_root(const size_t mboard_idx, const size_t chan) = 0;
+
+ virtual uhd::fs_path tx_fe_root(const size_t mboard_idx, const size_t chan) = 0;
+
+ virtual void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, size_t mboard, size_t chan) = 0;
+
+ virtual uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args) = 0;
+
+ virtual uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args) = 0;
+
+ virtual void set_rx_rate(const double rate, const size_t chan) = 0;
+
+ virtual void set_tx_rate(const double rate, const size_t chan) = 0;
+
+ static sptr make(
+ uhd::device3::sptr device,
+ const uhd::device_addr_t &args
+ );
+ };
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_RFNOC_LEGACY_COMPAT_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/radio_ctrl_impl.hpp b/host/lib/include/uhdlib/rfnoc/radio_ctrl_impl.hpp
new file mode 100644
index 000000000..a5876584e
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/radio_ctrl_impl.hpp
@@ -0,0 +1,247 @@
+//
+// Copyright 2014-2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_IMPL_HPP
+#define INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_IMPL_HPP
+
+#include <uhd/rfnoc/radio_ctrl.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhdlib/usrp/cores/rx_vita_core_3000.hpp>
+#include <uhdlib/usrp/cores/tx_vita_core_3000.hpp>
+#include <uhdlib/usrp/cores/time_core_3000.hpp>
+#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
+#include <boost/thread.hpp>
+#include <mutex>
+
+//! Shorthand for radio block constructor
+#define UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(CLASS_NAME) \
+ CLASS_NAME##_impl(const make_args_t &make_args);
+
+#define UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(CLASS_NAME) \
+ CLASS_NAME##_impl::CLASS_NAME##_impl( \
+ const make_args_t &make_args \
+ ) : block_ctrl_base(make_args), radio_ctrl_impl()
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! \brief Provide access to a radio.
+ *
+ */
+class radio_ctrl_impl : public radio_ctrl
+{
+public:
+ /************************************************************************
+ * Structors
+ ***********************************************************************/
+ radio_ctrl_impl();
+ virtual ~radio_ctrl_impl() {};
+
+ /************************************************************************
+ * Public Radio API calls
+ ***********************************************************************/
+ virtual double set_rate(double rate);
+ virtual void set_tx_antenna(const std::string &ant, const size_t chan);
+ virtual void set_rx_antenna(const std::string &ant, const size_t chan);
+ virtual double set_tx_frequency(const double freq, const size_t chan);
+ virtual double set_rx_frequency(const double freq, const size_t chan);
+ virtual double set_tx_gain(const double gain, const size_t chan);
+ virtual double set_rx_gain(const double gain, const size_t chan);
+ virtual double set_tx_bandwidth(const double bandwidth, const size_t chan);
+ virtual double set_rx_bandwidth(const double bandwidth, const size_t chan);
+
+ virtual double get_rate() const;
+ virtual std::string get_tx_antenna(const size_t chan) /* const */;
+ virtual std::string get_rx_antenna(const size_t chan) /* const */;
+ virtual double get_tx_frequency(const size_t) /* const */;
+ virtual double get_rx_frequency(const size_t) /* const */;
+ virtual double get_tx_gain(const size_t) /* const */;
+ virtual double get_rx_gain(const size_t) /* const */;
+ virtual double get_tx_bandwidth(const size_t) /* const */;
+ virtual double get_rx_bandwidth(const size_t) /* const */;
+
+ virtual std::vector<std::string> get_rx_lo_names(const size_t chan);
+ virtual std::vector<std::string> get_rx_lo_sources(const std::string &name, const size_t chan);
+ virtual freq_range_t get_rx_lo_freq_range(const std::string &name, const size_t chan);
+
+ virtual void set_rx_lo_source(const std::string &src, const std::string &name, const size_t chan);
+ virtual const std::string get_rx_lo_source(const std::string &name, const size_t chan);
+
+ virtual void set_rx_lo_export_enabled(bool enabled, const std::string &name, const size_t chan);
+ virtual bool get_rx_lo_export_enabled(const std::string &name, const size_t chan);
+
+ virtual double set_rx_lo_freq(const double freq, const std::string &name, const size_t chan);
+ virtual double get_rx_lo_freq(const std::string &name, const size_t chan);
+
+ virtual std::vector<std::string> get_tx_lo_names(const size_t chan);
+ virtual std::vector<std::string> get_tx_lo_sources(const std::string &name, const size_t chan);
+ virtual freq_range_t get_tx_lo_freq_range(const std::string &name, const size_t chan);
+
+ virtual void set_tx_lo_source(const std::string &src, const std::string &name, const size_t chan);
+ virtual const std::string get_tx_lo_source(const std::string &name, const size_t chan);
+
+ virtual void set_tx_lo_export_enabled(const bool enabled, const std::string &name, const size_t chan);
+ virtual bool get_tx_lo_export_enabled(const std::string &name, const size_t chan);
+
+ virtual double set_tx_lo_freq(const double freq, const std::string &name, const size_t chan);
+ virtual double get_tx_lo_freq(const std::string &name, const size_t chan);
+
+ void set_time_now(const time_spec_t &time_spec);
+ void set_time_next_pps(const time_spec_t &time_spec);
+ void set_time_sync(const uhd::time_spec_t &time);
+ time_spec_t get_time_now();
+ time_spec_t get_time_last_pps();
+ virtual void set_time_source(const std::string &source);
+ virtual std::string get_time_source();
+ virtual std::vector<std::string> get_time_sources();
+ virtual void set_clock_source(const std::string &source);
+ virtual std::string get_clock_source();
+ virtual std::vector<std::string> get_clock_sources();
+
+ virtual std::vector<std::string> get_gpio_banks() const;
+ virtual void set_gpio_attr(
+ const std::string &bank,
+ const std::string &attr,
+ const uint32_t value,
+ const uint32_t mask
+ );
+ virtual uint32_t get_gpio_attr(const std::string &bank, const std::string &attr);
+
+ /***********************************************************************
+ * Block control API calls
+ **********************************************************************/
+ void set_rx_streamer(bool active, const size_t port);
+ void set_tx_streamer(bool active, const size_t port);
+
+ void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t port);
+
+ virtual double get_input_samp_rate(size_t /* port */) { return get_rate(); }
+ virtual double get_output_samp_rate(size_t /* port */) { return get_rate(); }
+ double _get_tick_rate() { return get_rate(); }
+
+ std::vector<size_t> get_active_rx_ports();
+ bool in_continuous_streaming_mode(const size_t chan) { return _continuous_streaming.at(chan); }
+ void rx_ctrl_clear_cmds(const size_t port) { sr_write(regs::RX_CTRL_CLEAR_CMDS, 0, port); }
+
+protected: // TODO see what's protected and what's private
+ void _register_loopback_self_test(size_t chan);
+
+ /***********************************************************************
+ * Registers
+ **********************************************************************/
+ struct regs {
+ static inline uint32_t sr_addr(const uint32_t offset)
+ {
+ return offset * 4;
+ }
+
+ static const uint32_t BASE = 128;
+
+ // defined in radio_core_regs.vh
+ static const uint32_t TIME = 128; // time hi - 128, time lo - 129, ctrl - 130
+ static const uint32_t CLEAR_CMDS = 131; // Any write to this reg clears the command FIFO
+ static const uint32_t LOOPBACK = 132;
+ static const uint32_t TEST = 133;
+ static const uint32_t CODEC_IDLE = 134;
+ static const uint32_t TX_CTRL_ERROR_POLICY = 144;
+ static const uint32_t RX_CTRL_CMD = 152;
+ static const uint32_t RX_CTRL_TIME_HI = 153;
+ static const uint32_t RX_CTRL_TIME_LO = 154;
+ static const uint32_t RX_CTRL_HALT = 155;
+ static const uint32_t RX_CTRL_MAXLEN = 156;
+ static const uint32_t RX_CTRL_CLEAR_CMDS = 157;
+ static const uint32_t MISC_OUTS = 160;
+ static const uint32_t DACSYNC = 161;
+ static const uint32_t SPI = 168;
+ static const uint32_t LEDS = 176;
+ static const uint32_t FP_GPIO = 184;
+ static const uint32_t GPIO = 192;
+ // NOTE: Upper 32 registers (224-255) are reserved for the output settings bus for use with
+ // device specific front end control
+
+ // frontend control: needs rethinking TODO
+ //static const uint32_t TX_FRONT = BASE + 96;
+ //static const uint32_t RX_FRONT = BASE + 112;
+ //static const uint32_t READBACK = BASE + 127;
+
+ static const uint32_t RB_TIME_NOW = 0;
+ static const uint32_t RB_TIME_PPS = 1;
+ static const uint32_t RB_TEST = 2;
+ static const uint32_t RB_CODEC_READBACK = 3;
+ static const uint32_t RB_RADIO_NUM = 4;
+ static const uint32_t RB_MISC_IO = 16;
+ static const uint32_t RB_SPI = 17;
+ static const uint32_t RB_LEDS = 18;
+ static const uint32_t RB_DB_GPIO = 19;
+ static const uint32_t RB_FP_GPIO = 20;
+ };
+
+ /***********************************************************************
+ * Block control API calls
+ **********************************************************************/
+ void _update_spp(int spp);
+
+ inline size_t _get_num_radios() const {
+ return std::max(_num_rx_channels, _num_tx_channels);
+ }
+
+ inline timed_wb_iface::sptr _get_ctrl(size_t radio_num) const {
+ return _perifs.at(radio_num).ctrl;
+ }
+
+ inline bool _is_streamer_active(uhd::direction_t dir, const size_t chan) const {
+ switch (dir) {
+ case uhd::TX_DIRECTION:
+ return _tx_streamer_active.at(chan);
+ case uhd::RX_DIRECTION:
+ return _rx_streamer_active.at(chan);
+ case uhd::DX_DIRECTION:
+ return _rx_streamer_active.at(chan) and _tx_streamer_active.at(chan);
+ default:
+ return false;
+ }
+ }
+
+ virtual bool check_radio_config() { return true; };
+
+ //! There is always only one time core per radio
+ time_core_3000::sptr _time64;
+
+ std::mutex _mutex;
+
+private:
+ /************************************************************************
+ * Peripherals
+ ***********************************************************************/
+ //! Stores pointers to all streaming-related radio cores
+ struct radio_perifs_t
+ {
+ timed_wb_iface::sptr ctrl;
+ };
+ std::map<size_t, radio_perifs_t> _perifs;
+
+ size_t _num_tx_channels;
+ size_t _num_rx_channels;
+
+ // Cached values
+ double _tick_rate;
+ std::map<size_t, std::string> _tx_antenna;
+ std::map<size_t, std::string> _rx_antenna;
+ std::map<size_t, double> _tx_freq;
+ std::map<size_t, double> _rx_freq;
+ std::map<size_t, double> _tx_gain;
+ std::map<size_t, double> _rx_gain;
+ std::map<size_t, double> _tx_bandwidth;
+ std::map<size_t, double> _rx_bandwidth;
+
+ std::vector<bool> _continuous_streaming;
+}; /* class radio_ctrl_impl */
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_IMPL_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/include/uhdlib/rfnoc/rpc_block_ctrl.hpp b/host/lib/include/uhdlib/rfnoc/rpc_block_ctrl.hpp
new file mode 100644
index 000000000..91091d8c2
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/rpc_block_ctrl.hpp
@@ -0,0 +1,41 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_RPC_BLOCK_CTRL_HPP
+#define INCLUDED_LIBUHD_RFNOC_RPC_BLOCK_CTRL_HPP
+
+#include <uhd/types/device_addr.hpp>
+#include <uhdlib/utils/rpc.hpp>
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! Abstraction for RPC client
+ *
+ * Purpose of this class is to wrap the underlying RPC implementation.
+ * This class holds a connection to an RPC server (the connection is severed on
+ * destruction).
+ */
+class rpc_block_ctrl
+{
+public:
+ virtual ~rpc_block_ctrl() {}
+
+ /*! Pass in an RPC client for the block to use
+ *
+ * \param rpcc Reference to the RPC client
+ * \param block_args Additional block arguments
+ */
+ virtual void set_rpc_client(
+ uhd::rpc_client::sptr rpcc,
+ const uhd::device_addr_t &block_args
+ ) = 0;
+
+};
+
+}}
+
+#endif /* INCLUDED_LIBUHD_RFNOC_RPC_BLOCK_CTRL_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/rx_stream_terminator.hpp b/host/lib/include/uhdlib/rfnoc/rx_stream_terminator.hpp
new file mode 100644
index 000000000..3f82ab12f
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/rx_stream_terminator.hpp
@@ -0,0 +1,79 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_TERMINATOR_RECV_HPP
+#define INCLUDED_LIBUHD_RFNOC_TERMINATOR_RECV_HPP
+
+#include <uhd/rfnoc/sink_node_ctrl.hpp>
+#include <uhd/rfnoc/rate_node_ctrl.hpp>
+#include <uhd/rfnoc/tick_node_ctrl.hpp>
+#include <uhd/rfnoc/scalar_node_ctrl.hpp>
+#include <uhd/rfnoc/terminator_node_ctrl.hpp>
+#include <uhd/rfnoc/block_ctrl_base.hpp> // For the block macros
+#include <mutex>
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! \brief Terminator node for Rx streamers.
+ *
+ * This node is only used by rx_streamers. It terminates the flow graph
+ * inside the streamer and does not have a counterpart on the FPGA.
+ */
+class rx_stream_terminator :
+ public sink_node_ctrl,
+ public rate_node_ctrl,
+ public tick_node_ctrl,
+ public scalar_node_ctrl,
+ public terminator_node_ctrl
+{
+public:
+ UHD_RFNOC_BLOCK_OBJECT(rx_stream_terminator)
+
+ static sptr make()
+ {
+ return sptr(new rx_stream_terminator);
+ }
+
+ // If this is called, then by a send terminator at the other end
+ // of a flow graph.
+ double get_input_samp_rate(size_t) { return _samp_rate; };
+
+ // Same for the scaling factor
+ double get_input_scale_factor(size_t) { return scalar_node_ctrl::SCALE_UNDEFINED; };
+
+ std::string unique_id() const;
+
+ void set_rx_streamer(bool active, const size_t port);
+
+ void set_tx_streamer(bool active, const size_t port);
+
+ virtual ~rx_stream_terminator();
+
+ void handle_overrun(boost::weak_ptr<uhd::rx_streamer>, const size_t);
+
+protected:
+ rx_stream_terminator();
+
+ virtual double _get_tick_rate() { return _tick_rate; };
+
+private:
+ //! Every terminator has a unique index
+ const size_t _term_index;
+ static size_t _count;
+
+ double _samp_rate;
+ double _tick_rate;
+
+ std::mutex _overrun_handler_mutex;
+
+}; /* class rx_stream_terminator */
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_TERMINATOR_RECV_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/include/uhdlib/rfnoc/tx_stream_terminator.hpp b/host/lib/include/uhdlib/rfnoc/tx_stream_terminator.hpp
new file mode 100644
index 000000000..0a07e48ef
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/tx_stream_terminator.hpp
@@ -0,0 +1,80 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_TERMINATOR_SEND_HPP
+#define INCLUDED_LIBUHD_RFNOC_TERMINATOR_SEND_HPP
+
+#include <uhd/rfnoc/source_node_ctrl.hpp>
+#include <uhd/rfnoc/rate_node_ctrl.hpp>
+#include <uhd/rfnoc/tick_node_ctrl.hpp>
+#include <uhd/rfnoc/scalar_node_ctrl.hpp>
+#include <uhd/rfnoc/terminator_node_ctrl.hpp>
+#include <uhd/rfnoc/block_ctrl_base.hpp> // For the block macros
+#include <uhd/utils/log.hpp>
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! \brief Terminator node for Tx streamers.
+ *
+ * This node is only used by tx_streamers. It terminates the flow graph
+ * inside the streamer and does not have a counterpart on the FPGA.
+ */
+class tx_stream_terminator :
+ public source_node_ctrl,
+ public rate_node_ctrl,
+ public tick_node_ctrl,
+ public scalar_node_ctrl,
+ public terminator_node_ctrl
+{
+public:
+ UHD_RFNOC_BLOCK_OBJECT(tx_stream_terminator)
+
+ static sptr make()
+ {
+ return sptr(new tx_stream_terminator);
+ }
+
+ void issue_stream_cmd(const uhd::stream_cmd_t &, const size_t)
+ {
+ UHD_RFNOC_BLOCK_TRACE() << "tx_stream_terminator::issue_stream_cmd()" ;
+ }
+
+ // If this is called, then by a send terminator at the other end
+ // of a flow graph.
+ double get_output_samp_rate(size_t) { return _samp_rate; };
+
+ // Same for the scaling factor
+ double get_output_scale_factor(size_t) { return scalar_node_ctrl::SCALE_UNDEFINED; };
+
+ std::string unique_id() const;
+
+ void set_rx_streamer(bool active, const size_t port);
+
+ void set_tx_streamer(bool active, const size_t port);
+
+ virtual ~tx_stream_terminator();
+
+protected:
+ tx_stream_terminator();
+
+ virtual double _get_tick_rate() { return _tick_rate; };
+
+private:
+ //! Every terminator has a unique index
+ const size_t _term_index;
+ static size_t _count;
+
+ double _samp_rate;
+ double _tick_rate;
+
+}; /* class tx_stream_terminator */
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_TERMINATOR_SEND_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/include/uhdlib/rfnoc/utils.hpp b/host/lib/include/uhdlib/rfnoc/utils.hpp
new file mode 100644
index 000000000..28c69987f
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/utils.hpp
@@ -0,0 +1,67 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_UTILS_HPP
+#define INCLUDED_LIBUHD_RFNOC_UTILS_HPP
+
+#include <uhd/rfnoc/node_ctrl_base.hpp>
+#include <boost/lexical_cast.hpp>
+#include <set>
+
+namespace uhd { namespace rfnoc { namespace utils {
+
+ /*! If \p suggested_port equals ANY_PORT, return the first available
+ * port number on \p nodes. Otherwise, return \p suggested_port.
+ *
+ * If \p allowed_ports is given, another condition is that the port
+ * number must be listed in here.
+ * If \p allowed_ports is not specified or empty, the assumption is
+ * that all ports are valid.
+ *
+ * On failure, ANY_PORT is returned.
+ */
+ static size_t node_map_find_first_free(
+ node_ctrl_base::node_map_t nodes,
+ const size_t suggested_port,
+ const std::set<size_t> allowed_ports=std::set<size_t>()
+ ) {
+ size_t port = suggested_port;
+ if (port == ANY_PORT) {
+ if (allowed_ports.empty()) {
+ port = 0;
+ while (nodes.count(port) and (port != ANY_PORT)) {
+ port++;
+ }
+ } else {
+ for(const size_t allowed_port: allowed_ports) {
+ if (not nodes.count(port)) {
+ return allowed_port;
+ }
+ return ANY_PORT;
+ }
+ }
+ } else {
+ if (not (allowed_ports.empty() or allowed_ports.count(port))) {
+ return ANY_PORT;
+ }
+ }
+ return port;
+ }
+
+ template <typename T>
+ static std::set<T> str_list_to_set(const std::vector<std::string> &list) {
+ std::set<T> return_set;
+ for(const std::string &S: list) {
+ return_set.insert(boost::lexical_cast<T>(S));
+ }
+ return return_set;
+ }
+
+}}}; /* namespace uhd::rfnoc::utils */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_UTILS_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/include/uhdlib/rfnoc/wb_iface_adapter.hpp b/host/lib/include/uhdlib/rfnoc/wb_iface_adapter.hpp
new file mode 100644
index 000000000..753fa13af
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/wb_iface_adapter.hpp
@@ -0,0 +1,60 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_RFNOC_WB_IFACE_ADAPTER_HPP
+#define INCLUDED_RFNOC_WB_IFACE_ADAPTER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <boost/function.hpp>
+
+namespace uhd {
+ namespace rfnoc {
+
+class UHD_API wb_iface_adapter : public uhd::timed_wb_iface
+{
+public:
+ typedef boost::shared_ptr<wb_iface_adapter> sptr;
+ typedef boost::function<void(wb_addr_type, uint32_t)> poke32_type;
+ typedef boost::function<uint32_t(wb_addr_type)> peek32_type;
+ typedef boost::function<uint64_t(wb_addr_type)> peek64_type;
+ typedef boost::function<time_spec_t(void)> gettime_type;
+ typedef boost::function<void(const time_spec_t&)> settime_type;
+
+ wb_iface_adapter(
+ const poke32_type &,
+ const peek32_type &,
+ const peek64_type &,
+ const gettime_type &,
+ const settime_type &
+ );
+
+ wb_iface_adapter(
+ const poke32_type &,
+ const peek32_type &,
+ const peek64_type &
+ );
+
+ virtual ~wb_iface_adapter(void) {};
+
+ virtual void poke32(const wb_addr_type addr, const uint32_t data);
+ virtual uint32_t peek32(const wb_addr_type addr);
+ virtual uint64_t peek64(const wb_addr_type addr);
+ virtual time_spec_t get_time(void);
+ virtual void set_time(const time_spec_t& t);
+
+private:
+ const poke32_type poke32_functor;
+ const peek32_type peek32_functor;
+ const peek64_type peek64_functor;
+ const gettime_type gettime_functor;
+ const settime_type settime_functor;
+};
+
+}} // namespace uhd::rfnoc
+
+#endif /* INCLUDED_RFNOC_WB_IFACE_ADAPTER_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/xports.hpp b/host/lib/include/uhdlib/rfnoc/xports.hpp
new file mode 100644
index 000000000..5ee65175a
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/xports.hpp
@@ -0,0 +1,29 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/types/sid.hpp>
+#include <uhd/types/endianness.hpp>
+#include <uhd/transport/zero_copy.hpp>
+
+namespace uhd {
+
+ /*! Holds all necessary items for a bidirectional link
+ */
+ struct both_xports_t
+ {
+ both_xports_t(): recv_buff_size(0), send_buff_size(0) {}
+ uhd::transport::zero_copy_if::sptr recv;
+ uhd::transport::zero_copy_if::sptr send;
+ size_t recv_buff_size;
+ size_t send_buff_size;
+ uhd::sid_t send_sid;
+ uhd::sid_t recv_sid;
+ uhd::endianness_t endianness;
+ };
+
+};
+
diff --git a/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp b/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp
new file mode 100644
index 000000000..b9e81074b
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp
@@ -0,0 +1,161 @@
+//
+// Copyright 2012-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_AD9361_CTRL_HPP
+#define INCLUDED_AD9361_CTRL_HPP
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/exception.hpp>
+#include <boost/shared_ptr.hpp>
+#include <ad9361_device.h>
+#include <string>
+#include <complex>
+#include <uhd/types/filters.hpp>
+#include <vector>
+
+namespace uhd { namespace usrp {
+
+/*! AD936x Control Interface
+ *
+ * This is a convenient way to access the AD936x RF IC.
+ * It basically encodes knowledge of register values etc. into
+ * accessible API calls.
+ *
+ * \section ad936x_which The `which` parameter
+ *
+ * Many function calls require a `which` parameter to select
+ * the RF frontend. Valid values for `which` are:
+ * - RX1, RX2
+ * - TX1, TX2
+ *
+ * Frontend numbering is as designed by the AD9361.
+ */
+class ad9361_ctrl : public boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<ad9361_ctrl> sptr;
+
+ virtual ~ad9361_ctrl(void) {};
+
+ //! make a new codec control object
+ static sptr make_spi(
+ ad9361_params::sptr client_settings,
+ uhd::spi_iface::sptr spi_iface,
+ uint32_t slave_num
+ );
+
+ virtual void set_timed_spi(uhd::spi_iface::sptr spi_iface, uint32_t slave_num) = 0;
+ virtual void set_safe_spi(uhd::spi_iface::sptr spi_iface, uint32_t slave_num) = 0;
+
+ //! Get a list of gain names for RX or TX
+ static std::vector<std::string> get_gain_names(const std::string &/*which*/)
+ {
+ return std::vector<std::string>(1, "PGA");
+ }
+
+ //! get the gain range for a particular gain element
+ static uhd::meta_range_t get_gain_range(const std::string &which)
+ {
+ if(which[0] == 'R') {
+ return uhd::meta_range_t(0.0, 76.0, 1.0);
+ } else {
+ return uhd::meta_range_t(0.0, 89.75, 0.25);
+ }
+ }
+
+ //! get the freq range for the frontend which
+ static uhd::meta_range_t get_rf_freq_range(void)
+ {
+ return uhd::meta_range_t(50e6, 6e9);
+ }
+
+ //! get the filter range for the frontend which
+ static uhd::meta_range_t get_bw_filter_range(const std::string &/*which*/)
+ {
+ return uhd::meta_range_t(200e3, 56e6);
+ }
+
+ //! get the clock rate range for the frontend
+ static uhd::meta_range_t get_clock_rate_range(void)
+ {
+ return uhd::meta_range_t(
+ ad9361_device_t::AD9361_MIN_CLOCK_RATE,
+ ad9361_device_t::AD9361_MAX_CLOCK_RATE
+ );
+ }
+
+ //! set the filter bandwidth for the frontend's analog low pass
+ virtual double set_bw_filter(const std::string &/*which*/, const double /*bw*/) = 0;
+
+ //! set the gain for a particular gain element
+ virtual double set_gain(const std::string &which, const double value) = 0;
+
+ //! Enable or disable the AGC module
+ virtual void set_agc(const std::string &which, bool enable) = 0;
+
+ //! configure the AGC module to slow or fast mode
+ virtual void set_agc_mode(const std::string &which, const std::string &mode) = 0;
+
+ //! set a new clock rate, return the exact value
+ virtual double set_clock_rate(const double rate) = 0;
+
+ //! set which RX and TX chains/antennas are active
+ virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0;
+
+ //! tune the given frontend, return the exact value
+ virtual double tune(const std::string &which, const double value) = 0;
+
+ //! set the DC offset for I and Q manually
+ void set_dc_offset(const std::string &, const std::complex<double>)
+ {
+ //This feature should not be used according to Analog Devices
+ throw uhd::runtime_error("ad9361_ctrl::set_dc_offset this feature is not supported on this device.");
+ }
+
+ //! enable or disable the BB/RF DC tracking feature
+ virtual void set_dc_offset_auto(const std::string &which, const bool on) = 0;
+
+ //! set the IQ correction value manually
+ void set_iq_balance(const std::string &, const std::complex<double>)
+ {
+ //This feature should not be used according to Analog Devices
+ throw uhd::runtime_error("ad9361_ctrl::set_iq_balance this feature is not supported on this device.");
+ }
+
+ //! enable or disable the quadrature calibration
+ virtual void set_iq_balance_auto(const std::string &which, const bool on) = 0;
+
+ //! get the current frequency for the given frontend
+ virtual double get_freq(const std::string &which) = 0;
+
+ //! turn on/off Catalina's data port loopback
+ virtual void data_port_loopback(const bool on) = 0;
+
+ //! read internal RSSI sensor
+ virtual sensor_value_t get_rssi(const std::string &which) = 0;
+
+ //! read the internal temp sensor
+ virtual sensor_value_t get_temperature() = 0;
+
+ //! List all available filters by name
+ virtual std::vector<std::string> get_filter_names(const std::string &which) = 0;
+
+ //! Return a list of all filters
+ virtual filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) = 0;
+
+ //! Write back a filter
+ virtual void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr) = 0;
+
+ virtual void output_digital_test_tone(bool enb) = 0;
+};
+
+}}
+
+#endif /* INCLUDED_AD9361_CTRL_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/ad936x_manager.hpp b/host/lib/include/uhdlib/usrp/common/ad936x_manager.hpp
new file mode 100644
index 000000000..7934b1050
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/ad936x_manager.hpp
@@ -0,0 +1,122 @@
+//
+// Copyright 2015 Ettus Research
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_AD9361_MANAGER_HPP
+#define INCLUDED_AD9361_MANAGER_HPP
+
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhdlib/usrp/common/ad9361_ctrl.hpp>
+#include <boost/format.hpp>
+#include <boost/shared_ptr.hpp>
+#include <stdint.h>
+
+namespace uhd { namespace usrp {
+
+/*! AD936x Manager class
+ *
+ * This class performs higher (management) tasks on the AD936x.
+ * It requires a uhd::usrp::ad9361_ctrl object to do the actual
+ * register peeks/pokes etc.
+ */
+class ad936x_manager
+{
+public:
+ typedef boost::shared_ptr<ad936x_manager> sptr;
+
+ static const double DEFAULT_GAIN;
+ static const double DEFAULT_BANDWIDTH;
+ static const double DEFAULT_TICK_RATE;
+ static const double DEFAULT_FREQ; // Hz
+ static const uint32_t DEFAULT_DECIM;
+ static const uint32_t DEFAULT_INTERP;
+ static const bool DEFAULT_AUTO_DC_OFFSET;
+ static const bool DEFAULT_AUTO_IQ_BALANCE;
+ static const bool DEFAULT_AGC_ENABLE;
+
+ /*!
+ * \param codec_ctrl The actual AD936x control object
+ * \param n_frontends Number of frontends (1 or 2)
+ */
+ static sptr make(
+ const ad9361_ctrl::sptr &codec_ctrl,
+ const size_t n_frontends
+ );
+
+ virtual ~ad936x_manager(void) {};
+
+ /*! Put the AD936x into a default state.
+ *
+ * Sets gains, LOs, bandwidths, etc. according to the DEFAULT_* constants.
+ */
+ virtual void init_codec(void) = 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(
+ boost::function<void(uint32_t)> poker_functor,
+ boost::function<uint64_t()> peeker_functor
+ ) = 0;
+
+ /*! Determine a tick rate that will work with a given sampling rate
+ * (assuming a DDC/DUC chain is also available elsewhere).
+ *
+ * Example: If we want to stream with a rate of 5 Msps, then the AD936x
+ * must run at an integer multiple of that. Although not strictly necessary,
+ * we always try and return a multiple of 2. Let's say we need those 5 Msps
+ * on two channels, then a good rate is 20 MHz, which is 4 times the sampling
+ * rate (thus we can use 2 halfbands elsewhere).
+ * If different rates are used on different channels, this can be particularly
+ * useful. The clock rate of the AD936x needs to be a multiple of the least
+ * common multiple of all the rates. Example: We want to transmit with 3 Msps
+ * and receive with 5 Msps. The LCM of this is 15 Msps, which is used as an
+ * argument for this function. A good rate is then 30 MHz, which is twice
+ * the LCM.
+ *
+ * \param lcm_rate Least Common Multiple of all the rates involved.
+ * \param num_chans The number of channels used for the stream.
+ *
+ * \returns a valid tick rate that can be used with the given rate
+ * \throws a uhd::value_error if \p lcm_rate exceeds the max tick rate
+ */
+ virtual double get_auto_tick_rate(
+ const double lcm_rate,
+ size_t num_chans
+ ) = 0;
+
+ /*! Check if a given sampling rate is within the available analog bandwidth.
+ *
+ * If not, outputs a warning message and returns false.
+ */
+ virtual bool check_bandwidth(double rate, const std::string dir) = 0;
+
+ /*! Populate the property tree for the device frontend
+ */
+ virtual void populate_frontend_subtree(
+ uhd::property_tree::sptr subtree,
+ const std::string &key,
+ uhd::direction_t dir
+ ) = 0;
+
+}; /* class ad936x_manager */
+
+}} /* namespace uhd::usrp */
+
+#endif /* INCLUDED_AD9361_MANAGER_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/adf4001_ctrl.hpp b/host/lib/include/uhdlib/usrp/common/adf4001_ctrl.hpp
new file mode 100644
index 000000000..8aa449539
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/adf4001_ctrl.hpp
@@ -0,0 +1,131 @@
+//
+// Copyright 2013 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// Original ADF4001 driver written by: bistromath
+// Mar 1, 2013
+//
+// Re-used and re-licensed with permission.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP
+
+#include <uhd/types/serial.hpp>
+#include <uhdlib/usrp/cores/spi_core_3000.hpp>
+#include <boost/thread/thread.hpp>
+#include <stdint.h>
+
+namespace uhd { namespace usrp {
+
+class adf4001_regs_t {
+public:
+
+ /* Function prototypes */
+ uint32_t get_reg(uint8_t addr);
+ adf4001_regs_t(void);
+
+ /* Register values / addresses */
+ uint16_t ref_counter; //14 bits
+ uint16_t n; //13 bits
+ uint8_t charge_pump_current_1; //3 bits
+ uint8_t charge_pump_current_2; //3 bits
+
+ enum anti_backlash_width_t {
+ ANTI_BACKLASH_WIDTH_2_9NS = 0,
+ ANTI_BACKLASH_WIDTH_1_3NS = 1,
+ ANTI_BACKLASH_WIDTH_6_0NS = 2,
+ ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3
+ };
+ anti_backlash_width_t anti_backlash_width;
+
+ enum lock_detect_precision_t {
+ LOCK_DETECT_PRECISION_3CYC = 0,
+ LOCK_DETECT_PRECISION_5CYC = 1
+ };
+ lock_detect_precision_t lock_detect_precision;
+ enum charge_pump_gain_t {
+ CHARGE_PUMP_GAIN_1 = 0,
+ CHARGE_PUMP_GAIN_2 = 1
+ };
+ charge_pump_gain_t charge_pump_gain;
+ enum counter_reset_t {
+ COUNTER_RESET_NORMAL = 0,
+ COUNTER_RESET_RESET = 1
+ };
+ counter_reset_t counter_reset;
+ enum power_down_t {
+ POWER_DOWN_NORMAL = 0,
+ POWER_DOWN_ASYNC = 1,
+ POWER_DOWN_SYNC = 3
+ };
+ power_down_t power_down;
+ enum muxout_t {
+ MUXOUT_TRISTATE_OUT = 0,
+ MUXOUT_DLD = 1,
+ MUXOUT_NDIV = 2,
+ MUXOUT_AVDD = 3,
+ MUXOUT_RDIV = 4,
+ MUXOUT_NCH_OD_ALD = 5,
+ MUXOUT_SDO = 6,
+ MUXOUT_GND = 7
+ };
+ muxout_t muxout;
+ enum phase_detector_polarity_t {
+ PHASE_DETECTOR_POLARITY_NEGATIVE = 0,
+ PHASE_DETECTOR_POLARITY_POSITIVE = 1
+ };
+ phase_detector_polarity_t phase_detector_polarity;
+ enum charge_pump_mode_t {
+ CHARGE_PUMP_NORMAL = 0,
+ CHARGE_PUMP_TRISTATE = 1
+ };
+ charge_pump_mode_t charge_pump_mode;
+ enum fastlock_mode_t {
+ FASTLOCK_MODE_DISABLED = 0,
+ FASTLOCK_MODE_1 = 1,
+ FASTLOCK_MODE_2 = 2
+ };
+ fastlock_mode_t fastlock_mode;
+ enum timer_counter_control_t {
+ TIMEOUT_3CYC = 0,
+ TIMEOUT_7CYC = 1,
+ TIMEOUT_11CYC = 2,
+ TIMEOUT_15CYC = 3,
+ TIMEOUT_19CYC = 4,
+ TIMEOUT_23CYC = 5,
+ TIMEOUT_27CYC = 6,
+ TIMEOUT_31CYC = 7,
+ TIMEOUT_35CYC = 8,
+ TIMEOUT_39CYC = 9,
+ TIMEOUT_43CYC = 10,
+ TIMEOUT_47CYC = 11,
+ TIMEOUT_51CYC = 12,
+ TIMEOUT_55CYC = 13,
+ TIMEOUT_59CYC = 14,
+ TIMEOUT_63CYC = 15,
+ };
+ timer_counter_control_t timer_counter_control;
+};
+
+
+class adf4001_ctrl {
+public:
+ adf4001_ctrl(uhd::spi_iface::sptr _spi, int slaveno);
+ virtual void set_lock_to_ext_ref(bool external);
+
+private:
+ uhd::spi_iface::sptr spi_iface;
+ int slaveno;
+ spi_config_t spi_config;
+ adf4001_regs_t adf4001_regs;
+
+ void program_regs(void);
+ void write_reg(uint8_t addr);
+};
+
+}}
+
+#endif
diff --git a/host/lib/include/uhdlib/usrp/common/adf435x.hpp b/host/lib/include/uhdlib/usrp/common/adf435x.hpp
new file mode 100644
index 000000000..bd7b91b95
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/adf435x.hpp
@@ -0,0 +1,422 @@
+//
+// Copyright 2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_ADF435X_HPP
+#define INCLUDED_ADF435X_HPP
+
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/function.hpp>
+#include <boost/thread.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <vector>
+#include "adf4350_regs.hpp"
+#include "adf4351_regs.hpp"
+
+class adf435x_iface
+{
+public:
+ typedef boost::shared_ptr<adf435x_iface> sptr;
+ typedef boost::function<void(std::vector<uint32_t>)> write_fn_t;
+
+ static sptr make_adf4350(write_fn_t write);
+ static sptr make_adf4351(write_fn_t write);
+
+ virtual ~adf435x_iface() = 0;
+
+ enum output_t { RF_OUTPUT_A, RF_OUTPUT_B };
+
+ enum prescaler_t { PRESCALER_4_5, PRESCALER_8_9 };
+
+ enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED };
+
+ enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM };
+
+ enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD };
+
+ /**
+ * Charge Pump Currents
+ */
+ enum charge_pump_current_t {
+ CHARGE_PUMP_CURRENT_0_31MA = 0,
+ CHARGE_PUMP_CURRENT_0_63MA = 1,
+ CHARGE_PUMP_CURRENT_0_94MA = 2,
+ CHARGE_PUMP_CURRENT_1_25MA = 3,
+ CHARGE_PUMP_CURRENT_1_56MA = 4,
+ CHARGE_PUMP_CURRENT_1_88MA = 5,
+ CHARGE_PUMP_CURRENT_2_19MA = 6,
+ CHARGE_PUMP_CURRENT_2_50MA = 7,
+ CHARGE_PUMP_CURRENT_2_81MA = 8,
+ CHARGE_PUMP_CURRENT_3_13MA = 9,
+ CHARGE_PUMP_CURRENT_3_44MA = 10,
+ CHARGE_PUMP_CURRENT_3_75MA = 11,
+ CHARGE_PUMP_CURRENT_4_07MA = 12,
+ CHARGE_PUMP_CURRENT_4_38MA = 13,
+ CHARGE_PUMP_CURRENT_4_69MA = 14,
+ CHARGE_PUMP_CURRENT_5_00MA = 15
+ };
+
+
+ virtual void set_reference_freq(double fref) = 0;
+
+ virtual void set_prescaler(prescaler_t prescaler) = 0;
+
+ virtual void set_feedback_select(feedback_sel_t fb_sel) = 0;
+
+ virtual void set_output_power(output_t output, output_power_t power) = 0;
+
+ void set_output_power(output_power_t power) {
+ set_output_power(RF_OUTPUT_A, power);
+ }
+
+ virtual void set_output_enable(output_t output, bool enable) = 0;
+
+ virtual void set_muxout_mode(muxout_t mode) = 0;
+
+ virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0;
+
+ virtual uhd::range_t get_int_range() = 0;
+
+ virtual double set_frequency(double target_freq, bool int_n_mode, bool flush = false) = 0;
+
+ virtual void commit(void) = 0;
+};
+
+template <typename adf435x_regs_t>
+class adf435x_impl : public adf435x_iface
+{
+public:
+ adf435x_impl(write_fn_t write_fn) :
+ _write_fn(write_fn),
+ _regs(),
+ _fb_after_divider(false),
+ _reference_freq(0.0),
+ _N_min(-1)
+ {}
+
+ virtual ~adf435x_impl() {};
+
+ void set_reference_freq(double fref)
+ {
+ _reference_freq = fref;
+ }
+
+ void set_feedback_select(feedback_sel_t fb_sel)
+ {
+ _fb_after_divider = (fb_sel == FB_SEL_DIVIDED);
+ }
+
+ void set_prescaler(prescaler_t prescaler)
+ {
+ if (prescaler == PRESCALER_8_9) {
+ _regs.prescaler = adf435x_regs_t::PRESCALER_8_9;
+ _N_min = 75;
+ } else {
+ _regs.prescaler = adf435x_regs_t::PRESCALER_4_5;
+ _N_min = 23;
+ }
+ }
+
+ void set_output_power(output_t output, output_power_t power)
+ {
+ switch (output) {
+ case RF_OUTPUT_A:
+ switch (power) {
+ case OUTPUT_POWER_M4DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M4DBM; break;
+ case OUTPUT_POWER_M1DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M1DBM; break;
+ case OUTPUT_POWER_2DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_2DBM; break;
+ case OUTPUT_POWER_5DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_5DBM; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ break;
+ case RF_OUTPUT_B:
+ switch (power) {
+ case OUTPUT_POWER_M4DBM: _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_M4DBM; break;
+ case OUTPUT_POWER_M1DBM: _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_M1DBM; break;
+ case OUTPUT_POWER_2DBM: _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_2DBM; break;
+ case OUTPUT_POWER_5DBM: _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_5DBM; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ void set_output_enable(output_t output, bool enable)
+ {
+ switch (output) {
+ case RF_OUTPUT_A: _regs.rf_output_enable = enable ? adf435x_regs_t::RF_OUTPUT_ENABLE_ENABLED:
+ adf435x_regs_t::RF_OUTPUT_ENABLE_DISABLED;
+ break;
+ case RF_OUTPUT_B: _regs.aux_output_enable = enable ? adf435x_regs_t::AUX_OUTPUT_ENABLE_ENABLED:
+ adf435x_regs_t::AUX_OUTPUT_ENABLE_DISABLED;
+ break;
+ }
+ }
+
+ void set_muxout_mode(muxout_t mode)
+ {
+ switch (mode) {
+ case MUXOUT_3STATE: _regs.muxout = adf435x_regs_t::MUXOUT_3STATE; break;
+ case MUXOUT_DVDD: _regs.muxout = adf435x_regs_t::MUXOUT_DVDD; break;
+ case MUXOUT_DGND: _regs.muxout = adf435x_regs_t::MUXOUT_DGND; break;
+ case MUXOUT_RDIV: _regs.muxout = adf435x_regs_t::MUXOUT_RDIV; break;
+ case MUXOUT_NDIV: _regs.muxout = adf435x_regs_t::MUXOUT_NDIV; break;
+ case MUXOUT_ALD: _regs.muxout = adf435x_regs_t::MUXOUT_ANALOG_LD; break;
+ case MUXOUT_DLD: _regs.muxout = adf435x_regs_t::MUXOUT_DLD; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ void set_charge_pump_current(charge_pump_current_t cp_current)
+ {
+ switch (cp_current) {
+ case CHARGE_PUMP_CURRENT_0_31MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_0_31MA; break;
+ case CHARGE_PUMP_CURRENT_0_63MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_0_63MA; break;
+ case CHARGE_PUMP_CURRENT_0_94MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_0_94MA; break;
+ case CHARGE_PUMP_CURRENT_1_25MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_1_25MA; break;
+ case CHARGE_PUMP_CURRENT_1_56MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_1_56MA; break;
+ case CHARGE_PUMP_CURRENT_1_88MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_1_88MA; break;
+ case CHARGE_PUMP_CURRENT_2_19MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_2_19MA; break;
+ case CHARGE_PUMP_CURRENT_2_50MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_2_50MA; break;
+ case CHARGE_PUMP_CURRENT_2_81MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_2_81MA; break;
+ case CHARGE_PUMP_CURRENT_3_13MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_3_13MA; break;
+ case CHARGE_PUMP_CURRENT_3_44MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_3_44MA; break;
+ case CHARGE_PUMP_CURRENT_3_75MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_3_75MA; break;
+ case CHARGE_PUMP_CURRENT_4_07MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_4_07MA; break;
+ case CHARGE_PUMP_CURRENT_4_38MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_4_38MA; break;
+ case CHARGE_PUMP_CURRENT_4_69MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_4_69MA; break;
+ case CHARGE_PUMP_CURRENT_5_00MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_5_00MA; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ uhd::range_t get_int_range()
+ {
+ if (_N_min < 0) throw uhd::runtime_error("set_prescaler must be called before get_int_range");
+ return uhd::range_t(_N_min, 4095);
+ }
+
+ double set_frequency(double target_freq, bool int_n_mode, bool flush = false)
+ {
+ static const double REF_DOUBLER_THRESH_FREQ = 12.5e6;
+ static const double PFD_FREQ_MAX = 25.0e6;
+ static const double BAND_SEL_FREQ_MAX = 100e3;
+ static const double VCO_FREQ_MIN = 2.2e9;
+ static const double VCO_FREQ_MAX = 4.4e9;
+
+ //Default invalid value for actual_freq
+ double actual_freq = 0;
+
+ uhd::range_t rf_divider_range = _get_rfdiv_range();
+ uhd::range_t int_range = get_int_range();
+
+ double pfd_freq = 0;
+ uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0;
+ uint16_t RFdiv = static_cast<uint16_t>(rf_divider_range.start());
+ bool D = false, T = false;
+
+ //Reference doubler for 50% duty cycle
+ D = (_reference_freq <= REF_DOUBLER_THRESH_FREQ);
+
+ //increase RF divider until acceptable VCO frequency
+ double vco_freq = target_freq;
+ while (vco_freq < VCO_FREQ_MIN && RFdiv < static_cast<uint16_t>(rf_divider_range.stop())) {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exits when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * from pg.21
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_actual = f_vco/RFdiv)
+ */
+ double feedback_freq = _fb_after_divider ? target_freq : vco_freq;
+
+ for(R = 1; R <= 1023; R+=1){
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = _reference_freq*(D?2:1)/(R*(T?2:1));
+
+ //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
+ if (pfd_freq > PFD_FREQ_MAX) continue;
+
+ //First, ignore fractional part of tuning
+ N = uint16_t(std::floor(feedback_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < static_cast<uint16_t>(int_range.start())) continue;
+
+ for(BS=1; BS <= 255; BS+=1){
+ //keep the band select frequency at or below band_sel_freq_max
+ //constraint on band select clock
+ if (pfd_freq/BS > BAND_SEL_FREQ_MAX) continue;
+ goto done_loop;
+ }
+ } done_loop:
+
+ //Fractional-N calculation
+ MOD = 4095; //max fractional accuracy
+ FRAC = static_cast<uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD));
+ if (int_n_mode) {
+ if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target
+ N++;
+ }
+ FRAC = 0;
+ }
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0) {
+ T = true;
+ R /= 2;
+ }
+
+ //Typical phase resync time documented in data sheet pg.24
+ static const double PHASE_RESYNC_TIME = 400e-6;
+
+ //If feedback after divider, then compensation for the divider is pulled into the INT value
+ int rf_div_compensation = _fb_after_divider ? 1 : RFdiv;
+
+ //Compute the actual frequency in terms of _reference_freq, N, FRAC, MOD, D, R and T.
+ actual_freq = (
+ double((N + (double(FRAC)/double(MOD))) *
+ (_reference_freq*(D?2:1)/(R*(T?2:1))))
+ ) / rf_div_compensation;
+
+ _regs.frac_12_bit = FRAC;
+ _regs.int_16_bit = N;
+ _regs.mod_12_bit = MOD;
+ _regs.clock_divider_12_bit = std::max<uint16_t>(1, uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD)));
+ _regs.feedback_select = _fb_after_divider ?
+ adf435x_regs_t::FEEDBACK_SELECT_DIVIDED :
+ adf435x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ _regs.clock_div_mode = _fb_after_divider ?
+ adf435x_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE :
+ adf435x_regs_t::CLOCK_DIV_MODE_FAST_LOCK;
+ _regs.r_counter_10_bit = R;
+ _regs.reference_divide_by_2 = T ?
+ adf435x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ adf435x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = D ?
+ adf435x_regs_t::REFERENCE_DOUBLER_ENABLED :
+ adf435x_regs_t::REFERENCE_DOUBLER_DISABLED;
+ _regs.band_select_clock_div = uint8_t(BS);
+ _regs.rf_divider_select = static_cast<typename adf435x_regs_t::rf_divider_select_t>(_get_rfdiv_setting(RFdiv));
+ _regs.ldf = int_n_mode ?
+ adf435x_regs_t::LDF_INT_N :
+ adf435x_regs_t::LDF_FRAC_N;
+
+ std::string tuning_str = (int_n_mode) ? "Integer-N" : "Fractional";
+ UHD_LOGGER_TRACE("ADF435X")
+ << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f")
+ % (target_freq/1e6) % (actual_freq/1e6)
+ << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f")
+ % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (_reference_freq/1e6)
+ << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str()
+ << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d")
+ % R % BS % N % FRAC % MOD % T % D % RFdiv
+ ;
+
+ UHD_ASSERT_THROW((_regs.frac_12_bit & ((uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((_regs.mod_12_bit & ((uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((_regs.clock_divider_12_bit & ((uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((uint16_t)~0x3FF)) == 0);
+
+ UHD_ASSERT_THROW(vco_freq >= VCO_FREQ_MIN and vco_freq <= VCO_FREQ_MAX);
+ UHD_ASSERT_THROW(RFdiv >= static_cast<uint16_t>(rf_divider_range.start()));
+ UHD_ASSERT_THROW(RFdiv <= static_cast<uint16_t>(rf_divider_range.stop()));
+ UHD_ASSERT_THROW(_regs.int_16_bit >= static_cast<uint16_t>(int_range.start()));
+ UHD_ASSERT_THROW(_regs.int_16_bit <= static_cast<uint16_t>(int_range.stop()));
+
+ if (flush) commit();
+ return actual_freq;
+ }
+
+ void commit()
+ {
+ //reset counters
+ _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_ENABLED;
+ std::vector<uint32_t> regs;
+ regs.push_back(_regs.get_reg(uint32_t(2)));
+ _write_fn(regs);
+ _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_DISABLED;
+
+ //write the registers
+ //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
+ regs.clear();
+ for (int addr = 5; addr >= 0; addr--) {
+ regs.push_back(_regs.get_reg(uint32_t(addr)));
+ }
+ _write_fn(regs);
+ }
+
+protected:
+ uhd::range_t _get_rfdiv_range();
+ int _get_rfdiv_setting(uint16_t div);
+
+ write_fn_t _write_fn;
+ adf435x_regs_t _regs;
+ double _fb_after_divider;
+ double _reference_freq;
+ int _N_min;
+};
+
+template <>
+inline uhd::range_t adf435x_impl<adf4350_regs_t>::_get_rfdiv_range()
+{
+ return uhd::range_t(1, 16);
+}
+
+template <>
+inline uhd::range_t adf435x_impl<adf4351_regs_t>::_get_rfdiv_range()
+{
+ return uhd::range_t(1, 64);
+}
+
+template <>
+inline int adf435x_impl<adf4350_regs_t>::_get_rfdiv_setting(uint16_t div)
+{
+ switch (div) {
+ case 1: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV1);
+ case 2: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV2);
+ case 4: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV4);
+ case 8: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV8);
+ case 16: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV16);
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <>
+inline int adf435x_impl<adf4351_regs_t>::_get_rfdiv_setting(uint16_t div)
+{
+ switch (div) {
+ case 1: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV1);
+ case 2: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV2);
+ case 4: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV4);
+ case 8: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV8);
+ case 16: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV16);
+ case 32: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV32);
+ case 64: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV64);
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+#endif // INCLUDED_ADF435X_HPP
diff --git a/host/lib/include/uhdlib/usrp/common/adf535x.hpp b/host/lib/include/uhdlib/usrp/common/adf535x.hpp
new file mode 100644
index 000000000..8380c9d04
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/adf535x.hpp
@@ -0,0 +1,477 @@
+//
+// Copyright 2015, 2017 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_ADF535X_HPP
+#define INCLUDED_ADF535X_HPP
+
+#include "adf5355_regs.hpp"
+#include "adf5356_regs.hpp"
+#include <uhd/utils/math.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/function.hpp>
+#include <boost/math/common_factor_rt.hpp> //gcd
+#include <utility>
+#include <vector>
+#include <algorithm>
+#include <stdint.h>
+#include <boost/format.hpp>
+#include <uhd/utils/safe_call.hpp>
+
+class adf535x_iface
+{
+public:
+ typedef std::shared_ptr<adf535x_iface> sptr;
+ typedef std::function<void(std::vector<uint32_t>)> write_fn_t;
+
+ static sptr make_adf5355(write_fn_t write);
+ static sptr make_adf5356(write_fn_t write);
+
+ virtual ~adf535x_iface() = default;
+
+ enum output_t { RF_OUTPUT_A, RF_OUTPUT_B };
+
+ enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED };
+
+ enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM };
+
+ enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD };
+
+ virtual void set_reference_freq(double fref, bool force = false) = 0;
+
+ virtual void set_pfd_freq(double pfd_freq) = 0;
+
+ virtual void set_feedback_select(feedback_sel_t fb_sel) = 0;
+
+ virtual void set_output_power(output_power_t power) = 0;
+
+ virtual void set_output_enable(output_t output, bool enable) = 0;
+
+ virtual void set_muxout_mode(muxout_t mode) = 0;
+
+ virtual double set_frequency(double target_freq, double freq_resolution, bool flush = false) = 0;
+
+ virtual void commit() = 0;
+};
+
+using namespace uhd;
+
+namespace {
+ const double ADF535X_DOUBLER_MAX_REF_FREQ = 60e6;
+ const double ADF535X_MAX_FREQ_PFD = 125e6;
+//const double ADF535X_PRESCALER_THRESH = 7e9;
+
+ const double ADF535X_MIN_VCO_FREQ = 3.4e9;
+//const double ADF535X_MAX_VCO_FREQ = 6.8e9;
+ const double ADF535X_MAX_OUT_FREQ = 6.8e9;
+ const double ADF535X_MIN_OUT_FREQ = (3.4e9 / 64);
+//const double ADF535X_MAX_OUTB_FREQ = (6.8e9 * 2);
+//const double ADF535X_MIN_OUTB_FREQ = (3.4e9 * 2);
+
+ const double ADF535X_PHASE_RESYNC_TIME = 400e-6;
+
+ const uint32_t ADF535X_MOD1 = 16777216;
+ const uint32_t ADF535X_MAX_MOD2 = 16383;
+ const uint32_t ADF535X_MAX_FRAC2 = 16383;
+//const uint16_t ADF535X_MIN_INT_PRESCALER_89 = 75;
+}
+
+template <typename adf535x_regs_t>
+class adf535x_impl : public adf535x_iface
+{
+public:
+ explicit adf535x_impl(write_fn_t write_fn) :
+ _write_fn(std::move(write_fn)),
+ _regs(),
+ _rewrite_regs(true),
+ _wait_time_us(0),
+ _ref_freq(0.0),
+ _pfd_freq(0.0),
+ _fb_after_divider(false)
+ {
+
+ _regs.vco_band_div = 3;
+ _regs.timeout = 11;
+ _regs.auto_level_timeout = 30;
+ _regs.synth_lock_timeout = 12;
+
+ _regs.adc_clock_divider = 16;
+ _regs.adc_conversion = adf535x_regs_t::ADC_CONVERSION_ENABLED;
+ _regs.adc_enable = adf535x_regs_t::ADC_ENABLE_ENABLED;
+
+ // TODO Needs to be enabled for phase resync
+ _regs.phase_resync = adf535x_regs_t::PHASE_RESYNC_DISABLED;
+
+ // TODO Default should be divided, but there seems to be a bug preventing that. Needs rechecking
+ _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+
+ // TODO 0 is an invalid value for this field. Setting to 1 seemed to break phase sync, needs retesting.
+ _regs.phase_resync_clk_div = 0;
+ }
+
+ ~adf535x_impl() override
+ {
+ UHD_SAFE_CALL(
+ _regs.power_down = adf535x_regs_t::POWER_DOWN_ENABLED;
+ commit();
+ )
+ }
+
+ void set_feedback_select(const feedback_sel_t fb_sel) override
+ {
+ _fb_after_divider = (fb_sel == FB_SEL_DIVIDED);
+
+ if (_fb_after_divider) {
+ _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_DIVIDED;
+ } else {
+ _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ }
+ }
+
+ void set_pfd_freq(const double pfd_freq) override
+ {
+ if (pfd_freq > ADF535X_MAX_FREQ_PFD) {
+ UHD_LOGGER_ERROR("ADF535x") << boost::format("%f MHz is above the maximum PFD frequency of %f MHz\n")
+ % (pfd_freq/1e6) % (ADF535X_MAX_FREQ_PFD/1e6);
+ return;
+ }
+ _pfd_freq = pfd_freq;
+
+ set_reference_freq(_ref_freq);
+ }
+
+ void set_reference_freq(const double fref, const bool force = false) override
+ {
+ //Skip the body if the reference frequency does not change
+ if (uhd::math::frequencies_are_equal(fref, _ref_freq) and (not force))
+ return;
+
+ _ref_freq = fref;
+
+ //-----------------------------------------------------------
+ //Set reference settings
+ int ref_div_factor = static_cast<int>(std::floor(_ref_freq / _pfd_freq));
+
+ //Reference doubler for 50% duty cycle
+ const bool doubler_en = (_ref_freq <= ADF535X_DOUBLER_MAX_REF_FREQ);
+ if (doubler_en) {
+ ref_div_factor *= 2;
+ }
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ const bool div2_en = (ref_div_factor % 2 == 0);
+ if (div2_en) {
+ ref_div_factor /= 2;
+ }
+
+ _regs.reference_divide_by_2 = div2_en ?
+ adf535x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ adf535x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = doubler_en ?
+ adf535x_regs_t::REFERENCE_DOUBLER_ENABLED :
+ adf535x_regs_t::REFERENCE_DOUBLER_DISABLED;
+ _regs.r_counter_10_bit = ref_div_factor;
+ UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((uint16_t)~0x3FF)) == 0);
+
+ //-----------------------------------------------------------
+ //Set timeouts (code from ADI driver)
+ _regs.timeout = std::max(1, std::min(int(ceil(_pfd_freq / (20e3 * 30))), 1023));
+
+ UHD_ASSERT_THROW((_regs.timeout & ((uint16_t)~0x3FF)) == 0);
+ _regs.synth_lock_timeout =
+ static_cast<uint8_t>(ceil((_pfd_freq * 2) / (100e3 * _regs.timeout)));
+ UHD_ASSERT_THROW((_regs.synth_lock_timeout & ((uint16_t)~0x1F)) == 0);
+ _regs.auto_level_timeout =
+ static_cast<uint8_t>(ceil((_pfd_freq * 5) / (100e3 * _regs.timeout)));
+
+ //-----------------------------------------------------------
+ //Set VCO band divider
+ _regs.vco_band_div =
+ static_cast<uint8_t>(ceil(_pfd_freq / 2.4e6));
+
+ //-----------------------------------------------------------
+ //Set ADC delay (code from ADI driver)
+ _regs.adc_enable = adf535x_regs_t::ADC_ENABLE_ENABLED;
+ _regs.adc_conversion = adf535x_regs_t::ADC_CONVERSION_ENABLED;
+ _regs.adc_clock_divider = std::max(1, std::min(int(ceil(((_pfd_freq / 100e3) - 2) / 4)),255));
+
+ _wait_time_us = static_cast<uint32_t>(
+ ceil(16e6 / (_pfd_freq / ((4 * _regs.adc_clock_divider) + 2))));
+
+ //-----------------------------------------------------------
+ //Phase resync
+ // TODO Renable here, in initialization, or through separate set_phase_resync(bool enable) function
+ _regs.phase_resync = adf535x_regs_t::PHASE_RESYNC_DISABLED;
+
+ _regs.phase_adjust = adf535x_regs_t::PHASE_ADJUST_DISABLED;
+ _regs.sd_load_reset = adf535x_regs_t::SD_LOAD_RESET_ON_REG0_UPDATE;
+ _regs.phase_resync_clk_div = static_cast<uint16_t>(
+ floor(ADF535X_PHASE_RESYNC_TIME * _pfd_freq));
+
+ _rewrite_regs = true;
+ }
+
+ double set_frequency(const double target_freq, const double freq_resolution, const bool flush = false) override
+ {
+ return _set_frequency(target_freq, freq_resolution, flush);
+ }
+
+ void set_output_power(const output_power_t power) override
+ {
+ typename adf535x_regs_t::output_power_t setting;
+ switch (power) {
+ case OUTPUT_POWER_M4DBM: setting = adf535x_regs_t::OUTPUT_POWER_M4DBM; break;
+ case OUTPUT_POWER_M1DBM: setting = adf535x_regs_t::OUTPUT_POWER_M1DBM; break;
+ case OUTPUT_POWER_2DBM: setting = adf535x_regs_t::OUTPUT_POWER_2DBM; break;
+ case OUTPUT_POWER_5DBM: setting = adf535x_regs_t::OUTPUT_POWER_5DBM; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ if (_regs.output_power != setting) _rewrite_regs = true;
+ _regs.output_power = setting;
+ }
+
+ void set_output_enable(const output_t output, const bool enable) override
+ {
+ switch (output) {
+ case RF_OUTPUT_A: _regs.rf_out_a_enabled = enable ? adf535x_regs_t::RF_OUT_A_ENABLED_ENABLED :
+ adf535x_regs_t::RF_OUT_A_ENABLED_DISABLED;
+ break;
+ case RF_OUTPUT_B: _regs.rf_out_b_enabled = enable ? adf535x_regs_t::RF_OUT_B_ENABLED_ENABLED :
+ adf535x_regs_t::RF_OUT_B_ENABLED_DISABLED;
+ break;
+ }
+ }
+
+ void set_muxout_mode(const muxout_t mode) override
+ {
+ switch (mode) {
+ case MUXOUT_3STATE: _regs.muxout = adf535x_regs_t::MUXOUT_3STATE; break;
+ case MUXOUT_DVDD: _regs.muxout = adf535x_regs_t::MUXOUT_DVDD; break;
+ case MUXOUT_DGND: _regs.muxout = adf535x_regs_t::MUXOUT_DGND; break;
+ case MUXOUT_RDIV: _regs.muxout = adf535x_regs_t::MUXOUT_RDIV; break;
+ case MUXOUT_NDIV: _regs.muxout = adf535x_regs_t::MUXOUT_NDIV; break;
+ case MUXOUT_ALD: _regs.muxout = adf535x_regs_t::MUXOUT_ANALOG_LD; break;
+ case MUXOUT_DLD: _regs.muxout = adf535x_regs_t::MUXOUT_DLD; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ void commit() override
+ {
+ _commit();
+ }
+
+protected:
+ double _set_frequency(double, double, bool);
+ void _commit();
+
+private: //Members
+ typedef std::vector<uint32_t> addr_vtr_t;
+
+ write_fn_t _write_fn;
+ adf535x_regs_t _regs;
+ bool _rewrite_regs;
+ uint32_t _wait_time_us;
+ double _ref_freq;
+ double _pfd_freq;
+ double _fb_after_divider;
+};
+
+// ADF5355 Functions
+template <>
+inline double adf535x_impl<adf5355_regs_t>::_set_frequency(double target_freq, double freq_resolution, bool flush)
+{
+ if (target_freq > ADF535X_MAX_OUT_FREQ or target_freq < ADF535X_MIN_OUT_FREQ) {
+ throw uhd::runtime_error("requested frequency out of range.");
+ }
+ if ((uint32_t) freq_resolution == 0) {
+ throw uhd::runtime_error("requested resolution cannot be less than 1.");
+ }
+
+ /* Calculate target VCOout frequency */
+ //Increase RF divider until acceptable VCO frequency
+ double target_vco_freq = target_freq;
+ uint32_t rf_divider = 1;
+ while (target_vco_freq < ADF535X_MIN_VCO_FREQ && rf_divider < 64) {
+ target_vco_freq *= 2;
+ rf_divider *= 2;
+ }
+
+ switch (rf_divider) {
+ case 1: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV1; break;
+ case 2: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV2; break;
+ case 4: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV4; break;
+ case 8: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV8; break;
+ case 16: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV16; break;
+ case 32: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV32; break;
+ case 64: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV64; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ //Compute fractional PLL params
+ double prescaler_input_freq = target_vco_freq;
+ if (_fb_after_divider) {
+ prescaler_input_freq /= rf_divider;
+ }
+
+ const double N = prescaler_input_freq / _pfd_freq;
+ const auto INT = static_cast<uint16_t>(floor(N));
+ const auto FRAC1 = static_cast<uint32_t>(floor((N - INT) * ADF535X_MOD1));
+ const double residue = (N - INT) * ADF535X_MOD1 - FRAC1;
+
+ const double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution));
+ const auto MOD2 = static_cast<uint16_t>(std::min(floor(_pfd_freq / gcd), static_cast<double>(ADF535X_MAX_MOD2)));
+ const auto FRAC2 = static_cast<uint16_t>(std::min(ceil(residue * MOD2), static_cast<double>(ADF535X_MAX_FRAC2)));
+
+ const double coerced_vco_freq = _pfd_freq * (
+ double(INT) + (
+ (double(FRAC1) +
+ (double(FRAC2) / double(MOD2)))
+ / double(ADF535X_MOD1)
+ )
+ );
+
+ const double coerced_out_freq = coerced_vco_freq / rf_divider;
+
+ /* Update registers */
+ _regs.int_16_bit = INT;
+ _regs.frac1_24_bit = FRAC1;
+ _regs.frac2_14_bit = FRAC2;
+ _regs.mod2_14_bit = MOD2;
+ _regs.phase_24_bit = 0;
+
+ if (flush) commit();
+ return coerced_out_freq;
+}
+
+template <>
+inline void adf535x_impl<adf5355_regs_t>::_commit()
+{
+ const size_t ONE_REG = 1;
+
+ if (_rewrite_regs) {
+ //For a full state sync write registers in reverse order 12 - 0
+ addr_vtr_t regs;
+ for (uint8_t addr = 12; addr > 0; addr--) {
+ regs.push_back(_regs.get_reg(addr));
+ }
+ _write_fn(regs);
+ // TODO Add FPGA based delay between these writes
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ _rewrite_regs = false;
+
+ } else {
+ //Frequency update sequence from data sheet
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6)));
+ _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_ENABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1)));
+ _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_DISABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4)));
+ // TODO Add FPGA based delay between these writes
+ _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ }
+}
+
+// ADF5356 Functions
+template <>
+inline double adf535x_impl<adf5356_regs_t>::_set_frequency(double target_freq, double freq_resolution, bool flush)
+{
+ if (target_freq > ADF535X_MAX_OUT_FREQ or target_freq < ADF535X_MIN_OUT_FREQ) {
+ throw uhd::runtime_error("requested frequency out of range.");
+ }
+ if ((uint32_t) freq_resolution == 0) {
+ throw uhd::runtime_error("requested resolution cannot be less than 1.");
+ }
+
+ /* Calculate target VCOout frequency */
+ //Increase RF divider until acceptable VCO frequency
+ double target_vco_freq = target_freq;
+ uint32_t rf_divider = 1;
+ while (target_vco_freq < ADF535X_MIN_VCO_FREQ && rf_divider < 64) {
+ target_vco_freq *= 2;
+ rf_divider *= 2;
+ }
+
+ switch (rf_divider) {
+ case 1: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV1; break;
+ case 2: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV2; break;
+ case 4: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV4; break;
+ case 8: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV8; break;
+ case 16: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV16; break;
+ case 32: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV32; break;
+ case 64: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV64; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ //Compute fractional PLL params
+ double prescaler_input_freq = target_vco_freq;
+ if (_fb_after_divider) {
+ prescaler_input_freq /= rf_divider;
+ }
+
+ const double N = prescaler_input_freq / _pfd_freq;
+ const auto INT = static_cast<uint16_t>(floor(N));
+ const auto FRAC1 = static_cast<uint32_t>(floor((N - INT) * ADF535X_MOD1));
+ const double residue = (N - INT) * ADF535X_MOD1 - FRAC1;
+
+ const double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution));
+ const auto MOD2 = static_cast<uint16_t>(std::min(floor(_pfd_freq / gcd), static_cast<double>(ADF535X_MAX_MOD2)));
+ const auto FRAC2 = static_cast<uint16_t>(std::min(ceil(residue * MOD2), static_cast<double>(ADF535X_MAX_FRAC2)));
+
+ const double coerced_vco_freq = _pfd_freq * (
+ double(INT) + (
+ (double(FRAC1) +
+ (double(FRAC2) / double(MOD2)))
+ / double(ADF535X_MOD1)
+ )
+ );
+
+ const double coerced_out_freq = coerced_vco_freq / rf_divider;
+
+ /* Update registers */
+ _regs.int_16_bit = INT;
+ _regs.frac1_24_bit = FRAC1;
+ _regs.frac2_msb = FRAC2;
+ _regs.mod2_msb = MOD2;
+ _regs.phase_24_bit = 0;
+
+ if (flush) commit();
+ return coerced_out_freq;
+}
+
+template <>
+inline void adf535x_impl<adf5356_regs_t>::_commit()
+{
+ const size_t ONE_REG = 1;
+ if (_rewrite_regs) {
+ //For a full state sync write registers in reverse order 12 - 0
+ addr_vtr_t regs;
+ for (uint8_t addr = 13; addr > 0; addr--) {
+ regs.push_back(_regs.get_reg(addr));
+ }
+ _write_fn(regs);
+ // TODO Add FPGA based delay between these writes
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ _rewrite_regs = false;
+
+ } else {
+ //Frequency update sequence from data sheet
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(13)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(10)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2)));
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1)));
+ // TODO Add FPGA based delay between these writes
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ }
+}
+
+#endif // INCLUDED_ADF535X_HPP
diff --git a/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
new file mode 100644
index 000000000..3b4669f9a
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp
@@ -0,0 +1,43 @@
+//
+// Copyright 2011-2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/property_tree.hpp>
+#include <string>
+
+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 &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
+ );
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/async_packet_handler.hpp b/host/lib/include/uhdlib/usrp/common/async_packet_handler.hpp
new file mode 100644
index 000000000..de9d17001
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/async_packet_handler.hpp
@@ -0,0 +1,74 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/log.hpp>
+
+namespace uhd{ namespace usrp{
+
+ template <typename to_host_type>
+ void load_metadata_from_buff(
+ const to_host_type &to_host,
+ async_metadata_t &metadata,
+ const transport::vrt::if_packet_info_t &if_packet_info,
+ const uint32_t *vrt_hdr,
+ const double tick_rate,
+ const size_t channel = 0
+ ){
+ const uint32_t *payload = vrt_hdr + if_packet_info.num_header_words32;
+
+ //load into metadata
+ metadata.channel = channel;
+ metadata.has_time_spec = if_packet_info.has_tsf;
+ if (tick_rate == 0.0) {
+ metadata.time_spec = 0.0;
+ } else {
+ metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate);
+ }
+ metadata.event_code = async_metadata_t::event_code_t(to_host(payload[0]) & 0xff);
+
+ //load user payload
+ for (size_t i = 1; i < if_packet_info.num_payload_words32; i++){
+ if (i-1 == 4) break; //limit of 4 words32
+ metadata.user_payload[i-1] = to_host(payload[i]);
+ }
+ }
+
+ UHD_INLINE void standard_async_msg_prints(const async_metadata_t &metadata)
+ {
+ if (metadata.event_code &
+ ( async_metadata_t::EVENT_CODE_UNDERFLOW
+ | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET)
+ )
+ {
+ UHD_LOG_FASTPATH("U")
+ }
+ else if (metadata.event_code &
+ ( async_metadata_t::EVENT_CODE_SEQ_ERROR
+ | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST)
+ )
+ {
+ UHD_LOG_FASTPATH("S")
+ }
+ else if (metadata.event_code &
+ async_metadata_t::EVENT_CODE_TIME_ERROR
+ )
+ {
+ UHD_LOG_FASTPATH("L")
+ }
+ }
+
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/include/uhdlib/usrp/common/fifo_ctrl_excelsior.hpp
new file mode 100644
index 000000000..91c006a1a
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/fifo_ctrl_excelsior.hpp
@@ -0,0 +1,51 @@
+//
+// Copyright 2012,2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_B200_CTRL_HPP
+#define INCLUDED_B200_CTRL_HPP
+
+#include <uhd/types/time_spec.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/types/serial.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+
+
+struct fifo_ctrl_excelsior_config
+{
+ size_t async_sid_base;
+ size_t num_async_chan;
+ size_t ctrl_sid_base;
+ size_t spi_base;
+ size_t spi_rb;
+};
+
+/*!
+ * Provide access to peek, poke, spi, and async messages.
+ */
+class fifo_ctrl_excelsior : public uhd::timed_wb_iface, public uhd::spi_iface
+{
+public:
+ typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr;
+
+ //! Make a new control object
+ static sptr make(
+ uhd::transport::zero_copy_if::sptr xport,
+ const fifo_ctrl_excelsior_config &config
+ );
+
+ //! Set the tick rate (converting time into ticks)
+ virtual void set_tick_rate(const double rate) = 0;
+
+ //! Pop an async message from the queue or timeout
+ virtual bool pop_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0;
+};
+
+#endif /* INCLUDED_B200_CTRL_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/fx2_ctrl.hpp b/host/lib/include/uhdlib/usrp/common/fx2_ctrl.hpp
new file mode 100644
index 000000000..258aa7d04
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/fx2_ctrl.hpp
@@ -0,0 +1,138 @@
+//
+// Copyright 2010-2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP
+
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/types/serial.hpp> //i2c iface
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+#define FL_BEGIN 0
+#define FL_END 2
+#define FL_XFER 1
+#define USRP_HASH_SLOT_0_ADDR 0xe1e0
+#define USRP_HASH_SLOT_1_ADDR 0xe1f0
+#define VRQ_FPGA_LOAD 0x02
+#define VRQ_FPGA_SET_RESET 0x04
+#define VRQ_FPGA_SET_TX_ENABLE 0x05
+#define VRQ_FPGA_SET_RX_ENABLE 0x06
+#define VRQ_FPGA_SET_TX_RESET 0x0a
+#define VRQ_FPGA_SET_RX_RESET 0x0b
+#define VRQ_I2C_READ 0x81
+#define VRQ_I2C_WRITE 0x08
+#define VRQ_SET_LED 0x01
+#define VRT_VENDOR_IN 0xC0
+#define VRT_VENDOR_OUT 0x40
+
+namespace uhd{ namespace usrp{
+
+class fx2_ctrl : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<fx2_ctrl> sptr;
+
+ /*!
+ * Make a usrp control object from a control transport
+ * \param ctrl_transport a USB control transport
+ * \return a new usrp control object
+ */
+ static sptr make(uhd::transport::usb_control::sptr ctrl_transport);
+
+ //! Call init after the fpga is loaded
+ virtual void usrp_init(void) = 0;
+
+ //! For emergency situations
+ virtual void usrp_fx2_reset(void) = 0;
+
+ /*!
+ * Load firmware in Intel HEX Format onto device
+ * \param filename name of firmware file
+ * \param force reload firmware if already loaded
+ */
+ virtual void usrp_load_firmware(std::string filename,
+ bool force = false) = 0;
+
+ /*!
+ * Load fpga file onto usrp
+ * \param filename name of fpga image
+ */
+ virtual void usrp_load_fpga(std::string filename) = 0;
+
+ /*!
+ * Load USB descriptor file in Intel HEX format into EEPROM
+ * \param filestring name of EEPROM image
+ */
+ virtual void usrp_load_eeprom(std::string filestring) = 0;
+
+ /*!
+ * Submit an IN transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer to place data
+ * \param length length of buffer
+ * \return number of bytes read or error
+ */
+ virtual int usrp_control_read(uint8_t request,
+ uint16_t value,
+ uint16_t index,
+ unsigned char *buff,
+ uint16_t length) = 0;
+
+ /*!
+ * Submit an OUT transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer of data to be sent
+ * \param length length of buffer
+ * \return number of bytes written or error
+ */
+ virtual int usrp_control_write(uint8_t request,
+ uint16_t value,
+ uint16_t index,
+ unsigned char *buff,
+ uint16_t length) = 0;
+
+ /*!
+ * Perform an I2C write
+ * \param i2c_addr I2C device address
+ * \param buf data to be written
+ * \param len length of data in bytes
+ * \return number of bytes written or error
+ */
+
+ virtual int usrp_i2c_write(uint16_t i2c_addr,
+ unsigned char *buf,
+ uint16_t len) = 0;
+
+ /*!
+ * Perform an I2C read
+ * \param i2c_addr I2C device address
+ * \param buf data to be read
+ * \param len length of data in bytes
+ * \return number of bytes read or error
+ */
+
+ virtual int usrp_i2c_read(uint16_t i2c_addr,
+ unsigned char *buf,
+ uint16_t len) = 0;
+
+ //! enable/disable the rx path
+ virtual void usrp_rx_enable(bool on) = 0;
+
+ //! enable/disable the tx path
+ virtual void usrp_tx_enable(bool on) = 0;
+
+ //! reset the fpga
+ virtual void usrp_fpga_reset(bool on) = 0;
+};
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/max287x.hpp b/host/lib/include/uhdlib/usrp/common/max287x.hpp
new file mode 100644
index 000000000..762daf7b6
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/max287x.hpp
@@ -0,0 +1,935 @@
+//
+// Copyright 2015-2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef MAX287X_HPP_INCLUDED
+#define MAX287X_HPP_INCLUDED
+
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/assign.hpp>
+#include <boost/function.hpp>
+#include <boost/thread.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <stdint.h>
+#include <vector>
+#include "max2870_regs.hpp"
+#include "max2871_regs.hpp"
+
+/**
+ * MAX287x interface
+ */
+class max287x_iface
+{
+public:
+ typedef boost::shared_ptr<max287x_iface> sptr;
+
+ typedef boost::function<void(std::vector<uint32_t>)> write_fn;
+
+ /**
+ * LD Pin Modes
+ */
+ typedef enum{
+ LD_PIN_MODE_LOW,
+ LD_PIN_MODE_DLD,
+ LD_PIN_MODE_ALD,
+ LD_PIN_MODE_HIGH
+ } ld_pin_mode_t;
+
+ /**
+ * MUXOUT Modes
+ */
+ typedef enum{
+ MUXOUT_TRI_STATE,
+ MUXOUT_HIGH,
+ MUXOUT_LOW,
+ MUXOUT_RDIV,
+ MUXOUT_NDIV,
+ MUXOUT_ALD,
+ MUXOUT_DLD,
+ MUXOUT_SYNC,
+ MUXOUT_SPI
+ } muxout_mode_t;
+
+ /**
+ * Charge Pump Currents
+ */
+ typedef enum{
+ CHARGE_PUMP_CURRENT_0_32MA,
+ CHARGE_PUMP_CURRENT_0_64MA,
+ CHARGE_PUMP_CURRENT_0_96MA,
+ CHARGE_PUMP_CURRENT_1_28MA,
+ CHARGE_PUMP_CURRENT_1_60MA,
+ CHARGE_PUMP_CURRENT_1_92MA,
+ CHARGE_PUMP_CURRENT_2_24MA,
+ CHARGE_PUMP_CURRENT_2_56MA,
+ CHARGE_PUMP_CURRENT_2_88MA,
+ CHARGE_PUMP_CURRENT_3_20MA,
+ CHARGE_PUMP_CURRENT_3_52MA,
+ CHARGE_PUMP_CURRENT_3_84MA,
+ CHARGE_PUMP_CURRENT_4_16MA,
+ CHARGE_PUMP_CURRENT_4_48MA,
+ CHARGE_PUMP_CURRENT_4_80MA,
+ CHARGE_PUMP_CURRENT_5_12MA
+ } charge_pump_current_t;
+
+ /**
+ * Output Powers
+ */
+ typedef enum{
+ OUTPUT_POWER_M4DBM,
+ OUTPUT_POWER_M1DBM,
+ OUTPUT_POWER_2DBM,
+ OUTPUT_POWER_5DBM
+ } output_power_t;
+
+ typedef enum {
+ LOW_NOISE_AND_SPUR_LOW_NOISE,
+ LOW_NOISE_AND_SPUR_LOW_SPUR_1,
+ LOW_NOISE_AND_SPUR_LOW_SPUR_2
+ } low_noise_and_spur_t;
+
+ typedef enum {
+ CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF,
+ CLOCK_DIV_MODE_FAST_LOCK,
+ CLOCK_DIV_MODE_PHASE
+ } clock_divider_mode_t;
+
+ /**
+ * Make a synthesizer
+ * @param write write function
+ * @return shared pointer to object
+ */
+ template <typename max287X_t> static sptr make(write_fn write)
+ {
+ return sptr(new max287X_t(write));
+ }
+
+ /**
+ * Destructor
+ */
+ virtual ~max287x_iface() {};
+
+ /**
+ * Power up the synthesizer
+ */
+ virtual void power_up(void) = 0;
+
+ /**
+ * Shut down the synthesizer
+ */
+ virtual void shutdown(void) = 0;
+
+ /**
+ * Check if the synthesizer is shut down
+ */
+ virtual bool is_shutdown(void) = 0;
+
+ /**
+ * Set frequency
+ * @param target_freq target frequency
+ * @param ref_freq reference frequency
+ * @param target_pfd_freq target phase detector frequency
+ * @param is_int_n enable integer-N tuning
+ * @return actual frequency
+ */
+ virtual double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n) = 0;
+
+ /**
+ * Set output power
+ * @param power output power
+ */
+ virtual void set_output_power(output_power_t power) = 0;
+
+ /**
+ * Set lock detect pin mode
+ * @param mode lock detect pin mode
+ */
+ virtual void set_ld_pin_mode(ld_pin_mode_t mode) = 0;
+
+ /**
+ * Set muxout pin mode
+ * @param mode muxout mode
+ */
+ virtual void set_muxout_mode(muxout_mode_t mode) = 0;
+
+ /**
+ * Set charge pump current
+ * @param cp_current charge pump current
+ */
+ virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0;
+
+ /**
+ * Enable or disable auto retune
+ * @param enabled enable auto retune
+ */
+ virtual void set_auto_retune(bool enabled) = 0;
+
+ /**
+ * Set clock divider mode
+ * @param mode clock divider mode
+ */
+ virtual void set_clock_divider_mode(clock_divider_mode_t mode) = 0;
+
+ /**
+ * Enable or disable cycle slip mode
+ * @param enabled enable cycle slip mode
+ */
+ virtual void set_cycle_slip_mode(bool enabled) = 0;
+
+ /**
+ * Set low noise and spur mode
+ * @param mode low noise and spur mode
+ */
+ virtual void set_low_noise_and_spur(low_noise_and_spur_t mode) = 0;
+
+ /**
+ * Set phase
+ * @param phase the phase offset
+ */
+ virtual void set_phase(uint16_t phase) = 0;
+
+ /**
+ * Write values configured by the set_* functions.
+ */
+ virtual void commit(void) = 0;
+
+ /**
+ * Check whether this is in a state where it can be synchronized
+ */
+ virtual bool can_sync(void) = 0;
+
+ /**
+ * Configure synthesizer for phase synchronization
+ */
+ virtual void config_for_sync(bool enable) = 0;
+};
+
+/**
+ * MAX287x
+ * Base class for all MAX287x synthesizers
+ */
+template <typename max287x_regs_t>
+class max287x : public max287x_iface
+{
+public:
+ max287x(write_fn func);
+ virtual ~max287x();
+ virtual void power_up(void);
+ virtual void shutdown(void);
+ virtual bool is_shutdown(void);
+ virtual double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n);
+ virtual void set_output_power(output_power_t power);
+ virtual void set_ld_pin_mode(ld_pin_mode_t mode);
+ virtual void set_muxout_mode(muxout_mode_t mode);
+ virtual void set_charge_pump_current(charge_pump_current_t cp_current);
+ virtual void set_auto_retune(bool enabled);
+ virtual void set_clock_divider_mode(clock_divider_mode_t mode);
+ virtual void set_cycle_slip_mode(bool enabled);
+ virtual void set_low_noise_and_spur(low_noise_and_spur_t mode);
+ virtual void set_phase(uint16_t phase);
+ virtual void commit();
+ virtual bool can_sync();
+ virtual void config_for_sync(bool enable);
+
+protected:
+ max287x_regs_t _regs;
+ bool _can_sync;
+ bool _config_for_sync;
+ bool _write_all_regs;
+
+private:
+ write_fn _write;
+ bool _delay_after_write;
+};
+
+/**
+ * MAX2870
+ */
+class max2870 : public max287x<max2870_regs_t>
+{
+public:
+ max2870(write_fn func) : max287x<max2870_regs_t>(func) {}
+ ~max2870() {}
+ double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+ {
+ _regs.cpoc = is_int_n ? max2870_regs_t::CPOC_ENABLED : max2870_regs_t::CPOC_DISABLED;
+ _regs.feedback_select = target_freq >= 3.0e9 ?
+ max2870_regs_t::FEEDBACK_SELECT_DIVIDED :
+ max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+
+ return max287x<max2870_regs_t>::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+ }
+ void commit(void)
+ {
+ // For MAX2870, we always need to write all registers.
+ _write_all_regs = true;
+ max287x<max2870_regs_t>::commit();
+ }
+};
+
+/**
+ * MAX2871
+ */
+// Table of frequency ranges for each VCO value.
+// The values were derived from sampling multiple
+// units over a temperature range of -10 to 40 deg C.
+typedef std::map<uint8_t,uhd::range_t> vco_map_t;
+static const vco_map_t max2871_vco_map =
+ boost::assign::map_list_of
+ (0,uhd::range_t(2767776024.0,2838472816.0))
+ (1,uhd::range_t(2838472816.0,2879070053.0))
+ (1,uhd::range_t(2879070053.0,2921202504.0))
+ (3,uhd::range_t(2921202504.0,2960407579.0))
+ (4,uhd::range_t(2960407579.0,3001687422.0))
+ (5,uhd::range_t(3001687422.0,3048662562.0))
+ (6,uhd::range_t(3048662562.0,3097511550.0))
+ (7,uhd::range_t(3097511550.0,3145085864.0))
+ (8,uhd::range_t(3145085864.0,3201050835.0))
+ (9,uhd::range_t(3201050835.0,3259581909.0))
+ (10,uhd::range_t(3259581909.0,3321408729.0))
+ (11,uhd::range_t(3321408729.0,3375217285.0))
+ (12,uhd::range_t(3375217285.0,3432807972.0))
+ (13,uhd::range_t(3432807972.0,3503759088.0))
+ (14,uhd::range_t(3503759088.0,3579011283.0))
+ (15,uhd::range_t(3579011283.0,3683570865.0))
+ (20,uhd::range_t(3683570865.0,3711845712.0))
+ (21,uhd::range_t(3711845712.0,3762188221.0))
+ (22,uhd::range_t(3762188221.0,3814209551.0))
+ (23,uhd::range_t(3814209551.0,3865820020.0))
+ (24,uhd::range_t(3865820020.0,3922520021.0))
+ (25,uhd::range_t(3922520021.0,3981682709.0))
+ (26,uhd::range_t(3981682709.0,4043154280.0))
+ (27,uhd::range_t(4043154280.0,4100400020.0))
+ (28,uhd::range_t(4100400020.0,4159647583.0))
+ (29,uhd::range_t(4159647583.0,4228164842.0))
+ (30,uhd::range_t(4228164842.0,4299359879.0))
+ (31,uhd::range_t(4299359879.0,4395947962.0))
+ (33,uhd::range_t(4395947962.0,4426512061.0))
+ (34,uhd::range_t(4426512061.0,4480333656.0))
+ (35,uhd::range_t(4480333656.0,4526297331.0))
+ (36,uhd::range_t(4526297331.0,4574689510.0))
+ (37,uhd::range_t(4574689510.0,4633102021.0))
+ (38,uhd::range_t(4633102021.0,4693755616.0))
+ (39,uhd::range_t(4693755616.0,4745624435.0))
+ (40,uhd::range_t(4745624435.0,4803922123.0))
+ (41,uhd::range_t(4803922123.0,4871523881.0))
+ (42,uhd::range_t(4871523881.0,4942111286.0))
+ (43,uhd::range_t(4942111286.0,5000192446.0))
+ (44,uhd::range_t(5000192446.0,5059567510.0))
+ (45,uhd::range_t(5059567510.0,5136258187.0))
+ (46,uhd::range_t(5136258187.0,5215827295.0))
+ (47,uhd::range_t(5215827295.0,5341282949.0))
+ (49,uhd::range_t(5341282949.0,5389819310.0))
+ (50,uhd::range_t(5389819310.0,5444868434.0))
+ (51,uhd::range_t(5444868434.0,5500079705.0))
+ (52,uhd::range_t(5500079705.0,5555329630.0))
+ (53,uhd::range_t(5555329630.0,5615049833.0))
+ (54,uhd::range_t(5615049833.0,5676098527.0))
+ (55,uhd::range_t(5676098527.0,5744191577.0))
+ (56,uhd::range_t(5744191577.0,5810869917.0))
+ (57,uhd::range_t(5810869917.0,5879176194.0))
+ (58,uhd::range_t(5879176194.0,5952430629.0))
+ (59,uhd::range_t(5952430629.0,6016743964.0))
+ (60,uhd::range_t(6016743964.0,6090658690.0))
+ (61,uhd::range_t(6090658690.0,6128133570.0));
+
+class max2871 : public max287x<max2871_regs_t>
+{
+public:
+ max2871(write_fn func) : max287x<max2871_regs_t>(func) {}
+ ~max2871() {};
+ void set_muxout_mode(muxout_mode_t mode)
+ {
+ switch(mode)
+ {
+ case MUXOUT_SYNC:
+ _regs.muxout = max2871_regs_t::MUXOUT_SYNC;
+ break;
+ case MUXOUT_SPI:
+ _regs.muxout = max2871_regs_t::MUXOUT_SPI;
+ break;
+ default:
+ max287x<max2871_regs_t>::set_muxout_mode(mode);
+ }
+ }
+
+ double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+ {
+ _regs.feedback_select = max2871_regs_t::FEEDBACK_SELECT_DIVIDED;
+ double freq = max287x<max2871_regs_t>::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+
+ // To support phase synchronization on MAX2871, the same VCO
+ // subband must be manually programmed on all synthesizers and
+ // several registers must be set to specific values.
+ if (_config_for_sync)
+ {
+ // Need to manually program VCO value
+ static const double MIN_VCO_FREQ = 3e9;
+ double vco_freq = target_freq;
+ while (vco_freq < MIN_VCO_FREQ)
+ vco_freq *=2;
+ uint8_t vco_index = 0xFF;
+ for(const vco_map_t::value_type &vco: max2871_vco_map)
+ {
+ if (uhd::math::fp_compare::fp_compare_epsilon<double>(vco_freq) < vco.second.stop())
+ {
+ vco_index = vco.first;
+ break;
+ }
+ }
+ if (vco_index == 0xFF)
+ throw uhd::index_error("Invalid VCO frequency");
+
+ // Settings required for phase synchronization as per MAX2871 datasheet
+ _regs.shutdown_vas = max2871_regs_t::SHUTDOWN_VAS_DISABLED;
+ _regs.vco = vco_index;
+ _regs.low_noise_and_spur = max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE;
+ _regs.f01 = max2871_regs_t::F01_FRAC_N;
+ _regs.aux_output_select = max2871_regs_t::AUX_OUTPUT_SELECT_DIVIDED;
+ }
+ else
+ {
+ // Reset values to defaults
+ _regs.shutdown_vas = max2871_regs_t::SHUTDOWN_VAS_ENABLED; // turn VCO auto selection on
+ _regs.low_noise_and_spur = max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2;
+ _regs.f01 = max2871_regs_t::F01_AUTO;
+ _regs.aux_output_select = max2871_regs_t::AUX_OUTPUT_SELECT_FUNDAMENTAL;
+ }
+
+ return freq;
+ }
+
+ void commit()
+ {
+ max287x<max2871_regs_t>::commit();
+
+ // According to Maxim support, the following factors must be true to allow for phase synchronization
+ if (_regs.int_n_mode == max2871_regs_t::INT_N_MODE_FRAC_N and
+ _regs.feedback_select == max2871_regs_t::FEEDBACK_SELECT_DIVIDED and
+ _regs.aux_output_select == max2871_regs_t::AUX_OUTPUT_SELECT_DIVIDED and
+ _regs.rf_divider_select <= max2871_regs_t::RF_DIVIDER_SELECT_DIV16 and
+ _regs.low_noise_and_spur == max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE and
+ _regs.f01 == max2871_regs_t::F01_FRAC_N and
+ _regs.reference_doubler == max2871_regs_t::REFERENCE_DOUBLER_DISABLED and
+ _regs.reference_divide_by_2 == max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED and
+ _regs.r_counter_10_bit == 1)
+ {
+ _can_sync = true;
+ } else {
+ _can_sync = false;
+ }
+ }
+};
+
+
+// Implementation of max287x template class
+// To avoid linker errors, it was either include
+// it here or put it in a .cpp file and include
+// that file in this header file. Decided to just
+// include it here.
+
+template <typename max287x_regs_t>
+max287x<max287x_regs_t>::max287x(write_fn func) :
+ _can_sync(false),
+ _config_for_sync(false),
+ _write_all_regs(true),
+ _write(func),
+ _delay_after_write(true)
+{
+ power_up();
+}
+
+template <typename max287x_regs_t>
+max287x<max287x_regs_t>::~max287x()
+{
+ UHD_SAFE_CALL
+ (
+ shutdown();
+ )
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::power_up(void)
+{
+ _regs.power_down = max287x_regs_t::POWER_DOWN_NORMAL;
+ _regs.double_buffer = max287x_regs_t::DOUBLE_BUFFER_ENABLED;
+
+ // According to MAX287x data sheets:
+ // "Upon power-up, the registers should be programmed twice with at
+ // least a 20ms pause between writes. The first write ensures that
+ // the device is enabled, and the second write starts the VCO
+ // selection process."
+ // The first write and the 20ms wait are done here. The second write
+ // is done when any other function that does a write to the registers
+ // is called (such as tuning).
+ _write_all_regs = true;
+ _delay_after_write = true;
+ commit();
+ _write_all_regs = true; // Next call to commit() writes all regs
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::shutdown(void)
+{
+ _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_DISABLED;
+ _regs.aux_output_enable = max287x_regs_t::AUX_OUTPUT_ENABLE_DISABLED;
+ _regs.power_down = max287x_regs_t::POWER_DOWN_SHUTDOWN;
+ commit();
+}
+
+template <typename max287x_regs_t>
+bool max287x<max287x_regs_t>::is_shutdown(void)
+{
+ return (_regs.power_down == max287x_regs_t::POWER_DOWN_SHUTDOWN);
+}
+
+template <typename max287x_regs_t>
+double max287x<max287x_regs_t>::set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+{
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, typename max287x_regs_t::rf_divider_select_t> rfdivsel_to_enum =
+ boost::assign::map_list_of
+ (1, max287x_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, max287x_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, max287x_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, max287x_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, max287x_regs_t::RF_DIVIDER_SELECT_DIV16)
+ (32, max287x_regs_t::RF_DIVIDER_SELECT_DIV32)
+ (64, max287x_regs_t::RF_DIVIDER_SELECT_DIV64)
+ (128, max287x_regs_t::RF_DIVIDER_SELECT_DIV128);
+
+ //map mode setting to valid integer divider (N) values
+ static const uhd::range_t int_n_mode_div_range(16,65535,1);
+ static const uhd::range_t frac_n_mode_div_range(19,4091,1);
+
+ //other ranges and constants from MAX287X datasheets
+ static const uhd::range_t clock_div_range(1,4095,1);
+ static const uhd::range_t r_range(1,1023,1);
+ static const double MIN_VCO_FREQ = 3e9;
+ static const double BS_FREQ = 50e3;
+ static const int MAX_BS_VALUE = 1023;
+
+ int T = 0;
+ int D = ref_freq <= 10.0e6 ? 1 : 0;
+ int R = 0;
+ int BS = 0;
+ int N = 0;
+ int FRAC = 0;
+ int MOD = 4095;
+ int RFdiv = 1;
+ double pfd_freq = target_pfd_freq;
+ bool feedback_divided = (_regs.feedback_select == max287x_regs_t::FEEDBACK_SELECT_DIVIDED);
+
+ //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz)
+ UHD_ASSERT_THROW(target_freq > 0);
+ double vco_freq = target_freq;
+ while (vco_freq < MIN_VCO_FREQ)
+ {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ // The feedback frequency can be the fundamental VCO frequency or
+ // divided frequency. The output divider for MAX287x is actually
+ // 2 dividers, but only the first (1/2/4/8/16) is included in the
+ // feedback loop.
+ int fb_divisor = feedback_divided ? (RFdiv > 16 ? 16 : RFdiv) : 1;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exits when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv
+ */
+ for(R = int(ref_freq*(1+D)/(target_pfd_freq*(1+T))); R <= r_range.stop(); R++)
+ {
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below target
+ if (pfd_freq > target_pfd_freq)
+ continue;
+
+ //ignore fractional part of tuning
+ N = int((vco_freq/pfd_freq)/fb_divisor);
+
+ //Fractional-N calculation
+ FRAC = int(boost::math::round(((vco_freq/pfd_freq)/fb_divisor - N)*MOD));
+
+ if(is_int_n)
+ {
+ if (FRAC > (MOD / 2)) //Round integer such that actual freq is closest to target
+ N++;
+ FRAC = 0;
+ }
+
+ //keep N within int divider requirements
+ if(is_int_n)
+ {
+ if(N <= int_n_mode_div_range.start()) continue;
+ if(N >= int_n_mode_div_range.stop()) continue;
+ }
+ else
+ {
+ if(N <= frac_n_mode_div_range.start()) continue;
+ if(N >= frac_n_mode_div_range.stop()) continue;
+ }
+
+ //keep pfd freq low enough to achieve 50kHz BS clock
+ BS = std::ceil(pfd_freq / BS_FREQ);
+ if(BS <= MAX_BS_VALUE) break;
+ }
+ UHD_ASSERT_THROW(R <= r_range.stop());
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0)
+ {
+ T = 1;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ double actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))) * fb_divisor / RFdiv;
+
+ UHD_LOGGER_TRACE("MAX287X")
+ << boost::format("MAX287x: Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f")
+ % ref_freq % double(RFdiv*2) % double(N + double(FRAC)/double(MOD))
+ << boost::format("MAX287x: tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s")
+ % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional")
+ << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f")
+ % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6)
+ ;
+
+ //load the register values
+ _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_ENABLED;
+
+ if(is_int_n) {
+ _regs.cpl = max287x_regs_t::CPL_DISABLED;
+ _regs.ldf = max287x_regs_t::LDF_INT_N;
+ _regs.int_n_mode = max287x_regs_t::INT_N_MODE_INT_N;
+ } else {
+ _regs.cpl = max287x_regs_t::CPL_ENABLED;
+ _regs.ldf = max287x_regs_t::LDF_FRAC_N;
+ _regs.int_n_mode = max287x_regs_t::INT_N_MODE_FRAC_N;
+ }
+
+ _regs.lds = pfd_freq <= 32e6 ? max287x_regs_t::LDS_SLOW : max287x_regs_t::LDS_FAST;
+
+ _regs.frac_12_bit = FRAC;
+ _regs.int_16_bit = N;
+ _regs.mod_12_bit = MOD;
+ _regs.clock_divider_12_bit = std::max(int(clock_div_range.start()), int(std::ceil(400e-6*pfd_freq/MOD)));
+ UHD_ASSERT_THROW(_regs.clock_divider_12_bit <= clock_div_range.stop());
+ _regs.r_counter_10_bit = R;
+ _regs.reference_divide_by_2 = T ?
+ max287x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ max287x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = D ?
+ max287x_regs_t::REFERENCE_DOUBLER_ENABLED :
+ max287x_regs_t::REFERENCE_DOUBLER_DISABLED;
+ _regs.band_select_clock_div = BS & 0xFF;
+ _regs.bs_msb = (BS & 0x300) >> 8;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ _regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ if (_regs.clock_div_mode == max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK)
+ {
+ // Charge pump current needs to be set to lowest value in fast lock mode
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA;
+ // Make sure the register containing the charge pump current is written
+ _write_all_regs = true;
+ }
+
+ return actual_freq;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_output_power(output_power_t power)
+{
+ switch (power)
+ {
+ case OUTPUT_POWER_M4DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_M4DBM;
+ break;
+ case OUTPUT_POWER_M1DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_M1DBM;
+ break;
+ case OUTPUT_POWER_2DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_2DBM;
+ break;
+ case OUTPUT_POWER_5DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_5DBM;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_ld_pin_mode(ld_pin_mode_t mode)
+{
+ switch(mode)
+ {
+ case LD_PIN_MODE_LOW:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_LOW;
+ break;
+ case LD_PIN_MODE_DLD:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_DLD;
+ break;
+ case LD_PIN_MODE_ALD:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_ALD;
+ break;
+ case LD_PIN_MODE_HIGH:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_HIGH;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_muxout_mode(muxout_mode_t mode)
+{
+ switch(mode)
+ {
+ case MUXOUT_TRI_STATE:
+ _regs.muxout = max287x_regs_t::MUXOUT_TRI_STATE;
+ break;
+ case MUXOUT_HIGH:
+ _regs.muxout = max287x_regs_t::MUXOUT_HIGH;
+ break;
+ case MUXOUT_LOW:
+ _regs.muxout = max287x_regs_t::MUXOUT_LOW;
+ break;
+ case MUXOUT_RDIV:
+ _regs.muxout = max287x_regs_t::MUXOUT_RDIV;
+ break;
+ case MUXOUT_NDIV:
+ _regs.muxout = max287x_regs_t::MUXOUT_NDIV;
+ break;
+ case MUXOUT_ALD:
+ _regs.muxout = max287x_regs_t::MUXOUT_ALD;
+ break;
+ case MUXOUT_DLD:
+ _regs.muxout = max287x_regs_t::MUXOUT_DLD;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_charge_pump_current(charge_pump_current_t cp_current)
+{
+ switch(cp_current)
+ {
+ case CHARGE_PUMP_CURRENT_0_32MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA;
+ break;
+ case CHARGE_PUMP_CURRENT_0_64MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_64MA;
+ break;
+ case CHARGE_PUMP_CURRENT_0_96MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_96MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_28MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_28MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_60MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_60MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_92MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_92MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_24MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_24MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_56MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_56MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_88MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_88MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_20MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_20MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_52MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_52MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_84MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_84MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_16MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_16MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_48MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_48MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_80MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_80MA;
+ break;
+ case CHARGE_PUMP_CURRENT_5_12MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_5_12MA;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_auto_retune(bool enabled)
+{
+ _regs.retune = enabled ? max287x_regs_t::RETUNE_ENABLED : max287x_regs_t::RETUNE_DISABLED;
+}
+
+template <>
+inline void max287x<max2871_regs_t>::set_auto_retune(bool enabled)
+{
+ _regs.retune = enabled ? max2871_regs_t::RETUNE_ENABLED : max2871_regs_t::RETUNE_DISABLED;
+ _regs.vas_dly = enabled ? max2871_regs_t::VAS_DLY_ENABLED : max2871_regs_t::VAS_DLY_DISABLED;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_clock_divider_mode(clock_divider_mode_t mode)
+{
+ switch(mode)
+ {
+ case CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF;
+ break;
+ case CLOCK_DIV_MODE_FAST_LOCK:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK;
+ break;
+ case CLOCK_DIV_MODE_PHASE:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_PHASE;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_cycle_slip_mode(bool enabled)
+{
+ if (enabled)
+ throw uhd::runtime_error("Cycle slip mode not supported on this MAX287x synthesizer.");
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_low_noise_and_spur(low_noise_and_spur_t mode)
+{
+ switch(mode)
+ {
+ case LOW_NOISE_AND_SPUR_LOW_NOISE:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE;
+ break;
+ case LOW_NOISE_AND_SPUR_LOW_SPUR_1:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_1;
+ break;
+ case LOW_NOISE_AND_SPUR_LOW_SPUR_2:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_phase(uint16_t phase)
+{
+ _regs.phase_12_bit = phase & 0xFFF;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::commit()
+{
+ std::vector<uint32_t> regs;
+ std::set<uint32_t> changed_regs;
+
+ // Get only regs with changes
+ if (_write_all_regs)
+ {
+ for (int addr = 5; addr >= 0; addr--)
+ regs.push_back(_regs.get_reg(uint32_t(addr)));
+ } else {
+ try {
+ changed_regs = _regs.template get_changed_addrs<uint32_t> ();
+ // register 0 must be written to apply double buffered fields
+ if (changed_regs.size() > 0)
+ {
+ changed_regs.insert(0);
+ }
+
+ for (int addr = 5; addr >= 0; addr--)
+ {
+ if (changed_regs.find(uint32_t(addr)) != changed_regs.end())
+ regs.push_back(_regs.get_reg(uint32_t(addr)));
+ }
+ } catch (uhd::runtime_error&) {
+ // No saved state - write all regs
+ for (int addr = 5; addr >= 0; addr--)
+ regs.push_back(_regs.get_reg(uint32_t(addr)));
+ }
+ }
+
+ _write(regs);
+ _regs.save_state();
+ _write_all_regs = false;
+
+ if (_delay_after_write)
+ {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(20));
+ _delay_after_write = false;
+ }
+}
+
+template <typename max287x_regs_t>
+bool max287x<max287x_regs_t>::can_sync(void)
+{
+ return _can_sync;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::config_for_sync(bool enable)
+{
+ _config_for_sync = enable;
+}
+
+#endif // MAX287X_HPP_INCLUDED
diff --git a/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer.hpp b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer.hpp
new file mode 100644
index 000000000..d158a919e
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer.hpp
@@ -0,0 +1,33 @@
+//
+// Copyright 2011,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/shared_ptr.hpp>
+#include <stdint.h>
+
+namespace uhd{ namespace usrp{
+
+ class recv_packet_demuxer{
+ public:
+ typedef boost::shared_ptr<recv_packet_demuxer> sptr;
+
+ virtual ~recv_packet_demuxer(void) = 0;
+
+ //! Make a new demuxer from a transport and parameters
+ static sptr make(transport::zero_copy_if::sptr transport, const size_t size, const uint32_t sid_base);
+
+ //! Get a buffer at the given index from the transport
+ virtual transport::managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout) = 0;
+ };
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer_3000.hpp
new file mode 100644
index 000000000..74807741f
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer_3000.hpp
@@ -0,0 +1,145 @@
+//
+// Copyright 2013,2017 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP
+
+#include <uhd/utils/system_time.hpp>
+#include <uhd/config.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/thread.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <queue>
+#include <map>
+#include <stdint.h>
+
+namespace uhd{ namespace usrp{
+
+ struct recv_packet_demuxer_3000 : boost::enable_shared_from_this<recv_packet_demuxer_3000>
+ {
+ typedef boost::shared_ptr<recv_packet_demuxer_3000> sptr;
+ static sptr make(transport::zero_copy_if::sptr xport)
+ {
+ return sptr(new recv_packet_demuxer_3000(xport));
+ }
+
+ recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport):
+ _xport(xport)
+ {/*NOP*/}
+
+ transport::managed_recv_buffer::sptr get_recv_buff(const uint32_t sid, const double timeout)
+ {
+ const time_spec_t exit_time =
+ time_spec_t(timeout) + uhd::get_system_time();
+ transport::managed_recv_buffer::sptr buff;
+ buff = _internal_get_recv_buff(sid, timeout);
+ while (not buff) //loop until timeout
+ {
+ const time_spec_t delta = exit_time - uhd::get_system_time();
+ const double new_timeout = delta.get_real_secs();
+ if (new_timeout < 0.0) break;
+ buff = _internal_get_recv_buff(sid, new_timeout);
+ }
+ return buff;
+ }
+
+ transport::managed_recv_buffer::sptr _internal_get_recv_buff(const uint32_t sid, const double timeout)
+ {
+ transport::managed_recv_buffer::sptr buff;
+
+ //----------------------------------------------------------
+ //-- Check the queue to see if we already have a buffer
+ //----------------------------------------------------------
+ {
+ boost::mutex::scoped_lock l(mutex);
+ queue_type_t &queue = _queues[sid];
+ if (not queue.empty())
+ {
+ buff = queue.front();
+ queue.front().reset();
+ queue.pop();
+ return buff;
+ }
+ }
+ {
+ buff = _xport->get_recv_buff(timeout);
+ if (buff)
+ {
+ const uint32_t new_sid = uhd::wtohx(buff->cast<const uint32_t *>()[1]);
+ if (new_sid != sid)
+ {
+ boost::mutex::scoped_lock l(mutex);
+ if (_queues.count(new_sid) == 0) UHD_LOGGER_ERROR("STREAMER")
+ << "recv packet demuxer unexpected sid 0x" << std::hex << new_sid << std::dec
+ ;
+ else _queues[new_sid].push(buff);
+ buff.reset();
+ }
+ }
+ }
+ return buff;
+ }
+
+ void realloc_sid(const uint32_t sid)
+ {
+ boost::mutex::scoped_lock l(mutex);
+ while(not _queues[sid].empty()) //allocated and clears if already allocated
+ {
+ _queues[sid].pop();
+ }
+ }
+
+ transport::zero_copy_if::sptr make_proxy(const uint32_t sid);
+
+ typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t;
+ std::map<uint32_t, queue_type_t> _queues;
+ transport::zero_copy_if::sptr _xport;
+ boost::mutex mutex;
+ };
+
+ struct recv_packet_demuxer_proxy_3000 : transport::zero_copy_if
+ {
+ recv_packet_demuxer_proxy_3000(recv_packet_demuxer_3000::sptr demux, transport::zero_copy_if::sptr xport, const uint32_t sid):
+ _demux(demux), _xport(xport), _sid(sid)
+ {
+ _demux->realloc_sid(_sid); //causes clear
+ }
+
+ ~recv_packet_demuxer_proxy_3000(void)
+ {
+ _demux->realloc_sid(_sid); //causes clear
+ }
+
+ size_t get_num_recv_frames(void) const {return _xport->get_num_recv_frames();}
+ size_t get_recv_frame_size(void) const {return _xport->get_recv_frame_size();}
+ transport::managed_recv_buffer::sptr get_recv_buff(double timeout)
+ {
+ return _demux->get_recv_buff(_sid, timeout);
+ }
+ size_t get_num_send_frames(void) const {return _xport->get_num_send_frames();}
+ size_t get_send_frame_size(void) const {return _xport->get_send_frame_size();}
+ transport::managed_send_buffer::sptr get_send_buff(double timeout)
+ {
+ return _xport->get_send_buff(timeout);
+ }
+
+ recv_packet_demuxer_3000::sptr _demux;
+ transport::zero_copy_if::sptr _xport;
+ const uint32_t _sid;
+ };
+
+ inline transport::zero_copy_if::sptr recv_packet_demuxer_3000::make_proxy(const uint32_t sid)
+ {
+ return transport::zero_copy_if::sptr(new recv_packet_demuxer_proxy_3000(this->shared_from_this(), _xport, sid));
+ }
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/common/validate_subdev_spec.hpp b/host/lib/include/uhdlib/usrp/common/validate_subdev_spec.hpp
new file mode 100644
index 000000000..f2f3ce035
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/common/validate_subdev_spec.hpp
@@ -0,0 +1,28 @@
+//
+// Copyright 2011 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/property_tree.hpp>
+#include <string>
+
+namespace uhd{ namespace usrp{
+
+ //! Validate a subdev spec against a property tree
+ void validate_subdev_spec(
+ property_tree::sptr tree,
+ const subdev_spec_t &spec,
+ const std::string &type, //rx or tx
+ const std::string &mb = "0"
+ );
+
+}} //namespace uhd::usrp
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp
new file mode 100644
index 000000000..17c02c93a
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp
@@ -0,0 +1,76 @@
+//
+// Copyright 2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+
+class dma_fifo_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<dma_fifo_core_3000> sptr;
+ virtual ~dma_fifo_core_3000(void) = 0;
+
+ /*!
+ * Create a DMA FIFO controller using the given bus, settings and readback base
+ * Throws uhd::runtime_error if a DMA FIFO is not instantiated in the FPGA
+ */
+ static sptr make(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr);
+
+ /*!
+ * Check if a DMA FIFO is instantiated in the FPGA
+ */
+ static bool check(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr);
+
+ /*!
+ * Flush the DMA FIFO. Will clear all contents.
+ */
+ virtual void flush() = 0;
+
+ /*!
+ * Resize and rebase the DMA FIFO. Will clear all contents.
+ */
+ virtual void resize(const uint32_t base_addr, const uint32_t size) = 0;
+
+ /*!
+ * Get the (approx) number of bytes currently in the DMA FIFO
+ */
+ virtual uint32_t get_bytes_occupied() = 0;
+
+ /*!
+ * Run the built-in-self-test routine for the DMA FIFO
+ */
+ virtual uint8_t run_bist(bool finite = true, uint32_t timeout_ms = 500) = 0;
+
+ /*!
+ * Is extended BIST supported
+ */
+ virtual bool ext_bist_supported() = 0;
+
+ /*!
+ * Run the built-in-self-test routine for the DMA FIFO (extended BIST only)
+ */
+ virtual uint8_t run_ext_bist(
+ bool finite,
+ uint32_t rx_samp_delay,
+ uint32_t tx_pkt_delay,
+ uint32_t sid,
+ uint32_t timeout_ms = 500) = 0;
+
+ /*!
+ * Get the throughput measured from the last invocation of the BIST (extended BIST only)
+ */
+ virtual double get_bist_throughput() = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp b/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp
new file mode 100644
index 000000000..739b973cb
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp
@@ -0,0 +1,23 @@
+//
+// Copyright 2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP
+#define INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP
+
+#include <stdint.h>
+
+/*! For a requested frequency and sampling rate, return the
+ * correct frequency word (to set the CORDIC) and the actual frequency.
+ */
+void get_freq_and_freq_word(
+ const double requested_freq,
+ const double tick_rate,
+ double &actual_freq,
+ int32_t &freq_word
+);
+
+#endif /* INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/gpio_atr_3000.hpp b/host/lib/include/uhdlib/usrp/cores/gpio_atr_3000.hpp
new file mode 100644
index 000000000..30c7d6fb7
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/gpio_atr_3000.hpp
@@ -0,0 +1,178 @@
+//
+// Copyright 2011,2014,2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+#include <uhdlib/usrp/gpio_defs.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace uhd { namespace usrp { namespace gpio_atr {
+
+class gpio_atr_3000 : boost::noncopyable {
+public:
+ typedef boost::shared_ptr<gpio_atr_3000> sptr;
+
+ static const uint32_t MASK_SET_ALL = 0xFFFFFFFF;
+
+ virtual ~gpio_atr_3000(void) {};
+
+ /*!
+ * Create a read-write GPIO ATR interface object
+ *
+ * \param iface register iface to GPIO ATR registers
+ * \param base base settings offset for GPIO ATR registers
+ * \param rb_addr readback offset for GPIO ATR registers
+ */
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const uhd::wb_iface::wb_addr_type base,
+ const uhd::wb_iface::wb_addr_type rb_addr);
+
+ /*!
+ * Create a write-only GPIO ATR interface object
+ *
+ * \param iface register iface to GPIO ATR registers
+ * \param base base settings offset for GPIO ATR registers
+ */
+ static sptr make_write_only(
+ uhd::wb_iface::sptr iface, const uhd::wb_iface::wb_addr_type base);
+
+ /*!
+ * Select the ATR mode for all bits in the mask
+ *
+ * \param mode the mode to apply {ATR = outputs driven by ATR state machine, GPIO = outputs static}
+ * \param mask apply the mode to all non-zero bits in the mask
+ */
+ virtual void set_atr_mode(const gpio_atr_mode_t mode, const uint32_t mask) = 0;
+
+ /*!
+ * Select the data direction for all bits in the mask
+ *
+ * \param dir the direction {OUTPUT, INPUT}
+ * \param mask apply the mode to all non-zero bits in the mask
+ */
+ virtual void set_gpio_ddr(const gpio_ddr_t dir, const uint32_t mask) = 0;
+
+ /*!
+ * Write the specified (masked) value to the ATR register
+ *
+ * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX}
+ * \param value the value to write
+ * \param mask only writes to the bits where mask is non-zero
+ */
+ virtual void set_atr_reg(const gpio_atr_reg_t atr, const uint32_t value, const uint32_t mask = MASK_SET_ALL) = 0;
+
+ /*!
+ * Write to a static GPIO output
+ *
+ * \param value the value to write
+ * \param mask only writes to the bits where mask is non-zero
+ */
+ virtual void set_gpio_out(const uint32_t value, const uint32_t mask = MASK_SET_ALL) = 0;
+
+ /*!
+ * Read the state of the GPIO pins
+ * If a pin is configured as an input, reads the actual value of the pin
+ * If a pin is configured as an output, reads the last value written to the pin
+ *
+ * \return the value read back
+ */
+ virtual uint32_t read_gpio() = 0;
+
+ /*!
+ * Set a GPIO attribute
+ *
+ * \param attr the attribute to set
+ * \param value the value to write to the attribute
+ */
+ virtual void set_gpio_attr(const gpio_attr_t attr, const uint32_t value) = 0;
+};
+
+class db_gpio_atr_3000 {
+public:
+ typedef boost::shared_ptr<db_gpio_atr_3000> sptr;
+
+ typedef uhd::usrp::dboard_iface::unit_t db_unit_t;
+
+ virtual ~db_gpio_atr_3000(void) {};
+
+ /*!
+ * Create a read-write GPIO ATR interface object for a daughterboard connector
+ *
+ * \param iface register iface to GPIO ATR registers
+ * \param base base settings offset for GPIO ATR registers
+ * \param rb_addr readback offset for GPIO ATR registers
+ */
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const uhd::wb_iface::wb_addr_type base,
+ const uhd::wb_iface::wb_addr_type rb_addr);
+
+ /*!
+ * Configure the GPIO mode for all pins in the daughterboard connector
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \param value if value[i] is 1, the i'th bit is in ATR mode otherwise it is in GPIO mode
+ * \param mask mask
+ */
+ virtual void set_pin_ctrl(const db_unit_t unit, const uint32_t value, const uint32_t mask) = 0;
+
+ virtual uint32_t get_pin_ctrl(const db_unit_t unit) = 0;
+
+ /*!
+ * Configure the direction for all pins in the daughterboard connector
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \param value if value[i] is 1, the i'th bit is an output otherwise it is an input
+ * \param mask mask
+ */
+ virtual void set_gpio_ddr(const db_unit_t unit, const uint32_t value, const uint32_t mask) = 0;
+
+ virtual uint32_t get_gpio_ddr(const db_unit_t unit) = 0;
+
+ /*!
+ * Write the specified value to the ATR register (all bits)
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX}
+ * \param value the value to write
+ * \param mask mask
+ */
+ virtual void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const uint32_t value, const uint32_t mask) = 0;
+
+ virtual uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr) = 0;
+
+ /*!
+ * Write the specified value to the GPIO register (all bits)
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \param value the value to write
+ * \param mask mask
+ */
+ virtual void set_gpio_out(const db_unit_t unit, const uint32_t value, const uint32_t mask) = 0;
+
+ virtual uint32_t get_gpio_out(const db_unit_t unit) = 0;
+
+ /*!
+ * Read the state of the GPIO pins
+ * If a pin is configured as an input, reads the actual value of the pin
+ * If a pin is configured as an output, reads the last value written to the pin
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \return the value read back
+ */
+ virtual uint32_t read_gpio(const db_unit_t unit) = 0;
+};
+
+}}} //namespaces
+
+#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/gpio_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/gpio_core_200.hpp
new file mode 100644
index 000000000..d5dff890e
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/gpio_core_200.hpp
@@ -0,0 +1,77 @@
+//
+// Copyright 2011,2014,2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+#include <boost/assign.hpp>
+#include <stdint.h>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <map>
+
+class gpio_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<gpio_core_200> sptr;
+
+ typedef uhd::usrp::dboard_iface::unit_t unit_t;
+ typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t;
+
+ virtual ~gpio_core_200(void) = 0;
+
+ //! makes a new GPIO core from iface and slave base
+ static sptr make(
+ uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr);
+
+ //! 1 = ATR
+ virtual void set_pin_ctrl(
+ const unit_t unit, const uint16_t value, const uint16_t mask) = 0;
+
+ virtual uint16_t get_pin_ctrl(unit_t unit) = 0;
+
+ virtual void set_atr_reg(
+ const unit_t unit, const atr_reg_t atr, const uint16_t value, const uint16_t mask) = 0;
+
+ virtual uint16_t get_atr_reg(unit_t unit, atr_reg_t reg) = 0;
+
+ //! 1 = OUTPUT
+ virtual void set_gpio_ddr(
+ const unit_t unit, const uint16_t value, const uint16_t mask) = 0;
+
+ virtual uint16_t get_gpio_ddr(unit_t unit) = 0;
+
+ virtual void set_gpio_out(
+ const unit_t unit, const uint16_t value, const uint16_t mask) = 0;
+
+ virtual uint16_t get_gpio_out(unit_t unit) = 0;
+
+ virtual uint16_t read_gpio(const unit_t unit) = 0;
+};
+
+//! Simple wrapper for 32 bit write only
+class gpio_core_200_32wo : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<gpio_core_200_32wo> sptr;
+
+ typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t;
+
+ virtual ~gpio_core_200_32wo(void) = 0;
+
+ static sptr make(uhd::wb_iface::sptr iface, const size_t);
+
+ virtual void set_ddr_reg() = 0;
+
+ virtual void set_atr_reg(const atr_reg_t atr, const uint32_t value) = 0;
+
+ virtual void set_all_regs(const uint32_t value) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/i2c_core_100_wb32.hpp b/host/lib/include/uhdlib/usrp/cores/i2c_core_100_wb32.hpp
new file mode 100644
index 000000000..6c4182544
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/i2c_core_100_wb32.hpp
@@ -0,0 +1,29 @@
+//
+// Copyright 2011-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP
+#define INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class i2c_core_100_wb32 : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<i2c_core_100_wb32> sptr;
+
+ virtual ~i2c_core_100_wb32(void) = 0;
+
+ //! makes a new i2c core from iface and slave base
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
+
+ virtual void set_clock_rate(const double rate) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/i2c_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/i2c_core_200.hpp
new file mode 100644
index 000000000..354244891
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/i2c_core_200.hpp
@@ -0,0 +1,27 @@
+//
+// Copyright 2011-2012,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class i2c_core_200 : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<i2c_core_200> sptr;
+
+ virtual ~i2c_core_200(void) = 0;
+
+ //! makes a new i2c core from iface and slave base
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback);
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/radio_ctrl_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/radio_ctrl_core_3000.hpp
new file mode 100644
index 000000000..f7be7ef91
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/radio_ctrl_core_3000.hpp
@@ -0,0 +1,54 @@
+//
+// Copyright 2012-2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP
+#define INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP
+
+#include <uhd/utils/msg_task.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <string>
+
+/*!
+ * Provide access to peek, poke for the radio ctrl module
+ */
+class radio_ctrl_core_3000 : public uhd::timed_wb_iface
+{
+public:
+ typedef boost::shared_ptr<radio_ctrl_core_3000> sptr;
+
+ virtual ~radio_ctrl_core_3000(void) = 0;
+
+ //! Make a new control object
+ static sptr make(
+ const bool big_endian,
+ uhd::transport::zero_copy_if::sptr ctrl_xport,
+ uhd::transport::zero_copy_if::sptr resp_xport,
+ const uint32_t sid,
+ const std::string &name = "0"
+ );
+
+ //! Hold a ref to a task thats feeding push response
+ virtual void hold_task(uhd::msg_task::sptr task) = 0;
+
+ //! Push a response externall (resp_xport is NULL)
+ virtual void push_response(const uint32_t *buff) = 0;
+
+ //! Set the command time that will activate
+ virtual void set_time(const uhd::time_spec_t &time) = 0;
+
+ //! Get the command time that will activate
+ virtual uhd::time_spec_t get_time(void) = 0;
+
+ //! Set the tick rate (converting time into ticks)
+ virtual void set_tick_rate(const double rate) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_200.hpp
new file mode 100644
index 000000000..e9b6b98cf
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_200.hpp
@@ -0,0 +1,59 @@
+//
+// Copyright 2011,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+
+class rx_dsp_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<rx_dsp_core_200> sptr;
+
+ virtual ~rx_dsp_core_200(void) = 0;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t dsp_base, const size_t ctrl_base,
+ const uint32_t sid, const bool lingering_packet = false
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void set_nsamps_per_packet(const size_t nsamps) = 0;
+
+ virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0;
+
+ virtual void set_mux(const std::string &mode, const bool fe_swapped = false) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void handle_overflow(void) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp
new file mode 100644
index 000000000..0fba8ed65
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp
@@ -0,0 +1,58 @@
+//
+// Copyright 2011-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/fe_connection.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+class rx_dsp_core_3000 : boost::noncopyable{
+public:
+ static const double DEFAULT_CORDIC_FREQ;
+ static const double DEFAULT_RATE;
+
+ typedef boost::shared_ptr<rx_dsp_core_3000> sptr;
+
+ virtual ~rx_dsp_core_3000(void) = 0;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t dsp_base,
+ const bool is_b200 = false //TODO: Obsolete this when we switch to the new DDC on the B200
+ );
+
+ virtual void set_mux(const uhd::usrp::fe_connection_t& fe_conn) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+
+ virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_200.hpp
new file mode 100644
index 000000000..c4848bde2
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_200.hpp
@@ -0,0 +1,43 @@
+//
+// Copyright 2011,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/property_tree.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <complex>
+#include <string>
+
+class rx_frontend_core_200 : boost::noncopyable{
+public:
+ static const std::complex<double> DEFAULT_DC_OFFSET_VALUE;
+ static const bool DEFAULT_DC_OFFSET_ENABLE;
+ static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE;
+
+ typedef boost::shared_ptr<rx_frontend_core_200> sptr;
+
+ virtual ~rx_frontend_core_200(void) = 0;
+
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
+
+ virtual void set_mux(const bool swap) = 0;
+
+ virtual void set_dc_offset_auto(const bool enb) = 0;
+
+ virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0;
+
+ virtual void set_iq_balance(const std::complex<double> &cor) = 0;
+
+ virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_3000.hpp
new file mode 100644
index 000000000..e425e51a4
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_3000.hpp
@@ -0,0 +1,59 @@
+//
+// Copyright 2011,2014-2016 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/fe_connection.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <complex>
+#include <string>
+
+class rx_frontend_core_3000 : boost::noncopyable{
+public:
+ static const std::complex<double> DEFAULT_DC_OFFSET_VALUE;
+ static const bool DEFAULT_DC_OFFSET_ENABLE;
+ static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE;
+
+ typedef boost::shared_ptr<rx_frontend_core_3000> sptr;
+
+ virtual ~rx_frontend_core_3000(void) = 0;
+
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
+
+ /*! Set the input sampling rate (i.e. ADC rate)
+ */
+ virtual void set_adc_rate(const double rate) = 0;
+
+ virtual void bypass_all(bool bypass_en) = 0;
+
+ virtual void set_fe_connection(const uhd::usrp::fe_connection_t& fe_conn) = 0;
+
+ virtual void set_dc_offset_auto(const bool enb) = 0;
+
+ virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0;
+
+ virtual void set_iq_balance(const std::complex<double> &cor) = 0;
+
+ virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;
+
+ /*! Return the sampling rate at the output
+ *
+ * In real mode, the frontend core will decimate the sampling rate by a
+ * factor of 2.
+ *
+ * \returns RX sampling rate
+ */
+ virtual double get_output_rate(void) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/rx_vita_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/rx_vita_core_3000.hpp
new file mode 100644
index 000000000..919631300
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/rx_vita_core_3000.hpp
@@ -0,0 +1,51 @@
+//
+// Copyright 2013,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+
+class rx_vita_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<rx_vita_core_3000> sptr;
+
+ virtual ~rx_vita_core_3000(void) = 0;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t base
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void set_nsamps_per_packet(const size_t nsamps) = 0;
+
+ virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_sid(const uint32_t sid) = 0;
+
+ virtual void handle_overflow(void) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+
+ virtual void configure_flow_control(const size_t window_size) = 0;
+
+ virtual bool in_continuous_streaming_mode(void) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/spi_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/spi_core_3000.hpp
new file mode 100644
index 000000000..ea0507754
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/spi_core_3000.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/serial.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class spi_core_3000 : boost::noncopyable, public uhd::spi_iface
+{
+public:
+ typedef boost::shared_ptr<spi_core_3000> sptr;
+
+ virtual ~spi_core_3000(void) = 0;
+
+ //! makes a new spi core from iface and slave base
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback);
+
+ //! Set the spi clock divider to something usable
+ virtual void set_divider(const double div) = 0;
+
+ //! Place SPI core in shutdown mode. All attempted SPI transactions are dropped by
+ // the core.
+ virtual void set_shutdown(const bool shutdown) = 0;
+
+ //! Get state of shutdown register
+ virtual bool get_shutdown() = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/time64_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/time64_core_200.hpp
new file mode 100644
index 000000000..23e0da922
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/time64_core_200.hpp
@@ -0,0 +1,55 @@
+//
+// Copyright 2011-2012,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+#include <vector>
+
+class time64_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<time64_core_200> sptr;
+
+ struct readback_bases_type{
+ size_t rb_hi_now, rb_lo_now;
+ size_t rb_hi_pps, rb_lo_pps;
+ };
+
+ virtual ~time64_core_200(void) = 0;
+
+ //! makes a new time64 core from iface and slave base
+ static sptr make(
+ uhd::wb_iface::sptr iface, const size_t base,
+ const readback_bases_type &readback_bases,
+ const size_t mimo_delay_cycles = 0 // 0 means no-mimo
+ );
+
+ virtual void enable_gpsdo(void) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual uhd::time_spec_t get_time_now(void) = 0;
+
+ virtual uhd::time_spec_t get_time_last_pps(void) = 0;
+
+ virtual void set_time_now(const uhd::time_spec_t &time) = 0;
+
+ virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0;
+
+ virtual void set_time_source(const std::string &source) = 0;
+
+ virtual std::vector<std::string> get_time_sources(void) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/time_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/time_core_3000.hpp
new file mode 100644
index 000000000..4fa2ae657
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/time_core_3000.hpp
@@ -0,0 +1,52 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class time_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<time_core_3000> sptr;
+
+ struct readback_bases_type
+ {
+ size_t rb_now;
+ size_t rb_pps;
+ };
+
+ virtual ~time_core_3000(void) = 0;
+
+ //! makes a new time core from iface and slave base
+ static sptr make(
+ uhd::wb_iface::sptr iface, const size_t base,
+ const readback_bases_type &readback_bases
+ );
+
+ virtual void self_test(void) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual uhd::time_spec_t get_time_now(void) = 0;
+
+ virtual uhd::time_spec_t get_time_last_pps(void) = 0;
+
+ virtual void set_time_now(const uhd::time_spec_t &time) = 0;
+
+ virtual void set_time_sync(const uhd::time_spec_t &time) = 0;
+
+ virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_200.hpp
new file mode 100644
index 000000000..98e8b5225
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_200.hpp
@@ -0,0 +1,51 @@
+//
+// Copyright 2011,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class tx_dsp_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<tx_dsp_core_200> sptr;
+
+ virtual ~tx_dsp_core_200(void) = 0;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t dsp_base, const size_t ctrl_base,
+ const uint32_t sid
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void set_updates(const size_t cycles_per_up, const size_t packets_per_up) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp
new file mode 100644
index 000000000..3eb53da0d
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp
@@ -0,0 +1,52 @@
+//
+// Copyright 2011-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/property_tree.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+
+class tx_dsp_core_3000 : boost::noncopyable{
+public:
+ static const double DEFAULT_CORDIC_FREQ;
+ static const double DEFAULT_RATE;
+
+ typedef boost::shared_ptr<tx_dsp_core_3000> sptr;
+
+ virtual ~tx_dsp_core_3000(void) = 0;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t dsp_base
+ );
+
+ virtual void set_tick_rate(const double rate) = 0;
+
+ virtual void set_link_rate(const double rate) = 0;
+
+ virtual double set_host_rate(const double rate) = 0;
+
+ virtual uhd::meta_range_t get_host_rates(void) = 0;
+
+ virtual double get_scaling_adjustment(void) = 0;
+
+ virtual uhd::meta_range_t get_freq_range(void) = 0;
+
+ virtual double set_freq(const double freq) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+
+ virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/tx_frontend_core_200.hpp
new file mode 100644
index 000000000..eb86bf85d
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/tx_frontend_core_200.hpp
@@ -0,0 +1,40 @@
+//
+// Copyright 2011,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/property_tree.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <complex>
+#include <string>
+
+class tx_frontend_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<tx_frontend_core_200> sptr;
+
+ static const std::complex<double> DEFAULT_DC_OFFSET_VALUE;
+ static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE;
+
+ virtual ~tx_frontend_core_200(void) = 0;
+
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
+
+ virtual void set_mux(const std::string &mode) = 0;
+
+ virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0;
+
+ virtual void set_iq_balance(const std::complex<double> &cor) = 0;
+
+ virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/tx_vita_core_3000.hpp
new file mode 100644
index 000000000..4d33bb0c2
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/tx_vita_core_3000.hpp
@@ -0,0 +1,51 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/stream.hpp>
+#include <uhd/types/ranges.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/types/wb_iface.hpp>
+#include <string>
+
+class tx_vita_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<tx_vita_core_3000> sptr;
+
+ enum fc_monitor_loc {
+ FC_DEFAULT,
+ FC_PRE_RADIO,
+ FC_PRE_FIFO
+ };
+
+ virtual ~tx_vita_core_3000(void) = 0;
+
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const size_t base,
+ fc_monitor_loc fc_location = FC_PRE_RADIO
+ );
+
+ static sptr make_no_radio_buff(
+ uhd::wb_iface::sptr iface,
+ const size_t base
+ );
+
+ virtual void clear(void) = 0;
+
+ virtual void setup(const uhd::stream_args_t &stream_args) = 0;
+
+ virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/user_settings_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/user_settings_core_200.hpp
new file mode 100644
index 000000000..354b5e637
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/user_settings_core_200.hpp
@@ -0,0 +1,28 @@
+//
+// Copyright 2012,2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP
+#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP
+
+#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class user_settings_core_200 : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<user_settings_core_200> sptr;
+ typedef std::pair<uint8_t, uint32_t> user_reg_t;
+
+ virtual ~user_settings_core_200(void) = 0;
+
+ static sptr make(uhd::wb_iface::sptr iface, const size_t base);
+
+ virtual void set_reg(const user_reg_t &reg) = 0;
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP */
diff --git a/host/lib/include/uhdlib/usrp/cores/user_settings_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/user_settings_core_3000.hpp
new file mode 100644
index 000000000..bc281e396
--- /dev/null
+++ b/host/lib/include/uhdlib/usrp/cores/user_settings_core_3000.hpp
@@ -0,0 +1,25 @@
+//
+// Copyright 2012 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class user_settings_core_3000 : public uhd::wb_iface {
+public:
+ virtual ~user_settings_core_3000() {}
+
+ static sptr make(
+ wb_iface::sptr iface,
+ const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr);
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP */
diff --git a/host/lib/include/uhdlib/utils/eeprom_utils.hpp b/host/lib/include/uhdlib/utils/eeprom_utils.hpp
new file mode 100644
index 000000000..28deca790
--- /dev/null
+++ b/host/lib/include/uhdlib/utils/eeprom_utils.hpp
@@ -0,0 +1,20 @@
+//
+// Copyright 2017 Ettus Research (National Instruments Corp.)
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/types/byte_vector.hpp>
+#include <uhd/types/mac_addr.hpp>
+#include <boost/asio/ip/address_v4.hpp>
+#include <string>
+#include <vector>
+
+static const size_t SERIAL_LEN = 9;
+static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN;
+
+//! convert a string to a byte vector to write to eeprom
+uhd::byte_vector_t string_to_uint16_bytes(const std::string &num_str);
+
+//! convert a byte vector read from eeprom to a string
+std::string uint16_bytes_to_string(const uhd::byte_vector_t &bytes);
diff --git a/host/lib/include/uhdlib/utils/ihex.hpp b/host/lib/include/uhdlib/utils/ihex.hpp
new file mode 100644
index 000000000..4b1be77f1
--- /dev/null
+++ b/host/lib/include/uhdlib/utils/ihex.hpp
@@ -0,0 +1,69 @@
+//
+// Copyright 2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_IHEX_READER_HPP
+#define INCLUDED_IHEX_READER_HPP
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace uhd {
+
+class ihex_reader
+{
+public:
+ // Arguments are: lower address bits, upper address bits, buff, length
+ typedef boost::function<int(uint16_t,uint16_t,unsigned char*,uint16_t)> record_handle_type;
+
+ /*
+ * \param ihex_filename Path to the *.ihx file
+ */
+ ihex_reader(const std::string &ihex_filename);
+
+ /*! Read an Intel HEX file and handle it record by record.
+ *
+ * Every record is individually passed off to a record handler function.
+ *
+ * \param record_handler The functor that will handle the records.
+ *
+ * \throws uhd::io_error if the HEX file is corrupted or unreadable.
+ */
+ void read(record_handle_type record_handler);
+
+ /* Convert the ihex file to a bin file.
+ *
+ * *Note:* This function makes the assumption that the hex file is
+ * contiguous, and starts at address zero.
+ *
+ * \param bin_filename Output filename.
+ *
+ * \throws uhd::io_error if the HEX file is corrupted or unreadable.
+ */
+ void to_bin_file(const std::string &bin_filename);
+
+ /*! Copy the ihex file into a buffer.
+ *
+ * Very similar functionality as to_bin_file().
+ *
+ * *Note:* This function makes the assumption that the hex file is
+ * contiguous, and starts at address zero.
+ *
+ * \throws uhd::io_error if the HEX file is corrupted or unreadable.
+ */
+ std::vector<uint8_t> to_vector(const size_t size_estimate = 0);
+
+private:
+ const std::string _ihex_filename;
+};
+
+}; /* namespace uhd */
+
+#endif /* INCLUDED_IHEX_READER_HPP */
+
diff --git a/host/lib/include/uhdlib/utils/rpc.hpp b/host/lib/include/uhdlib/utils/rpc.hpp
new file mode 100644
index 000000000..c7c27afd2
--- /dev/null
+++ b/host/lib/include/uhdlib/utils/rpc.hpp
@@ -0,0 +1,191 @@
+//
+// Copyright 2017 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_UTILS_RPC_HPP
+#define INCLUDED_UTILS_RPC_HPP
+
+#include <rpc/client.h>
+#include <rpc/rpc_error.h>
+#include <uhd/utils/log.hpp>
+#include <uhd/exception.hpp>
+#include <boost/format.hpp>
+
+namespace uhd {
+
+/*! Abstraction for RPC client
+ *
+ * Purpose of this class is to wrap the underlying RPC implementation.
+ * This class holds a connection to an RPC server (the connection is severed on
+ * destruction).
+ */
+class rpc_client
+{
+ public:
+ using sptr = std::shared_ptr<rpc_client>;
+
+ static sptr make(
+ const std::string &addr,
+ const uint16_t port,
+ const std::string &get_last_error_cmd=""
+ ) {
+ return std::make_shared<rpc_client>(addr, port, get_last_error_cmd);
+ }
+
+ /*!
+ * \param addr An IP address to connect to
+ * \param port Port to connect to
+ * \param get_last_error_cmd A command that queries an error string from
+ * the RPC server. If set, the RPC client will
+ * try and use this command to fetch information
+ * about what went wrong on the client side.
+ */
+ rpc_client(
+ const std::string &addr,
+ const uint16_t port,
+ std::string const &get_last_error_cmd=""
+ ) : _client(addr, port)
+ , _get_last_error_cmd(get_last_error_cmd)
+ {
+ // nop
+ }
+
+ /*! Perform an RPC request.
+ *
+ * Thread safe (locked). This function blocks until it receives a valid
+ * response from the server.
+ *
+ * \param func_name The function name that is called via RPC
+ * \param args All these arguments are passed to the RPC call
+ *
+ * \throws uhd::runtime_error in case of failure
+ */
+ template <typename return_type, typename... Args>
+ return_type request(std::string const& func_name, Args&&... args)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ try {
+ return _client.call(func_name, std::forward<Args>(args)...)
+ .template as<return_type>();
+ } catch (const ::rpc::rpc_error &ex) {
+ const std::string error = _get_last_error_safe();
+ if (not error.empty()) {
+ UHD_LOG_ERROR("RPC", error);
+ }
+ throw uhd::runtime_error(str(
+ boost::format("Error during RPC call to `%s'. Error message: %s")
+ % func_name % (error.empty() ? ex.what() : error)
+ ));
+ } catch (const std::bad_cast& ex) {
+ throw uhd::runtime_error(str(
+ boost::format("Error during RPC call to `%s'. Error message: %s")
+ % func_name % ex.what()
+ ));
+ }
+ };
+
+ /*! Perform an RPC notification.
+ *
+ * Thread safe (locked). This function does not require a response from the
+ * server, although the underlying implementation may provide one.
+ *
+ * \param func_name The function name that is called via RPC
+ * \param args All these arguments are passed to the RPC call
+ *
+ * \throws uhd::runtime_error in case of failure
+ */
+ template <typename... Args>
+ void notify(std::string const& func_name, Args&&... args)
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ try {
+ _client.call(func_name, std::forward<Args>(args)...);
+ } catch (const ::rpc::rpc_error &ex) {
+ const std::string error = _get_last_error_safe();
+ if (not error.empty()) {
+ UHD_LOG_ERROR("RPC", error);
+ }
+ throw uhd::runtime_error(str(
+ boost::format("Error during RPC call to `%s'. Error message: %s")
+ % func_name % (error.empty() ? ex.what() : error)
+ ));
+ } catch (const std::bad_cast& ex) {
+ throw uhd::runtime_error(str(
+ boost::format("Error during RPC call to `%s'. Error message: %s")
+ % func_name % ex.what()
+ ));
+ }
+ };
+
+ /*! Like request(), also provides a token.
+ *
+ * This is a convenience wrapper to directly call a function that requires
+ * a token without having to have a copy of the token.
+ */
+ template <typename return_type, typename... Args>
+ return_type request_with_token(std::string const& func_name, Args&&... args)
+ {
+ return request<return_type>(func_name, _token, std::forward<Args>(args)...);
+ };
+
+ /*! Like notify(), also provides a token.
+ *
+ * This is a convenience wrapper to directly call a function that requires
+ * a token without having to have a copy of the token.
+ */
+ template <typename... Args>
+ void notify_with_token(std::string const& func_name, Args&&... args)
+ {
+ notify(func_name, _token, std::forward<Args>(args)...);
+ };
+
+ /*! Sets the token value. This is used by the `_with_token` methods.
+ */
+ void set_token(const std::string &token)
+ {
+ _token = token;
+ }
+
+ void set_timeout(size_t timeout_ms)
+ {
+ _client.set_timeout(timeout_ms);
+ }
+
+ private:
+ /*! Pull the last error out of the RPC server. Not thread-safe, meant to
+ * be called from notify() or request().
+ *
+ * This function will do its best not to get in anyone's way. If it can't
+ * get an error string, it'll return an empty string.
+ */
+ std::string _get_last_error_safe()
+ {
+ if (_get_last_error_cmd.empty()) {
+ return "";
+ }
+ try {
+ return _client.call(_get_last_error_cmd).as<std::string>();
+ } catch (const ::rpc::rpc_error &ex) {
+ // nop
+ } catch (const std::bad_cast& ex) {
+ // nop
+ } catch (...) {
+ // nop
+ }
+ return "";
+ }
+
+ //! Reference the actual RPC client
+ ::rpc::client _client;
+ //! If set, this is the command that will retrieve an error
+ const std::string _get_last_error_cmd;
+
+ std::string _token;
+ std::mutex _mutex;
+};
+
+} /* namespace uhd */
+
+#endif /* INCLUDED_UTILS_RPC_HPP */