aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/common
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/common')
-rw-r--r--host/lib/usrp/common/CMakeLists.txt4
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.cpp54
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.hpp14
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.cpp15
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.h4
-rw-r--r--host/lib/usrp/common/ad936x_manager.cpp40
-rw-r--r--host/lib/usrp/common/ad936x_manager.hpp5
-rw-r--r--host/lib/usrp/common/adf435x.cpp34
-rw-r--r--host/lib/usrp/common/adf435x.hpp364
-rw-r--r--host/lib/usrp/common/adf435x_common.cpp161
-rw-r--r--host/lib/usrp/common/adf435x_common.hpp63
-rw-r--r--host/lib/usrp/common/adf5355.cpp376
-rw-r--r--host/lib/usrp/common/adf5355.hpp57
-rw-r--r--host/lib/usrp/common/apply_corrections.cpp51
-rw-r--r--host/lib/usrp/common/apply_corrections.hpp16
-rw-r--r--host/lib/usrp/common/constrained_device_args.hpp283
-rw-r--r--host/lib/usrp/common/fw_comm_protocol.h102
-rw-r--r--host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp246
-rw-r--r--host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp72
19 files changed, 1695 insertions, 266 deletions
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt
index 9dabc4e0b..9f4cf09b5 100644
--- a/host/lib/usrp/common/CMakeLists.txt
+++ b/host/lib/usrp/common/CMakeLists.txt
@@ -29,7 +29,8 @@ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver")
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/adf435x.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/adf5355.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp
@@ -37,4 +38,5 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp
${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp3_fw_ctrl_iface.cpp
)
diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp
index 54f0fcdbf..654311424 100644
--- a/host/lib/usrp/common/ad9361_ctrl.cpp
+++ b/host/lib/usrp/common/ad9361_ctrl.cpp
@@ -93,18 +93,30 @@ class ad9361_ctrl_impl : public ad9361_ctrl
{
public:
ad9361_ctrl_impl(ad9361_params::sptr client_settings, ad9361_io::sptr io_iface):
- _device(client_settings, io_iface)
+ _device(client_settings, io_iface), _safe_spi(io_iface), _timed_spi(io_iface)
{
_device.initialize();
}
+ void set_timed_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num)
+ {
+ _timed_spi = boost::make_shared<ad9361_io_spi>(spi_iface, slave_num);
+ _use_timed_spi();
+ }
+
+ void set_safe_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num)
+ {
+ _safe_spi = boost::make_shared<ad9361_io_spi>(spi_iface, slave_num);
+ }
+
double set_gain(const std::string &which, const double value)
{
boost::lock_guard<boost::mutex> lock(_mutex);
ad9361_device_t::direction_t direction = _get_direction_from_antenna(which);
ad9361_device_t::chain_t chain =_get_chain_from_antenna(which);
- return _device.set_gain(direction, chain, value);
+ double return_val = _device.set_gain(direction, chain, value);
+ return return_val;
}
void set_agc(const std::string &which, bool enable)
@@ -112,7 +124,7 @@ public:
boost::lock_guard<boost::mutex> lock(_mutex);
ad9361_device_t::chain_t chain =_get_chain_from_antenna(which);
- _device.set_agc(chain, enable);
+ _device.set_agc(chain, enable);
}
void set_agc_mode(const std::string &which, const std::string &mode)
@@ -133,6 +145,9 @@ public:
{
boost::lock_guard<boost::mutex> lock(_mutex);
+ // Changing clock rate will disrupt AD9361's sample clock
+ _use_safe_spi();
+
//clip to known bounds
const meta_range_t clock_rate_range = ad9361_ctrl::get_clock_rate_range();
const double clipped_rate = clock_rate_range.clip(rate);
@@ -144,7 +159,11 @@ public:
) % (rate/1e6) % (clipped_rate/1e6) << std::endl;
}
- return _device.set_clock_rate(clipped_rate);
+ double return_rate = _device.set_clock_rate(clipped_rate);
+
+ _use_timed_spi();
+
+ return return_rate;
}
//! set which RX and TX chains/antennas are active
@@ -152,7 +171,11 @@ public:
{
boost::lock_guard<boost::mutex> lock(_mutex);
+ // If both RX chains are disabled then the AD9361's sample clock is disabled
+ _use_safe_spi();
_device.set_active_chains(tx1, tx2, rx1, rx2);
+ _use_timed_spi();
+
}
//! tune the given frontend, return the exact value
@@ -166,7 +189,8 @@ public:
const double value = ad9361_ctrl::get_rf_freq_range().clip(clipped_freq);
ad9361_device_t::direction_t direction = _get_direction_from_antenna(which);
- return _device.tune(direction, value);
+ double return_val = _device.tune(direction, value);
+ return return_val;
}
//! get the current frequency for the given frontend
@@ -283,16 +307,28 @@ private:
return ad9361_device_t::CHAIN_1;
}
- ad9361_device_t _device;
- boost::mutex _mutex;
+ void _use_safe_spi() {
+ _device.set_io_iface(_safe_spi);
+ }
+
+ void _use_timed_spi() {
+ _device.set_io_iface(_timed_spi);
+ }
+
+ ad9361_device_t _device;
+ ad9361_io::sptr _safe_spi; // SPI core that uses an always available clock
+ ad9361_io::sptr _timed_spi; // SPI core that has a dependency on the AD9361's sample clock (i.e. radio clk)
+ boost::mutex _mutex;
};
//----------------------------------------------------------------------
// Make an instance of the AD9361 Control interface
//----------------------------------------------------------------------
ad9361_ctrl::sptr ad9361_ctrl::make_spi(
- ad9361_params::sptr client_settings, uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num)
-{
+ ad9361_params::sptr client_settings,
+ uhd::spi_iface::sptr spi_iface,
+ boost::uint32_t slave_num
+) {
boost::shared_ptr<ad9361_io_spi> spi_io_iface = boost::make_shared<ad9361_io_spi>(spi_iface, slave_num);
return sptr(new ad9361_ctrl_impl(client_settings, spi_io_iface));
}
diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp
index 5c438ee9c..5770c3ec4 100644
--- a/host/lib/usrp/common/ad9361_ctrl.hpp
+++ b/host/lib/usrp/common/ad9361_ctrl.hpp
@@ -56,7 +56,13 @@ public:
//! make a new codec control object
static sptr make_spi(
- ad9361_params::sptr client_settings, uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num);
+ ad9361_params::sptr client_settings,
+ uhd::spi_iface::sptr spi_iface,
+ boost::uint32_t slave_num
+ );
+
+ virtual void set_timed_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num) = 0;
+ virtual void set_safe_spi(uhd::spi_iface::sptr spi_iface, boost::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*/)
@@ -89,8 +95,10 @@ public:
//! get the clock rate range for the frontend
static uhd::meta_range_t get_clock_rate_range(void)
{
- //return uhd::meta_range_t(220e3, 61.44e6);
- return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end
+ 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
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp
index 0a8a61575..095017bb6 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp
@@ -91,6 +91,7 @@ int get_num_taps(int max_num_taps) {
}
const double ad9361_device_t::AD9361_MAX_GAIN = 89.75;
+const double ad9361_device_t::AD9361_MIN_CLOCK_RATE = 220e3;
const double ad9361_device_t::AD9361_MAX_CLOCK_RATE = 61.44e6;
const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6;
// Max bandwdith is due to filter rolloff in analog filter stage
@@ -770,7 +771,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset()
size_t count = 0;
_io_iface->poke8(0x016, 0x02);
while (_io_iface->peek8(0x016) & 0x02) {
- if (count > 100) {
+ if (count > 200) {
throw uhd::runtime_error("[ad9361_device_t] RF DC Offset Calibration Failure");
break;
}
@@ -821,7 +822,7 @@ void ad9361_device_t::_calibrate_rx_quadrature()
size_t count = 0;
_io_iface->poke8(0x016, 0x20);
while (_io_iface->peek8(0x016) & 0x20) {
- if (count > 100) {
+ if (count > 1000) {
throw uhd::runtime_error("[ad9361_device_t] Rx Quadrature Calibration Failure");
break;
}
@@ -1564,6 +1565,12 @@ void ad9361_device_t::initialize()
_io_iface->poke8(0x000, 0x00);
boost::this_thread::sleep(boost::posix_time::milliseconds(20));
+ /* Check device ID to make sure iface works */
+ boost::uint32_t device_id = (_io_iface->peek8(0x037) & 0x8);
+ if (device_id != 0x8) {
+ throw uhd::runtime_error(str(boost::format("[ad9361_device_t::initialize] Device ID readback failure. Expected: 0x8, Received: 0x%x") % device_id));
+ }
+
/* There is not a WAT big enough for this. */
_io_iface->poke8(0x3df, 0x01);
@@ -1773,6 +1780,10 @@ void ad9361_device_t::initialize()
_io_iface->poke8(0x014, 0x21);
}
+void ad9361_device_t::set_io_iface(ad9361_io::sptr io_iface)
+{
+ _io_iface = io_iface;
+}
/* This function sets the RX / TX rate between AD9361 and the FPGA, and
* thus determines the interpolation / decimation required in the FPGA to
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h
index 66bc2e8b9..d0e8a7e39 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h
@@ -75,6 +75,9 @@ public:
/* Initialize the AD9361 codec. */
void initialize();
+ /* Set SPI interface */
+ void set_io_iface(ad9361_io::sptr io_iface);
+
/* This function sets the RX / TX rate between AD9361 and the FPGA, and
* thus determines the interpolation / decimation required in the FPGA to
* achieve the user's requested rate.
@@ -157,6 +160,7 @@ public:
//Constants
static const double AD9361_MAX_GAIN;
static const double AD9361_MAX_CLOCK_RATE;
+ static const double AD9361_MIN_CLOCK_RATE;
static const double AD9361_CAL_VALID_WINDOW;
static const double AD9361_RECOMMENDED_MAX_BANDWIDTH;
static const double DEFAULT_RX_FREQ;
diff --git a/host/lib/usrp/common/ad936x_manager.cpp b/host/lib/usrp/common/ad936x_manager.cpp
index de8c4c7ab..e7af411fa 100644
--- a/host/lib/usrp/common/ad936x_manager.cpp
+++ b/host/lib/usrp/common/ad936x_manager.cpp
@@ -93,14 +93,12 @@ class ad936x_manager_impl : public ad936x_manager
// worst case conditions to stress the interface.
//
void loopback_self_test(
- wb_iface::sptr iface,
- wb_iface::wb_addr_type codec_idle_addr,
- wb_iface::wb_addr_type codec_readback_addr
+ boost::function<void(uint32_t)> poker_functor,
+ boost::function<uint64_t()> peeker_functor
) {
// Put AD936x in loopback mode
_codec_ctrl->data_port_loopback(true);
UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
- UHD_ASSERT_THROW(bool(iface));
size_t hash = size_t(time(NULL));
// Allow some time for AD936x to enter loopback mode.
@@ -118,10 +116,10 @@ class ad936x_manager_impl : public ad936x_manager
const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
// Write test word to codec_idle idle register (on TX side)
- iface->poke32(codec_idle_addr, word32);
+ poker_functor(word32);
// Read back values - TX is lower 32-bits and RX is upper 32-bits
- const boost::uint64_t rb_word64 = iface->peek64(codec_readback_addr);
+ const boost::uint64_t rb_word64 = peeker_functor();
const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
@@ -136,7 +134,7 @@ class ad936x_manager_impl : public ad936x_manager
UHD_MSG(status) << "pass" << std::endl;
// Zero out the idle data.
- iface->poke32(codec_idle_addr, 0);
+ poker_functor(0);
// Take AD936x out of loopback mode
_codec_ctrl->data_port_loopback(false);
@@ -211,11 +209,11 @@ class ad936x_manager_impl : public ad936x_manager
// Sensors
subtree->create<sensor_value_t>("sensors/temp")
- .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl))
+ .set_publisher(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl))
;
if (dir == RX_DIRECTION) {
subtree->create<sensor_value_t>("sensors/rssi")
- .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key))
+ .set_publisher(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key))
;
}
@@ -226,7 +224,7 @@ class ad936x_manager_impl : public ad936x_manager
.set(ad9361_ctrl::get_gain_range(key));
subtree->create<double>(uhd::fs_path("gains") / name / "value")
.set(ad936x_manager::DEFAULT_GAIN)
- .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
+ .set_coercer(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
;
}
@@ -238,19 +236,19 @@ class ad936x_manager_impl : public ad936x_manager
// Analog Bandwidths
subtree->create<double>("bandwidth/value")
.set(ad936x_manager::DEFAULT_BANDWIDTH)
- .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
+ .set_coercer(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
;
subtree->create<meta_range_t>("bandwidth/range")
- .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key))
+ .set_publisher(boost::bind(&ad9361_ctrl::get_bw_filter_range, key))
;
// LO Tuning
subtree->create<meta_range_t>("freq/range")
- .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range))
+ .set_publisher(boost::bind(&ad9361_ctrl::get_rf_freq_range))
;
subtree->create<double>("freq/value")
- .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key))
- .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
+ .set_publisher(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key))
+ .set_coercer(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
;
// Frontend corrections
@@ -258,21 +256,21 @@ class ad936x_manager_impl : public ad936x_manager
{
subtree->create<bool>("dc_offset/enable" )
.set(ad936x_manager::DEFAULT_AUTO_DC_OFFSET)
- .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1))
+ .add_coerced_subscriber(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1))
;
subtree->create<bool>("iq_balance/enable" )
.set(ad936x_manager::DEFAULT_AUTO_IQ_BALANCE)
- .subscribe(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1))
+ .add_coerced_subscriber(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1))
;
// AGC setup
const std::list<std::string> mode_strings = boost::assign::list_of("slow")("fast");
subtree->create<bool>("gain/agc/enable")
.set(DEFAULT_AGC_ENABLE)
- .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1))
+ .add_coerced_subscriber(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1))
;
subtree->create<std::string>("gain/agc/mode/value")
- .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front())
+ .add_coerced_subscriber(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front())
;
subtree->create< std::list<std::string> >("gain/agc/mode/options")
.set(mode_strings)
@@ -282,8 +280,8 @@ class ad936x_manager_impl : public ad936x_manager
// Frontend filters
BOOST_FOREACH(const std::string &filter_name, _codec_ctrl->get_filter_names(key)) {
subtree->create<filter_info_base::sptr>(uhd::fs_path("filters") / filter_name / "value" )
- .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_name))
- .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_name, _1));
+ .set_publisher(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_name))
+ .add_coerced_subscriber(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_name, _1));
}
}
diff --git a/host/lib/usrp/common/ad936x_manager.hpp b/host/lib/usrp/common/ad936x_manager.hpp
index 9b4a351c6..c456715e3 100644
--- a/host/lib/usrp/common/ad936x_manager.hpp
+++ b/host/lib/usrp/common/ad936x_manager.hpp
@@ -80,9 +80,8 @@ public:
* \throws a uhd::runtime_error if the loopback value didn't match.
*/
virtual void loopback_self_test(
- wb_iface::sptr iface,
- wb_iface::wb_addr_type codec_idle_addr,
- wb_iface::wb_addr_type codec_readback_addr
+ 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
diff --git a/host/lib/usrp/common/adf435x.cpp b/host/lib/usrp/common/adf435x.cpp
new file mode 100644
index 000000000..f1ba6ad05
--- /dev/null
+++ b/host/lib/usrp/common/adf435x.cpp
@@ -0,0 +1,34 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "adf435x.hpp"
+
+using namespace uhd;
+
+adf435x_iface::~adf435x_iface()
+{
+}
+
+adf435x_iface::sptr adf435x_iface::make_adf4350(write_fn_t write)
+{
+ return sptr(new adf435x_impl<adf4350_regs_t>(write));
+}
+
+adf435x_iface::sptr adf435x_iface::make_adf4351(write_fn_t write)
+{
+ return sptr(new adf435x_impl<adf4351_regs_t>(write));
+}
diff --git a/host/lib/usrp/common/adf435x.hpp b/host/lib/usrp/common/adf435x.hpp
new file mode 100644
index 000000000..d08c6b9dd
--- /dev/null
+++ b/host/lib/usrp/common/adf435x.hpp
@@ -0,0 +1,364 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#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<boost::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 };
+
+ 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_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 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_power_t power)
+ {
+ 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();
+ }
+ }
+
+ 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();
+ }
+ }
+
+ 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;
+ boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0;
+ boost::uint16_t RFdiv = static_cast<boost::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<boost::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 = boost::uint16_t(std::floor(feedback_freq/pfd_freq));
+
+ //keep N > minimum int divider requirement
+ if (N < static_cast<boost::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<boost::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<boost::uint16_t>(1, boost::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 = boost::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_LOGV(often)
+ << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl
+ << 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) << std::endl
+ << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl
+ << 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 << std::endl;
+
+ UHD_ASSERT_THROW((_regs.frac_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((_regs.mod_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((_regs.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
+ UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0);
+
+ UHD_ASSERT_THROW(vco_freq >= VCO_FREQ_MIN and vco_freq <= VCO_FREQ_MAX);
+ UHD_ASSERT_THROW(RFdiv >= static_cast<boost::uint16_t>(rf_divider_range.start()));
+ UHD_ASSERT_THROW(RFdiv <= static_cast<boost::uint16_t>(rf_divider_range.stop()));
+ UHD_ASSERT_THROW(_regs.int_16_bit >= static_cast<boost::uint16_t>(int_range.start()));
+ UHD_ASSERT_THROW(_regs.int_16_bit <= static_cast<boost::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<boost::uint32_t> regs;
+ regs.push_back(_regs.get_reg(boost::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(boost::uint32_t(addr)));
+ }
+ _write_fn(regs);
+ }
+
+protected:
+ uhd::range_t _get_rfdiv_range();
+ int _get_rfdiv_setting(boost::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(boost::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(boost::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/usrp/common/adf435x_common.cpp b/host/lib/usrp/common/adf435x_common.cpp
deleted file mode 100644
index 474a1c932..000000000
--- a/host/lib/usrp/common/adf435x_common.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-//
-// Copyright 2013-2014 Ettus Research LLC
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#include "adf435x_common.hpp"
-
-#include <boost/math/special_functions/round.hpp>
-#include <uhd/types/tune_request.hpp>
-#include <uhd/utils/log.hpp>
-#include <cmath>
-
-
-using namespace uhd;
-
-/***********************************************************************
- * ADF 4350/4351 Tuning Utility
- **********************************************************************/
-adf435x_tuning_settings tune_adf435x_synth(
- const double target_freq,
- const double ref_freq,
- const adf435x_tuning_constraints& constraints,
- double& actual_freq)
-{
- //Default invalid value for actual_freq
- actual_freq = 0;
-
- double pfd_freq = 0;
- boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0;
- boost::uint16_t RFdiv = static_cast<boost::uint16_t>(constraints.rf_divider_range.start());
- bool D = false, T = false;
-
- //Reference doubler for 50% duty cycle
- //If ref_freq < 12.5MHz enable the reference doubler
- D = (ref_freq <= constraints.ref_doubler_threshold);
-
- static const double MIN_VCO_FREQ = 2.2e9;
- static const double MAX_VCO_FREQ = 4.4e9;
-
- //increase RF divider until acceptable VCO frequency
- double vco_freq = target_freq;
- while (vco_freq < MIN_VCO_FREQ && RFdiv < static_cast<boost::uint16_t>(constraints.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 = constraints.feedback_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 = ref_freq*(D?2:1)/(R*(T?2:1));
-
- //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth)
- if (pfd_freq > constraints.pfd_freq_max) continue;
-
- //First, ignore fractional part of tuning
- N = boost::uint16_t(std::floor(feedback_freq/pfd_freq));
-
- //keep N > minimum int divider requirement
- if (N < static_cast<boost::uint16_t>(constraints.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 > constraints.band_sel_freq_max) continue;
- goto done_loop;
- }
- } done_loop:
-
- //Fractional-N calculation
- MOD = 4095; //max fractional accuracy
- FRAC = static_cast<boost::uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD));
- if (constraints.force_frac0) {
- 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 = constraints.feedback_after_divider ? 1 : RFdiv;
-
- //Compute the actual frequency in terms of ref_freq, N, FRAC, MOD, D, R and T.
- actual_freq = (
- double((N + (double(FRAC)/double(MOD))) *
- (ref_freq*(D?2:1)/(R*(T?2:1))))
- ) / rf_div_compensation;
-
- //load the settings
- adf435x_tuning_settings settings;
- settings.frac_12_bit = FRAC;
- settings.int_16_bit = N;
- settings.mod_12_bit = MOD;
- settings.clock_divider_12_bit = std::max<boost::uint16_t>(1, boost::uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD)));
- settings.r_counter_10_bit = R;
- settings.r_divide_by_2_en = T;
- settings.r_doubler_en = D;
- settings.band_select_clock_div = boost::uint8_t(BS);
- settings.rf_divider = RFdiv;
-
- std::string tuning_str = (constraints.force_frac0) ? "Integer-N" : "Fractional";
- UHD_LOGV(often)
- << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f"
- ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl
- << 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) % (ref_freq/1e6) << std::endl
- << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl
- << 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 << std::endl;
-
- UHD_ASSERT_THROW((settings.frac_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
- UHD_ASSERT_THROW((settings.mod_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
- UHD_ASSERT_THROW((settings.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0);
- UHD_ASSERT_THROW((settings.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0);
-
- UHD_ASSERT_THROW(vco_freq >= MIN_VCO_FREQ and vco_freq <= MAX_VCO_FREQ);
- UHD_ASSERT_THROW(settings.rf_divider >= static_cast<boost::uint16_t>(constraints.rf_divider_range.start()));
- UHD_ASSERT_THROW(settings.rf_divider <= static_cast<boost::uint16_t>(constraints.rf_divider_range.stop()));
- UHD_ASSERT_THROW(settings.int_16_bit >= static_cast<boost::uint16_t>(constraints.int_range.start()));
- UHD_ASSERT_THROW(settings.int_16_bit <= static_cast<boost::uint16_t>(constraints.int_range.stop()));
-
- return settings;
-}
diff --git a/host/lib/usrp/common/adf435x_common.hpp b/host/lib/usrp/common/adf435x_common.hpp
deleted file mode 100644
index 617b9d97f..000000000
--- a/host/lib/usrp/common/adf435x_common.hpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// Copyright 2014 Ettus Research LLC
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#ifndef INCLUDED_ADF435X_COMMON_HPP
-#define INCLUDED_ADF435X_COMMON_HPP
-
-#include <boost/cstdint.hpp>
-#include <uhd/property_tree.hpp>
-#include <uhd/types/ranges.hpp>
-
-//Common IO Pins
-#define ADF435X_CE (1 << 3)
-#define ADF435X_PDBRF (1 << 2)
-#define ADF435X_MUXOUT (1 << 1) // INPUT!!!
-#define LOCKDET_MASK (1 << 0) // INPUT!!!
-
-#define RX_ATTN_SHIFT 8 //lsb of RX Attenuator Control
-#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) //valid bits of RX Attenuator Control
-
-struct adf435x_tuning_constraints {
- bool force_frac0;
- bool feedback_after_divider;
- double ref_doubler_threshold;
- double pfd_freq_max;
- double band_sel_freq_max;
- uhd::range_t rf_divider_range;
- uhd::range_t int_range;
-};
-
-struct adf435x_tuning_settings {
- boost::uint16_t frac_12_bit;
- boost::uint16_t int_16_bit;
- boost::uint16_t mod_12_bit;
- boost::uint16_t r_counter_10_bit;
- bool r_doubler_en;
- bool r_divide_by_2_en;
- boost::uint16_t clock_divider_12_bit;
- boost::uint8_t band_select_clock_div;
- boost::uint16_t rf_divider;
-};
-
-adf435x_tuning_settings tune_adf435x_synth(
- const double target_freq,
- const double ref_freq,
- const adf435x_tuning_constraints& constraints,
- double& actual_freq
-);
-
-#endif /* INCLUDED_ADF435X_COMMON_HPP */
diff --git a/host/lib/usrp/common/adf5355.cpp b/host/lib/usrp/common/adf5355.cpp
new file mode 100644
index 000000000..bb0906724
--- /dev/null
+++ b/host/lib/usrp/common/adf5355.cpp
@@ -0,0 +1,376 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "adf5355.hpp"
+#include "adf5355_regs.hpp"
+#include <uhd/utils/math.hpp>
+#include <boost/math/common_factor_rt.hpp> //gcd
+#include <boost/thread.hpp>
+
+using namespace uhd;
+
+template<typename data_t>
+data_t clamp(data_t val, data_t min, data_t max) {
+ return (val < min) ? min : ((val > max) ? max : val);
+}
+
+template<typename data_t>
+double todbl(data_t val) {
+ return static_cast<double>(val);
+}
+
+static const double ADF5355_DOUBLER_MAX_REF_FREQ = 60e6;
+static const double ADF5355_MAX_FREQ_PFD = 125e6;
+static const double ADF5355_PRESCALER_THRESH = 7e9;
+
+static const double ADF5355_MIN_VCO_FREQ = 3.4e9;
+static const double ADF5355_MAX_VCO_FREQ = 6.8e9;
+static const double ADF5355_MAX_OUT_FREQ = 6.8e9;
+static const double ADF5355_MIN_OUT_FREQ = (3.4e9 / 64);
+static const double ADF5355_MAX_OUTB_FREQ = (6.8e9 * 2);
+static const double ADF5355_MIN_OUTB_FREQ = (3.4e9 * 2);
+
+static const double ADF5355_PHASE_RESYNC_TIME = 400e-6;
+
+static const boost::uint32_t ADF5355_MOD1 = 16777216;
+static const boost::uint32_t ADF5355_MAX_MOD2 = 16384;
+static const boost::uint16_t ADF5355_MIN_INT_PRESCALER_89 = 75;
+
+class adf5355_impl : public adf5355_iface
+{
+public:
+ adf5355_impl(write_fn_t write_fn) :
+ _write_fn(write_fn),
+ _regs(),
+ _rewrite_regs(true),
+ _wait_time_us(0),
+ _ref_freq(0.0),
+ _pfd_freq(0.0),
+ _fb_after_divider(false)
+ {
+ _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED;
+ _regs.cp_three_state = adf5355_regs_t::CP_THREE_STATE_DISABLED;
+ _regs.power_down = adf5355_regs_t::POWER_DOWN_DISABLED;
+ _regs.pd_polarity = adf5355_regs_t::PD_POLARITY_POSITIVE;
+ _regs.mux_logic = adf5355_regs_t::MUX_LOGIC_3_3V;
+ _regs.ref_mode = adf5355_regs_t::REF_MODE_SINGLE;
+ _regs.muxout = adf5355_regs_t::MUXOUT_DLD;
+ _regs.double_buff_div = adf5355_regs_t::DOUBLE_BUFF_DIV_DISABLED;
+
+ _regs.rf_out_a_enabled = adf5355_regs_t::RF_OUT_A_ENABLED_ENABLED;
+ _regs.rf_out_b_enabled = adf5355_regs_t::RF_OUT_B_ENABLED_DISABLED;
+ _regs.mute_till_lock_detect = adf5355_regs_t::MUTE_TILL_LOCK_DETECT_MUTE_DISABLED;
+ _regs.ld_mode = adf5355_regs_t::LD_MODE_FRAC_N;
+ _regs.frac_n_ld_precision = adf5355_regs_t::FRAC_N_LD_PRECISION_5NS;
+ _regs.ld_cyc_count = adf5355_regs_t::LD_CYC_COUNT_1024;
+ _regs.le_sync = adf5355_regs_t::LE_SYNC_LE_SYNCED_TO_REFIN;
+ _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED;
+ _regs.reference_divide_by_2 = adf5355_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = adf5355_regs_t::REFERENCE_DOUBLER_DISABLED;
+
+ _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED;
+ _regs.prescaler = adf5355_regs_t::PRESCALER_4_5;
+ _regs.charge_pump_current = adf5355_regs_t::CHARGE_PUMP_CURRENT_0_94MA;
+
+ _regs.gated_bleed = adf5355_regs_t::GATED_BLEED_DISABLED;
+ _regs.negative_bleed = adf5355_regs_t::NEGATIVE_BLEED_ENABLED;
+ _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ _regs.output_power = adf5355_regs_t::OUTPUT_POWER_5DBM;
+ _regs.cp_bleed_current = 2;
+ _regs.r_counter_10_bit = 8;
+
+
+ _regs.ld_cyc_count = adf5355_regs_t::LD_CYC_COUNT_1024;
+ _regs.loss_of_lock_mode = adf5355_regs_t::LOSS_OF_LOCK_MODE_DISABLED;
+ _regs.frac_n_ld_precision = adf5355_regs_t::FRAC_N_LD_PRECISION_5NS;
+ _regs.ld_mode = adf5355_regs_t::LD_MODE_FRAC_N;
+
+ _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 = adf5355_regs_t::ADC_CONVERSION_ENABLED;
+ _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED;
+
+ _regs.phase_resync_clk_div = 0;
+ }
+
+ ~adf5355_impl()
+ {
+ _regs.power_down = adf5355_regs_t::POWER_DOWN_ENABLED;
+ commit();
+ }
+
+ void set_feedback_select(feedback_sel_t fb_sel)
+ {
+ _fb_after_divider = (fb_sel == FB_SEL_DIVIDED);
+
+ if (_fb_after_divider) {
+ _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_DIVIDED;
+ } else {
+ _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+ }
+ }
+
+ void set_reference_freq(double fref, bool force = false)
+ {
+ //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
+
+ //Reference doubler for 50% duty cycle
+ bool doubler_en = (_ref_freq <= ADF5355_DOUBLER_MAX_REF_FREQ);
+
+ /* Calculate and maximize PFD frequency */
+ // TODO Target PFD should be configurable
+ /* TwinRX requires PFD of 6.25 MHz or less */
+ const double TWINRX_PFD_FREQ = 6.25e6;
+ _pfd_freq = TWINRX_PFD_FREQ;
+
+ int ref_div_factor = 16;
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ bool div2_en = (ref_div_factor % 2 == 0);
+ if (div2_en) {
+ ref_div_factor /= 2;
+ }
+
+ _regs.reference_divide_by_2 = div2_en ?
+ adf5355_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ adf5355_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = doubler_en ?
+ adf5355_regs_t::REFERENCE_DOUBLER_ENABLED :
+ adf5355_regs_t::REFERENCE_DOUBLER_DISABLED;
+ _regs.r_counter_10_bit = ref_div_factor;
+ UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0);
+
+ //-----------------------------------------------------------
+ //Set timeouts (code from ADI driver)
+ _regs.timeout = clamp<boost::uint16_t>(
+ static_cast<boost::uint16_t>(ceil(_pfd_freq / (20e3 * 30))), 1, 1023);
+ UHD_ASSERT_THROW((_regs.timeout & ((boost::uint16_t)~0x3FF)) == 0);
+ _regs.synth_lock_timeout =
+ static_cast<boost::uint8_t>(ceil((_pfd_freq * 2) / (100e3 * _regs.timeout)));
+ UHD_ASSERT_THROW((_regs.synth_lock_timeout & ((boost::uint16_t)~0x1F)) == 0);
+ _regs.auto_level_timeout =
+ static_cast<boost::uint8_t>(ceil((_pfd_freq * 5) / (100e3 * _regs.timeout)));
+
+ //-----------------------------------------------------------
+ //Set VCO band divider
+ _regs.vco_band_div =
+ static_cast<boost::uint8_t>(ceil(_pfd_freq / 2.4e6));
+
+ //-----------------------------------------------------------
+ //Set ADC delay (code from ADI driver)
+ _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED;
+ _regs.adc_conversion = adf5355_regs_t::ADC_CONVERSION_ENABLED;
+ _regs.adc_clock_divider = clamp<boost::uint8_t>(
+ static_cast<boost::uint8_t>(ceil(((_pfd_freq / 100e3) - 2) / 4)), 1, 255);
+ _wait_time_us = static_cast<boost::uint32_t>(
+ ceil(16e6 / (_pfd_freq / ((4 * _regs.adc_clock_divider) + 2))));
+
+ //-----------------------------------------------------------
+ //Phase resync
+ _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED; // Disabled during development
+ _regs.phase_adjust = adf5355_regs_t::PHASE_ADJUST_DISABLED;
+ _regs.sd_load_reset = adf5355_regs_t::SD_LOAD_RESET_ON_REG0_UPDATE;
+ _regs.phase_resync_clk_div = static_cast<boost::uint16_t>(
+ floor(ADF5355_PHASE_RESYNC_TIME * _pfd_freq));
+
+ _rewrite_regs = true;
+ }
+
+ void set_output_power(output_power_t power)
+ {
+ adf5355_regs_t::output_power_t setting;
+ switch (power) {
+ case OUTPUT_POWER_M4DBM: setting = adf5355_regs_t::OUTPUT_POWER_M4DBM; break;
+ case OUTPUT_POWER_M1DBM: setting = adf5355_regs_t::OUTPUT_POWER_M1DBM; break;
+ case OUTPUT_POWER_2DBM: setting = adf5355_regs_t::OUTPUT_POWER_2DBM; break;
+ case OUTPUT_POWER_5DBM: setting = adf5355_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(output_t output, bool enable) {
+
+ switch (output) {
+ case RF_OUTPUT_A: _regs.rf_out_a_enabled = enable ? adf5355_regs_t::RF_OUT_A_ENABLED_ENABLED :
+ adf5355_regs_t::RF_OUT_A_ENABLED_DISABLED;
+ break;
+ case RF_OUTPUT_B: _regs.rf_out_b_enabled = enable ? adf5355_regs_t::RF_OUT_B_ENABLED_ENABLED :
+ adf5355_regs_t::RF_OUT_B_ENABLED_DISABLED;
+ break;
+ }
+ }
+
+ void set_muxout_mode(muxout_t mode)
+ {
+ switch (mode) {
+ case MUXOUT_3STATE: _regs.muxout = adf5355_regs_t::MUXOUT_3STATE; break;
+ case MUXOUT_DVDD: _regs.muxout = adf5355_regs_t::MUXOUT_DVDD; break;
+ case MUXOUT_DGND: _regs.muxout = adf5355_regs_t::MUXOUT_DGND; break;
+ case MUXOUT_RDIV: _regs.muxout = adf5355_regs_t::MUXOUT_RDIV; break;
+ case MUXOUT_NDIV: _regs.muxout = adf5355_regs_t::MUXOUT_NDIV; break;
+ case MUXOUT_ALD: _regs.muxout = adf5355_regs_t::MUXOUT_ANALOG_LD; break;
+ case MUXOUT_DLD: _regs.muxout = adf5355_regs_t::MUXOUT_DLD; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+ double set_frequency(double target_freq, double freq_resolution, bool flush = false)
+ {
+ if (target_freq > ADF5355_MAX_OUT_FREQ or target_freq < ADF5355_MIN_OUT_FREQ) {
+ throw uhd::runtime_error("requested frequency out of range.");
+ }
+ if ((boost::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;
+ boost::uint32_t rf_divider = 1;
+ while (target_vco_freq < ADF5355_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;
+ }
+
+ double N = prescaler_input_freq / _pfd_freq;
+ boost::uint16_t INT = static_cast<boost::uint16_t>(floor(N));
+ boost::uint32_t FRAC1 = static_cast<boost::uint32_t>(floor((N - INT) * ADF5355_MOD1));
+ double residue = ADF5355_MOD1 * (N - (INT + FRAC1 / ADF5355_MOD1));
+
+ double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution));
+ boost::uint16_t MOD2 = static_cast<boost::uint16_t>(floor(_pfd_freq / gcd));
+
+ if (MOD2 > ADF5355_MAX_MOD2) {
+ MOD2 = ADF5355_MAX_MOD2;
+ }
+ boost::uint16_t FRAC2 = ceil(residue * MOD2);
+
+ double coerced_vco_freq = _pfd_freq * (
+ todbl(INT) + (
+ (todbl(FRAC1) +
+ (todbl(FRAC2) / todbl(MOD2)))
+ / todbl(ADF5355_MOD1)
+ )
+ );
+
+ 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 (_regs.int_16_bit >= ADF5355_MIN_INT_PRESCALER_89) {
+ _regs.prescaler = adf5355_regs_t::PRESCALER_8_9;
+ } else {
+ _regs.prescaler = adf5355_regs_t::PRESCALER_4_5;
+ }
+
+ // ADI: Tests have shown that the optimal bleed set is the following:
+ // 4/N < IBLEED/ICP < 10/N */
+/*
+ boost::uint32_t cp_curr_ua =
+ (static_cast<boost::uint32_t>(_regs.charge_pump_current) + 1) * 315;
+ _regs.cp_bleed_current = clamp<boost::uint8_t>(
+ ceil((todbl(400)*cp_curr_ua) / (_regs.int_16_bit*375)), 1, 255);
+ _regs.negative_bleed = adf5355_regs_t::NEGATIVE_BLEED_ENABLED;
+ _regs.gated_bleed = adf5355_regs_t::GATED_BLEED_DISABLED;
+*/
+
+ if (flush) commit();
+ return coerced_out_freq;
+ }
+
+ void commit()
+ {
+ if (_rewrite_regs) {
+ //For a full state sync write registers in reverse order 12 - 0
+ addr_vtr_t regs;
+ for (int addr = 12; addr >= 0; addr--) {
+ regs.push_back(_regs.get_reg(boost::uint32_t(addr)));
+ }
+ _write_fn(regs);
+ _rewrite_regs = false;
+
+ } else {
+ //Frequency update sequence from data sheet
+ static const size_t ONE_REG = 1;
+ _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)));
+ boost::this_thread::sleep(boost::posix_time::microsec(_wait_time_us));
+ _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED;
+ _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0)));
+ }
+ }
+
+private: //Members
+ typedef std::vector<boost::uint32_t> addr_vtr_t;
+
+ write_fn_t _write_fn;
+ adf5355_regs_t _regs;
+ bool _rewrite_regs;
+ boost::uint32_t _wait_time_us;
+ double _ref_freq;
+ double _pfd_freq;
+ double _fb_after_divider;
+};
+
+adf5355_iface::sptr adf5355_iface::make(write_fn_t write)
+{
+ return sptr(new adf5355_impl(write));
+}
diff --git a/host/lib/usrp/common/adf5355.hpp b/host/lib/usrp/common/adf5355.hpp
new file mode 100644
index 000000000..fb262cc9f
--- /dev/null
+++ b/host/lib/usrp/common/adf5355.hpp
@@ -0,0 +1,57 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_ADF5355_HPP
+#define INCLUDED_ADF5355_HPP
+
+#include <boost/function.hpp>
+#include <vector>
+
+class adf5355_iface
+{
+public:
+ typedef boost::shared_ptr<adf5355_iface> sptr;
+ typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn_t;
+
+ static sptr make(write_fn_t write);
+
+ virtual ~adf5355_iface() {}
+
+ 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_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(void) = 0;
+};
+
+#endif // INCLUDED_ADF5355_HPP
diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp
index 3d33e7d11..272e0e093 100644
--- a/host/lib/usrp/common/apply_corrections.cpp
+++ b/host/lib/usrp/common/apply_corrections.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011 Ettus Research LLC
+// Copyright 2011-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -144,6 +144,34 @@ static void apply_fe_corrections(
/***********************************************************************
* Wrapper routines with nice try/catch + print
**********************************************************************/
+void uhd::usrp::apply_tx_fe_corrections( //overloading to work according to rfnoc tree struct
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const uhd::fs_path db_path,
+ const uhd::fs_path tx_fe_corr_path,
+ const double lo_freq //actual lo freq
+){
+ boost::mutex::scoped_lock l(corrections_mutex);
+ try{
+ apply_fe_corrections(
+ sub_tree,
+ db_path + "/tx_eeprom",
+ tx_fe_corr_path + "/iq_balance/value",
+ "tx_iq_cal_v0.2_",
+ lo_freq
+ );
+ apply_fe_corrections(
+ sub_tree,
+ db_path + "/tx_eeprom",
+ tx_fe_corr_path + "/dc_offset/value",
+ "tx_dc_cal_v0.2_",
+ lo_freq
+ );
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl;
+ }
+}
+
void uhd::usrp::apply_tx_fe_corrections(
property_tree::sptr sub_tree, //starts at mboards/x
const std::string &slot, //name of dboard slot
@@ -171,6 +199,27 @@ void uhd::usrp::apply_tx_fe_corrections(
}
}
+void uhd::usrp::apply_rx_fe_corrections( //overloading to work according to rfnoc tree struct
+ property_tree::sptr sub_tree, //starts at mboards/x
+ const uhd::fs_path db_path,
+ const uhd::fs_path rx_fe_corr_path,
+ const double lo_freq //actual lo freq
+){
+ boost::mutex::scoped_lock l(corrections_mutex);
+ try{
+ apply_fe_corrections(
+ sub_tree,
+ db_path + "/rx_eeprom",
+ rx_fe_corr_path + "/iq_balance/value",
+ "rx_iq_cal_v0.2_",
+ lo_freq
+ );
+ }
+ catch(const std::exception &e){
+ UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl;
+ }
+}
+
void uhd::usrp::apply_rx_fe_corrections(
property_tree::sptr sub_tree, //starts at mboards/x
const std::string &slot, //name of dboard slot
diff --git a/host/lib/usrp/common/apply_corrections.hpp b/host/lib/usrp/common/apply_corrections.hpp
index c516862d1..0ab5377f3 100644
--- a/host/lib/usrp/common/apply_corrections.hpp
+++ b/host/lib/usrp/common/apply_corrections.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011 Ettus Research LLC
+// Copyright 2011-2016 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -26,16 +26,28 @@ namespace uhd{ namespace usrp{
void apply_tx_fe_corrections(
property_tree::sptr sub_tree, //starts at mboards/x
- const std::string &slot, //name of dboard slot
+ 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/usrp/common/constrained_device_args.hpp b/host/lib/usrp/common/constrained_device_args.hpp
new file mode 100644
index 000000000..1bfd1df00
--- /dev/null
+++ b/host/lib/usrp/common/constrained_device_args.hpp
@@ -0,0 +1,283 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP
+#define INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP
+
+#include <uhd/types/device_addr.hpp>
+#include <uhd/exception.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/assign/list_of.hpp>
+#include <vector>
+#include <string>
+
+namespace uhd {
+namespace usrp {
+
+ /*!
+ * constrained_device_args_t provides a base and utilities to
+ * map key=value pairs passed in through the device creation
+ * args interface (device_addr_t).
+ *
+ * Inherit from this class to create typed device specific
+ * arguments and use the base class methods to handle parsing
+ * the device_addr or any key=value string to populate the args
+ *
+ * This file contains a library of different types of args the
+ * the user can pass in. The library can be extended to support
+ * non-intrinsic types by the client.
+ *
+ */
+ class constrained_device_args_t {
+ public: //Types
+
+ /*!
+ * Base argument type. All other arguments inherit from this.
+ */
+ class generic_arg {
+ public:
+ generic_arg(const std::string& key): _key(key) {}
+ inline const std::string& key() const { return _key; }
+ inline virtual std::string to_string() const = 0;
+ private:
+ std::string _key;
+ };
+
+ /*!
+ * String argument type. Can be case sensitive or insensitive
+ */
+ template<bool case_sensitive>
+ class str_arg : public generic_arg {
+ public:
+ str_arg(const std::string& name, const std::string& default_value) :
+ generic_arg(name) { set(default_value); }
+
+ inline void set(const std::string& value) {
+ _value = case_sensitive ? value : boost::algorithm::to_lower_copy(value);
+ }
+ inline const std::string& get() const {
+ return _value;
+ }
+ inline void parse(const std::string& str_rep) {
+ set(str_rep);
+ }
+ inline virtual std::string to_string() const {
+ return key() + "=" + get();
+ }
+ inline bool operator==(const std::string& rhs) const {
+ return get() == boost::algorithm::to_lower_copy(rhs);
+ }
+ private:
+ std::string _value;
+ };
+ typedef str_arg<false> str_ci_arg;
+ typedef str_arg<true> str_cs_arg;
+
+ /*!
+ * Numeric argument type. The template type data_t allows the
+ * client to constrain the type of the number.
+ */
+ template<typename data_t>
+ class num_arg : public generic_arg {
+ public:
+ num_arg(const std::string& name, const data_t default_value) :
+ generic_arg(name) { set(default_value); }
+
+ inline void set(const data_t value) {
+ _value = value;
+ }
+ inline const data_t get() const {
+ return _value;
+ }
+ inline void parse(const std::string& str_rep) {
+ try {
+ _value = boost::lexical_cast<data_t>(str_rep);
+ } catch (std::exception& ex) {
+ throw uhd::value_error(str(boost::format(
+ "Error parsing numeric parameter %s: %s.") %
+ key() % ex.what()
+ ));
+ }
+ }
+ inline virtual std::string to_string() const {
+ return key() + "=" + boost::lexical_cast<std::string>(get());
+ }
+ private:
+ data_t _value;
+ };
+
+ /*!
+ * Enumeration argument type. The template type enum_t allows the
+ * client to use their own enum and specify a string mapping for
+ * the values of the enum
+ *
+ * NOTE: The constraint on enum_t is that the values must start with
+ * 0 and be sequential
+ */
+ template<typename enum_t>
+ class enum_arg : public generic_arg {
+ public:
+ enum_arg(
+ const std::string& name,
+ const enum_t default_value,
+ const std::vector<std::string>& values) :
+ generic_arg(name), _str_values(values)
+ { set(default_value); }
+
+ inline void set(const enum_t value) {
+ _value = value;
+ }
+ inline const enum_t get() const {
+ return _value;
+ }
+ inline void parse(const std::string& str_rep, bool assert_invalid = true) {
+ std::string valid_values_str;
+ for (size_t i = 0; i < _str_values.size(); i++) {
+ if (boost::algorithm::to_lower_copy(str_rep) ==
+ boost::algorithm::to_lower_copy(_str_values[i]))
+ {
+ valid_values_str += ((i==0)?"":", ") + _str_values[i];
+ set(static_cast<enum_t>(static_cast<int>(i)));
+ return;
+ }
+ }
+ //If we reach here then, the string enum value was invalid
+ if (assert_invalid) {
+ throw uhd::value_error(str(boost::format(
+ "Invalid device arg value: %s=%s (Valid: {%s})") %
+ key() % str_rep % valid_values_str
+ ));
+ }
+ }
+ inline virtual std::string to_string() const {
+ size_t index = static_cast<size_t>(static_cast<int>(_value));
+ UHD_ASSERT_THROW(index < _str_values.size());
+ return key() + "=" + _str_values[index];
+ }
+
+ private:
+ enum_t _value;
+ std::vector<std::string> _str_values;
+ };
+
+ /*!
+ * Boolean argument type.
+ */
+ class bool_arg : public generic_arg {
+ public:
+ bool_arg(const std::string& name, const bool default_value) :
+ generic_arg(name) { set(default_value); }
+
+ inline void set(const bool value) {
+ _value = value;
+ }
+ inline bool get() const {
+ return _value;
+ }
+ inline void parse(const std::string& str_rep) {
+ try {
+ _value = (boost::lexical_cast<int>(str_rep) != 0);
+ } catch (std::exception& ex) {
+ if (str_rep.empty()) {
+ //If str_rep is empty then the device_addr was set
+ //without a value which means that the user "set" the flag
+ _value = true;
+ } else if (boost::algorithm::to_lower_copy(str_rep) == "true" ||
+ boost::algorithm::to_lower_copy(str_rep) == "yes" ||
+ boost::algorithm::to_lower_copy(str_rep) == "y") {
+ _value = true;
+ } else if (boost::algorithm::to_lower_copy(str_rep) == "false" ||
+ boost::algorithm::to_lower_copy(str_rep) == "no" ||
+ boost::algorithm::to_lower_copy(str_rep) == "n") {
+ _value = false;
+ } else {
+ throw uhd::value_error(str(boost::format(
+ "Error parsing boolean parameter %s: %s.") %
+ key() % ex.what()
+ ));
+ }
+ }
+ }
+ inline virtual std::string to_string() const {
+ return key() + "=" + (get() ? "true" : "false");
+ }
+ private:
+ bool _value;
+ };
+
+ public: //Methods
+ constrained_device_args_t() {}
+ virtual ~constrained_device_args_t() {}
+
+ void parse(const std::string& str_args) {
+ device_addr_t dev_args(str_args);
+ _parse(dev_args);
+ }
+
+ void parse(const device_addr_t& dev_args) {
+ _parse(dev_args);
+ }
+
+ inline virtual std::string to_string() const = 0;
+
+ protected: //Methods
+ //Override _parse to provide an implementation to parse all
+ //client specific device args
+ virtual void _parse(const device_addr_t& dev_args) = 0;
+
+ /*!
+ * Utility: Ensure that the value of the device arg is between min and max
+ */
+ template<typename num_data_t>
+ static inline void _enforce_range(const num_arg<num_data_t>& arg, const num_data_t& min, const num_data_t& max) {
+ if (arg.get() > max || arg.get() < min) {
+ throw uhd::value_error(str(boost::format(
+ "Invalid device arg value: %s (Minimum: %s, Maximum: %s)") %
+ arg.to_string() %
+ boost::lexical_cast<std::string>(min) % boost::lexical_cast<std::string>(max)));
+ }
+ }
+
+ /*!
+ * Utility: Ensure that the value of the device arg is is contained in valid_values
+ */
+ template<typename arg_t, typename data_t>
+ static inline void _enforce_discrete(const arg_t& arg, const std::vector<data_t>& valid_values) {
+ bool match = false;
+ BOOST_FOREACH(const data_t& val, valid_values) {
+ if (val == arg.get()) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ std::string valid_values_str;
+ for (size_t i = 0; i < valid_values.size(); i++) {
+ valid_values_str += ((i==0)?"":", ") + boost::lexical_cast<std::string>(valid_values[i]);
+ throw uhd::value_error(str(boost::format(
+ "Invalid device arg value: %s (Valid: {%s})") %
+ arg.to_string() % valid_values_str
+ ));
+ }
+ }
+ }
+ };
+}} //namespaces
+
+#endif /* INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP */
diff --git a/host/lib/usrp/common/fw_comm_protocol.h b/host/lib/usrp/common/fw_comm_protocol.h
new file mode 100644
index 000000000..14adb33a9
--- /dev/null
+++ b/host/lib/usrp/common/fw_comm_protocol.h
@@ -0,0 +1,102 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_FW_COMM_PROTOCOL
+#define INCLUDED_FW_COMM_PROTOCOL
+
+#include <stdint.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+/*!
+ * Structs and constants for communication between firmware and host.
+ * This header is shared by the firmware and host code.
+ * Therefore, this header may only contain valid C code.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FW_COMM_PROTOCOL_SIGNATURE 0xACE3
+#define FW_COMM_PROTOCOL_VERSION 0
+#define FW_COMM_MAX_DATA_WORDS 16
+#define FW_COMM_PROTOCOL_MTU 256
+
+#define FW_COMM_FLAGS_ACK 0x00000001
+#define FW_COMM_FLAGS_CMD_MASK 0x00000FF0
+#define FW_COMM_FLAGS_ERROR_MASK 0xFF000000
+
+#define FW_COMM_CMD_ECHO 0x00000000
+#define FW_COMM_CMD_POKE32 0x00000010
+#define FW_COMM_CMD_PEEK32 0x00000020
+#define FW_COMM_CMD_BLOCK_POKE32 0x00000030
+#define FW_COMM_CMD_BLOCK_PEEK32 0x00000040
+
+#define FW_COMM_ERR_PKT_ERROR 0x80000000
+#define FW_COMM_ERR_CMD_ERROR 0x40000000
+#define FW_COMM_ERR_SIZE_ERROR 0x20000000
+
+#define FW_COMM_GENERATE_ID(prod) ((((uint32_t) FW_COMM_PROTOCOL_SIGNATURE) << 0) | \
+ (((uint32_t) prod) << 16) | \
+ (((uint32_t) FW_COMM_PROTOCOL_VERSION) << 24))
+
+#define FW_COMM_GET_PROTOCOL_SIG(id) ((uint16_t)(id & 0xFFFF))
+#define FW_COMM_GET_PRODUCT_ID(id) ((uint8_t)(id >> 16))
+#define FW_COMM_GET_PROTOCOL_VER(id) ((uint8_t)(id >> 24))
+
+typedef struct
+{
+ uint32_t id; //Protocol and device identifier
+ uint32_t flags; //Holds commands and ack messages
+ uint32_t sequence; //Sequence number (specific to FW communication transactions)
+ uint32_t data_words; //Number of data words in payload
+ uint32_t addr; //Address field for the command in flags
+ uint32_t data[FW_COMM_MAX_DATA_WORDS]; //Data field for the command in flags
+} fw_comm_pkt_t;
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+// The following definitions are only useful in firmware. Exclude in host code.
+#ifndef __cplusplus
+
+typedef void (*poke32_func)(const uint32_t addr, const uint32_t data);
+typedef uint32_t (*peek32_func)(const uint32_t addr);
+
+/*!
+ * Process a firmware communication packet and compute a response.
+ * Args:
+ * - (in) request: Pointer to the request struct
+ * - (out) response: Pointer to the response struct
+ * - (in) product_id: The 8-bit usrp3 specific product ID (for request filtering)
+ * - (func) poke_callback, peek_callback: Callback functions for a single peek/poke
+ * - return value: Send a response packet
+ */
+bool process_fw_comm_protocol_pkt(
+ const fw_comm_pkt_t* request,
+ fw_comm_pkt_t* response,
+ uint8_t product_id,
+ uint32_t iface_id,
+ poke32_func poke_callback,
+ peek32_func peek_callback
+);
+
+#endif //ifdef __cplusplus
+
+#endif /* INCLUDED_FW_COMM_PROTOCOL */
diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp
new file mode 100644
index 000000000..ef541e37f
--- /dev/null
+++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp
@@ -0,0 +1,246 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp3_fw_ctrl_iface.hpp"
+
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <boost/format.hpp>
+#include <boost/asio.hpp> //used for htonl and ntohl
+#include <boost/foreach.hpp>
+#include "fw_comm_protocol.h"
+
+namespace uhd { namespace usrp { namespace usrp3 {
+
+//----------------------------------------------------------
+// Factory method
+//----------------------------------------------------------
+uhd::wb_iface::sptr usrp3_fw_ctrl_iface::make(
+ uhd::transport::udp_simple::sptr udp_xport,
+ const boost::uint16_t product_id,
+ const bool verbose)
+{
+ return wb_iface::sptr(new usrp3_fw_ctrl_iface(udp_xport, product_id, verbose));
+}
+
+//----------------------------------------------------------
+// udp_fw_ctrl_iface
+//----------------------------------------------------------
+
+usrp3_fw_ctrl_iface::usrp3_fw_ctrl_iface(
+ uhd::transport::udp_simple::sptr udp_xport,
+ const boost::uint16_t product_id,
+ const bool verbose) :
+ _product_id(product_id), _verbose(verbose), _udp_xport(udp_xport),
+ _seq_num(0)
+{
+ flush();
+ peek32(0);
+}
+
+usrp3_fw_ctrl_iface::~usrp3_fw_ctrl_iface()
+{
+ flush();
+}
+
+void usrp3_fw_ctrl_iface::flush()
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ _flush();
+}
+
+void usrp3_fw_ctrl_iface::poke32(const wb_addr_type addr, const boost::uint32_t data)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ for (size_t i = 1; i <= NUM_RETRIES; i++) {
+ try {
+ _poke32(addr, data);
+ return;
+ } catch(const std::exception &ex) {
+ const std::string error_msg = str(boost::format(
+ "udp fw poke32 failure #%u\n%s") % i % ex.what());
+ if (_verbose) UHD_MSG(warning) << error_msg << std::endl;
+ if (i == NUM_RETRIES) throw uhd::io_error(error_msg);
+ }
+ }
+}
+
+boost::uint32_t usrp3_fw_ctrl_iface::peek32(const wb_addr_type addr)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ for (size_t i = 1; i <= NUM_RETRIES; i++) {
+ try {
+ return _peek32(addr);
+ } catch(const std::exception &ex) {
+ const std::string error_msg = str(boost::format(
+ "udp fw peek32 failure #%u\n%s") % i % ex.what());
+ if (_verbose) UHD_MSG(warning) << error_msg << std::endl;
+ if (i == NUM_RETRIES) throw uhd::io_error(error_msg);
+ }
+ }
+ return 0;
+}
+
+void usrp3_fw_ctrl_iface::_poke32(const wb_addr_type addr, const boost::uint32_t data)
+{
+ //Load request struct
+ fw_comm_pkt_t request;
+ request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id));
+ request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_POKE32);
+ request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++);
+ request.addr = uhd::htonx(addr);
+ request.data_words = 1;
+ request.data[0] = uhd::htonx(data);
+
+ //Send request
+ _flush();
+ _udp_xport->send(boost::asio::buffer(&request, sizeof(request)));
+
+ //Recv reply
+ fw_comm_pkt_t reply;
+ const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0);
+ if (nbytes == 0) throw uhd::io_error("udp fw poke32 - reply timed out");
+
+ //Sanity checks
+ const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags);
+ UHD_ASSERT_THROW(nbytes == sizeof(reply));
+ UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK));
+ UHD_ASSERT_THROW(flags & FW_COMM_CMD_POKE32);
+ UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK);
+ UHD_ASSERT_THROW(reply.sequence == request.sequence);
+ UHD_ASSERT_THROW(reply.addr == request.addr);
+ UHD_ASSERT_THROW(reply.data[0] == request.data[0]);
+}
+
+boost::uint32_t usrp3_fw_ctrl_iface::_peek32(const wb_addr_type addr)
+{
+ //Load request struct
+ fw_comm_pkt_t request;
+ request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id));
+ request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_PEEK32);
+ request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++);
+ request.addr = uhd::htonx(addr);
+ request.data_words = 1;
+ request.data[0] = 0;
+
+ //Send request
+ _flush();
+ _udp_xport->send(boost::asio::buffer(&request, sizeof(request)));
+
+ //Recv reply
+ fw_comm_pkt_t reply;
+ const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0);
+ if (nbytes == 0) throw uhd::io_error("udp fw peek32 - reply timed out");
+
+ //Sanity checks
+ const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags);
+ UHD_ASSERT_THROW(nbytes == sizeof(reply));
+ UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK));
+ UHD_ASSERT_THROW(flags & FW_COMM_CMD_PEEK32);
+ UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK);
+ UHD_ASSERT_THROW(reply.sequence == request.sequence);
+ UHD_ASSERT_THROW(reply.addr == request.addr);
+
+ //return result!
+ return uhd::ntohx<boost::uint32_t>(reply.data[0]);
+}
+
+void usrp3_fw_ctrl_iface::_flush(void)
+{
+ char buff[FW_COMM_PROTOCOL_MTU] = {};
+ while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) {
+ /*NOP*/
+ }
+}
+
+std::vector<std::string> usrp3_fw_ctrl_iface::discover_devices(
+ const std::string& addr_hint, const std::string& port,
+ boost::uint16_t product_id)
+{
+ std::vector<std::string> addrs;
+
+ //Create a UDP transport to communicate:
+ //Some devices will cause a throw when opened for a broadcast address.
+ //We print and recover so the caller can loop through all bcast addrs.
+ uhd::transport::udp_simple::sptr udp_bcast_xport;
+ try {
+ udp_bcast_xport = uhd::transport::udp_simple::make_broadcast(addr_hint, port);
+ } catch(const std::exception &e) {
+ UHD_MSG(error) << boost::format("Cannot open UDP transport on %s for discovery\n%s")
+ % addr_hint % e.what() << std::endl;
+ return addrs;
+ }
+
+ //Send dummy request
+ fw_comm_pkt_t request;
+ request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id));
+ request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO);
+ request.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ udp_bcast_xport->send(boost::asio::buffer(&request, sizeof(request)));
+
+ //loop for replies until timeout
+ while (true) {
+ char buff[FW_COMM_PROTOCOL_MTU] = {};
+ const size_t nbytes = udp_bcast_xport->recv(boost::asio::buffer(buff), 0.050);
+ if (nbytes != sizeof(fw_comm_pkt_t)) break; //No more responses or responses are invalid
+
+ const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff;
+ if (request.id == reply->id &&
+ request.flags == reply->flags &&
+ request.sequence == reply->sequence)
+ {
+ addrs.push_back(udp_bcast_xport->get_recv_addr());
+ }
+ }
+
+ return addrs;
+}
+
+boost::uint32_t usrp3_fw_ctrl_iface::get_iface_id(
+ const std::string& addr, const std::string& port,
+ boost::uint16_t product_id)
+{
+ uhd::transport::udp_simple::sptr udp_xport =
+ uhd::transport::udp_simple::make_connected(addr, port);
+
+ //Send dummy request
+ fw_comm_pkt_t request;
+ request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id));
+ request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO);
+ request.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ udp_xport->send(boost::asio::buffer(&request, sizeof(request)));
+
+ //loop for replies until timeout
+ char buff[FW_COMM_PROTOCOL_MTU] = {};
+ const size_t nbytes = udp_xport->recv(boost::asio::buffer(buff), 1.0);
+
+ const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff;
+ if (nbytes > 0 &&
+ request.id == reply->id &&
+ request.flags == reply->flags &&
+ request.sequence == reply->sequence)
+ {
+ return uhd::ntohx<boost::uint32_t>(reply->data[0]);
+ } else {
+ throw uhd::io_error("udp get_iface_id - bad response");
+ }
+}
+
+}}} //namespace
diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp
new file mode 100644
index 000000000..33286861b
--- /dev/null
+++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp
@@ -0,0 +1,72 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_USRP3_UDP_FW_CTRL_IFACE_HPP
+#define INCLUDED_LIBUHD_USRP_USRP3_UDP_FW_CTRL_IFACE_HPP
+
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <boost/thread/mutex.hpp>
+#include <vector>
+
+namespace uhd { namespace usrp { namespace usrp3 {
+
+class usrp3_fw_ctrl_iface : public uhd::wb_iface
+{
+public:
+ usrp3_fw_ctrl_iface(
+ uhd::transport::udp_simple::sptr udp_xport,
+ const boost::uint16_t product_id,
+ const bool verbose);
+ virtual ~usrp3_fw_ctrl_iface();
+
+ // -- uhd::wb_iface --
+ void poke32(const wb_addr_type addr, const boost::uint32_t data);
+ boost::uint32_t peek32(const wb_addr_type addr);
+ void flush();
+
+ static uhd::wb_iface::sptr make(
+ uhd::transport::udp_simple::sptr udp_xport,
+ const boost::uint16_t product_id,
+ const bool verbose = true);
+ // -- uhd::wb_iface --
+
+ static std::vector<std::string> discover_devices(
+ const std::string& addr_hint, const std::string& port,
+ boost::uint16_t product_id);
+
+ static boost::uint32_t get_iface_id(
+ const std::string& addr, const std::string& port,
+ boost::uint16_t product_id);
+
+private:
+ void _poke32(const wb_addr_type addr, const boost::uint32_t data);
+ boost::uint32_t _peek32(const wb_addr_type addr);
+ void _flush(void);
+
+ const boost::uint16_t _product_id;
+ const bool _verbose;
+ uhd::transport::udp_simple::sptr _udp_xport;
+ boost::uint32_t _seq_num;
+ boost::mutex _mutex;
+
+ static const size_t NUM_RETRIES = 3;
+};
+
+}}} //namespace
+
+#endif //INCLUDED_LIBUHD_USRP_USRP3_USRP3_UDP_FW_CTRL_HPP