aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp')
-rw-r--r--host/lib/usrp/CMakeLists.txt13
-rw-r--r--host/lib/usrp/b200/CMakeLists.txt1
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp4
-rw-r--r--host/lib/usrp/b200/b200_iface.hpp2
-rw-r--r--host/lib/usrp/b200/b200_image_loader.cpp125
-rw-r--r--host/lib/usrp/b200/b200_impl.cpp346
-rw-r--r--host/lib/usrp/b200/b200_impl.hpp103
-rw-r--r--host/lib/usrp/b200/b200_io_impl.cpp161
-rw-r--r--host/lib/usrp/b200/b200_regs.hpp2
-rw-r--r--host/lib/usrp/common/CMakeLists.txt1
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.cpp78
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.hpp69
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_client.h15
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.cpp1009
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_device.h143
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h15
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h177
-rw-r--r--host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h43
-rw-r--r--host/lib/usrp/common/ad936x_manager.cpp280
-rw-r--r--host/lib/usrp/common/ad936x_manager.hpp132
-rw-r--r--host/lib/usrp/cores/gpio_core_200.hpp26
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.cpp1
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.cpp21
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.hpp10
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.cpp21
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.hpp9
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.cpp21
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.hpp8
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.cpp16
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.hpp8
-rw-r--r--host/lib/usrp/dboard/db_cbx.cpp2
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.hpp4
-rw-r--r--host/lib/usrp/dboard_eeprom.cpp27
-rw-r--r--host/lib/usrp/e100/e100_impl.cpp34
-rw-r--r--host/lib/usrp/e100/e100_impl.hpp4
-rw-r--r--host/lib/usrp/e100/fpga_downloader.cpp41
-rw-r--r--host/lib/usrp/e300/CMakeLists.txt12
-rw-r--r--host/lib/usrp/e300/e300_async_serial.cpp245
-rw-r--r--host/lib/usrp/e300/e300_async_serial.hpp113
-rw-r--r--host/lib/usrp/e300/e300_common.cpp35
-rw-r--r--host/lib/usrp/e300/e300_defaults.hpp9
-rw-r--r--host/lib/usrp/e300/e300_fpga_defs.hpp2
-rw-r--r--host/lib/usrp/e300/e300_impl.cpp363
-rw-r--r--host/lib/usrp/e300/e300_impl.hpp27
-rw-r--r--host/lib/usrp/e300/e300_io_impl.cpp12
-rw-r--r--host/lib/usrp/e300/e300_network.cpp34
-rw-r--r--host/lib/usrp/e300/e300_remote_codec_ctrl.cpp110
-rw-r--r--host/lib/usrp/e300/e300_remote_codec_ctrl.hpp14
-rw-r--r--host/lib/usrp/e300/e300_sensor_manager.cpp141
-rw-r--r--host/lib/usrp/e300/e300_sensor_manager.hpp9
-rw-r--r--host/lib/usrp/e300/e300_ublox_control.hpp50
-rw-r--r--host/lib/usrp/e300/e300_ublox_control_impl.cpp505
-rw-r--r--host/lib/usrp/e300/e300_ublox_control_impl.hpp457
-rw-r--r--host/lib/usrp/gpsd_iface.cpp309
-rw-r--r--host/lib/usrp/gpsd_iface.hpp36
-rw-r--r--host/lib/usrp/mboard_eeprom.cpp43
-rw-r--r--host/lib/usrp/multi_usrp.cpp190
-rw-r--r--host/lib/usrp/usrp2/CMakeLists.txt15
-rw-r--r--host/lib/usrp/usrp2/n200_image_loader.cpp616
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp10
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.cpp2
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp3
-rw-r--r--host/lib/usrp/x300/CMakeLists.txt5
-rw-r--r--host/lib/usrp/x300/cdecode.c80
-rw-r--r--host/lib/usrp/x300/cdecode.h36
-rw-r--r--host/lib/usrp/x300/x300_adc_ctrl.cpp4
-rw-r--r--host/lib/usrp/x300/x300_adc_dac_utils.cpp412
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.cpp238
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.hpp18
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.cpp16
-rw-r--r--host/lib/usrp/x300/x300_fw_common.h7
-rw-r--r--host/lib/usrp/x300/x300_image_loader.cpp402
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp520
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp63
-rw-r--r--host/lib/usrp/x300/x300_io_impl.cpp52
-rw-r--r--host/lib/usrp/x300/x300_regs.hpp169
76 files changed, 5745 insertions, 2611 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
index f6788b5ef..ce913aaf6 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -18,6 +18,10 @@
########################################################################
# This file included, use CMake directory variables
########################################################################
+find_package(GPSD)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/dboard_base.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dboard_eeprom.cpp
@@ -30,6 +34,15 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp
)
+LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF)
+
+IF(ENABLE_GPSD)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.cpp
+ )
+ LIBUHD_APPEND_LIBS(${LIBGPS_LIBRARY})
+ENDIF(ENABLE_GPSD)
+
INCLUDE_SUBDIRECTORY(cores)
INCLUDE_SUBDIRECTORY(dboard)
INCLUDE_SUBDIRECTORY(common)
diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt
index ce89b5d80..cd8ebcba7 100644
--- a/host/lib/usrp/b200/CMakeLists.txt
+++ b/host/lib/usrp/b200/CMakeLists.txt
@@ -26,6 +26,7 @@ LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)
IF(ENABLE_B200)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp
diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp
index 270d3bb4b..4754a6357 100644
--- a/host/lib/usrp/b200/b200_iface.cpp
+++ b/host/lib/usrp/b200/b200_iface.cpp
@@ -511,7 +511,7 @@ public:
throw uhd::io_error((boost::format("Short write on set FPGA hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str());
}
- boost::uint32_t load_fpga(const std::string filestring) {
+ boost::uint32_t load_fpga(const std::string filestring, bool force) {
boost::uint8_t fx3_state = 0;
boost::uint32_t wait_count;
@@ -522,7 +522,7 @@ public:
hash_type hash = generate_hash(filename);
hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
- if (hash == loaded_hash) return 0;
+ if (hash == loaded_hash and !force) return 0;
// Establish default largest possible control request transfer size based on operating USB speed
int transfer_size = VREQ_DEFAULT_SIZE;
diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp
index 1d123439a..0c7ee6b9e 100644
--- a/host/lib/usrp/b200/b200_iface.hpp
+++ b/host/lib/usrp/b200/b200_iface.hpp
@@ -97,7 +97,7 @@ public:
virtual void set_fpga_reset_pin(const bool reset) = 0;
//! load an FPGA image
- virtual boost::uint32_t load_fpga(const std::string filestring) = 0;
+ virtual boost::uint32_t load_fpga(const std::string filestring, bool force=false) = 0;
virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0;
diff --git a/host/lib/usrp/b200/b200_image_loader.cpp b/host/lib/usrp/b200/b200_image_loader.cpp
new file mode 100644
index 000000000..87010244c
--- /dev/null
+++ b/host/lib/usrp/b200/b200_image_loader.cpp
@@ -0,0 +1,125 @@
+//
+// Copyright 2014-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/>.
+//
+
+#include <boost/assign.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "b200_iface.hpp"
+#include "b200_impl.hpp"
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+namespace uhd{
+
+static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t &image_loader_args,
+ mboard_eeprom_t &mb_eeprom,
+ bool user_specified){
+
+ std::vector<usb_device_handle::sptr> dev_handles = get_b200_device_handles(image_loader_args.args);
+ b200_iface::sptr iface;
+
+ if(dev_handles.size() > 0){
+ BOOST_FOREACH(usb_device_handle::sptr dev_handle, dev_handles){
+ if(dev_handle->firmware_loaded()){
+ iface = b200_iface::make(usb_control::make(dev_handle,0));
+ mb_eeprom = mboard_eeprom_t(*iface, "B200");
+ if(user_specified){
+ if(image_loader_args.args.has_key("serial") and
+ mb_eeprom.get("serial") != image_loader_args.args.get("serial")){
+ continue;
+ }
+ if(image_loader_args.args.has_key("name") and
+ mb_eeprom.get("name") != image_loader_args.args.get("name")){
+ continue;
+ }
+ return iface;
+ }
+ else return iface; // Just return first found
+ }
+ }
+ }
+
+ // No applicable devices found, return empty sptr so we can exit
+ iface.reset();
+ mb_eeprom = mboard_eeprom_t();
+ return iface;
+}
+
+static bool b200_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ if(!image_loader_args.load_fpga)
+ return false;
+
+ bool user_specified = (image_loader_args.args.has_key("serial") or
+ image_loader_args.args.has_key("name"));
+
+ // See if a B2x0 with the given args is found
+ mboard_eeprom_t mb_eeprom;
+ b200_iface::sptr iface = get_b200_iface(image_loader_args, mb_eeprom, user_specified);
+ if(!iface) return false; // No initialized B2x0 found
+
+ std::string fpga_path;
+ if(image_loader_args.fpga_path == ""){
+ /*
+ * Normally, we can auto-generate the FPGA filename from what's in the EEPROM,
+ * but if the applicable value is not in the EEPROM, the user must give a specific
+ * filename for us to use.
+ */
+ std::string product = mb_eeprom.get("product");
+ if(not B2X0_PRODUCT_ID.has_key(boost::lexical_cast<boost::uint16_t>(product))){
+ if(user_specified){
+ // The user specified a bad device but expects us to know what it is
+ throw uhd::runtime_error("Could not determine model. You must manually specify an FPGA image filename.");
+ }
+ else{
+ return false;
+ }
+ }
+ else{
+ fpga_path = find_image_path(B2X0_FPGA_FILE_NAME.get(get_b200_type(mb_eeprom)));
+ }
+ }
+ else fpga_path = image_loader_args.fpga_path;
+
+ std::cout << boost::format("Unit: USRP %s (%s)")
+ % B2X0_STR_NAMES.get(get_b200_type(mb_eeprom), "B2XX")
+ % mb_eeprom.get("serial")
+ << std::endl;
+
+ iface->load_fpga(fpga_path, true);
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_b200_image_loader){
+ std::string recovery_instructions = "This device is likely in an unusable state. Power-cycle the\n"
+ "device, and the firmware/FPGA will be reloaded the next time\n"
+ "UHD uses the device.";
+
+ image_loader::register_image_loader("b200", b200_image_loader, recovery_instructions);
+}
+
+} /* namespace uhd */
diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp
index 0409adf30..f5129bc24 100644
--- a/host/lib/usrp/b200/b200_impl.cpp
+++ b/host/lib/usrp/b200/b200_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012-2014 Ettus Research LLC
+// Copyright 2012-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
@@ -37,6 +37,8 @@
#include <ctime>
#include <cmath>
+#include "../../transport/libusb1_base.hpp"
+
using namespace uhd;
using namespace uhd::usrp;
using namespace uhd::transport;
@@ -76,7 +78,7 @@ public:
//! Look up the type of B-Series device we're currently running.
// If the product ID stored in mb_eeprom is invalid, throws a
// uhd::runtime_error.
-static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
+b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
{
if (mb_eeprom["product"].empty()) {
throw uhd::runtime_error("B200: Missing product ID on EEPROM.");
@@ -91,6 +93,21 @@ static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
return B2X0_PRODUCT_ID[product_id];
}
+std::vector<usb_device_handle::sptr> get_b200_device_handles(const device_addr_t &hint)
+{
+ std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;
+
+ if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")),
+ uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid"))));
+ } else {
+ vid_pid_pair_list = b200_vid_pid_pairs;
+ }
+
+ //find the usrps and load firmware
+ return usb_device_handle::get_device_list(vid_pid_pair_list);
+}
+
static device_addrs_t b200_find(const device_addr_t &hint)
{
device_addrs_t b200_addrs;
@@ -104,25 +121,13 @@ static device_addrs_t b200_find(const device_addr_t &hint)
if (hint_i.has_key("addr") || hint_i.has_key("resource")) return b200_addrs;
}
- boost::uint16_t vid, pid;
-
- if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
- vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid"));
- pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid"));
- } else {
- vid = B200_VENDOR_ID;
- pid = B200_PRODUCT_ID;
- }
-
// Important note:
// The get device list calls are nested inside the for loop.
// This allows the usb guts to decontruct when not in use,
// so that re-enumeration after fw load can occur successfully.
// This requirement is a courtesy of libusb1.0 on windows.
-
- //find the usrps and load firmware
size_t found = 0;
- BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) {
+ BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint)) {
//extract the firmware path for the b200
std::string b200_fw_image;
try{
@@ -153,7 +158,7 @@ static device_addrs_t b200_find(const device_addr_t &hint)
//search for the device until found or timeout
while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0)
{
- BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid))
+ BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint))
{
usb_control::sptr control;
try{control = usb_control::make(handle, 0);}
@@ -191,7 +196,24 @@ static device_addrs_t b200_find(const device_addr_t &hint)
**********************************************************************/
static device::sptr b200_make(const device_addr_t &device_addr)
{
- return device::sptr(new b200_impl(device_addr));
+ uhd::transport::usb_device_handle::sptr handle;
+
+ // We try twice, because the first time, the link might be in a bad state
+ // and we might need to reset the link, but if that didn't help, trying
+ // a third time is pointless.
+ try {
+ return device::sptr(new b200_impl(device_addr, handle));
+ }
+ catch (const uhd::usb_error &e) {
+ libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle(
+ boost::static_pointer_cast<libusb::special_handle>(handle)->get_device()
+ ));
+ dev_handle->clear_endpoints(B200_USB_CTRL_RECV_ENDPOINT, B200_USB_CTRL_SEND_ENDPOINT);
+ dev_handle->clear_endpoints(B200_USB_DATA_RECV_ENDPOINT, B200_USB_DATA_SEND_ENDPOINT);
+ dev_handle->reset_device();
+ }
+
+ return device::sptr(new b200_impl(device_addr, handle));
}
UHD_STATIC_BLOCK(register_b200_device)
@@ -202,7 +224,7 @@ UHD_STATIC_BLOCK(register_b200_device)
/***********************************************************************
* Structors
**********************************************************************/
-b200_impl::b200_impl(const device_addr_t &device_addr) :
+b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::sptr &handle) :
_revision(0),
_tick_rate(0.0) // Forces a clock initialization at startup
{
@@ -213,16 +235,52 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
//try to match the given device address with something on the USB bus
boost::uint16_t vid = B200_VENDOR_ID;
boost::uint16_t pid = B200_PRODUCT_ID;
+ bool specified_vid = false;
+ bool specified_pid = false;
+
if (device_addr.has_key("vid"))
+ {
vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid"));
+ specified_vid = true;
+ }
+
if (device_addr.has_key("pid"))
+ {
pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid"));
+ specified_pid = true;
+ }
+
+ std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//search list for devices.
+
+ // Search only for specified VID and PID if both specified
+ if (specified_vid && specified_pid)
+ {
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,pid));
+ }
+ // Search for all supported PIDs limited to specified VID if only VID specified
+ else if (specified_vid)
+ {
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B200_PRODUCT_ID));
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B200_PRODUCT_NI_ID));
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B210_PRODUCT_NI_ID));
+ }
+ // Search for all supported VIDs limited to specified PID if only PID specified
+ else if (specified_pid)
+ {
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,pid));
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,pid));
+ }
+ // Search for all supported devices if neither VID nor PID specified
+ else
+ {
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,B200_PRODUCT_ID));
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,B200_PRODUCT_NI_ID));
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,B210_PRODUCT_NI_ID));
+ }
- std::vector<usb_device_handle::sptr> device_list =
- usb_device_handle::get_device_list(vid, pid);
+ std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid_pid_pair_list);
//locate the matching handle in the device list
- usb_device_handle::sptr handle;
BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) {
if (dev_handle->get_serial() == device_addr["serial"]){
handle = dev_handle;
@@ -320,10 +378,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
ctrl_xport_args["send_frame_size"] = min_frame_size;
ctrl_xport_args["num_send_frames"] = "16";
+ // This may throw a uhd::usb_error, which will be caught by b200_make().
_ctrl_transport = usb_zero_copy::make(
handle,
- 4, 8, //interface, endpoint
- 3, 4, //interface, endpoint
+ B200_USB_CTRL_RECV_INTERFACE, B200_USB_CTRL_RECV_ENDPOINT, //interface, endpoint
+ B200_USB_CTRL_SEND_INTERFACE, B200_USB_CTRL_SEND_ENDPOINT, //interface, endpoint
ctrl_xport_args
);
while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport
@@ -402,10 +461,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "8192");
data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16");
+ // This may throw a uhd::usb_error, which will be caught by b200_make().
_data_transport = usb_zero_copy::make(
handle, // identifier
- 2, 6, // IN interface, endpoint
- 1, 2, // OUT interface, endpoint
+ B200_USB_DATA_RECV_INTERFACE, B200_USB_DATA_RECV_ENDPOINT, //interface, endpoint
+ B200_USB_DATA_SEND_INTERFACE, B200_USB_DATA_SEND_ENDPOINT, //interface, endpoint
data_xport_args // param hints
);
while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport
@@ -447,6 +507,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
.publish(boost::bind(&b200_impl::get_tick_rate, this))
.subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1));
_tree->create<time_spec_t>(mb_path / "time" / "cmd");
+ _tree->create<bool>(mb_path / "auto_tick_rate").set(false);
////////////////////////////////////////////////////////////////////
// and do the misc mboard sensors
@@ -477,15 +538,17 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
UHD_ASSERT_THROW(num_radio_chains > 0);
UHD_ASSERT_THROW(num_radio_chains <= 2);
_radio_perifs.resize(num_radio_chains);
- for (size_t i = 0; i < _radio_perifs.size(); i++) this->setup_radio(i);
+ _codec_mgr = ad936x_manager::make(_codec_ctrl, num_radio_chains);
+ _codec_mgr->init_codec();
+ for (size_t i = 0; i < _radio_perifs.size(); i++)
+ this->setup_radio(i);
+
//now test each radio module's connection to the codec interface
- _codec_ctrl->data_port_loopback(true);
BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
{
- this->codec_loopback_self_test(perif.ctrl);
+ _codec_mgr->loopback_self_test(perif.ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK);
}
- _codec_ctrl->data_port_loopback(false);
//register time now and pps onto available radio cores
_tree->create<time_spec_t>(mb_path / "time" / "now")
@@ -512,6 +575,19 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
_tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources);
////////////////////////////////////////////////////////////////////
+ // front panel gpio
+ ////////////////////////////////////////////////////////////////////
+ _radio_perifs[0].fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO);
+ BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)
+ {
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)
+ .set(0)
+ .subscribe(boost::bind(&b200_impl::set_fp_gpio, this, _radio_perifs[0].fp_gpio, attr.first, _1));
+ }
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK")
+ .publish(boost::bind(&b200_impl::get_fp_gpio, this, _radio_perifs[0].fp_gpio));
+
+ ////////////////////////////////////////////////////////////////////
// dboard eeproms but not really
////////////////////////////////////////////////////////////////////
dboard_eeprom_t db_eeprom;
@@ -524,7 +600,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
////////////////////////////////////////////////////////////////////
//init the clock rate to something reasonable
- double default_tick_rate = device_addr.cast<double>("master_clock_rate", B200_DEFAULT_TICK_RATE);
+ double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE);
_tree->access<double>(mb_path / "tick_rate").set(default_tick_rate);
//subdev spec contains full width of selections
@@ -546,8 +622,13 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
// Set the DSP chains to some safe value
for (size_t i = 0; i < _radio_perifs.size(); i++) {
- _radio_perifs[i].ddc->set_host_rate(default_tick_rate / B200_DEFAULT_DECIM);
- _radio_perifs[i].duc->set_host_rate(default_tick_rate / B200_DEFAULT_INTERP);
+ _radio_perifs[i].ddc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_DECIM);
+ _radio_perifs[i].duc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_INTERP);
+ }
+ // We can automatically choose a master clock rate, but not if the user specifies one
+ _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate"));
+ if (not device_addr.has_key("master_clock_rate")) {
+ UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl;
}
//GPS installed: use external ref, time, and init time spec
@@ -585,10 +666,18 @@ void b200_impl::setup_radio(const size_t dspno)
const fs_path mb_path = "/mboards/0";
////////////////////////////////////////////////////////////////////
+ // Set up transport
+ ////////////////////////////////////////////////////////////////////
+ const boost::uint32_t sid = (dspno == 0) ? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID;
+
+ ////////////////////////////////////////////////////////////////////
// radio control
////////////////////////////////////////////////////////////////////
- const boost::uint32_t sid = (dspno == 0)? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID;
- perif.ctrl = radio_ctrl_core_3000::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, sid);
+ perif.ctrl = radio_ctrl_core_3000::make(
+ false/*lilE*/,
+ _ctrl_transport,
+ zero_copy_if::sptr()/*null*/,
+ sid);
perif.ctrl->hold_task(_async_task);
_async_task_data->radio_ctrl[dspno] = perif.ctrl; //weak
_tree->access<time_spec_t>(mb_path / "time" / "cmd")
@@ -596,121 +685,96 @@ void b200_impl::setup_radio(const size_t dspno)
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1));
this->register_loopback_self_test(perif.ctrl);
- perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR));
////////////////////////////////////////////////////////////////////
- // create rx dsp control objects
+ // Set up peripherals
////////////////////////////////////////////////////////////////////
+ perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR));
+ // create rx dsp control objects
perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));
perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP), true /*is_b200?*/);
perif.ddc->set_link_rate(10e9/8); //whatever
perif.ddc->set_mux("IQ", false, dspno == 1 ? true : false, dspno == 1 ? true : false);
+ perif.ddc->set_freq(rx_dsp_core_3000::DEFAULT_CORDIC_FREQ);
+ perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
+ perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
+ perif.duc->set_link_rate(10e9/8); //whatever
+ perif.duc->set_freq(tx_dsp_core_3000::DEFAULT_CORDIC_FREQ);
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time_core_3000::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_now = RB64_TIME_NOW;
+ time64_rb_bases.rb_pps = RB64_TIME_PPS;
+ perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+
+ ////////////////////////////////////////////////////////////////////
+ // connect rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1))
.subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1));
const fs_path rx_dsp_path = mb_path / "rx_dsps" / dspno;
- _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range")
- .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc));
- _tree->create<double>(rx_dsp_path / "rate" / "value")
- .set(0.0) // We can only load a sensible value after the tick rate was set
- .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1))
+ perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path));
+ _tree->access<double>(rx_dsp_path / "rate" / "value")
+ .coerce(boost::bind(&b200_impl::coerce_rx_samp_rate, this, perif.ddc, dspno, _1))
.subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1))
;
- _tree->create<double>(rx_dsp_path / "freq" / "value")
- .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1))
- .set(0.0);
- _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range")
- .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc));
_tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
.subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));
////////////////////////////////////////////////////////////////////
// create tx dsp control objects
////////////////////////////////////////////////////////////////////
- perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
- perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
- perif.duc->set_link_rate(10e9/8); //whatever
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))
.subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));
const fs_path tx_dsp_path = mb_path / "tx_dsps" / dspno;
- _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range")
- .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc));
- _tree->create<double>(tx_dsp_path / "rate" / "value")
- .set(0.0) // We can only load a sensible value after the tick rate was set
- .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1))
+ perif.duc->populate_subtree(_tree->subtree(tx_dsp_path));
+ _tree->access<double>(tx_dsp_path / "rate" / "value")
+ .coerce(boost::bind(&b200_impl::coerce_tx_samp_rate, this, perif.duc, dspno, _1))
.subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1))
;
- _tree->create<double>(tx_dsp_path / "freq" / "value")
- .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1))
- .set(0.0);
- _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range")
- .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc));
-
- ////////////////////////////////////////////////////////////////////
- // create time control objects
- ////////////////////////////////////////////////////////////////////
- time_core_3000::readback_bases_type time64_rb_bases;
- time64_rb_bases.rb_now = RB64_TIME_NOW;
- time64_rb_bases.rb_pps = RB64_TIME_PPS;
- perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
////////////////////////////////////////////////////////////////////
// create RF frontend interfacing
////////////////////////////////////////////////////////////////////
- for(size_t direction = 0; direction < 2; direction++)
- {
- const std::string x = direction? "rx" : "tx";
- const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == _fe1)? "1" : "2"));
- const fs_path rf_fe_path = mb_path / "dboards" / "A" / (x+"_frontends") / (dspno? "B" : "A");
-
- _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key);
- _tree->create<int>(rf_fe_path / "sensors");
+ static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION);
+ BOOST_FOREACH(direction_t dir, dirs) {
+ const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx";
+ const std::string key = std::string(((dir == RX_DIRECTION) ? "RX" : "TX")) + std::string(((dspno == _fe1) ? "1" : "2"));
+ const fs_path rf_fe_path
+ = mb_path / "dboards" / "A" / (x + "_frontends") / (dspno ? "B" : "A");
+
+ // This will connect all the AD936x-specific items
+ _codec_mgr->populate_frontend_subtree(
+ _tree->subtree(rf_fe_path), key, dir
+ );
+
+ // Now connect all the b200_impl-specific items
_tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked")
- .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, x == "tx"));
- BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
- {
- _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range")
- .set(ad9361_ctrl::get_gain_range(key));
-
- _tree->create<double>(rf_fe_path / "gains" / name / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
- .set(x == "rx" ? B200_DEFAULT_RX_GAIN : B200_DEFAULT_TX_GAIN);
- }
- _tree->create<std::string>(rf_fe_path / "connection").set("IQ");
- _tree->create<bool>(rf_fe_path / "enabled").set(true);
- _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false);
- _tree->create<double>(rf_fe_path / "bandwidth" / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
- .set(40e6);
- _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key));
- _tree->create<double>(rf_fe_path / "freq" / "value")
- .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key))
- .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
+ .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, dir == TX_DIRECTION))
+ ;
+ _tree->access<double>(rf_fe_path / "freq" / "value")
.subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1))
- .set(B200_DEFAULT_FREQ);
- _tree->create<meta_range_t>(rf_fe_path / "freq" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range));
-
- //setup RX related stuff
- if (key[0] == 'R')
+ ;
+ if (dir == RX_DIRECTION)
{
static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value")
.subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1))
- .set("RX2");
- _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi")
- .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key));
+ .set("RX2")
+ ;
+
}
- if (key[0] == 'T')
+ else if (dir == TX_DIRECTION)
{
static const std::vector<std::string> ants(1, "TX/RX");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX");
}
-
}
}
@@ -733,34 +797,10 @@ void b200_impl::register_loopback_self_test(wb_iface::sptr iface)
UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl;
}
-void b200_impl::codec_loopback_self_test(wb_iface::sptr iface)
-{
- UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
- size_t hash = size_t(time(NULL));
- for (size_t i = 0; i < 100; i++)
- {
- boost::hash_combine(hash, i);
- const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
- iface->poke32(TOREG(SR_CODEC_IDLE), word32);
- iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate
- const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK);
- const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
- const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
- bool test_fail = word32 != rb_tx or word32 != rb_rx;
- if (test_fail) {
- UHD_MSG(status) << "fail" << std::endl;
- throw uhd::runtime_error("CODEC loopback test failed.");
- }
- }
- UHD_MSG(status) << "pass" << std::endl;
- /* Zero out the idle data. */
- iface->poke32(TOREG(SR_CODEC_IDLE), 0);
-}
-
/***********************************************************************
* Sample and tick rate comprehension below
**********************************************************************/
-void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction /*= NULL*/)
+void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction /*= ""*/)
{
const size_t max_chans = 2;
if (chan_count > max_chans)
@@ -768,7 +808,7 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co
throw uhd::value_error(boost::str(
boost::format("cannot not setup %d %s channels (maximum is %d)")
% chan_count
- % (direction ? direction : "data")
+ % (direction.empty() ? "data" : direction)
% max_chans
));
}
@@ -782,20 +822,26 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co
% (tick_rate/1e6)
% (max_tick_rate/1e6)
% chan_count
- % (direction ? direction : "data")
+ % (direction.empty() ? "data" : direction)
));
}
}
}
-double b200_impl::set_tick_rate(const double rate)
+double b200_impl::set_tick_rate(const double new_tick_rate)
{
- UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz\n") % (rate/1e6));
-
- check_tick_rate_with_current_streamers(rate); // Defined in b200_io_impl.cpp
+ UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz... ") % (new_tick_rate/1e6)) << std::flush;
+ check_tick_rate_with_current_streamers(new_tick_rate); // Defined in b200_io_impl.cpp
+
+ // Make sure the clock rate is actually changed before doing
+ // the full Monty of setting regs and loopback tests etc.
+ if (std::abs(new_tick_rate - _tick_rate) < 1.0) {
+ UHD_MSG(status) << "OK" << std::endl;
+ return _tick_rate;
+ }
- _tick_rate = _codec_ctrl->set_clock_rate(rate);
- UHD_MSG(status) << (boost::format("Actually got clock rate %.6f MHz\n") % (_tick_rate/1e6));
+ _tick_rate = _codec_ctrl->set_clock_rate(new_tick_rate);
+ UHD_MSG(status) << std::endl << (boost::format("Actually got clock rate %.6f MHz.") % (_tick_rate/1e6)) << std::endl;
//reset after clock rate change
this->reset_codec_dcm();
@@ -856,6 +902,26 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom)
}
+boost::uint32_t b200_impl::get_fp_gpio(gpio_core_200::sptr gpio)
+{
+ return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));
+}
+
+void b200_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value)
+{
+ switch (attr)
+ {
+ case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value);
+ case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value);
+ case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value);
+ case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value);
+ case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value);
+ case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value);
+ case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
/***********************************************************************
* Reference time and clock
**********************************************************************/
diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp
index 65796d1a4..cbd51426d 100644
--- a/host/lib/usrp/b200/b200_impl.hpp
+++ b/host/lib/usrp/b200/b200_impl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012-2013 Ettus Research LLC
+// Copyright 2012-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
@@ -22,6 +22,7 @@
#include "b200_uart.hpp"
#include "b200_cores.hpp"
#include "ad9361_ctrl.hpp"
+#include "ad936x_manager.hpp"
#include "adf4001_ctrl.hpp"
#include "rx_vita_core_3000.hpp"
#include "tx_vita_core_3000.hpp"
@@ -43,18 +44,13 @@
#include <uhd/usrp/gps_ctrl.hpp>
#include <uhd/transport/usb_zero_copy.hpp>
#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/assign.hpp>
#include <boost/weak_ptr.hpp>
#include "recv_packet_demuxer_3000.hpp"
-static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 7;
+static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 8;
static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0;
-static const boost::uint16_t B200_FPGA_COMPAT_NUM = 8;
+static const boost::uint16_t B200_FPGA_COMPAT_NUM = 9;
static const double B200_BUS_CLOCK_RATE = 100e6;
-static const double B200_DEFAULT_TICK_RATE = 32e6;
-static const double B200_DEFAULT_FREQ = 100e6; // Hz
-static const double B200_DEFAULT_DECIM = 128;
-static const double B200_DEFAULT_INTERP = 128;
-static const double B200_DEFAULT_RX_GAIN = 0; // dB
-static const double B200_DEFAULT_TX_GAIN = 0; // dB
static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83;
static const size_t B200_MAX_RATE_USB2 = 53248000; // bytes/s
static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s
@@ -82,23 +78,48 @@ static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SI
static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040;
static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID);
-/***********************************************************************
- * The B200 Capability Constants
- **********************************************************************/
+static const unsigned char B200_USB_CTRL_RECV_INTERFACE = 4;
+static const unsigned char B200_USB_CTRL_RECV_ENDPOINT = 8;
+static const unsigned char B200_USB_CTRL_SEND_INTERFACE = 3;
+static const unsigned char B200_USB_CTRL_SEND_ENDPOINT = 4;
+
+static const unsigned char B200_USB_DATA_RECV_INTERFACE = 2;
+static const unsigned char B200_USB_DATA_RECV_ENDPOINT = 6;
+static const unsigned char B200_USB_DATA_SEND_INTERFACE = 1;
+static const unsigned char B200_USB_DATA_SEND_ENDPOINT = 2;
+
+/*
+ * VID/PID pairs for all B2xx products
+ */
+static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs =
+ boost::assign::list_of
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID))
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID))
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID))
+ ;
+
+b200_type_t get_b200_type(const uhd::usrp::mboard_eeprom_t &mb_eeprom);
+std::vector<uhd::transport::usb_device_handle::sptr> get_b200_device_handles(const uhd::device_addr_t &hint);
//! Implementation guts
class b200_impl : public uhd::device
{
public:
//structors
- b200_impl(const uhd::device_addr_t &);
+ b200_impl(const uhd::device_addr_t &, uhd::transport::usb_device_handle::sptr &handle);
~b200_impl(void);
//the io interface
uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args);
uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args);
bool recv_async_msg(uhd::async_metadata_t &, double);
- void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction = NULL);
+
+ //! Check that the combination of stream args and tick rate are valid.
+ //
+ // Basically figures out the arguments for enforce_tick_rate_limits()
+ // and calls said method. If arguments are invalid, throws a
+ // uhd::value_error.
+ void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction = "");
private:
b200_type_t _b200_type;
@@ -108,6 +129,7 @@ private:
b200_iface::sptr _iface;
radio_ctrl_core_3000::sptr _local_ctrl;
uhd::usrp::ad9361_ctrl::sptr _codec_ctrl;
+ uhd::usrp::ad936x_manager::sptr _codec_mgr;
b200_local_spi_core::sptr _spi_iface;
boost::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface;
uhd::gps_ctrl::sptr _gps;
@@ -136,7 +158,6 @@ private:
boost::optional<uhd::msg_task::msg_type_t> handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>);
void register_loopback_self_test(uhd::wb_iface::sptr iface);
- void codec_loopback_self_test(uhd::wb_iface::sptr iface);
void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);
void check_fw_compat(void);
void check_fpga_compat(void);
@@ -154,6 +175,7 @@ private:
{
radio_ctrl_core_3000::sptr ctrl;
gpio_core_200_32wo::sptr atr;
+ gpio_core_200::sptr fp_gpio;
time_core_3000::sptr time64;
rx_vita_core_3000::sptr framer;
rx_dsp_core_3000::sptr ddc;
@@ -200,14 +222,63 @@ private:
void update_enables(void);
void update_atrs(void);
+ boost::uint32_t get_fp_gpio(gpio_core_200::sptr);
+ void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t);
+
double _tick_rate;
double get_tick_rate(void){return _tick_rate;}
double set_tick_rate(const double rate);
+
+ /*! \brief Choose a tick rate (master clock rate) that works well for the given sampling rate.
+ *
+ * This function will try and choose a master clock rate automatically.
+ * See the function definition for details on the algorithm.
+ *
+ * The chosen tick rate is the largest multiple of two that is smaler
+ * than the max tick rate.
+ * The base rate is either given explicitly, or is the lcm() of the tx
+ * and rx sampling rates. In that case, it reads the rates directly
+ * from the property tree. It also tries to guess the number of channels
+ * (for the max possible tick rate) by checking the available streamers.
+ * This value, too, can explicitly be given.
+ *
+ * \param rate If this is given, it will be used as a minimum rate, or
+ * argument to lcm().
+ * \param tree_dsp_path The sampling rate from this property tree path
+ * will be ignored.
+ * \param num_chans If given, specifies the number of channels.
+ */
+ void set_auto_tick_rate(
+ const double rate=0,
+ const uhd::fs_path &tree_dsp_path="",
+ size_t num_chans=0
+ );
+
void update_tick_rate(const double);
- void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction = NULL);
+
+ /*! Check if \p tick_rate works with \p chan_count channels.
+ *
+ * Throws a uhd::value_error if not.
+ */
+ void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction = "");
void check_tick_rate_with_current_streamers(double rate);
+ /*! Return the max number of channels on active rx_streamer or tx_streamer objects associated with this device.
+ *
+ * \param direction Set to "TX" to only check tx_streamers, "RX" to only check
+ * rx_streamers. Any other value will check if \e any active
+ * streamers are available.
+ * \return Return the number of tx streamers (direction=="TX"), the number of rx
+ * streamers (direction=="RX") or the total number of streamers.
+ */
+ size_t max_chan_count(const std::string &direction="");
+
+ //! Coercer, attached to the "rate/value" property on the rx dsps.
+ double coerce_rx_samp_rate(rx_dsp_core_3000::sptr, size_t, const double);
void update_rx_samp_rate(const size_t, const double);
+
+ //! Coercer, attached to the "rate/value" property on the tx dsps.
+ double coerce_tx_samp_rate(tx_dsp_core_3000::sptr, size_t, const double);
void update_tx_samp_rate(const size_t, const double);
};
diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp
index 1e11e7ff6..20807bdd4 100644
--- a/host/lib/usrp/b200/b200_io_impl.cpp
+++ b/host/lib/usrp/b200/b200_io_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2012-2013 Ettus Research LLC
+// Copyright 2012-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
@@ -21,8 +21,10 @@
#include "../../transport/super_recv_packet_handler.hpp"
#include "../../transport/super_send_packet_handler.hpp"
#include "async_packet_handler.hpp"
+#include <uhd/utils/math.hpp>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
+#include <boost/math/common_factor.hpp>
#include <set>
using namespace uhd;
@@ -34,30 +36,32 @@ using namespace uhd::transport;
**********************************************************************/
void b200_impl::check_tick_rate_with_current_streamers(double rate)
{
- size_t max_tx_chan_count = 0, max_rx_chan_count = 0;
+ // Defined in b200_impl.cpp
+ enforce_tick_rate_limits(max_chan_count("RX"), rate, "RX");
+ enforce_tick_rate_limits(max_chan_count("TX"), rate, "TX");
+}
+
+// direction can either be "TX", "RX", or empty (default)
+size_t b200_impl::max_chan_count(const std::string &direction /* = "" */)
+{
+ size_t max_count = 0;
BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
{
- {
+ if ((direction == "RX" or direction.empty()) and not perif.rx_streamer.expired()) {
boost::shared_ptr<sph::recv_packet_streamer> rx_streamer =
boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock());
- if (rx_streamer)
- max_rx_chan_count = std::max(max_rx_chan_count, rx_streamer->get_num_channels());
+ max_count = std::max(max_count, rx_streamer->get_num_channels());
}
-
- {
+ if ((direction == "TX" or direction.empty()) and not perif.tx_streamer.expired()) {
boost::shared_ptr<sph::send_packet_streamer> tx_streamer =
boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());
- if (tx_streamer)
- max_tx_chan_count = std::max(max_tx_chan_count, tx_streamer->get_num_channels());
+ max_count = std::max(max_count, tx_streamer->get_num_channels());
}
}
-
- // Defined in b200_impl.cpp
- enforce_tick_rate_limits(max_rx_chan_count, rate, "RX");
- enforce_tick_rate_limits(max_tx_chan_count, rate, "TX");
+ return max_count;
}
-void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction /*= NULL*/)
+void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction /*= ""*/)
{
std::set<size_t> chans_set;
for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
@@ -69,33 +73,115 @@ void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_
enforce_tick_rate_limits(chans_set.size(), tick_rate, direction); // Defined in b200_impl.cpp
}
-void b200_impl::update_tick_rate(const double rate)
+void b200_impl::set_auto_tick_rate(
+ const double rate,
+ const fs_path &tree_dsp_path,
+ size_t num_chans
+) {
+ if (num_chans == 0) { // Divine them
+ num_chans = std::max(size_t(1), max_chan_count());
+ }
+ const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE/num_chans;
+ if (rate != 0.0 and
+ (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >
+ uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ))) {
+ throw uhd::value_error(str(
+ boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.")
+ % (rate / 1e6) % (max_tick_rate / 1e6)
+ ));
+ }
+
+ // See also the doxygen documentation for these steps in b200_impl.hpp
+ // Step 1: Obtain LCM and max rate from all relevant dsps
+ boost::uint32_t lcm_rate = (rate == 0) ? 1 : static_cast<boost::uint32_t>(floor(rate + 0.5));
+ for (int i = 0; i < 2; i++) { // Loop through rx and tx
+ std::string dir = (i == 0) ? "tx" : "rx";
+ // We have no way of knowing which DSPs are used, so we check them all.
+ BOOST_FOREACH(const std::string &dsp_no, _tree->list(str(boost::format("/mboards/0/%s_dsps") % dir))) {
+ fs_path dsp_path = str(boost::format("/mboards/0/%s_dsps/%s") % dir % dsp_no);
+ if (dsp_path == tree_dsp_path) {
+ continue;
+ }
+ double this_dsp_rate = _tree->access<double>(dsp_path / "rate/value").get();
+ // Check if the user selected something completely unreasonable:
+ if (uhd::math::fp_compare::fp_compare_delta<double>(this_dsp_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >
+ uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) {
+ throw uhd::value_error(str(
+ boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.")
+ % (this_dsp_rate / 1e6) % (max_tick_rate / 1e6)
+ ));
+ }
+ // If this_dsp_rate == 0.0, the sampling rate for this DSP hasn't been set, so
+ // we don't take that into consideration.
+ if (this_dsp_rate == 0.0) {
+ continue;
+ }
+ lcm_rate = boost::math::lcm<boost::uint32_t>(
+ lcm_rate,
+ static_cast<boost::uint32_t>(floor(this_dsp_rate + 0.5))
+ );
+ }
+ }
+ if (lcm_rate == 1) {
+ // In this case, no one has ever set a sampling rate.
+ return;
+ }
+
+ double base_rate = static_cast<double>(lcm_rate);
+ try {
+ // Step 2: Get a good tick rate value
+ const double new_rate = _codec_mgr->get_auto_tick_rate(base_rate, num_chans);
+ // Step 3: Set the new tick rate value (if any change)
+ if (!uhd::math::frequencies_are_equal(_tree->access<double>("/mboards/0/tick_rate").get(), new_rate)) {
+ _tree->access<double>("/mboards/0/tick_rate").set(new_rate);
+ }
+ } catch (const uhd::value_error &e) {
+ UHD_MSG(warning)
+ << "Cannot automatically determine an appropriate tick rate for these sampling rates." << std::endl
+ << "Consider using different sampling rates, or manually specify a suitable master clock rate." << std::endl;
+ return; // Let the others handle this
+ }
+}
+
+void b200_impl::update_tick_rate(const double new_tick_rate)
{
- check_tick_rate_with_current_streamers(rate);
+ check_tick_rate_with_current_streamers(new_tick_rate);
BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
{
boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock());
- if (my_streamer) my_streamer->set_tick_rate(rate);
- perif.framer->set_tick_rate(_tick_rate);
+ if (my_streamer) my_streamer->set_tick_rate(new_tick_rate);
+ perif.framer->set_tick_rate(new_tick_rate);
}
BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
{
boost::shared_ptr<sph::send_packet_streamer> my_streamer =
boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());
- if (my_streamer) my_streamer->set_tick_rate(rate);
- perif.deframer->set_tick_rate(_tick_rate);
+ if (my_streamer) my_streamer->set_tick_rate(new_tick_rate);
+ perif.deframer->set_tick_rate(new_tick_rate);
}
}
-#define CHECK_BANDWIDTH(dir) \
- if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \
- UHD_MSG(warning) \
- << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \
- << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \
- << std::endl; \
+#define CHECK_RATE_AND_THROW(rate) \
+ if (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > \
+ uhd::math::fp_compare::fp_compare_delta<double>(ad9361_device_t::AD9361_MAX_CLOCK_RATE, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { \
+ throw uhd::value_error(str( \
+ boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate.") \
+ % (rate / 1e6) \
+ )); \
+ }
+
+double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno, const double rx_rate)
+{
+ // Have to set tick rate first, or the ddc will change the requested rate based on default tick rate
+ if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) {
+ CHECK_RATE_AND_THROW(rx_rate);
+ const std::string dsp_path = (boost::format("/mboards/0/rx_dsps/%s") % dspno).str();
+ set_auto_tick_rate(rx_rate, dsp_path);
}
+ return ddc->set_host_rate(rx_rate);
+}
void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)
{
@@ -105,7 +191,18 @@ void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)
my_streamer->set_samp_rate(rate);
const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment();
my_streamer->set_scale_factor(adj);
- CHECK_BANDWIDTH("Rx");
+ _codec_mgr->check_bandwidth(rate, "Rx");
+}
+
+double b200_impl::coerce_tx_samp_rate(tx_dsp_core_3000::sptr duc, size_t dspno, const double tx_rate)
+{
+ // Have to set tick rate first, or the duc will change the requested rate based on default tick rate
+ if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) {
+ CHECK_RATE_AND_THROW(tx_rate);
+ const std::string dsp_path = (boost::format("/mboards/0/tx_dsps/%s") % dspno).str();
+ set_auto_tick_rate(tx_rate, dsp_path);
+ }
+ return duc->set_host_rate(tx_rate);
}
void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate)
@@ -116,7 +213,7 @@ void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate)
my_streamer->set_samp_rate(rate);
const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment();
my_streamer->set_scale_factor(adj);
- CHECK_BANDWIDTH("Tx");
+ _codec_mgr->check_bandwidth(rate, "Tx");
}
/***********************************************************************
@@ -276,6 +373,9 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_)
if (args.otw_format.empty()) args.otw_format = "sc16";
args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+ if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) {
+ set_auto_tick_rate(0, "", args.channels.size());
+ }
check_streamer_args(args, this->get_tick_rate(), "RX");
boost::shared_ptr<sph::recv_packet_streamer> my_streamer;
@@ -383,7 +483,10 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_)
if (args.otw_format.empty()) args.otw_format = "sc16";
args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
- check_streamer_args(args, this->get_tick_rate(), "TX");
+ if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) {
+ set_auto_tick_rate(0, "", args.channels.size());
+ }
+ check_streamer_args(args, this->get_tick_rate(), "RX");
boost::shared_ptr<sph::send_packet_streamer> my_streamer;
for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp
index 900651f94..8f2dd03f3 100644
--- a/host/lib/usrp/b200/b200_regs.hpp
+++ b/host/lib/usrp/b200/b200_regs.hpp
@@ -46,11 +46,13 @@ localparam SR_TX_DSP = 184;
localparam SR_TIME = 128;
localparam SR_RX_FMT = 136;
localparam SR_TX_FMT = 138;
+localparam SR_FP_GPIO = 200;
localparam RB32_TEST = 0;
localparam RB64_TIME_NOW = 8;
localparam RB64_TIME_PPS = 16;
localparam RB64_CODEC_READBACK = 24;
+localparam RB32_FP_GPIO = 32;
//pll constants
static const int AD9361_SLAVENO = (1 << 0);
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt
index 129cc569b..e63a09935 100644
--- a/host/lib/usrp/common/CMakeLists.txt
+++ b/host/lib/usrp/common/CMakeLists.txt
@@ -33,6 +33,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp
${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp
diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp
index 65e8e2df9..2d9f297b3 100644
--- a/host/lib/usrp/common/ad9361_ctrl.cpp
+++ b/host/lib/usrp/common/ad9361_ctrl.cpp
@@ -16,7 +16,6 @@
//
#include "ad9361_ctrl.hpp"
-#include <uhd/exception.hpp>
#include <uhd/types/ranges.hpp>
#include <uhd/utils/msg.hpp>
#include <uhd/types/serial.hpp>
@@ -108,6 +107,27 @@ public:
return _device.set_gain(direction, chain, value);
}
+ void set_agc(const std::string &which, bool enable)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ ad9361_device_t::chain_t chain =_get_chain_from_antenna(which);
+ _device.set_agc(chain, enable);
+ }
+
+ void set_agc_mode(const std::string &which, const std::string &mode)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ ad9361_device_t::chain_t chain =_get_chain_from_antenna(which);
+ if(mode == "slow") {
+ _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_SLOW_AGC);
+ } else if (mode == "fast"){
+ _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_FAST_AGC);
+ } else {
+ throw uhd::runtime_error("ad9361_ctrl got an invalid AGC option.");
+ }
+ }
+
//! set a new clock rate, return the exact value
double set_clock_rate(const double rate)
{
@@ -175,6 +195,62 @@ public:
return sensor_value_t("RSSI", _device.get_rssi(chain), "dB");
}
+ //! read the internal temp sensor. Average over 3 results
+ sensor_value_t get_temperature()
+ {
+ return sensor_value_t("temp", _device.get_average_temperature(), "C");
+ }
+
+ void set_dc_offset_auto(const std::string &which, const bool on)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ ad9361_device_t::direction_t direction = _get_direction_from_antenna(which);
+ _device.set_dc_offset_auto(direction,on);
+ }
+
+ void set_iq_balance_auto(const std::string &which, const bool on)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ ad9361_device_t::direction_t direction = _get_direction_from_antenna(which);
+ _device.set_iq_balance_auto(direction,on);
+ }
+
+ double set_bw_filter(const std::string &which, const double bw)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ ad9361_device_t::direction_t direction = _get_direction_from_antenna(which);
+ return _device.set_bw_filter(direction, bw);
+ }
+
+ std::vector<std::string> get_filter_names(const std::string &which)
+ {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ ad9361_device_t::direction_t direction = _get_direction_from_antenna(which);
+ return _device.get_filter_names(direction);
+ }
+
+ filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name)
+ {
+ 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.get_filter(direction, chain, filter_name);
+ }
+
+ void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr filter)
+ {
+ 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);
+ _device.set_filter(direction, chain, filter_name, filter);
+ }
+
private:
static ad9361_device_t::direction_t _get_direction_from_antenna(const std::string& antenna)
{
diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp
index b7d7b8e26..044265422 100644
--- a/host/lib/usrp/common/ad9361_ctrl.hpp
+++ b/host/lib/usrp/common/ad9361_ctrl.hpp
@@ -22,15 +22,31 @@
#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 {
-/***********************************************************************
- * AD9361 Control Interface
- **********************************************************************/
+/*! 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:
@@ -77,15 +93,18 @@ public:
return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end
}
- //! set the filter bandwidth for the frontend
- double set_bw_filter(const std::string &/*which*/, const double /*bw*/)
- {
- return 56e6; //TODO
- }
+ //! 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;
@@ -95,14 +114,46 @@ public:
//! 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 data port loopback
+ //! 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;
};
}}
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_client.h b/host/lib/usrp/common/ad9361_driver/ad9361_client.h
index 5e848d4c0..e9ea1404a 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_client.h
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_client.h
@@ -1,5 +1,18 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014 Ettus Research
+//
+// 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_AD9361_CLIENT_H
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp
index db5de52d0..29241f6ba 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp
@@ -1,5 +1,18 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014 Ettus Research
+//
+// 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 "ad9361_filter_taps.h"
@@ -11,6 +24,7 @@
#include <cmath>
#include <uhd/exception.hpp>
#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
#include <boost/cstdint.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
@@ -78,6 +92,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_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
const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6;
@@ -87,7 +102,7 @@ const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6;
* how many taps are in the filter, and given a vector of the taps
* themselves. */
-void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs)
+void ad9361_device_t::_program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs)
{
boost::uint16_t base;
@@ -102,8 +117,20 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b
/* Encode number of filter taps for programming register */
boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5;
+ boost::uint8_t reg_chain = 0;
+ switch (chain) {
+ case CHAIN_1:
+ reg_chain = 0x01 << 3;
+ break;
+ case CHAIN_2:
+ reg_chain = 0x02 << 3;
+ break;
+ default:
+ reg_chain = 0x03 << 3;
+ }
+
/* Turn on the filter clock. */
- _io_iface->poke8(base + 5, reg_numtaps | 0x1a);
+ _io_iface->poke8(base + 5, reg_numtaps | reg_chain | 0x02);
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
/* Zero the unused taps just in case they have stale data */
@@ -112,7 +139,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b
_io_iface->poke8(base + 0, addr);
_io_iface->poke8(base + 1, 0x0);
_io_iface->poke8(base + 2, 0x0);
- _io_iface->poke8(base + 5, reg_numtaps | 0x1e);
+ _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2));
_io_iface->poke8(base + 4, 0x00);
_io_iface->poke8(base + 4, 0x00);
}
@@ -122,7 +149,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b
_io_iface->poke8(base + 0, addr);
_io_iface->poke8(base + 1, (coeffs[addr]) & 0xff);
_io_iface->poke8(base + 2, (coeffs[addr] >> 8) & 0xff);
- _io_iface->poke8(base + 5, reg_numtaps | 0x1e);
+ _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2));
_io_iface->poke8(base + 4, 0x00);
_io_iface->poke8(base + 4, 0x00);
}
@@ -133,9 +160,9 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b
before the clock stops. Wait 4 sample clock periods after setting D2 high while that data writes into the table"
*/
- _io_iface->poke8(base + 5, reg_numtaps | 0x1A);
+ _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1));
if (direction == RX) {
- _io_iface->poke8(base + 5, reg_numtaps | 0x18);
+ _io_iface->poke8(base + 5, reg_numtaps | reg_chain );
/* Rx Gain, set to prevent digital overflow/saturation in filters
0:+6dB, 1:0dB, 2:-6dB, 3:-12dB
page 35 of UG-671 */
@@ -144,7 +171,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b
/* Tx Gain. bit[0]. set to prevent digital overflow/saturation in filters
0: 0dB, 1:-6dB
page 25 of UG-671 */
- _io_iface->poke8(base + 5, reg_numtaps | 0x18);
+ _io_iface->poke8(base + 5, reg_numtaps | reg_chain );
}
}
@@ -175,7 +202,7 @@ void ad9361_device_t::_setup_rx_fir(size_t num_taps, boost::int32_t decimation)
}
}
- _program_fir_filter(RX, num_taps, coeffs.get());
+ _program_fir_filter(RX, CHAIN_BOTH, num_taps, coeffs.get());
}
/* Program the TX FIR Filter. */
@@ -207,7 +234,7 @@ void ad9361_device_t::_setup_tx_fir(size_t num_taps, boost::int32_t interpolatio
}
}
- _program_fir_filter(TX, num_taps, coeffs.get());
+ _program_fir_filter(TX, CHAIN_BOTH, num_taps, coeffs.get());
}
/***********************************************************************
@@ -282,16 +309,24 @@ void ad9361_device_t::_calibrate_synth_charge_pumps()
*
* Note that the filter calibration depends heavily on the baseband
* bandwidth, so this must be re-done after any change to the RX sample
- * rate. */
-double ad9361_device_t::_calibrate_baseband_rx_analog_filter()
+ * rate.
+ * UG570 Page 33 states that this filter should be calibrated to 1.4 * bbbw*/
+double ad9361_device_t::_calibrate_baseband_rx_analog_filter(double req_rfbw)
{
- /* For filter tuning, baseband BW is half the complex BW, and must be
- * between 28e6 and 0.2e6. */
- double bbbw = _baseband_bw / 2.0;
+ double bbbw = req_rfbw / 2.0;
+ if(bbbw > _baseband_bw / 2.0)
+ {
+ UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw;
+ bbbw = _baseband_bw / 2.0;
+ }
+
+ /* Baseband BW must be between 28e6 and 0.143e6.
+ * Max filter BW is 39.2 MHz. 39.2 / 1.4 = 28
+ * Min filter BW is 200kHz. 200 / 1.4 = 143 */
if (bbbw > 28e6) {
bbbw = 28e6;
- } else if (bbbw < 0.20e6) {
- bbbw = 0.20e6;
+ } else if (bbbw < 0.143e6) {
+ bbbw = 0.143e6;
}
double rxtune_clk = ((1.4 * bbbw * 2 * M_PI) / M_LN2);
@@ -340,16 +375,25 @@ double ad9361_device_t::_calibrate_baseband_rx_analog_filter()
*
* Note that the filter calibration depends heavily on the baseband
* bandwidth, so this must be re-done after any change to the TX sample
- * rate. */
-double ad9361_device_t::_calibrate_baseband_tx_analog_filter()
+ * rate.
+ * UG570 Page 32 states that this filter should be calibrated to 1.6 * bbbw*/
+double ad9361_device_t::_calibrate_baseband_tx_analog_filter(double req_rfbw)
{
- /* For filter tuning, baseband BW is half the complex BW, and must be
- * between 28e6 and 0.2e6. */
- double bbbw = _baseband_bw / 2.0;
+ double bbbw = req_rfbw / 2.0;
+
+ if(bbbw > _baseband_bw / 2.0)
+ {
+ UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw;
+ bbbw = _baseband_bw / 2.0;
+ }
+
+ /* Baseband BW must be between 20e6 and 0.391e6.
+ * Max filter BW is 32 MHz. 32 / 1.6 = 20
+ * Min filter BW is 625 kHz. 625 / 1.6 = 391 */
if (bbbw > 20e6) {
bbbw = 20e6;
- } else if (bbbw < 0.625e6) {
- bbbw = 0.625e6;
+ } else if (bbbw < 0.391e6) {
+ bbbw = 0.391e6;
}
double txtune_clk = ((1.6 * bbbw * 2 * M_PI) / M_LN2);
@@ -386,16 +430,25 @@ double ad9361_device_t::_calibrate_baseband_tx_analog_filter()
/* Calibrate the secondary TX filter.
*
* This filter also depends on the TX sample rate, so if a rate change is
- * made, the previous calibration will no longer be valid. */
-void ad9361_device_t::_calibrate_secondary_tx_filter()
+ * made, the previous calibration will no longer be valid.
+ * UG570 Page 32 states that this filter should be calibrated to 5 * bbbw*/
+double ad9361_device_t::_calibrate_secondary_tx_filter(double req_rfbw)
{
- /* For filter tuning, baseband BW is half the complex BW, and must be
- * between 20e6 and 0.53e6. */
- double bbbw = _baseband_bw / 2.0;
+ double bbbw = req_rfbw / 2.0;
+
+ if(bbbw > _baseband_bw / 2.0)
+ {
+ UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw;
+ bbbw = _baseband_bw / 2.0;
+ }
+
+ /* Baseband BW must be between 20e6 and 0.54e6.
+ * Max filter BW is 100 MHz. 100 / 5 = 20
+ * Min filter BW is 2.7 MHz. 2.7 / 5 = 0.54 */
if (bbbw > 20e6) {
bbbw = 20e6;
- } else if (bbbw < 0.53e6) {
- bbbw = 0.53e6;
+ } else if (bbbw < 0.54e6) {
+ bbbw = 0.54e6;
}
double bbbw_mhz = bbbw / 1e6;
@@ -456,13 +509,17 @@ void ad9361_device_t::_calibrate_secondary_tx_filter()
_io_iface->poke8(0x0d2, reg0d2);
_io_iface->poke8(0x0d1, reg0d1);
_io_iface->poke8(0x0d0, reg0d0);
+
+ return bbbw;
}
/* Calibrate the RX TIAs.
*
* Note that the values in the TIA register, after calibration, vary with
- * the RX gain settings. */
-void ad9361_device_t::_calibrate_rx_TIAs()
+ * the RX gain settings.
+ * We do not really program the BW here. Most settings are taken form the BB LPF registers
+ * UG570 page 33 states that this filter should be calibrated to 2.5 * bbbw */
+double ad9361_device_t::_calibrate_rx_TIAs(double req_rfbw)
{
boost::uint8_t reg1eb = _io_iface->peek8(0x1eb) & 0x3F;
boost::uint8_t reg1ec = _io_iface->peek8(0x1ec) & 0x7F;
@@ -473,13 +530,21 @@ void ad9361_device_t::_calibrate_rx_TIAs()
boost::uint8_t reg1de = 0x00;
boost::uint8_t reg1df = 0x00;
- /* For calibration, baseband BW is half the complex BW, and must be
- * between 28e6 and 0.2e6. */
- double bbbw = _baseband_bw / 2.0;
- if (bbbw > 20e6) {
- bbbw = 20e6;
- } else if (bbbw < 0.20e6) {
- bbbw = 0.20e6;
+ double bbbw = req_rfbw / 2.0;
+
+ if(bbbw > _baseband_bw / 2.0)
+ {
+ UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw;
+ bbbw = _baseband_bw / 2.0;
+ }
+
+ /* Baseband BW must be between 28e6 and 0.4e6.
+ * Max filter BW is 70 MHz. 70 / 2.5 = 28
+ * Min filter BW is 1 MHz. 1 / 2.5 = 0.4*/
+ if (bbbw > 28e6) {
+ bbbw = 28e6;
+ } else if (bbbw < 0.40e6) {
+ bbbw = 0.40e6;
}
double ceil_bbbw_mhz = std::ceil(bbbw / 1e6);
@@ -520,6 +585,8 @@ void ad9361_device_t::_calibrate_rx_TIAs()
_io_iface->poke8(0x1df, reg1df);
_io_iface->poke8(0x1dc, reg1dc);
_io_iface->poke8(0x1de, reg1de);
+
+ return bbbw;
}
/* Setup the AD9361 ADC.
@@ -651,11 +718,12 @@ void ad9361_device_t::_setup_adc()
}
/* Calibrate the baseband DC offset.
- *
- * Note that this function is called from within the TX quadrature
- * calibration function! */
+ * Disables tracking
+ */
void ad9361_device_t::_calibrate_baseband_dc_offset()
{
+ _io_iface->poke8(0x18b, 0x83); //Reset RF DC tracking flag
+
_io_iface->poke8(0x193, 0x3f); // Calibration settings
_io_iface->poke8(0x190, 0x0f); // Set tracking coefficient
//write_ad9361_reg(device, 0x190, /*0x0f*//*0xDF*/0x80*1 | 0x40*1 | (16+8/*+4*/)); // Set tracking coefficient: don't *4 counter, do decim /4, increased gain shift
@@ -675,9 +743,8 @@ void ad9361_device_t::_calibrate_baseband_dc_offset()
}
/* Calibrate the RF DC offset.
- *
- * Note that this function is called from within the TX quadrature
- * calibration function. */
+ * Disables tracking
+ */
void ad9361_device_t::_calibrate_rf_dc_offset()
{
/* Some settings are frequency-dependent. */
@@ -692,7 +759,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset()
}
_io_iface->poke8(0x185, 0x20); // RF DC Offset wait count
- _io_iface->poke8(0x18b, 0x83);
+ _io_iface->poke8(0x18b, 0x83); // Disable tracking
_io_iface->poke8(0x189, 0x30);
/* Run the calibration! */
@@ -708,6 +775,16 @@ void ad9361_device_t::_calibrate_rf_dc_offset()
}
}
+void ad9361_device_t::_configure_bb_rf_dc_tracking(const bool on)
+{
+ if(on)
+ {
+ _io_iface->poke8(0x18b, 0xad); // Enable BB and RF DC tracking
+ } else {
+ _io_iface->poke8(0x18b, 0x83); // Disable BB and RF DC tracking
+ }
+}
+
/* Start the RX quadrature calibration.
*
* Note that we are using AD9361's 'tracking' feature for RX quadrature
@@ -719,17 +796,21 @@ void ad9361_device_t::_calibrate_rx_quadrature()
_io_iface->poke8(0x168, 0x03); // Set tone level for cal
_io_iface->poke8(0x16e, 0x25); // RX Gain index to use for cal
_io_iface->poke8(0x16a, 0x75); // Set Kexp phase
- _io_iface->poke8(0x16b, 0x15); // Set Kexp amplitude
- _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode
- _io_iface->poke8(0x18b, 0xad);
+ _io_iface->poke8(0x16b, 0x95); // Set Kexp amplitude
+
+ if(_use_iq_balance_correction)
+ {
+ _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode. Gets disabled in _tx_quadrature_cal_routine!
+ }
}
-/* TX quadtrature calibration routine.
+/* TX quadrature calibration routine.
*
* The TX quadrature needs to be done twice, once for each TX chain, with
* only one register change in between. Thus, this function enacts the
* calibrations, and it is called from calibrate_tx_quadrature. */
void ad9361_device_t::_tx_quadrature_cal_routine() {
+
/* This is a weird process, but here is how it works:
* 1) Read the calibrated NCO frequency bits out of 0A3.
* 2) Write the two bits to the RX NCO freq part of 0A0.
@@ -765,7 +846,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {
/* The gain table index used for calibration must be adjusted for the
* mid-table to get a TIA index = 1 and LPF index = 0. */
- if ((_rx_freq >= 1300e6) && (_rx_freq < 4000e6)) {
+ if (_rx_freq < 1300e6) {
_io_iface->poke8(0x0aa, 0x22); // Cal gain table index
} else {
_io_iface->poke8(0x0aa, 0x25); // Cal gain table index
@@ -774,12 +855,6 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {
_io_iface->poke8(0x0a4, 0xf0); // Cal setting conut
_io_iface->poke8(0x0ae, 0x00); // Cal LPF gain index (split mode)
- /* First, calibrate the baseband DC offset. */
- _calibrate_baseband_dc_offset();
-
- /* Second, calibrate the RF DC offset. */
- _calibrate_rf_dc_offset();
-
/* Now, calibrate the TX quadrature! */
size_t count = 0;
_io_iface->poke8(0x016, 0x10);
@@ -794,9 +869,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {
}
/* Run the TX quadrature calibration.
- *
- * Note that from within this function we are also triggering the baseband
- * and RF DC calibrations. */
+ */
void ad9361_device_t::_calibrate_tx_quadrature()
{
/* Make sure we are, in fact, in the ALERT state. If not, something is
@@ -880,7 +953,7 @@ void ad9361_device_t::_program_mixer_gm_subtable()
void ad9361_device_t::_program_gain_table() {
/* Figure out which gain table we should be using for our current
* frequency band. */
- boost::uint8_t (*gain_table)[5] = NULL;
+ boost::uint8_t (*gain_table)[3] = NULL;
boost::uint8_t new_gain_table;
if (_rx_freq < 1300e6) {
gain_table = gain_table_sub_1300mhz;
@@ -911,9 +984,9 @@ void ad9361_device_t::_program_gain_table() {
boost::uint8_t index = 0;
for (; index < 77; index++) {
_io_iface->poke8(0x130, index);
- _io_iface->poke8(0x131, gain_table[index][1]);
- _io_iface->poke8(0x132, gain_table[index][2]);
- _io_iface->poke8(0x133, gain_table[index][3]);
+ _io_iface->poke8(0x131, gain_table[index][0]);
+ _io_iface->poke8(0x132, gain_table[index][1]);
+ _io_iface->poke8(0x133, gain_table[index][2]);
_io_iface->poke8(0x137, 0x1E);
_io_iface->poke8(0x134, 0x00);
_io_iface->poke8(0x134, 0x00);
@@ -939,28 +1012,58 @@ void ad9361_device_t::_program_gain_table() {
/* Setup gain control registers.
*
- * This really only needs to be done once, at initialization. */
-void ad9361_device_t::_setup_gain_control()
+ * This really only needs to be done once, at initialization.
+ * If AGC is used the mode select bits (Reg 0x0FA) must be written manually */
+void ad9361_device_t::_setup_gain_control(bool agc)
{
- _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select
- _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl
- _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size
- _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index
- _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time
- _io_iface->poke8(0x100, 0x6F); // Max Digital Gain
- _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold
- _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold
- _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold
- _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold
- _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index
- _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index
- _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index
- _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index
- _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index
- _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index
- _io_iface->poke8(0x114, 0x30); // Low Power Threshold
- _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit
- _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control
+ /* The AGC mode configuration should be good for all cases.
+ * However, non AGC configuration still used for backward compatibility. */
+ if (agc) {
+ /*mode select bits must be written before hand!*/
+ _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl
+ _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size
+ _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index
+ _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time
+ _io_iface->poke8(0x100, 0x6F); // Max Digital Gain
+ _io_iface->poke8(0x101, 0x0A); // Max Digital Gain
+ _io_iface->poke8(0x103, 0x08); // Max Digital Gain
+ _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold
+ _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold
+ _io_iface->poke8(0x106, 0x22); // Max Digital Gain
+ _io_iface->poke8(0x107, 0x2B); // Large LMT Overload Threshold
+ _io_iface->poke8(0x108, 0x31);
+ _io_iface->poke8(0x111, 0x0A);
+ _io_iface->poke8(0x11A, 0x1C);
+ _io_iface->poke8(0x120, 0x0C);
+ _io_iface->poke8(0x121, 0x44);
+ _io_iface->poke8(0x122, 0x44);
+ _io_iface->poke8(0x123, 0x11);
+ _io_iface->poke8(0x124, 0xF5);
+ _io_iface->poke8(0x125, 0x3B);
+ _io_iface->poke8(0x128, 0x03);
+ _io_iface->poke8(0x129, 0x56);
+ _io_iface->poke8(0x12A, 0x22);
+ } else {
+ _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select
+ _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl
+ _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size
+ _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index
+ _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time
+ _io_iface->poke8(0x100, 0x6F); // Max Digital Gain
+ _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold
+ _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold
+ _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold
+ _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold
+ _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index
+ _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index
+ _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index
+ _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index
+ _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index
+ _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index
+ _io_iface->poke8(0x114, 0x30); // Low Power Threshold
+ _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit
+ _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control
+ }
}
/* Setup the RX or TX synthesizers.
@@ -1257,6 +1360,7 @@ double ad9361_device_t::_setup_rates(const double rate)
int divfactor = 0;
_tfir_factor = 0;
_rfir_factor = 0;
+
if (rate < 0.33e6) {
// RX1 + RX2 enabled, 3, 2, 2, 4
_regs.rxfilt = B8(11101111);
@@ -1412,6 +1516,19 @@ void ad9361_device_t::initialize()
_rx2_gain = 0;
_tx1_gain = 0;
_tx2_gain = 0;
+ _use_dc_offset_correction = true;
+ _use_iq_balance_correction = true;
+ _rx1_agc_mode = GAIN_MODE_SLOW_AGC;
+ _rx2_agc_mode = GAIN_MODE_SLOW_AGC;
+ _rx1_agc_enable = false;
+ _rx2_agc_enable = false;
+ _last_calibration_freq = -AD9361_CAL_VALID_WINDOW;
+ _rx_analog_bw = 0;
+ _tx_analog_bw = 0;
+ _rx_tia_lp_bw = 0;
+ _tx_sec_lp_bw = 0;
+ _rx_bb_lp_bw = 0;
+ _tx_bb_lp_bw = 0;
/* Reset the device. */
_io_iface->poke8(0x000, 0x01);
@@ -1490,7 +1607,6 @@ void ad9361_device_t::initialize()
_io_iface->poke8(0x019, 0x00); // AuxDAC2 Word[9:2]
_io_iface->poke8(0x01A, 0x00); // AuxDAC1 Config and Word[1:0]
_io_iface->poke8(0x01B, 0x00); // AuxDAC2 Config and Word[1:0]
- _io_iface->poke8(0x022, 0x4A); // Invert Bypassed LNA
_io_iface->poke8(0x023, 0xFF); // AuxDAC Manaul/Auto Control
_io_iface->poke8(0x026, 0x00); // AuxDAC Manual Select Bit/GPO Manual Select
_io_iface->poke8(0x030, 0x00); // AuxDAC1 Rx Delay
@@ -1498,10 +1614,18 @@ void ad9361_device_t::initialize()
_io_iface->poke8(0x032, 0x00); // AuxDAC2 Rx Delay
_io_iface->poke8(0x033, 0x00); // AuxDAC2 Tx Delay
+ /* LNA bypass polarity inversion
+ * According to the register map, we should invert the bypass path to
+ * match LNA phase. Extensive testing, however, shows otherwise and that
+ * to align bypass and LNA phases, the bypass inversion switch should be
+ * turned off.
+ */
+ _io_iface->poke8(0x022, 0x0A);
+
/* Setup AuxADC */
_io_iface->poke8(0x00B, 0x00); // Temp Sensor Setup (Offset)
_io_iface->poke8(0x00C, 0x00); // Temp Sensor Setup (Temp Window)
- _io_iface->poke8(0x00D, 0x03); // Temp Sensor Setup (Periodic Measure)
+ _io_iface->poke8(0x00D, 0x00); // Temp Sensor Setup (Manual Measure)
_io_iface->poke8(0x00F, 0x04); // Temp Sensor Setup (Decimation)
_io_iface->poke8(0x01C, 0x10); // AuxADC Setup (Clock Div)
_io_iface->poke8(0x01D, 0x01); // AuxADC Setup (Decimation/Enable)
@@ -1555,17 +1679,18 @@ void ad9361_device_t::initialize()
_program_mixer_gm_subtable();
_program_gain_table();
- _setup_gain_control();
+ _setup_gain_control(false);
- _calibrate_baseband_rx_analog_filter();
- _calibrate_baseband_tx_analog_filter();
- _calibrate_rx_TIAs();
- _calibrate_secondary_tx_filter();
+ set_bw_filter(RX, _baseband_bw);
+ set_bw_filter(TX, _baseband_bw);
_setup_adc();
+ _calibrate_baseband_dc_offset();
+ _calibrate_rf_dc_offset();
_calibrate_tx_quadrature();
_calibrate_rx_quadrature();
+ _configure_bb_rf_dc_tracking(_use_dc_offset_correction);
// cals done, set PPORT config
switch (_client_params->get_digital_interface_mode()) {
@@ -1680,18 +1805,19 @@ double ad9361_device_t::set_clock_rate(const double req_rate)
_program_mixer_gm_subtable();
_program_gain_table();
- _setup_gain_control();
+ _setup_gain_control(false);
_reprogram_gains();
- _calibrate_baseband_rx_analog_filter();
- _calibrate_baseband_tx_analog_filter();
- _calibrate_rx_TIAs();
- _calibrate_secondary_tx_filter();
+ set_bw_filter(RX, _baseband_bw);
+ set_bw_filter(TX, _baseband_bw);
_setup_adc();
+ _calibrate_baseband_dc_offset();
+ _calibrate_rf_dc_offset();
_calibrate_tx_quadrature();
_calibrate_rx_quadrature();
+ _configure_bb_rf_dc_tracking(_use_dc_offset_correction);
// cals done, set PPORT config
switch (_client_params->get_digital_interface_mode()) {
@@ -1843,9 +1969,16 @@ double ad9361_device_t::tune(direction_t direction, const double value)
/* Update the gain settings. */
_reprogram_gains();
- /* Run the calibration algorithms. */
- _calibrate_tx_quadrature();
- _calibrate_rx_quadrature();
+ /* Only run the following calibrations if we are more than 100MHz away
+ * from the previous calibration point. */
+ if (std::abs(_last_calibration_freq - tune_freq) > AD9361_CAL_VALID_WINDOW) {
+ /* Run the calibration algorithms. */
+ _calibrate_rf_dc_offset();
+ _calibrate_tx_quadrature();
+ _calibrate_rx_quadrature();
+ _configure_bb_rf_dc_tracking(_use_dc_offset_correction);
+ _last_calibration_freq = tune_freq;
+ }
/* If we were in the FDD state, return it now. */
if (not_in_alert) {
@@ -1960,4 +2093,678 @@ double ad9361_device_t::get_rssi(chain_t chain)
return rssi;
}
+/*
+ * Returns the reading of the internal temperature sensor.
+ * One point calibration of the sensor was done according to datasheet
+ * leading to the given default constant correction factor.
+ */
+double ad9361_device_t::_get_temperature(const double cal_offset, const double timeout)
+{
+ //set 0x01D[0] to 1 to disable AuxADC GPIO reading
+ boost::uint8_t tmp = 0;
+ tmp = _io_iface->peek8(0x01D);
+ _io_iface->poke8(0x01D, (tmp | 0x01));
+ _io_iface->poke8(0x00B, 0); //set offset to 0
+
+ _io_iface->poke8(0x00C, 0x01); //start reading, clears bit 0x00C[1]
+ boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time();
+ boost::posix_time::time_duration elapsed;
+ //wait for valid data (toggle of bit 1 in 0x00C)
+ while(((_io_iface->peek8(0x00C) >> 1) & 0x01) == 0) {
+ boost::this_thread::sleep(boost::posix_time::microseconds(100));
+ elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
+ if(elapsed.total_milliseconds() > (timeout*1000))
+ {
+ throw uhd::runtime_error("[ad9361_device_t] timeout while reading temperature");
+ }
+ }
+ _io_iface->poke8(0x00C, 0x00); //clear read flag
+
+ boost::uint8_t temp = _io_iface->peek8(0x00E); //read temperature.
+ double tmp_temp = temp/1.140f; //according to ADI driver
+ tmp_temp = tmp_temp + cal_offset; //Constant offset acquired by one point calibration.
+
+ return tmp_temp;
+}
+
+double ad9361_device_t::get_average_temperature(const double cal_offset, const size_t num_samples)
+{
+ double d_temp = 0;
+ for(size_t i = 0; i < num_samples; i++) {
+ double tmp_temp = _get_temperature(cal_offset);
+ d_temp += (tmp_temp/num_samples);
+ }
+ return d_temp;
+}
+
+void ad9361_device_t::set_dc_offset_auto(direction_t direction, const bool on)
+{
+ if(direction == RX)
+ {
+ _use_dc_offset_correction = on;
+ _configure_bb_rf_dc_tracking(_use_dc_offset_correction);
+ if(on)
+ {
+ _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2))))); //Clear force bits
+ //Do a single shot DC offset cal before enabling tracking (Not possible if not in ALERT state. Is it necessary?)
+ } else {
+ //clear current config values
+ _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2)))); //Set input A and input B&C force enable bits
+ _io_iface->poke8(0x174, 0x00);
+ _io_iface->poke8(0x175, 0x00);
+ _io_iface->poke8(0x176, 0x00);
+ _io_iface->poke8(0x177, 0x00);
+ _io_iface->poke8(0x178, 0x00);
+ _io_iface->poke8(0x17D, 0x00);
+ _io_iface->poke8(0x17E, 0x00);
+ _io_iface->poke8(0x17F, 0x00);
+ _io_iface->poke8(0x180, 0x00);
+ _io_iface->poke8(0x181, 0x00);
+ }
+ } else {
+ // DC offset is removed during TX quad cal
+ throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH");
+ }
+}
+
+void ad9361_device_t::set_iq_balance_auto(direction_t direction, const bool on)
+{
+ if(direction == RX)
+ {
+ _use_iq_balance_correction = on;
+ if(on)
+ {
+ //disable force registers and enable tracking
+ _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~ ( (1<<1) | (1<<0) | (1<<5) | (1<<4) ))));
+ _calibrate_rx_quadrature();
+ } else {
+ //disable IQ tracking
+ _io_iface->poke8(0x169, 0xc0);
+ //clear current config values
+ _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 1) | (1 << 0) | (1 << 5) | (1 << 4)))); //Set Rx2 input B&C force enable bit
+ _io_iface->poke8(0x17B, 0x00);
+ _io_iface->poke8(0x17C, 0x00);
+ _io_iface->poke8(0x179, 0x00);
+ _io_iface->poke8(0x17A, 0x00);
+ _io_iface->poke8(0x170, 0x00);
+ _io_iface->poke8(0x171, 0x00);
+ _io_iface->poke8(0x172, 0x00);
+ _io_iface->poke8(0x173, 0x00);
+ }
+ } else {
+ throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH");
+ }
+}
+
+/* Sets the RX gain mode to be used.
+ * If a transition from an AGC to an non AGC mode occurs (or vice versa)
+ * the gain configuration will be reloaded. */
+void ad9361_device_t::_setup_agc(chain_t chain, gain_mode_t gain_mode)
+{
+ boost::uint8_t gain_mode_reg = 0;
+ boost::uint8_t gain_mode_prev = 0;
+ boost::uint8_t gain_mode_bits_pos = 0;
+
+ gain_mode_reg = _io_iface->peek8(0x0FA);
+ gain_mode_prev = (gain_mode_reg & 0x0F);
+
+ if (chain == CHAIN_1) {
+ gain_mode_bits_pos = 0;
+ } else if (chain == CHAIN_2) {
+ gain_mode_bits_pos = 2;
+ } else
+ {
+ throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain");
+ }
+
+ gain_mode_reg = (gain_mode_reg & (~(0x03<<gain_mode_bits_pos))); //clear mode bits
+ switch (gain_mode) {
+ case GAIN_MODE_MANUAL:
+ //leave bits cleared
+ break;
+ case GAIN_MODE_SLOW_AGC:
+ gain_mode_reg = (gain_mode_reg | (0x02<<gain_mode_bits_pos));
+ break;
+ case GAIN_MODE_FAST_AGC:
+ gain_mode_reg = (gain_mode_reg | (0x01<<gain_mode_bits_pos));
+ break;
+ default:
+ throw uhd::runtime_error("[ad9361_device_t] Gain mode does not exist");
+ }
+ _io_iface->poke8(0x0FA, gain_mode_reg);
+ boost::uint8_t gain_mode_status = _io_iface->peek8(0x0FA);
+ gain_mode_status = (gain_mode_status & 0x0F);
+ /*Check if gain mode configuration needs to be reprogrammed*/
+ if (((gain_mode_prev == 0) && (gain_mode_status != 0)) || ((gain_mode_prev != 0) && (gain_mode_status == 0))) {
+ if (gain_mode_status == 0) {
+ /*load manual mode config*/
+ _setup_gain_control(false);
+ } else {
+ /*load agc mode config*/
+ _setup_gain_control(true);
+ }
+ }
+}
+
+void ad9361_device_t::set_agc(chain_t chain, bool enable)
+{
+ if(chain == CHAIN_1) {
+ _rx1_agc_enable = enable;
+ if(enable) {
+ _setup_agc(chain, _rx1_agc_mode);
+ } else {
+ _setup_agc(chain, GAIN_MODE_MANUAL);
+ }
+ } else if (chain == CHAIN_2){
+ _rx2_agc_enable = enable;
+ if(enable) {
+ _setup_agc(chain, _rx2_agc_mode);
+ } else {
+ _setup_agc(chain, GAIN_MODE_MANUAL);
+ }
+ } else
+ {
+ throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain");
+ }
+}
+
+void ad9361_device_t::set_agc_mode(chain_t chain, gain_mode_t gain_mode)
+{
+ if(chain == CHAIN_1) {
+ _rx1_agc_mode = gain_mode;
+ if(_rx1_agc_enable) {
+ _setup_agc(chain, _rx1_agc_mode);
+ }
+ } else if(chain == CHAIN_2){
+ _rx2_agc_mode = gain_mode;
+ if(_rx2_agc_enable) {
+ _setup_agc(chain, _rx2_agc_mode);
+ }
+ } else
+ {
+ throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain");
+ }
+}
+
+std::vector<std::string> ad9361_device_t::get_filter_names(direction_t direction)
+{
+ std::vector<std::string> ret;
+ if(direction == RX) {
+ for(std::map<std::string, filter_query_helper>::iterator it = _rx_filters.begin(); it != _rx_filters.end(); ++it) {
+ ret.push_back(it->first);
+ }
+ } else if (direction == TX)
+ {
+ for(std::map<std::string, filter_query_helper>::iterator it = _tx_filters.begin(); it != _tx_filters.end(); ++it) {
+ ret.push_back(it->first);
+ }
+ }
+ return ret;
+}
+
+filter_info_base::sptr ad9361_device_t::get_filter(direction_t direction, chain_t chain, const std::string &name)
+{
+ if(direction == RX) {
+ if (not _rx_filters[name].get)
+ {
+ throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read.");
+ }
+ return _rx_filters[name].get(direction, chain);
+ } else if (direction == TX) {
+ if (not _tx_filters[name].get)
+ {
+ throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read.");
+ }
+ return _tx_filters[name].get(direction, chain);
+ }
+
+ throw uhd::runtime_error("ad9361_device_t::get_filter wrong direction parameter.");
+}
+
+void ad9361_device_t::set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter)
+{
+
+ if(direction == RX) {
+ if(not _rx_filters[name].set)
+ {
+ throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written.");
+ }
+ _rx_filters[name].set(direction, chain, filter);
+ } else if (direction == TX) {
+ if(not _tx_filters[name].set)
+ {
+ throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written.");
+ }
+ _tx_filters[name].set(direction, chain, filter);
+ }
+
+}
+
+double ad9361_device_t::set_bw_filter(direction_t direction, const double rf_bw)
+{
+ //both low pass filters are programmed to the same bw. However, their cutoffs will differ.
+ //Together they should create the requested bb bw.
+ double set_analog_bb_bw = 0;
+ if(direction == RX)
+ {
+ _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(rf_bw); //returns bb bw
+ _rx_tia_lp_bw = _calibrate_rx_TIAs(rf_bw);
+ _rx_analog_bw = _rx_bb_lp_bw;
+ set_analog_bb_bw = _rx_analog_bw;
+ } else {
+ _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(rf_bw); //returns bb bw
+ _tx_sec_lp_bw = _calibrate_secondary_tx_filter(rf_bw);
+ _tx_analog_bw = _tx_bb_lp_bw;
+ set_analog_bb_bw = _tx_analog_bw;
+ }
+ return (2.0 * set_analog_bb_bw);
+}
+
+void ad9361_device_t::_set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps)
+{
+ size_t num_taps = taps.size();
+ size_t num_taps_avail = _get_num_fir_taps(direction);
+ if(num_taps == num_taps_avail)
+ {
+ boost::scoped_array<boost::uint16_t> coeffs(new boost::uint16_t[num_taps_avail]);
+ for (size_t i = 0; i < num_taps_avail; i++)
+ {
+ coeffs[i] = boost::uint16_t(taps[i]);
+ }
+ _program_fir_filter(direction, chain, num_taps_avail, coeffs.get());
+ } else if(num_taps < num_taps_avail){
+ throw uhd::runtime_error("ad9361_device_t::_set_fir_taps not enough coefficients.");
+ } else {
+ throw uhd::runtime_error("ad9361_device_t::_set_fir_taps too many coefficients.");
+ }
+}
+
+size_t ad9361_device_t::_get_num_fir_taps(direction_t direction)
+{
+ boost::uint8_t num = 0;
+ if(direction == RX)
+ num = _io_iface->peek8(0x0F5);
+ else
+ num = _io_iface->peek8(0x065);
+ num = ((num >> 5) & 0x07);
+ return ((num + 1) * 16);
+}
+
+size_t ad9361_device_t::_get_fir_dec_int(direction_t direction)
+{
+ boost::uint8_t dec_int = 0;
+ if(direction == RX)
+ dec_int = _io_iface->peek8(0x003);
+ else
+ dec_int = _io_iface->peek8(0x002);
+ /*
+ * 0 = dec/int by 1 and bypass filter
+ * 1 = dec/int by 1
+ * 2 = dec/int by 2
+ * 3 = dec/int by 4 */
+ dec_int = (dec_int & 0x03);
+ if(dec_int == 3)
+ {
+ return 4;
+ }
+ return dec_int;
+}
+
+std::vector<boost::int16_t> ad9361_device_t::_get_fir_taps(direction_t direction, chain_t chain)
+{
+ int base;
+ size_t num_taps = _get_num_fir_taps(direction);
+ boost::uint8_t config;
+ boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5;
+ config = reg_numtaps | 0x02; //start the programming clock
+
+ if(chain == CHAIN_1)
+ {
+ config = config | (1 << 3);
+ } else if (chain == CHAIN_2){
+ config = config | (1 << 4);
+ } else {
+ throw uhd::runtime_error("[ad9361_device_t] Can not read both chains synchronously");
+ }
+
+ if(direction == RX)
+ {
+ base = 0xF0;
+ } else {
+ base = 0x60;
+ }
+
+ _io_iface->poke8(base+5,config);
+
+ std::vector<boost::int16_t> taps;
+ boost::uint8_t lower_val;
+ boost::uint8_t higher_val;
+ boost::uint16_t coeff;
+ for(size_t i = 0;i < num_taps;i++)
+ {
+ _io_iface->poke8(base,0x00+i);
+ lower_val = _io_iface->peek8(base+3);
+ higher_val = _io_iface->peek8(base+4);
+ coeff = ((higher_val << 8) | lower_val);
+ taps.push_back(boost::int16_t(coeff));
+ }
+
+ config = (config & (~(1 << 1))); //disable filter clock
+ _io_iface->poke8(base+5,config);
+ return taps;
+}
+
+/*
+ * Returns either RX TIA LPF or TX Secondary LPF
+ * depending on the direction.
+ * See UG570 for details on used scaling factors. */
+filter_info_base::sptr ad9361_device_t::_get_filter_lp_tia_sec(direction_t direction)
+{
+ double cutoff = 0;
+
+ if(direction == RX)
+ {
+ cutoff = 2.5 * _rx_tia_lp_bw;
+ } else {
+ cutoff = 5 * _tx_sec_lp_bw;
+ }
+
+ filter_info_base::sptr lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 0, "single-pole", cutoff, 20));
+ return lp;
+}
+
+/*
+ * Returns RX/TX BB LPF.
+ * See UG570 for details on used scaling factors. */
+filter_info_base::sptr ad9361_device_t::_get_filter_lp_bb(direction_t direction)
+{
+ double cutoff = 0;
+ if(direction == RX)
+ {
+ cutoff = 1.4 * _rx_bb_lp_bw;
+ } else {
+ cutoff = 1.6 * _tx_bb_lp_bw;
+ }
+
+ filter_info_base::sptr bb_lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 1, "third-order Butterworth", cutoff, 60));
+ return bb_lp;
+}
+
+/*
+ * For RX direction the DEC3 is returned.
+ * For TX direction the INT3 is returned. */
+filter_info_base::sptr ad9361_device_t::_get_filter_dec_int_3(direction_t direction)
+{
+ boost::uint8_t enable = 0;
+ double rate = _adcclock_freq;
+ double full_scale;
+ size_t dec = 0;
+ size_t interpol = 0;
+ filter_info_base::filter_type type = filter_info_base::DIGITAL_I16;
+ std::string name;
+ boost::int16_t taps_array_rx[] = {55, 83, 0, -393, -580, 0, 1914, 4041, 5120, 4041, 1914, 0, -580, -393, 0, 83, 55};
+ boost::int16_t taps_array_tx[] = {36, -19, 0, -156, -12, 0, 479, 233, 0, -1215, -993, 0, 3569, 6277, 8192, 6277, 3569, 0, -993, -1215, 0, 223, 479, 0, -12, -156, 0, -19, 36};
+ std::vector<boost::int16_t> taps;
+
+ filter_info_base::sptr ret;
+
+ if(direction == RX)
+ {
+ full_scale = 16384;
+ dec = 3;
+ interpol = 1;
+
+ enable = _io_iface->peek8(0x003);
+ enable = ((enable >> 4) & 0x03);
+ taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) );
+
+ } else {
+ full_scale = 8192;
+ dec = 1;
+ interpol = 3;
+
+ boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A);
+ use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01);
+ if(use_dac_clk_div == 1)
+ {
+ rate = rate / 2;
+ }
+
+ enable = _io_iface->peek8(0x002);
+ enable = ((enable >> 4) & 0x03);
+ if(enable == 2) //0 => int. by 1, 1 => int. by 2 (HB3), 2 => int. by 3
+ {
+ rate /= 3;
+ }
+
+ taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) );
+ }
+
+ ret = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 2) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps));
+ return ret;
+}
+
+filter_info_base::sptr ad9361_device_t::_get_filter_hb_3(direction_t direction)
+{
+ boost::uint8_t enable = 0;
+ double rate = _adcclock_freq;
+ double full_scale = 0;
+ size_t dec = 1;
+ size_t interpol = 1;
+ filter_info_base::filter_type type = filter_info_base::DIGITAL_I16;
+ boost::int16_t taps_array_rx[] = {1, 4, 6, 4, 1};
+ boost::int16_t taps_array_tx[] = {1, 2, 1};
+ std::vector<boost::int16_t> taps;
+
+ if(direction == RX)
+ {
+ full_scale = 16;
+ dec = 2;
+
+ enable = _io_iface->peek8(0x003);
+ enable = ((enable >> 4) & 0x03);
+ taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) );
+ } else {
+ full_scale = 2;
+ interpol = 2;
+
+ boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A);
+ use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01);
+ if(use_dac_clk_div == 1)
+ {
+ rate = rate / 2;
+ }
+
+ enable = _io_iface->peek8(0x002);
+ enable = ((enable >> 4) & 0x03);
+ if(enable == 1)
+ {
+ rate /= 2;
+ }
+ taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) );
+ }
+
+ filter_info_base::sptr hb = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 1) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps));
+ return hb;
+}
+
+filter_info_base::sptr ad9361_device_t::_get_filter_hb_2(direction_t direction)
+{
+ boost::uint8_t enable = 0;
+ double rate = _adcclock_freq;
+ double full_scale = 0;
+ size_t dec = 1;
+ size_t interpol = 1;
+ filter_info_base::filter_type type = filter_info_base::DIGITAL_I16;
+ boost::int16_t taps_array[] = {-9, 0, 73, 128, 73, 0, -9};
+ std::vector<boost::int16_t> taps(taps_array, taps_array + sizeof(taps_array) / sizeof(boost::int16_t) );
+
+ digital_filter_base<boost::int16_t>::sptr hb_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_3(direction));
+ digital_filter_base<boost::int16_t>::sptr dec_int_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_dec_int_3(direction));
+
+ if(direction == RX)
+ {
+ full_scale = 256;
+ dec = 2;
+ enable = _io_iface->peek8(0x003);
+ } else {
+ full_scale = 128;
+ interpol = 2;
+ enable = _io_iface->peek8(0x002);
+ }
+
+ enable = ((enable >> 3) & 0x01);
+
+ if(!(hb_3->is_bypassed()))
+ {
+ if(direction == RX)
+ {
+ rate = hb_3->get_output_rate();
+ }else if (direction == TX) {
+ rate = hb_3->get_input_rate();
+ if(enable)
+ {
+ rate /= 2;
+ }
+ }
+ } else { //else dec3/int3 or none of them is used.
+ if(direction == RX)
+ {
+ rate = dec_int_3->get_output_rate();
+ }else if (direction == TX) {
+ rate = dec_int_3->get_input_rate();
+ if(enable)
+ {
+ rate /= 2;
+ }
+ }
+ }
+
+ filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 3, rate, interpol, dec, full_scale, taps.size(), taps));
+ return hb;
+}
+
+filter_info_base::sptr ad9361_device_t::_get_filter_hb_1(direction_t direction)
+{
+ boost::uint8_t enable = 0;
+ double rate = 0;
+ double full_scale = 0;
+ size_t dec = 1;
+ size_t interpol = 1;
+ filter_info_base::filter_type type = filter_info_base::DIGITAL_I16;
+
+ std::vector<boost::int16_t> taps;
+ boost::int16_t taps_rx_array[] = {-8, 0, 42, 0, -147, 0, 619, 1013, 619, 0, -147, 0, 42, 0, -8};
+ boost::int16_t taps_tx_array[] = {-53, 0, 313, 0, -1155, 0, 4989, 8192, 4989, 0, -1155, 0, 313, 0, -53};
+
+ digital_filter_base<boost::int16_t>::sptr hb_2 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_2(direction));
+
+ if(direction == RX)
+ {
+ full_scale = 2048;
+ dec = 2;
+ enable = _io_iface->peek8(0x003);
+ enable = ((enable >> 2) & 0x01);
+ rate = hb_2->get_output_rate();
+ taps.assign(taps_rx_array, taps_rx_array + sizeof(taps_rx_array) / sizeof(boost::int16_t) );
+ } else if (direction == TX) {
+ full_scale = 8192;
+ interpol = 2;
+ enable = _io_iface->peek8(0x002);
+ enable = ((enable >> 2) & 0x01);
+ rate = hb_2->get_input_rate();
+ if(enable)
+ {
+ rate /= 2;
+ }
+ taps.assign(taps_tx_array, taps_tx_array + sizeof(taps_tx_array) / sizeof(boost::int16_t) );
+ }
+
+ filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 4, rate, interpol, dec, full_scale, taps.size(), taps));
+ return hb;
+}
+
+filter_info_base::sptr ad9361_device_t::_get_filter_fir(direction_t direction, chain_t chain)
+{
+ double rate = 0;
+ size_t dec = 1;
+ size_t interpol = 1;
+ size_t max_num_taps = 128;
+ boost::uint8_t enable = 1;
+
+ digital_filter_base<boost::int16_t>::sptr hb_1 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_1(direction));
+
+ if(direction == RX)
+ {
+ dec = _get_fir_dec_int(direction);
+ if(dec == 0)
+ {
+ enable = 0;
+ dec = 1;
+ }
+ interpol = 1;
+ rate = hb_1->get_output_rate();
+ }else if (direction == TX) {
+ interpol = _get_fir_dec_int(direction);
+ if(interpol == 0)
+ {
+ enable = 0;
+ interpol = 1;
+ }
+ dec = 1;
+ rate = hb_1->get_input_rate();
+ if(enable)
+ {
+ rate /= interpol;
+ }
+ }
+ max_num_taps = _get_num_fir_taps(direction);
+
+ filter_info_base::sptr fir(new digital_filter_fir<boost::int16_t>(filter_info_base::DIGITAL_FIR_I16, (enable == 0) ? true : false, 5, rate, interpol, dec, 32767, max_num_taps, _get_fir_taps(direction, chain)));
+
+ return fir;
+}
+
+void ad9361_device_t::_set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter)
+{
+ digital_filter_fir<boost::int16_t>::sptr fir = boost::dynamic_pointer_cast<digital_filter_fir<boost::int16_t> >(filter);
+ //only write taps. Ignore everything else for now
+ _set_fir_taps(direction, channel, fir->get_taps());
+}
+
+/*
+ * If BW of one of the analog filters gets overwritten manually,
+ * _tx_analog_bw and _rx_analog_bw are not valid any more!
+ * For useful data in those variables set_bw_filter method should be used
+ */
+void ad9361_device_t::_set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter)
+{
+ analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter);
+ double bw = lpf->get_cutoff();
+ if(direction == RX)
+ {
+ //remember: this function takes rf bw as its input and calibrated to 1.4 x the given value
+ _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(2 * bw / 1.4); //returns bb bw
+
+ } else {
+ //remember: this function takes rf bw as its input and calibrates to 1.6 x the given value
+ _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(2 * bw / 1.6);
+ }
+}
+
+void ad9361_device_t::_set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter)
+{
+ analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter);
+ double bw = lpf->get_cutoff();
+ if(direction == RX)
+ {
+ //remember: this function takes rf bw as its input and calibrated to 2.5 x the given value
+ _rx_tia_lp_bw = _calibrate_rx_TIAs(2 * bw / 2.5); //returns bb bw
+
+ } else {
+ //remember: this function takes rf bw as its input and calibrates to 5 x the given value
+ _tx_sec_lp_bw = _calibrate_secondary_tx_filter(2 * bw / 5);
+ }
+}
+
}}
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h
index 71ce78da7..98369c2fc 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h
@@ -1,5 +1,18 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014 Ettus Research
+//
+// 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_AD9361_DEVICE_H
@@ -8,6 +21,14 @@
#include <ad9361_client.h>
#include <boost/noncopyable.hpp>
#include <boost/thread/recursive_mutex.hpp>
+#include <uhd/types/filters.hpp>
+#include <uhd/types/sensors.hpp>
+#include <complex>
+#include <vector>
+#include <map>
+#include "boost/assign.hpp"
+#include "boost/bind.hpp"
+#include "boost/function.hpp"
namespace uhd { namespace usrp {
@@ -15,10 +36,41 @@ class ad9361_device_t : public boost::noncopyable
{
public:
enum direction_t { RX, TX };
- enum chain_t { CHAIN_1, CHAIN_2 };
+ enum gain_mode_t {GAIN_MODE_MANUAL, GAIN_MODE_SLOW_AGC, GAIN_MODE_FAST_AGC};
+ enum chain_t { CHAIN_1, CHAIN_2, CHAIN_BOTH };
ad9361_device_t(ad9361_params::sptr client, ad9361_io::sptr io_iface) :
- _client_params(client), _io_iface(io_iface) {}
+ _client_params(client), _io_iface(io_iface) {
+
+ /*
+ * This Boost.Assign to_container() workaround is necessary because STL containers
+ * apparently confuse newer versions of MSVC.
+ *
+ * Source: http://www.boost.org/doc/libs/1_55_0/libs/assign/doc/#portability
+ */
+
+ _rx_filters = (boost::assign::map_list_of("LPF_TIA", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1),
+ boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3)))
+ ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1),
+ boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3)))
+ ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0))
+ ("DEC_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0))
+ ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0))
+ ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0))
+ ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2),
+ boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_rx_filters);
+
+ _tx_filters = (boost::assign::map_list_of("LPF_SECONDARY", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1),
+ boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3)))
+ ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1),
+ boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3)))
+ ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0))
+ ("INT_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0))
+ ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0))
+ ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0))
+ ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2),
+ boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_tx_filters);
+ }
/* Initialize the AD9361 codec. */
void initialize();
@@ -69,21 +121,56 @@ public:
/* Read back the internal RSSI measurement data. */
double get_rssi(chain_t chain);
+ /*! Read the internal temperature sensor
+ *\param calibrate return raw sensor readings or apply calibration factor.
+ *\param num_samples number of measurements to average over
+ */
+ double get_average_temperature(const double cal_offset = -30.0, const size_t num_samples = 3);
+
+ /* Turn on/off AD9361's RX DC offset correction */
+ void set_dc_offset_auto(direction_t direction, const bool on);
+
+ /* Turn on/off AD9361's RX IQ imbalance correction */
+ void set_iq_balance_auto(direction_t direction, const bool on);
+
+ /* Configure AD9361's AGC module to use either fast or slow AGC mode. */
+ void set_agc_mode(chain_t chain, gain_mode_t gain_mode);
+
+ /* Enable AD9361's AGC gain mode. */
+ void set_agc(chain_t chain, bool enable);
+
+ /* Set bandwidth of AD9361's analog LP filters.
+ * Bandwidth should be RF bandwidth */
+ double set_bw_filter(direction_t direction, const double rf_bw);
+
+ /*
+ * Filter API implementation
+ * */
+ filter_info_base::sptr get_filter(direction_t direction, chain_t chain, const std::string &name);
+
+ void set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter);
+
+ std::vector<std::string> get_filter_names(direction_t direction);
+
//Constants
static const double AD9361_MAX_GAIN;
static const double AD9361_MAX_CLOCK_RATE;
+ static const double AD9361_CAL_VALID_WINDOW;
static const double AD9361_RECOMMENDED_MAX_BANDWIDTH;
private: //Methods
void _program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs);
void _setup_tx_fir(size_t num_taps, boost::int32_t interpolation);
void _setup_rx_fir(size_t num_taps, boost::int32_t decimation);
+ void _program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs);
+ void _setup_tx_fir(size_t num_taps);
+ void _setup_rx_fir(size_t num_taps);
void _calibrate_lock_bbpll();
void _calibrate_synth_charge_pumps();
- double _calibrate_baseband_rx_analog_filter();
- double _calibrate_baseband_tx_analog_filter();
- void _calibrate_secondary_tx_filter();
- void _calibrate_rx_TIAs();
+ double _calibrate_baseband_rx_analog_filter(double rfbw);
+ double _calibrate_baseband_tx_analog_filter(double rfbw);
+ double _calibrate_secondary_tx_filter(double rfbw);
+ double _calibrate_rx_TIAs(double rfbw);
void _setup_adc();
void _calibrate_baseband_dc_offset();
void _calibrate_rf_dc_offset();
@@ -92,12 +179,29 @@ private: //Methods
void _calibrate_tx_quadrature();
void _program_mixer_gm_subtable();
void _program_gain_table();
- void _setup_gain_control();
+ void _setup_gain_control(bool use_agc);
void _setup_synth(direction_t direction, double vcorate);
double _tune_bbvco(const double rate);
void _reprogram_gains();
double _tune_helper(direction_t direction, const double value);
double _setup_rates(const double rate);
+ double _get_temperature(const double cal_offset, const double timeout = 0.1);
+ void _configure_bb_rf_dc_tracking(const bool on);
+ void _setup_agc(chain_t chain, gain_mode_t gain_mode);
+ void _set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps);
+ std::vector<boost::int16_t> _get_fir_taps(direction_t direction, chain_t chain);
+ size_t _get_num_fir_taps(direction_t direction);
+ size_t _get_fir_dec_int(direction_t direction);
+ filter_info_base::sptr _get_filter_lp_tia_sec(direction_t direction);
+ filter_info_base::sptr _get_filter_lp_bb(direction_t direction);
+ filter_info_base::sptr _get_filter_dec_int_3(direction_t direction);
+ filter_info_base::sptr _get_filter_hb_3(direction_t direction);
+ filter_info_base::sptr _get_filter_hb_2(direction_t direction);
+ filter_info_base::sptr _get_filter_hb_1(direction_t direction);
+ filter_info_base::sptr _get_filter_fir(direction_t direction, chain_t chain);
+ void _set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter);
+ void _set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter);
+ void _set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter);
private: //Members
typedef struct {
@@ -110,11 +214,30 @@ private: //Members
boost::uint8_t bbftune_mode;
} chip_regs_t;
+ struct filter_query_helper
+ {
+ filter_query_helper(
+ boost::function<filter_info_base::sptr (direction_t, chain_t)> p_get,
+ boost::function<void (direction_t, chain_t, filter_info_base::sptr)> p_set
+ ) : get(p_get), set(p_set) { }
+
+ filter_query_helper(){ }
+
+ boost::function<filter_info_base::sptr (direction_t, chain_t)> get;
+ boost::function<void (direction_t, chain_t, filter_info_base::sptr)> set;
+ };
+
+ std::map<std::string, filter_query_helper> _rx_filters;
+ std::map<std::string, filter_query_helper> _tx_filters;
+
//Interfaces
ad9361_params::sptr _client_params;
ad9361_io::sptr _io_iface;
//Intermediate state
double _rx_freq, _tx_freq, _req_rx_freq, _req_tx_freq;
+ double _last_calibration_freq;
+ double _rx_analog_bw, _tx_analog_bw, _rx_bb_lp_bw, _tx_bb_lp_bw;
+ double _rx_tia_lp_bw, _tx_sec_lp_bw;
//! Current baseband sampling rate (this is the actual rate the device is
// is running at)
double _baseband_bw;
@@ -129,10 +252,14 @@ private: //Members
double _rx1_gain, _rx2_gain, _tx1_gain, _tx2_gain;
boost::int32_t _tfir_factor;
boost::int32_t _rfir_factor;
+ gain_mode_t _rx1_agc_mode, _rx2_agc_mode;
+ bool _rx1_agc_enable, _rx2_agc_enable;
//Register soft-copies
chip_regs_t _regs;
//Synchronization
boost::recursive_mutex _mutex;
+ bool _use_dc_offset_correction;
+ bool _use_iq_balance_correction;
};
}} //namespace
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h
index a1a85a49a..97ff858fd 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h
@@ -1,5 +1,18 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014 Ettus Research
+//
+// 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_AD9361_FILTER_TAPS_HPP
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h
index 786029d6e..8cd958e23 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h
@@ -1,5 +1,18 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014 Ettus Research
+//
+// 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_AD9361_GAIN_TABLES_HPP
@@ -7,91 +20,91 @@
#include <boost/cstdint.hpp>
-boost::uint8_t gain_table_sub_1300mhz[77][5] = { {0,0x00,0x00,0x20,1},
- {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0},
- {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0},
- {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0},
- {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0},
- {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0},
- {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0},
- {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0},
- {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0},
- {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0},
- {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x27,0x20,1},
- {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0},
- {34,0x04,0x2B,0x00,1}, {35,0x24,0x21,0x20,0}, {36,0x24,0x22,0x00,1},
- {37,0x44,0x20,0x20,0}, {38,0x44,0x21,0x00,0}, {39,0x44,0x22,0x00,0},
- {40,0x44,0x23,0x00,0}, {41,0x44,0x24,0x00,0}, {42,0x44,0x25,0x00,0},
- {43,0x44,0x26,0x00,0}, {44,0x44,0x27,0x00,0}, {45,0x44,0x28,0x00,0},
- {46,0x44,0x29,0x00,0}, {47,0x44,0x2A,0x00,0}, {48,0x44,0x2B,0x00,0},
- {49,0x44,0x2C,0x00,0}, {50,0x44,0x2D,0x00,0}, {51,0x44,0x2E,0x00,0},
- {52,0x44,0x2F,0x00,0}, {53,0x44,0x30,0x00,0}, {54,0x44,0x31,0x00,0},
- {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0},
- {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0},
- {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0},
- {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1},
- {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1},
- {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1},
- {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1},
- {76,0x6F,0x38,0x20,1}};
+boost::uint8_t gain_table_sub_1300mhz[77][3] = {
+{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 },
+{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 },
+{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 },
+{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 },
+{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 },
+{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 },
+{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 },
+{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 },
+{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 },
+{ 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 },
+{ 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 },
+{ 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 },
+{ 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 },
+{ 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 },
+{ 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 },
+{ 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 },
+{ 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 },
+{ 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 },
+{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 },
+{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 },
+{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 },
+{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } };
-boost::uint8_t gain_table_1300mhz_to_4000mhz[77][5] = { {0,0x00,0x00,0x20,1},
- {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0},
- {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0},
- {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0},
- {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0},
- {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0},
- {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0},
- {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0},
- {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0},
- {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0},
- {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x28,0x20,1},
- {31,0x04,0x29,0x00,0}, {32,0x04,0x2A,0x00,0}, {33,0x04,0x2B,0x00,0},
- {34,0x24,0x20,0x20,0}, {35,0x24,0x21,0x00,1}, {36,0x44,0x20,0x20,0},
- {37,0x44,0x21,0x00,1}, {38,0x44,0x22,0x00,0}, {39,0x44,0x23,0x00,0},
- {40,0x44,0x24,0x00,0}, {41,0x44,0x25,0x00,0}, {42,0x44,0x26,0x00,0},
- {43,0x44,0x27,0x00,0}, {44,0x44,0x28,0x00,0}, {45,0x44,0x29,0x00,0},
- {46,0x44,0x2A,0x00,0}, {47,0x44,0x2B,0x00,0}, {48,0x44,0x2C,0x00,0},
- {49,0x44,0x2D,0x00,0}, {50,0x44,0x2E,0x00,0}, {51,0x44,0x2F,0x00,0},
- {52,0x44,0x30,0x00,0}, {53,0x44,0x31,0x00,0}, {54,0x44,0x32,0x00,0},
- {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0},
- {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0},
- {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0},
- {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1},
- {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1},
- {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1},
- {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1},
- {76,0x6F,0x38,0x20,1}};
+boost::uint8_t gain_table_1300mhz_to_4000mhz[77][3] = {
+{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 },
+{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 },
+{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 },
+{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 },
+{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 },
+{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 },
+{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 },
+{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 },
+{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 },
+{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 },
+{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 },
+{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 },
+{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 },
+{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 },
+{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 },
+{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 },
+{ 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 },
+{ 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 },
+{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 },
+{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 },
+{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 },
+{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } };
-boost::uint8_t gain_table_4000mhz_to_6000mhz[77][5] = { {0,0x00,0x00,0x20,1},
- {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x00,0x00,0},
- {4,0x00,0x00,0x00,0}, {5,0x00,0x01,0x00,0}, {6,0x00,0x02,0x00,0},
- {7,0x00,0x03,0x00,0}, {8,0x01,0x01,0x20,1}, {9,0x01,0x02,0x00,0},
- {10,0x01,0x03,0x00,0}, {11,0x01,0x04,0x20,1}, {12,0x01,0x05,0x00,0},
- {13,0x01,0x06,0x00,0}, {14,0x01,0x07,0x00,0}, {15,0x01,0x08,0x00,0},
- {16,0x01,0x09,0x00,0}, {17,0x01,0x0A,0x00,0}, {18,0x01,0x0B,0x00,0},
- {19,0x01,0x0C,0x00,0}, {20,0x02,0x08,0x20,1}, {21,0x02,0x09,0x00,0},
- {22,0x02,0x0A,0x00,0}, {23,0x02,0x0B,0x20,1}, {24,0x02,0x0C,0x00,0},
- {25,0x02,0x0D,0x00,0}, {26,0x02,0x0E,0x00,0}, {27,0x02,0x0F,0x00,0},
- {28,0x02,0x2A,0x20,1}, {29,0x02,0x2B,0x00,0}, {30,0x04,0x27,0x20,1},
- {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0},
- {34,0x04,0x2B,0x00,0}, {35,0x04,0x2C,0x00,0}, {36,0x04,0x2D,0x00,0},
- {37,0x24,0x20,0x20,1}, {38,0x24,0x21,0x00,0}, {39,0x24,0x22,0x00,0},
- {40,0x44,0x20,0x20,1}, {41,0x44,0x21,0x00,0}, {42,0x44,0x22,0x00,0},
- {43,0x44,0x23,0x00,0}, {44,0x44,0x24,0x00,0}, {45,0x44,0x25,0x00,0},
- {46,0x44,0x26,0x00,0}, {47,0x44,0x27,0x00,0}, {48,0x44,0x28,0x00,0},
- {49,0x44,0x29,0x00,0}, {50,0x44,0x2A,0x00,0}, {51,0x44,0x2B,0x00,0},
- {52,0x44,0x2C,0x00,0}, {53,0x44,0x2D,0x00,0}, {54,0x44,0x2E,0x00,0},
- {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0},
- {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0},
- {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0},
- {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1},
- {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1},
- {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1},
- {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1},
- {76,0x6F,0x38,0x20,1}};
+boost::uint8_t gain_table_4000mhz_to_6000mhz[77][3] = {
+{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 },
+{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 },
+{ 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 },
+{ 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 },
+{ 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 },
+{ 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 },
+{ 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 },
+{ 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 },
+{ 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 },
+{ 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 },
+{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 },
+{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 },
+{ 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 },
+{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 },
+{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 },
+{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 },
+{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 },
+{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 },
+{ 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 },
+{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 },
+{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 },
+{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 },
+{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 },
+{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 },
+{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 },
+{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } };
#endif /* INCLUDED_AD9361_GAIN_TABLES_HPP */
diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h
index cb320e1f4..8155aae7c 100644
--- a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h
+++ b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h
@@ -1,25 +1,38 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014 Ettus Research
+//
+// 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_AD9361_SYNTH_LUT_HPP
#define INCLUDED_AD9361_SYNTH_LUT_HPP
-double vco_index[53] = {12605000000, 12245000000, 11906000000, 11588000000,
- 11288000000, 11007000000, 10742000000, 10492000000,
- 10258000000, 10036000000, 9827800000, 9631100000,
- 9445300000, 9269800000, 9103600000, 8946300000,
- 8797000000, 8655300000, 8520600000, 8392300000,
- 8269900000, 8153100000, 8041400000, 7934400000,
- 7831800000, 7733200000, 7638400000, 7547100000,
- 7459000000, 7374000000, 7291900000, 7212400000,
- 7135500000, 7061000000, 6988700000, 6918600000,
- 6850600000, 6784600000, 6720500000, 6658200000,
- 6597800000, 6539200000, 6482300000, 6427000000,
- 6373400000, 6321400000, 6270900000, 6222000000,
- 6174500000, 6128400000, 6083600000, 6040100000,
- 5997700000};
+double vco_index[53] = {12605000000.0, 12245000000.0, 11906000000.0, 11588000000.0,
+ 11288000000.0, 11007000000.0, 10742000000.0, 10492000000.0,
+ 10258000000.0, 10036000000.0, 9827800000.0, 9631100000.0,
+ 9445300000.0, 9269800000.0, 9103600000.0, 8946300000.0,
+ 8797000000.0, 8655300000.0, 8520600000.0, 8392300000.0,
+ 8269900000.0, 8153100000.0, 8041400000.0, 7934400000.0,
+ 7831800000.0, 7733200000.0, 7638400000.0, 7547100000.0,
+ 7459000000.0, 7374000000.0, 7291900000.0, 7212400000.0,
+ 7135500000.0, 7061000000.0, 6988700000.0, 6918600000.0,
+ 6850600000.0, 6784600000.0, 6720500000.0, 6658200000.0,
+ 6597800000.0, 6539200000.0, 6482300000.0, 6427000000.0,
+ 6373400000.0, 6321400000.0, 6270900000.0, 6222000000.0,
+ 6174500000.0, 6128400000.0, 6083600000.0, 6040100000.0,
+ 5997700000.0};
int synth_cal_lut[53][12] = { {10, 0, 4, 0, 15, 8, 8, 13, 4, 13, 15, 9},
{10, 0, 4, 0, 15, 8, 9, 13, 4, 13, 15, 9},
diff --git a/host/lib/usrp/common/ad936x_manager.cpp b/host/lib/usrp/common/ad936x_manager.cpp
new file mode 100644
index 000000000..b060880cd
--- /dev/null
+++ b/host/lib/usrp/common/ad936x_manager.cpp
@@ -0,0 +1,280 @@
+//
+// Copyright 2015 Ettus Research
+//
+// 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 "ad936x_manager.hpp"
+#include <uhd/utils/msg.hpp>
+#include <boost/foreach.hpp>
+#include <boost/functional/hash.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/****************************************************************************
+ * Default values
+ ***************************************************************************/
+const double ad936x_manager::DEFAULT_GAIN = 0;
+const double ad936x_manager::DEFAULT_BANDWIDTH = 56e6;
+const double ad936x_manager::DEFAULT_TICK_RATE = 16e6;
+const double ad936x_manager::DEFAULT_FREQ = 100e6; // Hz
+const uint32_t ad936x_manager::DEFAULT_DECIM = 128;
+const uint32_t ad936x_manager::DEFAULT_INTERP = 128;
+const bool ad936x_manager::DEFAULT_AUTO_DC_OFFSET = true;
+const bool ad936x_manager::DEFAULT_AUTO_IQ_BALANCE = true;
+const bool ad936x_manager::DEFAULT_AGC_ENABLE = true;
+
+class ad936x_manager_impl : public ad936x_manager
+{
+ public:
+ /************************************************************************
+ * Structor
+ ***********************************************************************/
+ ad936x_manager_impl(
+ const ad9361_ctrl::sptr &codec_ctrl,
+ const size_t n_frontends
+ ) : _codec_ctrl(codec_ctrl),
+ _n_frontends(n_frontends)
+ {
+ if (_n_frontends < 1 or _n_frontends > 2) {
+ throw uhd::runtime_error(str(
+ boost::format("AD936x device can only have either 1 or 2 frontends, not %d.")
+ % _n_frontends
+ ));
+ }
+ for (size_t i = 1; i <= _n_frontends; i++) {
+ _rx_frontends.push_back(str(boost::format("RX%d") % i));
+ _tx_frontends.push_back(str(boost::format("TX%d") % i));
+ }
+ }
+
+ /************************************************************************
+ * API Calls
+ ***********************************************************************/
+ void init_codec()
+ {
+ BOOST_FOREACH(const std::string &rx_fe, _rx_frontends) {
+ _codec_ctrl->set_gain(rx_fe, DEFAULT_GAIN);
+ _codec_ctrl->set_bw_filter(rx_fe, DEFAULT_BANDWIDTH);
+ _codec_ctrl->tune(rx_fe, DEFAULT_FREQ);
+ _codec_ctrl->set_dc_offset_auto(rx_fe, DEFAULT_AUTO_DC_OFFSET);
+ _codec_ctrl->set_iq_balance_auto(rx_fe, DEFAULT_AUTO_IQ_BALANCE);
+ _codec_ctrl->set_agc(rx_fe, DEFAULT_AGC_ENABLE);
+ }
+ BOOST_FOREACH(const std::string &tx_fe, _tx_frontends) {
+ _codec_ctrl->set_gain(tx_fe, DEFAULT_GAIN);
+ _codec_ctrl->set_bw_filter(tx_fe, DEFAULT_BANDWIDTH);
+ _codec_ctrl->tune(tx_fe, DEFAULT_FREQ);
+ }
+ }
+
+ void loopback_self_test(
+ wb_iface::sptr iface,
+ wb_iface::wb_addr_type codec_idle_addr,
+ wb_iface::wb_addr_type codec_readback_addr
+ ) {
+ _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));
+ for (size_t i = 0; i < 100; i++)
+ {
+ boost::hash_combine(hash, i);
+ const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
+ iface->poke32(codec_idle_addr, word32);
+ // We do 2 peeks so we have enough idleness for loopback to propagate
+ iface->peek64(codec_readback_addr);
+ const boost::uint64_t rb_word64 = iface->peek64(codec_readback_addr);
+ const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
+ const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
+ bool test_fail = word32 != rb_tx or word32 != rb_rx;
+ if (test_fail) {
+ UHD_MSG(status) << "fail" << std::endl;
+ throw uhd::runtime_error("CODEC loopback test failed.");
+ }
+ }
+ UHD_MSG(status) << "pass" << std::endl;
+ /* Zero out the idle data. */
+ iface->poke32(codec_idle_addr, 0);
+ _codec_ctrl->data_port_loopback(false);
+ }
+
+
+ double get_auto_tick_rate(
+ const double lcm_rate,
+ size_t num_chans
+ ) {
+ UHD_ASSERT_THROW(num_chans >= 1 and num_chans <= _n_frontends);
+ const uhd::meta_range_t rate_range = _codec_ctrl->get_clock_rate_range();
+ const double min_tick_rate = rate_range.start();
+ const double max_tick_rate = rate_range.stop() / num_chans;
+
+ // Check if the requested rate is within available limits:
+ if (uhd::math::fp_compare::fp_compare_delta<double>(lcm_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >
+ uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) {
+ throw uhd::value_error(str(
+ boost::format("[ad936x_manager] Cannot get determine a tick rate if sampling rate exceeds maximum tick rate (%f > %f)")
+ % lcm_rate % max_tick_rate
+ ));
+ }
+
+ // **** Choose the new rate ****
+ // Rules for choosing the tick rate:
+ // Choose a rate that is a power of 2 larger than the sampling rate,
+ // but at least 4. Cannot exceed the max tick rate, of course, but must
+ // be larger than the minimum tick rate.
+ // An equation that does all that is:
+ //
+ // f_auto = r * 2^floor(log2(f_max/r))
+ // = lcm_rate * multiplier
+ //
+ // where r is the base rate and f_max is the maximum tick rate. The case
+ // where floor() yields 1 must be caught.
+ // We use shifts here instead of 2^x because exp2() is not available in all compilers,
+ // also this guarantees no rounding issues. The type cast to int32_t serves as floor():
+ int32_t multiplier = (1 << int32_t(uhd::math::log2(max_tick_rate / lcm_rate)));
+ if (multiplier == 2 and lcm_rate >= min_tick_rate) {
+ // Don't bother (see above)
+ multiplier = 1;
+ }
+ const double new_rate = lcm_rate * multiplier;
+ UHD_ASSERT_THROW(
+ uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >=
+ uhd::math::fp_compare::fp_compare_delta<double>(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)
+ );
+ UHD_ASSERT_THROW(
+ uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <=
+ uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)
+ );
+
+ return new_rate;
+ }
+
+ bool check_bandwidth(double rate, const std::string dir)
+ {
+ if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) {
+ UHD_MSG(warning)
+ << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n"
+ << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)."
+ << std::endl;
+ return false;
+ }
+ return true;
+ }
+
+ void populate_frontend_subtree(uhd::property_tree::sptr subtree, const std::string &key, uhd::direction_t dir)
+ {
+ subtree->create<std::string>("name").set("FE-"+key);
+
+ // Sensors
+ subtree->create<sensor_value_t>("sensors/temp")
+ .publish(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))
+ ;
+ }
+
+ // Gains
+ BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
+ {
+ subtree->create<meta_range_t>(uhd::fs_path("gains") / name / "range")
+ .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))
+ ;
+ }
+
+ // FE Settings
+ subtree->create<std::string>("connection").set("IQ");
+ subtree->create<bool>("enabled").set(true);
+ subtree->create<bool>("use_lo_offset").set(false);
+
+ // Analog Bandwidths
+ subtree->create<double>("bandwidth/value")
+ .set(ad936x_manager::DEFAULT_BANDWIDTH)
+ .coerce(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))
+ ;
+
+ // LO Tuning
+ subtree->create<meta_range_t>("freq/range")
+ .publish(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))
+ ;
+
+ // Frontend corrections
+ if(dir == RX_DIRECTION)
+ {
+ 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))
+ ;
+ 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))
+ ;
+
+ // 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))
+ ;
+ subtree->create<std::string>("gain/agc/mode/value")
+ .subscribe(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)
+ ;
+ }
+
+ // 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));
+ }
+ }
+
+ private:
+ //! Store a pointer to an actual AD936x control object
+ ad9361_ctrl::sptr _codec_ctrl;
+
+ //! Do we have 1 or 2 frontends?
+ const size_t _n_frontends;
+
+ //! List of valid RX frontend names (RX1, RX2)
+ std::vector<std::string> _rx_frontends;
+ //! List of valid TX frontend names (TX1, TX2)
+ std::vector<std::string> _tx_frontends;
+}; /* class ad936x_manager_impl */
+
+ad936x_manager::sptr ad936x_manager::make(
+ const ad9361_ctrl::sptr &codec_ctrl,
+ const size_t n_frontends
+) {
+ return sptr(
+ new ad936x_manager_impl(codec_ctrl, n_frontends)
+ );
+}
+
diff --git a/host/lib/usrp/common/ad936x_manager.hpp b/host/lib/usrp/common/ad936x_manager.hpp
new file mode 100644
index 000000000..9b4a351c6
--- /dev/null
+++ b/host/lib/usrp/common/ad936x_manager.hpp
@@ -0,0 +1,132 @@
+//
+// Copyright 2015 Ettus Research
+//
+// 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_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 <boost/shared_ptr.hpp>
+#include "ad9361_ctrl.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(
+ wb_iface::sptr iface,
+ wb_iface::wb_addr_type codec_idle_addr,
+ wb_iface::wb_addr_type codec_readback_addr
+ ) = 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/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp
index 164437f40..e22834fd9 100644
--- a/host/lib/usrp/cores/gpio_core_200.hpp
+++ b/host/lib/usrp/cores/gpio_core_200.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011,2014 Ettus Research LLC
+// Copyright 2011,2014,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
@@ -20,10 +20,34 @@
#include <uhd/config.hpp>
#include <uhd/usrp/dboard_iface.hpp>
+#include <boost/assign.hpp>
#include <boost/cstdint.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include <uhd/types/wb_iface.hpp>
+#include <map>
+
+typedef enum {
+ GPIO_CTRL,
+ GPIO_DDR,
+ GPIO_OUT,
+ GPIO_ATR_0X,
+ GPIO_ATR_RX,
+ GPIO_ATR_TX,
+ GPIO_ATR_XX
+} gpio_attr_t;
+
+typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t;
+static const gpio_attr_map_t gpio_attr_map =
+ boost::assign::map_list_of
+ (GPIO_CTRL, "CTRL")
+ (GPIO_DDR, "DDR")
+ (GPIO_OUT, "OUT")
+ (GPIO_ATR_0X, "ATR_0X")
+ (GPIO_ATR_RX, "ATR_RX")
+ (GPIO_ATR_TX, "ATR_TX")
+ (GPIO_ATR_XX, "ATR_XX")
+;
class gpio_core_200 : boost::noncopyable{
public:
diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp
index 6a36e8fa1..b899085c0 100644
--- a/host/lib/usrp/cores/rx_dsp_core_200.cpp
+++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp
@@ -295,6 +295,7 @@ public:
_iface->poke32(REG_RX_CTRL_FORMAT, format_word);
}
+
private:
wb_iface::sptr _iface;
const size_t _dsp_base, _ctrl_base;
diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
index 8a131ffb4..18dabade0 100644
--- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp
+++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
@@ -46,6 +46,9 @@ template <class T> T ceil_log2(T num){
using namespace uhd;
+const double rx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0;
+const double rx_dsp_core_3000::DEFAULT_RATE = 1e6;
+
rx_dsp_core_3000::~rx_dsp_core_3000(void){
/* NOP */
}
@@ -263,6 +266,24 @@ public:
this->update_scalar();
}
+ void populate_subtree(property_tree::sptr subtree)
+ {
+ subtree->create<meta_range_t>("rate/range")
+ .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, this))
+ ;
+ subtree->create<double>("rate/value")
+ .set(DEFAULT_RATE)
+ .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1))
+ ;
+ subtree->create<double>("freq/value")
+ .set(DEFAULT_CORDIC_FREQ)
+ .coerce(boost::bind(&rx_dsp_core_3000::set_freq, this, _1))
+ ;
+ subtree->create<meta_range_t>("freq/range")
+ .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, this))
+ ;
+ }
+
private:
wb_iface::sptr _iface;
const size_t _dsp_base;
diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp
index 89059e953..65801de1d 100644
--- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp
+++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp
@@ -21,14 +21,18 @@
#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 <uhd/property_tree.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;
@@ -56,6 +60,8 @@ public:
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/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp
index b73896b57..7ac920553 100644
--- a/host/lib/usrp/cores/rx_frontend_core_200.cpp
+++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp
@@ -17,6 +17,7 @@
#include "rx_frontend_core_200.hpp"
#include <boost/math/special_functions/round.hpp>
+#include <boost/bind.hpp>
using namespace uhd;
@@ -38,6 +39,10 @@ rx_frontend_core_200::~rx_frontend_core_200(void){
/* NOP */
}
+const std::complex<double> rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0);
+const bool rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE = true;
+const std::complex<double> rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0);
+
class rx_frontend_core_200_impl : public rx_frontend_core_200{
public:
rx_frontend_core_200_impl(wb_iface::sptr iface, const size_t base):
@@ -74,6 +79,22 @@ public:
_iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18));
}
+ void populate_subtree(uhd::property_tree::sptr subtree)
+ {
+ subtree->create<std::complex<double> >("dc_offset/value")
+ .set(DEFAULT_DC_OFFSET_VALUE)
+ .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1))
+ ;
+ subtree->create<bool>("dc_offset/enable")
+ .set(DEFAULT_DC_OFFSET_ENABLE)
+ .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1))
+ ;
+ subtree->create<std::complex<double> >("iq_balance/value")
+ .set(DEFAULT_IQ_BALANCE_VALUE)
+ .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1))
+ ;
+ }
+
private:
boost::int32_t _i_dc_off, _q_dc_off;
wb_iface::sptr _iface;
diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp
index 9b18e2089..32ce77e00 100644
--- a/host/lib/usrp/cores/rx_frontend_core_200.hpp
+++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp
@@ -19,14 +19,19 @@
#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 <uhd/types/wb_iface.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;
@@ -41,6 +46,8 @@ public:
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/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
index 736205402..93b70435f 100644
--- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp
+++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
@@ -37,6 +37,9 @@ template <class T> T ceil_log2(T num){
using namespace uhd;
+const double tx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0;
+const double tx_dsp_core_3000::DEFAULT_RATE = 1e6;
+
tx_dsp_core_3000::~tx_dsp_core_3000(void){
/* NOP */
}
@@ -200,6 +203,24 @@ public:
this->update_scalar();
}
+ void populate_subtree(property_tree::sptr subtree)
+ {
+ subtree->create<meta_range_t>("rate/range")
+ .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, this))
+ ;
+ subtree->create<double>("rate/value")
+ .set(DEFAULT_RATE)
+ .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1))
+ ;
+ subtree->create<double>("freq/value")
+ .set(DEFAULT_CORDIC_FREQ)
+ .coerce(boost::bind(&tx_dsp_core_3000::set_freq, this, _1))
+ ;
+ subtree->create<meta_range_t>("freq/range")
+ .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, this))
+ ;
+ }
+
private:
wb_iface::sptr _iface;
const size_t _dsp_base;
diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp
index a51cb2803..fbc43add6 100644
--- a/host/lib/usrp/cores/tx_dsp_core_3000.hpp
+++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp
@@ -21,12 +21,16 @@
#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>
-#include <uhd/types/wb_iface.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;
@@ -51,6 +55,8 @@ public:
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/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp
index 7000f46bd..0fa028571 100644
--- a/host/lib/usrp/cores/tx_frontend_core_200.cpp
+++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp
@@ -20,6 +20,7 @@
#include <uhd/exception.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/math/special_functions/round.hpp>
+#include <boost/bind.hpp>
using namespace uhd;
@@ -29,6 +30,9 @@ using namespace uhd;
#define REG_TX_FE_PHASE_CORRECTION _base + 12 //18 bits
#define REG_TX_FE_MUX _base + 16 //8 bits (std output = 0x10, reversed = 0x01)
+const std::complex<double> tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0);
+const std::complex<double> tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0);
+
static boost::uint32_t fs_to_bits(const double num, const size_t bits){
return boost::int32_t(boost::math::round(num * (1 << (bits-1))));
}
@@ -71,6 +75,18 @@ public:
_iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18));
}
+ void populate_subtree(uhd::property_tree::sptr subtree)
+ {
+ subtree->create< std::complex<double> >("dc_offset/value")
+ .set(DEFAULT_DC_OFFSET_VALUE)
+ .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1))
+ ;
+ subtree->create< std::complex<double> >("iq_balance/value")
+ .set(DEFAULT_IQ_BALANCE_VALUE)
+ .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1))
+ ;
+ }
+
private:
wb_iface::sptr _iface;
const size_t _base;
diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp
index 0b89ea818..912256329 100644
--- a/host/lib/usrp/cores/tx_frontend_core_200.hpp
+++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp
@@ -19,9 +19,10 @@
#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 <uhd/types/wb_iface.hpp>
#include <complex>
#include <string>
@@ -29,6 +30,9 @@ 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);
@@ -39,6 +43,8 @@ public:
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/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp
index 8336117b8..daf9a8dfd 100644
--- a/host/lib/usrp/dboard/db_cbx.cpp
+++ b/host/lib/usrp/dboard/db_cbx.cpp
@@ -38,7 +38,7 @@ sbx_xcvr::cbx::~cbx(void){
/* NOP */
}
-void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> &regs)
+void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> &regs)
{
BOOST_FOREACH(boost::uint32_t reg, regs)
{
diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp
index a08d22537..4800bbd83 100644
--- a/host/lib/usrp/dboard/db_sbx_common.hpp
+++ b/host/lib/usrp/dboard/db_sbx_common.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011-2014 Ettus Research LLC
+// Copyright 2011-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
@@ -225,7 +225,7 @@ protected:
/*! This is the registered instance of the wrapper class, sbx_base. */
sbx_xcvr *self_base;
private:
- void write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> &regs);
+ void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> &regs);
max287x_iface::sptr _txlo;
max287x_iface::sptr _rxlo;
};
diff --git a/host/lib/usrp/dboard_eeprom.cpp b/host/lib/usrp/dboard_eeprom.cpp
index f2bee47a9..3b56ae19a 100644
--- a/host/lib/usrp/dboard_eeprom.cpp
+++ b/host/lib/usrp/dboard_eeprom.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2011 Ettus Research LLC
+// Copyright 2010-2011,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
@@ -15,6 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
+#include <uhd/types/byte_vector.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/exception.hpp>
#include <uhd/utils/log.hpp>
@@ -27,30 +28,6 @@
using namespace uhd;
using namespace uhd::usrp;
-/***********************************************************************
- * Utility functions
- **********************************************************************/
-
-//! create a string from a byte vector, return empty if invalid ascii
-static const std::string bytes_to_string(const byte_vector_t &bytes){
- std::string out;
- BOOST_FOREACH(boost::uint8_t byte, bytes){
- if (byte < 32 or byte > 127) return out;
- out += byte;
- }
- return out;
-}
-
-//! create a byte vector from a string, null terminate unless max length
-static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){
- byte_vector_t bytes;
- for (size_t i = 0; i < std::min(string.size(), max_length); i++){
- bytes.push_back(string[i]);
- }
- if (bytes.size() < max_length - 1) bytes.push_back('\0');
- return bytes;
-}
-
////////////////////////////////////////////////////////////////////////
// format of daughterboard EEPROM
// 00: 0xDB code for ``I'm a daughterboard''
diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp
index ac419e0e0..6d3c08534 100644
--- a/host/lib/usrp/e100/e100_impl.cpp
+++ b/host/lib/usrp/e100/e100_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2012,2014 Ettus Research LLC
+// Copyright 2010-2012,2014-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
@@ -28,6 +28,7 @@
#include <boost/functional/hash.hpp>
#include <boost/assign/list_of.hpp>
#include <fstream>
+#include <iostream>
#include <ctime>
using namespace uhd;
@@ -45,7 +46,7 @@ namespace fs = boost::filesystem;
/***********************************************************************
* Discovery
**********************************************************************/
-static device_addrs_t e100_find(const device_addr_t &hint){
+device_addrs_t e100_find(const device_addr_t &hint){
device_addrs_t e100_addrs;
//return an empty list of addresses when type is set to non-usrp-e
@@ -104,17 +105,10 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost
("E110", "usrp_e110_fpga.bin")
;
-/***********************************************************************
- * Structors
- **********************************************************************/
-e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
- _tree = property_tree::make();
- _type = device::USRP;
- _ignore_cal_file = device_addr.has_key("ignore-cal-file");
-
+std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr){
//read the eeprom so we can determine the hardware
- _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE);
- const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY);
+ uhd::i2c_iface::sptr dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE);
+ const mboard_eeprom_t mb_eeprom(*dev_i2c_iface, E100_EEPROM_MAP_KEY);
//determine the model string for this device
const std::string model = device_addr.get("model", mb_eeprom.get("model", ""));
@@ -126,7 +120,21 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
) % model));
//extract the fpga path and compute hash
- const std::string default_fpga_file_name = model_to_fpga_file_name[model];
+ return model_to_fpga_file_name[model];
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
+ _tree = property_tree::make();
+ _type = device::USRP;
+ _ignore_cal_file = device_addr.has_key("ignore-cal-file");
+
+ _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE);
+ const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY);
+ const std::string default_fpga_file_name = get_default_e1x0_fpga_image(device_addr);
+ const std::string model = device_addr["model"];
std::string e100_fpga_image;
try{
e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name));
diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp
index 4efc21427..d00668224 100644
--- a/host/lib/usrp/e100/e100_impl.hpp
+++ b/host/lib/usrp/e100/e100_impl.hpp
@@ -29,6 +29,7 @@
#include "recv_packet_demuxer.hpp"
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
@@ -68,6 +69,9 @@ uhd::usrp::dboard_iface::sptr make_e100_dboard_iface(
e100_codec_ctrl::sptr codec
);
+uhd::device_addrs_t e100_find(const uhd::device_addr_t &hint);
+std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr);
+
/*!
* USRP-E100 implementation guts:
* The implementation details are encapsulated here.
diff --git a/host/lib/usrp/e100/fpga_downloader.cpp b/host/lib/usrp/e100/fpga_downloader.cpp
index c9d77f560..9abde32f7 100644
--- a/host/lib/usrp/e100/fpga_downloader.cpp
+++ b/host/lib/usrp/e100/fpga_downloader.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2011,2014 Ettus Research LLC
+// Copyright 2010-2011,2014-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
@@ -17,8 +17,16 @@
#include <uhd/config.hpp>
#ifdef UHD_DLL_EXPORTS
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
#include <uhd/exception.hpp>
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/utils/msg.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include "e100_impl.hpp"
#else //special case when this file is externally included
#include <stdexcept>
#include <iostream>
@@ -270,3 +278,34 @@ void e100_load_fpga(const std::string &bin_file){
}
+#ifdef UHD_DLL_EXPORTS
+namespace fs = boost::filesystem;
+
+static bool e100_image_loader(const uhd::image_loader::image_loader_args_t &image_loader_args){
+ // Make sure this is an E1x0
+ uhd::device_addrs_t devs = e100_find(uhd::device_addr_t());
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ std::string fpga_filename;
+ if(image_loader_args.fpga_path == ""){
+ fpga_filename = uhd::find_image_path(get_default_e1x0_fpga_image(devs[0]));
+ }
+ else{
+ if(not fs::exists(image_loader_args.fpga_path)){
+ throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.")
+ % image_loader_args.fpga_path));
+ }
+ else fpga_filename = image_loader_args.fpga_path;
+ }
+
+ e100_load_fpga(fpga_filename);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_e100_image_loader){
+ std::string recovery_instructions = "The default FPGA image will be loaded the next time "
+ "UHD uses this device.";
+
+ uhd::image_loader::register_image_loader("e100", e100_image_loader, recovery_instructions);
+}
+#endif /* UHD_DLL_EXPORTS */
diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt
index 9ee9b5521..26e34294a 100644
--- a/host/lib/usrp/e300/CMakeLists.txt
+++ b/host/lib/usrp/e300/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2013-2014 Ettus Research LLC
+# Copyright 2013-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
@@ -39,8 +39,6 @@ IF(ENABLE_E300)
${CMAKE_CURRENT_SOURCE_DIR}/e300_i2c.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/e300_async_serial.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/e300_ublox_control_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp
)
LIBUHD_APPEND_SOURCES(${E300_SOURCES})
@@ -52,4 +50,12 @@ IF(ENABLE_E300)
PROPERTIES COMPILE_DEFINITIONS "E300_NATIVE=1"
)
ENDIF(UDEV_FOUND)
+
+ IF(ENABLE_GPSD)
+ SET_SOURCE_FILES_PROPERTIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.hpp
+ PROPERTIES COMPILE_DEFINITIONS "E300_GPSD=1"
+ )
+ ENDIF(ENABLE_GPSD)
ENDIF(ENABLE_E300)
diff --git a/host/lib/usrp/e300/e300_async_serial.cpp b/host/lib/usrp/e300/e300_async_serial.cpp
deleted file mode 100644
index cdf18f7f7..000000000
--- a/host/lib/usrp/e300/e300_async_serial.cpp
+++ /dev/null
@@ -1,245 +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/>.
-//
-
-#include "e300_async_serial.hpp"
-
-namespace uhd { namespace usrp { namespace gps {
-
-async_serial::async_serial()
- : _io(),
- _port(_io),
- _background_thread(),
- _open(false),
- _error(false)
-{
-}
-
-async_serial::async_serial(
- const std::string &node,
- const size_t baud_rate,
- boost::asio::serial_port_base::parity opt_parity,
- boost::asio::serial_port_base::character_size opt_csize,
- boost::asio::serial_port_base::flow_control opt_flow,
- boost::asio::serial_port_base::stop_bits opt_stop)
- : _io(),
- _port(_io),
- _background_thread(),
- _open(false),
- _error(false)
-{
- open(node, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop);
-}
-
-void async_serial::open(
- const std::string &node,
- const size_t baud_rate,
- boost::asio::serial_port_base::parity opt_parity,
- boost::asio::serial_port_base::character_size opt_csize,
- boost::asio::serial_port_base::flow_control opt_flow,
- boost::asio::serial_port_base::stop_bits opt_stop)
-{
- if(is_open())
- close();
-
- _set_error_status(true);
- _port.open(node);
- _port.set_option(
- boost::asio::serial_port_base::baud_rate(baud_rate));
- _port.set_option(opt_parity);
- _port.set_option(opt_csize);
- _port.set_option(opt_flow);
- _port.set_option(opt_stop);
-
- _io.post(boost::bind(&async_serial::_do_read, this));
-
- boost::thread t(boost::bind(&boost::asio::io_service::run, &_io));
- _background_thread.swap(t);
- _set_error_status(false);
- _open=true;
-}
-
-bool async_serial::is_open() const
-{
- return _open;
-}
-
-bool async_serial::error_status() const
-{
- boost::lock_guard<boost::mutex> l(_error_mutex);
- return _error;
-}
-
-void async_serial::close()
-{
- if(!is_open())
- return;
-
- _open=false;
- _io.post(boost::bind(&async_serial::_do_close, this));
- _background_thread.join();
- _io.reset();
- if(error_status())
- throw(boost::system::system_error(boost::system::error_code(),
- "Error while closing the device"));
-}
-
-void async_serial::write(const char *data, size_t size)
-{
- {
- boost::lock_guard<boost::mutex> l(_write_queue_mutex);
- _write_queue.insert(_write_queue.end(), data, data+size);
- }
- _io.post(boost::bind(&async_serial::_do_write, this));
-}
-
-void async_serial::write(const std::vector<char> &data)
-{
- {
- boost::lock_guard<boost::mutex> l(_write_queue_mutex);
- _write_queue.insert(
- _write_queue.end(),
- data.begin(),
- data.end());
- }
- _io.post(boost::bind(&async_serial::_do_write, this));
-}
-
-void async_serial::write_string(const std::string &s)
-{
- {
- boost::lock_guard<boost::mutex> l(_write_queue_mutex);
- _write_queue.insert(
- _write_queue.end(),
- s.begin(),
- s.end());
- }
- _io.post(boost::bind(&async_serial::_do_write, this));
-}
-
-async_serial::~async_serial()
-{
- if(is_open()) {
- try {
- close();
- } catch(...) {
- //Don't throw from a destructor
- }
- }
-}
-
-void async_serial::_do_read()
-{
- _port.async_read_some(boost::asio::buffer(
- _read_buffer,READ_BUFFER_SIZE),
- boost::bind(&async_serial::_read_end,
- this,
- boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred));
-}
-
-void async_serial::_read_end(
- const boost::system::error_code& error,
- size_t bytes_transferred)
-{
- if(error) {
- if(is_open()) {
- _do_close();
- _set_error_status(true);
- }
- } else {
- if(_callback)
- _callback(
- _read_buffer,
- bytes_transferred);
- _do_read();
- }
-}
-
-void async_serial::_do_write()
-{
- // if a write operation is already in progress, do nothing
- if(_write_buffer == 0) {
- boost::lock_guard<boost::mutex> l(_write_queue_mutex);
- _write_buffer_size=_write_queue.size();
- _write_buffer.reset(new char[_write_queue.size()]);
- std::copy(_write_queue.begin(),_write_queue.end(),
- _write_buffer.get());
- _write_queue.clear();
- async_write(
- _port, boost::asio::buffer(_write_buffer.get(),
- _write_buffer_size),
- boost::bind(
- &async_serial::_write_end,
- this,
- boost::asio::placeholders::error));
- }
-}
-
-void async_serial::_write_end(const boost::system::error_code& error)
-{
- if(!error) {
- boost::lock_guard<boost::mutex> l(_write_queue_mutex);
- if(_write_queue.empty()) {
- _write_buffer.reset();
- _write_buffer_size=0;
- return;
- }
- _write_buffer_size = _write_queue.size();
- _write_buffer.reset(new char[_write_queue.size()]);
- std::copy(_write_queue.begin(),_write_queue.end(),
- _write_buffer.get());
- _write_queue.clear();
- async_write(
- _port,
- boost::asio::buffer(_write_buffer.get(),
- _write_buffer_size),
- boost::bind(
- &async_serial::_write_end,
- this,
- boost::asio::placeholders::error));
- } else {
- _set_error_status(true);
- _do_close();
- }
-}
-
-void async_serial::_do_close()
-{
- boost::system::error_code ec;
- _port.cancel(ec);
- if(ec)
- _set_error_status(true);
- _port.close(ec);
- if(ec)
- _set_error_status(true);
-}
-
-void async_serial::_set_error_status(const bool e)
-{
- boost::lock_guard<boost::mutex> l(_error_mutex);
- _error = e;
-}
-
-
-void async_serial::set_read_callback(
- const boost::function<void (const char*, size_t)> &callback)
-{
- _callback = callback;
-}
-
-
-}}} // namespace
diff --git a/host/lib/usrp/e300/e300_async_serial.hpp b/host/lib/usrp/e300/e300_async_serial.hpp
deleted file mode 100644
index fafc7de3d..000000000
--- a/host/lib/usrp/e300/e300_async_serial.hpp
+++ /dev/null
@@ -1,113 +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/>.
-//
-
-#ifndef INCLUDED_ASYNC_SERIAL_HPP
-#define INCLUDED_ASYNC_SERIAL_HPP
-
-#include <boost/asio.hpp>
-#include <boost/bind.hpp>
-#include <boost/thread.hpp>
-#include <boost/utility.hpp>
-#include <boost/function.hpp>
-#include <boost/shared_array.hpp>
-
-namespace uhd { namespace usrp { namespace gps {
-
-class async_serial : private boost::noncopyable
-{
-public:
- async_serial();
- ~async_serial();
-
- async_serial(const std::string &node, const size_t baud_rate,
- boost::asio::serial_port_base::parity opt_parity=
- boost::asio::serial_port_base::parity(
- boost::asio::serial_port_base::parity::none),
- boost::asio::serial_port_base::character_size opt_csize=
- boost::asio::serial_port_base::character_size(8),
- boost::asio::serial_port_base::flow_control opt_flow=
- boost::asio::serial_port_base::flow_control(
- boost::asio::serial_port_base::flow_control::none),
- boost::asio::serial_port_base::stop_bits opt_stop=
- boost::asio::serial_port_base::stop_bits(
- boost::asio::serial_port_base::stop_bits::one));
-
- void open(const std::string& node, const size_t baud_rate,
- boost::asio::serial_port_base::parity opt_parity=
- boost::asio::serial_port_base::parity(
- boost::asio::serial_port_base::parity::none),
- boost::asio::serial_port_base::character_size opt_csize=
- boost::asio::serial_port_base::character_size(8),
- boost::asio::serial_port_base::flow_control opt_flow=
- boost::asio::serial_port_base::flow_control(
- boost::asio::serial_port_base::flow_control::none),
- boost::asio::serial_port_base::stop_bits opt_stop=
- boost::asio::serial_port_base::stop_bits(
- boost::asio::serial_port_base::stop_bits::one));
-
- bool is_open(void) const;
-
- bool error_status(void) const;
-
- void close(void);
-
- void write(const char *data, const size_t size);
- void write(const std::vector<char> &data);
-
- void write_string(const std::string &s);
-
- static const size_t READ_BUFFER_SIZE=512;
-
- void set_read_callback(
- const boost::function<void (const char*, size_t)>& callback);
-
- void clear_callback();
-
-private: // methods
- void _do_read();
-
- void _read_end(
- const boost::system::error_code &error,
- size_t bytes_transferred);
-
- void _do_write();
-
- void _write_end(const boost::system::error_code &error);
-
- void _do_close();
-
- void _set_error_status(const bool e);
-private: // members
- boost::asio::io_service _io;
- boost::asio::serial_port _port;
- boost::thread _background_thread;
- bool _open;
- bool _error;
- mutable boost::mutex _error_mutex;
-
- std::vector<char> _write_queue;
- boost::shared_array<char> _write_buffer;
- size_t _write_buffer_size;
- boost::mutex _write_queue_mutex;
- char _read_buffer[READ_BUFFER_SIZE];
-
- boost::function<void (const char*, size_t)> _callback;
-};
-
-}}} // namespace
-
-#endif //INCLUDED_ASYNC_SERIAL_HPP
diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp
index db5b37055..29117e21f 100644
--- a/host/lib/usrp/e300/e300_common.cpp
+++ b/host/lib/usrp/e300/e300_common.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014-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
@@ -14,8 +14,12 @@
// 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 <uhd/image_loader.hpp>
#include <uhd/utils/msg.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include "e300_impl.hpp"
#include "e300_fifo_config.hpp"
#include "e300_fifo_config.hpp"
@@ -23,6 +27,7 @@
#include <boost/filesystem.hpp>
#include <fstream>
+#include <string>
namespace uhd { namespace usrp { namespace e300 {
@@ -54,6 +59,34 @@ void load_fpga_image(const std::string &path)
UHD_MSG(status) << " done" << std::endl;
}
+static bool e300_image_loader(const image_loader::image_loader_args_t &image_loader_args) {
+ // Make sure this is an E3x0 and we don't want to use anything connected
+ uhd::device_addrs_t devs = e300_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ std::string fpga_filename, idle_image; // idle_image never used, just needed for function
+ if(image_loader_args.fpga_path == "") {
+ get_e3x0_fpga_images(devs[0], fpga_filename, idle_image);
+ }
+ else {
+ if(not boost::filesystem::exists(image_loader_args.fpga_path)) {
+ throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.")
+ % image_loader_args.fpga_path));
+ }
+ else fpga_filename = image_loader_args.fpga_path;
+ }
+
+ load_fpga_image(fpga_filename);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_e300_image_loader) {
+ std::string recovery_instructions = "The default FPGA image will be loaded the next "
+ "time UHD uses this device.";
+
+ image_loader::register_image_loader("e3x0", e300_image_loader, recovery_instructions);
+}
+
}
}}}
diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp
index d409062c5..267897e03 100644
--- a/host/lib/usrp/e300/e300_defaults.hpp
+++ b/host/lib/usrp/e300/e300_defaults.hpp
@@ -23,18 +23,13 @@
namespace uhd { namespace usrp { namespace e300 {
static const double DEFAULT_TICK_RATE = 32e6;
-static const double MAX_TICK_RATE = 50e6;
-static const double MIN_TICK_RATE = 1e6;
+static const double MIN_TICK_RATE = 10e6;
static const double DEFAULT_TX_SAMP_RATE = 1.0e6;
static const double DEFAULT_RX_SAMP_RATE = 1.0e6;
static const double DEFAULT_DDC_FREQ = 0.0;
static const double DEFAULT_DUC_FREQ = 0.0;
-static const double DEFAULT_FE_GAIN = 0.0;
-static const double DEFAULT_FE_FREQ = 1.0e9;
-static const double DEFAULT_FE_BW = 56e6;
-
static const std::string DEFAULT_TIME_SRC = "internal";
static const std::string DEFAULT_CLOCK_SRC = "internal";
@@ -73,7 +68,7 @@ public:
digital_interface_delays_t get_digital_interface_timing() {
digital_interface_delays_t delays;
delays.rx_clk_delay = 0;
- delays.rx_data_delay = 0xF;
+ delays.rx_data_delay = 0x8;
delays.tx_clk_delay = 0;
delays.tx_data_delay = 0xF;
return delays;
diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp
index eea4d7f63..fbbca329a 100644
--- a/host/lib/usrp/e300/e300_fpga_defs.hpp
+++ b/host/lib/usrp/e300/e300_fpga_defs.hpp
@@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga {
static const size_t NUM_RADIOS = 2;
-static const boost::uint32_t COMPAT_MAJOR = 8;
+static const boost::uint32_t COMPAT_MAJOR = 9;
static const boost::uint32_t COMPAT_MINOR = 0;
}}}} // namespace
diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp
index a08168eab..6eb63c786 100644
--- a/host/lib/usrp/e300/e300_impl.cpp
+++ b/host/lib/usrp/e300/e300_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-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
@@ -121,7 +121,7 @@ static bool is_loopback(const if_addrs_t &if_addrs)
return if_addrs.inet == asio::ip::address_v4::loopback().to_string();
}
-static device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
+device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
{
// handle multi device discovery
device_addrs_t hints = separate_device_addr(multi_dev_hint);
@@ -268,6 +268,36 @@ static device::sptr e300_make(const device_addr_t &device_addr)
return device::sptr(new e300_impl(device_addr));
}
+// Common code used by e300_impl and e300_image_loader
+void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr,
+ std::string &fpga_image,
+ std::string &idle_image){
+ const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
+ device_addr["product"]);
+
+ //extract the FPGA path for the e300
+ switch(e300_eeprom_manager::get_mb_type(pid)) {
+ case e300_eeprom_manager::USRP_E310_MB:
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E310_FPGA_FILE_NAME));
+ idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME);
+ break;
+ case e300_eeprom_manager::USRP_E300_MB:
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E300_FPGA_FILE_NAME));
+ idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
+ break;
+ case e300_eeprom_manager::UNKNOWN:
+ default:
+ UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
+ << std::endl;
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E300_FPGA_FILE_NAME));
+ idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
+ break;
+ }
+}
+
/***********************************************************************
* Structors
**********************************************************************/
@@ -286,33 +316,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
if (_xport_path == AXI) {
_do_not_reload = device_addr.has_key("no_reload_fpga");
if (not _do_not_reload) {
- // Load FPGA image if provided via args
- const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
- device_addr["product"]);
-
std::string fpga_image;
-
- //extract the FPGA path for the e300
- switch(e300_eeprom_manager::get_mb_type(pid)) {
- case e300_eeprom_manager::USRP_E310_MB:
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E310_FPGA_FILE_NAME));
- _idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME);
- break;
- case e300_eeprom_manager::USRP_E300_MB:
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E300_FPGA_FILE_NAME));
- _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
- break;
- case e300_eeprom_manager::UNKNOWN:
- default:
- UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
- << std::endl;
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E300_FPGA_FILE_NAME));
- _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
- break;
- }
+ get_e3x0_fpga_images(device_addr,
+ fpga_image,
+ _idle_image);
common::load_fpga_image(fpga_image);
}
}
@@ -387,18 +394,35 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
// This is horrible ... why do I have to sleep here?
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
_eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE));
+ _sensor_manager = e300_sensor_manager::make_local(_global_regs);
}
+ _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS);
- UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush;
- if (_xport_path == AXI) {
- try {
- _gps = gps::ublox::ubx::control::make("/dev/ttyPS1", 9600);
- } catch (std::exception &e) {
- UHD_MSG(error) << "An error occured making GPSDO control: " << e.what() << std::endl;
+#ifdef E300_GPSD
+ UHD_MSG(status) << "Detecting internal GPSDO " << std::flush;
+ try {
+ if (_xport_path == AXI)
+ _gps = gpsd_iface::make("localhost", 2947);
+ else
+ _gps = gpsd_iface::make(device_addr["addr"], 2947);
+ } catch (std::exception &e) {
+ UHD_MSG(error) << "An error occured making GPSDd interface: " << e.what() << std::endl;
+ }
+
+ if (_gps) {
+ for (size_t i = 0; i < _GPS_TIMEOUT; i++)
+ {
+ boost::this_thread::sleep(boost::posix_time::seconds(1));
+ if (!_gps->gps_detected())
+ std::cout << "." << std::flush;
+ else {
+ std::cout << ".... " << std::flush;
+ break;
+ }
}
- _sensor_manager = e300_sensor_manager::make_local(_gps, _global_regs);
+ UHD_MSG(status) << (_gps->gps_detected() ? "found" : "not found") << std::endl;
}
- UHD_MSG(status) << (_sensor_manager->get_gps_found() ? "found" : "not found") << std::endl;
+#endif
// Verify we can talk to the e300 core control registers ...
UHD_MSG(status) << "Initializing core control..." << std::endl;
@@ -443,6 +467,15 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_tree->create<sensor_value_t>(mb_path / "sensors" / name)
.publish(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name));
}
+#ifdef E300_GPSD
+ if (_gps) {
+ BOOST_FOREACH(const std::string &name, _gps->get_sensors())
+ {
+ _tree->create<sensor_value_t>(mb_path / "sensors" / name)
+ .publish(boost::bind(&gpsd_iface::get_sensor, _gps, name));
+ }
+ }
+#endif
////////////////////////////////////////////////////////////////////
// setup the mboard eeprom
@@ -471,28 +504,23 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++)
this->_setup_radio(instance);
- _codec_ctrl->data_port_loopback(true);
-
// Radio 0 loopback through AD9361
- this->_codec_loopback_self_test(_radio_perifs[0].ctrl);
+ _codec_mgr->loopback_self_test(_radio_perifs[0].ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK);
// Radio 1 loopback through AD9361
- this->_codec_loopback_self_test(_radio_perifs[1].ctrl);
-
- _codec_ctrl->data_port_loopback(false);
+ _codec_mgr->loopback_self_test(_radio_perifs[1].ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK);
////////////////////////////////////////////////////////////////////
// internal gpios
////////////////////////////////////////////////////////////////////
gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO);
- const std::vector<std::string> gpio_attrs = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX");
- BOOST_FOREACH(const std::string &attr, gpio_attrs)
+ BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)
{
- _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr)
- .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr, _1))
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr.second)
+ .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr.first, _1))
.set(0);
}
_tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK")
- .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio, "READBACK"));
+ .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio));
////////////////////////////////////////////////////////////////////
@@ -510,7 +538,11 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_tree->create<std::string>(mb_path / "time_source" / "value")
.subscribe(boost::bind(&e300_impl::_update_time_source, this, _1))
.set(e300::DEFAULT_TIME_SRC);
+#ifdef E300_GPSD
static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo");
+#else
+ static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external");
+#endif
_tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources);
//setup reference source props
_tree->create<std::string>(mb_path / "clock_source" / "value")
@@ -575,7 +607,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
// init the clock rate to something reasonable
_tree->access<double>(mb_path / "tick_rate").set(
- device_addr.cast<double>("master_clock_rate", e300::DEFAULT_TICK_RATE));
+ device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE));
// subdev spec contains full width of selections
subdev_spec_t rx_spec, tx_spec;
@@ -590,40 +622,56 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec);
_tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec);
- UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl;
- const time_t tp = time_t(_sensor_manager->get_sensor("gps_time").to_int()+1);
- _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp));
+#ifdef E300_GPSD
+ //GPS installed: use external ref, time, and init time spec
+ if (_gps and _gps->gps_detected()) {
+ UHD_MSG(status) << "Setting references to the internal GPSDO"
+ << std::endl;
+ _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo");
+ UHD_MSG(status) << "Initializing time to the internal GPSDO"
+ << std::endl;
+ const time_t tp = time_t(_gps->get_sensor("gps_time").to_int()+1);
+ _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp));
+
+ // wait for time to be actually set
+ boost::this_thread::sleep(boost::posix_time::seconds(1));
+ }
+#else
+ //init to internal clock and time source
+ _tree->access<std::string>(mb_path / "time_source/value").set("internal");
+#endif// E300_GPSD
- // wait for time to be actually set
- boost::this_thread::sleep(boost::posix_time::seconds(1));
}
-boost::uint8_t e300_impl::_get_internal_gpio(
- gpio_core_200::sptr gpio,
- const std::string &)
+boost::uint8_t e300_impl::_get_internal_gpio(gpio_core_200::sptr gpio)
{
return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));
}
void e300_impl::_set_internal_gpio(
gpio_core_200::sptr gpio,
- const std::string &attr,
+ const gpio_attr_t attr,
const boost::uint32_t value)
{
- if (attr == "CTRL")
+ switch (attr)
+ {
+ case GPIO_CTRL:
return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value);
- else if (attr == "DDR")
+ case GPIO_DDR:
return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value);
- else if (attr == "OUT")
+ case GPIO_OUT:
return gpio->set_gpio_out(dboard_iface::UNIT_RX, value);
- else if (attr == "ATR_0X")
+ case GPIO_ATR_0X:
return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value);
- else if (attr == "ATR_RX")
+ case GPIO_ATR_RX:
return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value);
- else if (attr == "ATR_TX")
+ case GPIO_ATR_TX:
return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value);
- else if (attr == "ATR_XX")
+ case GPIO_ATR_XX:
return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
}
uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx)
@@ -665,6 +713,17 @@ void e300_impl::_enforce_tick_rate_limits(
% direction
));
}
+ // Minimum rate restriction due to MMCM used in capture interface to AD9361.
+ // Xilinx Artix-7 FPGA MMCM minimum input frequency is 10 MHz.
+ const double min_tick_rate = uhd::usrp::e300::MIN_TICK_RATE / ((chan_count <= 1) ? 1 : 2);
+ if (tick_rate - min_tick_rate < 0.0)
+ {
+ throw uhd::value_error(boost::str(
+ boost::format("current master clock rate (%.6f MHz) set below minimum possible master clock rate (%.6f MHz)")
+ % (tick_rate/1e6)
+ % (min_tick_rate/1e6)
+ ));
+ }
}
}
@@ -721,30 +780,6 @@ std::string e300_impl::_get_version_hash(void)
% ((git_hash & 0xF000000) ? "-dirty" : ""));
}
-void e300_impl::_codec_loopback_self_test(wb_iface::sptr iface)
-{
- bool test_fail = false;
- UHD_ASSERT_THROW(bool(iface));
- UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
- size_t hash = size_t(time(NULL));
- for (size_t i = 0; i < 100; i++)
- {
- boost::hash_combine(hash, i);
- const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
- iface->poke32(TOREG(SR_CODEC_IDLE), word32);
- iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate
- const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK);
- const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
- const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
- test_fail = word32 != rb_tx or word32 != rb_rx;
- if (test_fail) break; //exit loop on any failure
- }
- UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
-
- /* Zero out the idle data. */
- iface->poke32(TOREG(SR_CODEC_IDLE), 0);
-}
-
boost::uint32_t e300_impl::_allocate_sid(const sid_config_t &config)
{
const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff;
@@ -799,8 +834,10 @@ void e300_impl::_update_time_source(const std::string &source)
UHD_MSG(status) << boost::format("Setting time source to %s") % source << std::endl;
if (source == "none" or source == "internal") {
_misc.pps_sel = global_regs::PPS_INT;
+#ifdef E300_GPSD
} else if (source == "gpsdo") {
_misc.pps_sel = global_regs::PPS_GPS;
+#endif
} else if (source == "external") {
_misc.pps_sel = global_regs::PPS_EXT;
} else {
@@ -928,6 +965,7 @@ void e300_impl::_setup_radio(const size_t dspno)
{
radio_perifs_t &perif = _radio_perifs[dspno];
const fs_path mb_path = "/mboards/0";
+ std::string slot_name = (dspno == 0) ? "A" : "B";
////////////////////////////////////////////////////////////////////
// crossbar config for ctrl xports
@@ -956,137 +994,102 @@ void e300_impl::_setup_radio(const size_t dspno)
ctrl_sid,
dspno ? "1" : "0");
this->_register_loopback_self_test(perif.ctrl);
- perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO));
////////////////////////////////////////////////////////////////////
- // front end corrections
+ // Set up peripherals
////////////////////////////////////////////////////////////////////
- std::string slot_name = (dspno == 0) ? "A" : "B";
+ perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO));
perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT));
- const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name;
- _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value")
- .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
- _tree->create<bool>(rx_fe_path / "dc_offset" / "enable")
- .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1))
- .set(true);
- _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value")
- .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
-
+ perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE);
+ perif.rx_fe->set_iq_balance(rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT));
- const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name;
- _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value")
- .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
- _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value")
- .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
-
- ////////////////////////////////////////////////////////////////////
- // create rx dsp control objects
- ////////////////////////////////////////////////////////////////////
+ perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));
perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP));
perif.ddc->set_link_rate(10e9/8); //whatever
+ perif.ddc->set_freq(e300::DEFAULT_DDC_FREQ);
+ perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
+ perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
+ perif.duc->set_link_rate(10e9/8); //whatever
+ perif.duc->set_freq(e300::DEFAULT_DUC_FREQ);
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time_core_3000::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_now = RB64_TIME_NOW;
+ time64_rb_bases.rb_pps = RB64_TIME_PPS;
+ perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+
+ ////////////////////////////////////////////////////////////////////
+ // front end corrections
+ ////////////////////////////////////////////////////////////////////
+ perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name));
+ perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name));
+
+ ////////////////////////////////////////////////////////////////////
+ // connect rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1))
.subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1));
const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno);
- _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range")
- .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc));
- _tree->create<double>(rx_dsp_path / "rate" / "value")
- .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1))
+ perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path));
+ _tree->access<double>(rx_dsp_path / "rate" / "value")
.subscribe(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1))
- .set(e300::DEFAULT_RX_SAMP_RATE);
- _tree->create<double>(rx_dsp_path / "freq" / "value")
- .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1))
- .set(e300::DEFAULT_DDC_FREQ);
- _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range")
- .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc));
+ ;
_tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
.subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));
////////////////////////////////////////////////////////////////////
// create tx dsp control objects
////////////////////////////////////////////////////////////////////
- perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
- perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
- perif.duc->set_link_rate(10e9/8); //whatever
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))
.subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));
const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno);
- _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range")
- .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc));
- _tree->create<double>(tx_dsp_path / "rate" / "value")
- .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1))
+ perif.duc->populate_subtree(_tree->subtree(tx_dsp_path));
+ _tree->access<double>(tx_dsp_path / "rate" / "value")
.subscribe(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1))
- .set(e300::DEFAULT_TX_SAMP_RATE);
- _tree->create<double>(tx_dsp_path / "freq" / "value")
- .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1))
- .set(e300::DEFAULT_DUC_FREQ);
- _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range")
- .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc));
-
- ////////////////////////////////////////////////////////////////////
- // create time control objects
- ////////////////////////////////////////////////////////////////////
- time_core_3000::readback_bases_type time64_rb_bases;
- time64_rb_bases.rb_now = RB64_TIME_NOW;
- time64_rb_bases.rb_pps = RB64_TIME_PPS;
- perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+ ;
////////////////////////////////////////////////////////////////////
// create RF frontend interfacing
////////////////////////////////////////////////////////////////////
- static const std::vector<std::string> data_directions = boost::assign::list_of("rx")("tx");
- BOOST_FOREACH(const std::string& direction, data_directions)
- {
- const std::string key = boost::to_upper_copy(direction) + std::string(((dspno == FE0)? "1" : "2"));
+ static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION);
+ BOOST_FOREACH(direction_t dir, dirs) {
+ const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx";
+ const std::string key = boost::to_upper_copy(x) + std::string(((dspno == FE0)? "1" : "2"));
const fs_path rf_fe_path
- = mb_path / "dboards" / "A" / (direction + "_frontends") / ((dspno == 0) ? "A" : "B");
+ = mb_path / "dboards" / "A" / (x + "_frontends") / ((dspno == 0) ? "A" : "B");
+
+ // This will connect all the AD936x-specific items
+ _codec_mgr->populate_frontend_subtree(
+ _tree->subtree(rf_fe_path), key, dir
+ );
- _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key);
- _tree->create<int>(rf_fe_path / "sensors"); //empty TODO
+ // This will connect all the e300_impl-specific items
_tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked")
- .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx"));
- BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
- {
- _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range")
- .set(ad9361_ctrl::get_gain_range(key));
+ .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION))
+ ;
- _tree->create<double>(rf_fe_path / "gains" / name / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
- .set(e300::DEFAULT_FE_GAIN);
+ // Network mode currently doesn't support the filter API, so
+ // prevent it from using it:
+ if (_xport_path != AXI) {
+ _tree->remove(rf_fe_path / "filters");
}
- _tree->create<std::string>(rf_fe_path / "connection").set("IQ");
- _tree->create<bool>(rf_fe_path / "enabled").set(true);
- _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false);
- _tree->create<double>(rf_fe_path / "bandwidth" / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
- .set(e300::DEFAULT_FE_BW);
- _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key));
- _tree->create<double>(rf_fe_path / "freq" / "value")
- .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key))
- .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
- .subscribe(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1))
- .set(e300::DEFAULT_FE_FREQ);
- _tree->create<meta_range_t>(rf_fe_path / "freq" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range));
-
- //setup RX related stuff
- if (key[0] == 'R') {
+
+ // Antenna Setup
+ if (dir == RX_DIRECTION) {
static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value")
.subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1))
.set("RX2");
- _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi")
- .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key));
}
- if (key[0] == 'T') {
+ else if (dir == TX_DIRECTION) {
static const std::vector<std::string> ants(1, "TX/RX");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX");
diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp
index 7f83c16ed..8aff51466 100644
--- a/host/lib/usrp/e300/e300_impl.hpp
+++ b/host/lib/usrp/e300/e300_impl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-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
@@ -20,6 +20,7 @@
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
@@ -28,6 +29,7 @@
#include <uhd/types/sensors.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/thread/mutex.hpp>
+#include <string>
#include "e300_fifo_config.hpp"
#include "radio_ctrl_core_3000.hpp"
#include "rx_frontend_core_200.hpp"
@@ -38,13 +40,18 @@
#include "rx_dsp_core_3000.hpp"
#include "tx_dsp_core_3000.hpp"
#include "ad9361_ctrl.hpp"
+#include "ad936x_manager.hpp"
#include "gpio_core_200.hpp"
#include "e300_global_regs.hpp"
#include "e300_i2c.hpp"
#include "e300_eeprom_manager.hpp"
#include "e300_sensor_manager.hpp"
-#include "e300_ublox_control.hpp"
+
+/* if we don't compile with gpsd support, don't bother */
+#ifdef E300_GPSD
+#include "gpsd_iface.hpp"
+#endif
namespace uhd { namespace usrp { namespace e300 {
@@ -98,6 +105,10 @@ static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_C
static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX;
static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX;
+uhd::device_addrs_t e300_find(const uhd::device_addr_t &multi_dev_hint);
+void get_e3x0_fpga_images(const uhd::device_addr_t &device_args,
+ std::string &fpga_image,
+ std::string &idle_image);
/*!
* USRP-E300 implementation guts:
@@ -267,13 +278,11 @@ private: // methods
uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx);
// internal gpios
- boost::uint8_t _get_internal_gpio(
- gpio_core_200::sptr,
- const std::string &);
+ boost::uint8_t _get_internal_gpio(gpio_core_200::sptr);
void _set_internal_gpio(
gpio_core_200::sptr gpio,
- const std::string &attr,
+ const gpio_attr_t attr,
const boost::uint32_t value);
private: // members
@@ -284,6 +293,7 @@ private: // members
radio_perifs_t _radio_perifs[2];
double _tick_rate;
ad9361_ctrl::sptr _codec_ctrl;
+ ad936x_manager::sptr _codec_mgr;
fe_control_settings_t _settings;
global_regs::sptr _global_regs;
e300_sensor_manager::sptr _sensor_manager;
@@ -293,7 +303,10 @@ private: // members
std::string _idle_image;
bool _do_not_reload;
gpio_t _misc;
- gps::ublox::ubx::control::sptr _gps;
+#ifdef E300_GPSD
+ gpsd_iface::sptr _gps;
+ static const size_t _GPS_TIMEOUT = 5;
+#endif
};
}}} // namespace
diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp
index dadfb71e9..29d250c8f 100644
--- a/host/lib/usrp/e300/e300_io_impl.cpp
+++ b/host/lib/usrp/e300/e300_io_impl.cpp
@@ -91,21 +91,13 @@ void e300_impl::_update_tick_rate(const double rate)
}
}
-#define CHECK_BANDWIDTH(dir) \
- if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \
- UHD_MSG(warning) \
- << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \
- << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \
- << std::endl; \
- }
-
void e300_impl::_update_rx_samp_rate(const size_t dspno, const double rate)
{
boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock());
if (my_streamer)
my_streamer->set_samp_rate(rate);
- CHECK_BANDWIDTH("Rx");
+ _codec_mgr->check_bandwidth(rate, "Rx");
}
void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate)
@@ -114,7 +106,7 @@ void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate)
boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock());
if (my_streamer)
my_streamer->set_samp_rate(rate);
- CHECK_BANDWIDTH("Tx");
+ _codec_mgr->check_bandwidth(rate, "Tx");
}
/***********************************************************************
diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp
index 408f9e62d..96387d6f7 100644
--- a/host/lib/usrp/e300/e300_network.cpp
+++ b/host/lib/usrp/e300/e300_network.cpp
@@ -230,6 +230,27 @@ static void e300_codec_ctrl_tunnel(
case codec_xact_t::ACTION_GET_RSSI:
out->rssi = _codec_ctrl->get_rssi(which_str).to_real();
break;
+ case codec_xact_t::ACTION_GET_TEMPERATURE:
+ out->temp = _codec_ctrl->get_temperature().to_real();
+ break;
+ case codec_xact_t::ACTION_SET_DC_OFFSET_AUTO:
+ _codec_ctrl->set_dc_offset_auto(which_str, in->use_dc_correction == 1);
+ break;
+ case codec_xact_t::ACTION_SET_IQ_BALANCE_AUTO:
+ _codec_ctrl->set_iq_balance_auto(which_str, in->use_iq_correction == 1);
+ case codec_xact_t::ACTION_SET_AGC:
+ _codec_ctrl->set_agc(which_str, in->use_agc == 1);
+ break;
+ case codec_xact_t::ACTION_SET_AGC_MODE:
+ if(in->agc_mode == 0) {
+ _codec_ctrl->set_agc_mode(which_str, "slow");
+ } else if (in->agc_mode == 1) {
+ _codec_ctrl->set_agc_mode(which_str, "fast");
+ }
+ break;
+ case codec_xact_t::ACTION_SET_BW:
+ out->bw = _codec_ctrl->set_bw_filter(which_str, in->bw);
+ break;
default:
UHD_MSG(status) << "Got unknown request?!" << std::endl;
//Zero out actions to fail this request on client
@@ -328,19 +349,9 @@ static void e300_sensor_tunnel(
// TODO: This is ugly ... use proper serialization
in->value = uhd::htonx<boost::uint32_t>(
e300_sensor_manager::pack_float_in_uint32_t(temp.to_real()));
- } else if (uhd::ntohx(in->which) == GPS_FOUND) {
- in->value = uhd::htonx<boost::uint32_t>(
- sensor_manager->get_gps_found() ? 1 : 0);
-
- } else if (uhd::ntohx(in->which) == GPS_LOCK) {
- in->value = uhd::htonx<boost::uint32_t>(
- sensor_manager->get_gps_lock().to_bool() ? 1 : 0);
} else if (uhd::ntohx(in->which) == REF_LOCK) {
in->value = uhd::htonx<boost::uint32_t>(
sensor_manager->get_ref_lock().to_bool() ? 1 : 0);
- } else if (uhd::ntohx(in->which) == GPS_TIME) {
- in->value = uhd::htonx<boost::uint32_t>(
- sensor_manager->get_gps_time().to_int());
} else
UHD_MSG(status) << "Got unknown request?!" << std::endl;
@@ -627,8 +638,7 @@ network_server_impl::network_server_impl(const uhd::device_addr_t &device_addr)
_codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1);
// This is horrible ... why do I have to sleep here?
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
- _sensor_manager = e300_sensor_manager::make_local(
- gps::ublox::ubx::control::make("/dev/ttyPS1", 9600), _global_regs);
+ _sensor_manager = e300_sensor_manager::make_local(_global_regs);
}
}}} // namespace
diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
index 6742f5f86..9708634dd 100644
--- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
+++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
@@ -130,10 +130,118 @@ public:
_args.bits = uhd::htonx<boost::uint32_t>(0);
_transact();
-
return sensor_value_t("RSSI", _retval.rssi, "dB");
}
+ sensor_value_t get_temperature()
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_GET_TEMPERATURE);
+ _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_NONE); /*Unused*/
+ _args.bits = uhd::htonx<boost::uint32_t>(0);
+
+ _transact();
+ return sensor_value_t("temp", _retval.temp, "C");
+ }
+
+ void set_dc_offset_auto(const std::string &which, const bool on)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_DC_OFFSET_AUTO);
+ if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1);
+ else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2);
+ else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1);
+ else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2);
+ else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string.");
+ _args.use_dc_correction = on ? 1 : 0;
+
+ _transact();
+ }
+
+ void set_iq_balance_auto(const std::string &which, const bool on)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_IQ_BALANCE_AUTO);
+ if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1);
+ else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2);
+ else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1);
+ else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2);
+ else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string.");
+ _args.use_iq_correction = on ? 1 : 0;
+
+ _transact();
+ }
+
+ void set_agc(const std::string &which, bool enable)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC);
+ if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1);
+ else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2);
+ else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1);
+ else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2);
+ else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string.");
+ _args.use_agc = enable ? 1 : 0;
+
+ _transact();
+ }
+
+ void set_agc_mode(const std::string &which, const std::string &mode)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC_MODE);
+
+ if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1);
+ else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2);
+ else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1);
+ else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2);
+ else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string.");
+
+ if(mode == "slow") {
+ _args.agc_mode = 0;
+ } else if (mode == "fast") {
+ _args.agc_mode = 1;
+ } else {
+ throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect agc mode.");
+ }
+
+ _transact();
+ }
+
+ //! set the filter bandwidth for the frontend's analog low pass
+ double set_bw_filter(const std::string &which, const double bw)
+ {
+ _clear();
+ _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_BW);
+ if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1);
+ else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2);
+ else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1);
+ else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2);
+ else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string.");
+ _args.bw = bw;
+
+ _transact();
+ return _retval.bw;
+ }
+
+ //! List all available filters by name
+ std::vector<std::string> get_filter_names(const std::string &)
+ {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ //! Return a list of all filters
+ filter_info_base::sptr get_filter(const std::string &, const std::string &)
+ {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ //! Write back a filter
+ void set_filter(const std::string &, const std::string &, const filter_info_base::sptr)
+ {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
private:
void _transact() {
{
diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp
index e21f2ef95..43723e0d5 100644
--- a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp
+++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp
@@ -34,6 +34,12 @@ public:
double gain;
double freq;
double rssi;
+ double temp;
+ double bw;
+ boost::uint32_t use_dc_correction;
+ boost::uint32_t use_iq_correction;
+ boost::uint32_t use_agc;
+ boost::uint32_t agc_mode;
boost::uint64_t bits;
};
@@ -44,7 +50,13 @@ public:
static const boost::uint32_t ACTION_TUNE = 13;
static const boost::uint32_t ACTION_SET_LOOPBACK = 14;
static const boost::uint32_t ACTION_GET_RSSI = 15;
- static const boost::uint32_t ACTION_GET_FREQ = 16;
+ static const boost::uint32_t ACTION_GET_TEMPERATURE = 16;
+ static const boost::uint32_t ACTION_SET_DC_OFFSET_AUTO = 17;
+ static const boost::uint32_t ACTION_SET_IQ_BALANCE_AUTO = 18;
+ static const boost::uint32_t ACTION_SET_AGC = 19;
+ static const boost::uint32_t ACTION_SET_AGC_MODE = 20;
+ static const boost::uint32_t ACTION_SET_BW = 21;
+ static const boost::uint32_t ACTION_GET_FREQ = 22;
//Values for "which"
static const boost::uint32_t CHAIN_NONE = 0;
diff --git a/host/lib/usrp/e300/e300_sensor_manager.cpp b/host/lib/usrp/e300/e300_sensor_manager.cpp
index 527cfb91a..a4319fa4b 100644
--- a/host/lib/usrp/e300/e300_sensor_manager.cpp
+++ b/host/lib/usrp/e300/e300_sensor_manager.cpp
@@ -24,7 +24,6 @@
#include <cstring>
#include <uhd/exception.hpp>
#include <uhd/utils/byteswap.hpp>
-#include <uhd/usrp/gps_ctrl.hpp>
namespace uhd { namespace usrp { namespace e300 {
@@ -38,17 +37,13 @@ public:
std::vector<std::string> get_sensors()
{
- return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked");
+ return boost::assign::list_of("temp")("ref_locked");
}
uhd::sensor_value_t get_sensor(const std::string &key)
{
if (key == "temp")
return get_mb_temp();
- else if (key == "gps_locked")
- return get_gps_lock();
- else if (key == "gps_time")
- return get_gps_time();
else if (key == "ref_locked")
return get_ref_lock();
else
@@ -94,108 +89,6 @@ public:
"C");
}
- uhd::sensor_value_t get_gps_time(void)
- {
- boost::mutex::scoped_lock(_mutex);
- sensor_transaction_t transaction;
- transaction.which = uhd::htonx<boost::uint32_t>(GPS_TIME);
- {
- uhd::transport::managed_send_buffer::sptr buff
- = _xport->get_send_buff(1.0);
- if (not buff or buff->size() < sizeof(transaction)) {
- throw uhd::runtime_error("sensor proxy send timeout");
- }
- std::memcpy(
- buff->cast<void *>(),
- &transaction,
- sizeof(transaction));
- buff->commit(sizeof(transaction));
- }
- {
- uhd::transport::managed_recv_buffer::sptr buff
- = _xport->get_recv_buff(1.0);
-
- if (not buff or buff->size() < sizeof(transaction))
- throw uhd::runtime_error("sensor proxy recv timeout");
-
- std::memcpy(
- &transaction,
- buff->cast<const void *>(),
- sizeof(transaction));
- }
- UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_TIME);
- // TODO: Use proper serialization here ...
- return sensor_value_t("GPS epoch time", int(uhd::ntohx<boost::uint32_t>(transaction.value)), "seconds");
- }
-
- bool get_gps_found(void)
- {
- boost::mutex::scoped_lock(_mutex);
- sensor_transaction_t transaction;
- transaction.which = uhd::htonx<boost::uint32_t>(GPS_FOUND);
- {
- uhd::transport::managed_send_buffer::sptr buff
- = _xport->get_send_buff(1.0);
- if (not buff or buff->size() < sizeof(transaction)) {
- throw uhd::runtime_error("sensor proxy send timeout");
- }
- std::memcpy(
- buff->cast<void *>(),
- &transaction,
- sizeof(transaction));
- buff->commit(sizeof(transaction));
- }
- {
- uhd::transport::managed_recv_buffer::sptr buff
- = _xport->get_recv_buff(1.0);
-
- if (not buff or buff->size() < sizeof(transaction))
- throw uhd::runtime_error("sensor proxy recv timeout");
-
- std::memcpy(
- &transaction,
- buff->cast<const void *>(),
- sizeof(transaction));
- }
- UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_FOUND);
- // TODO: Use proper serialization here ...
- return (uhd::ntohx(transaction.value) > 0);
- }
-
- uhd::sensor_value_t get_gps_lock(void)
- {
- boost::mutex::scoped_lock(_mutex);
- sensor_transaction_t transaction;
- transaction.which = uhd::htonx<boost::uint32_t>(GPS_LOCK);
- {
- uhd::transport::managed_send_buffer::sptr buff
- = _xport->get_send_buff(1.0);
- if (not buff or buff->size() < sizeof(transaction)) {
- throw uhd::runtime_error("sensor proxy send timeout");
- }
- std::memcpy(
- buff->cast<void *>(),
- &transaction,
- sizeof(transaction));
- buff->commit(sizeof(transaction));
- }
- {
- uhd::transport::managed_recv_buffer::sptr buff
- = _xport->get_recv_buff(1.0);
-
- if (not buff or buff->size() < sizeof(transaction))
- throw uhd::runtime_error("sensor proxy recv timeout");
-
- std::memcpy(
- &transaction,
- buff->cast<const void *>(),
- sizeof(transaction));
- }
- UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_LOCK);
- // TODO: Use proper serialization here ...
- return sensor_value_t("GPS lock status", (uhd::ntohx(transaction.value) > 0), "locked", "unlocked");
- }
-
uhd::sensor_value_t get_ref_lock(void)
{
boost::mutex::scoped_lock(_mutex);
@@ -255,24 +148,20 @@ static const std::string E300_TEMP_SYSFS = "iio:device0";
class e300_sensor_local : public e300_sensor_manager
{
public:
- e300_sensor_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs) :
- _gps_ctrl(gps_ctrl), _global_regs(global_regs)
+ e300_sensor_local(global_regs::sptr global_regs) :
+ _global_regs(global_regs)
{
}
std::vector<std::string> get_sensors()
{
- return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked");
+ return boost::assign::list_of("temp")("ref_locked");
}
uhd::sensor_value_t get_sensor(const std::string &key)
{
if (key == "temp")
return get_mb_temp();
- else if (key == "gps_locked")
- return get_gps_lock();
- else if (key == "gps_time")
- return get_gps_time();
else if (key == "ref_locked")
return get_ref_lock();
else
@@ -291,21 +180,6 @@ public:
return sensor_value_t("temp", (raw + offset) * scale / 1000, "C");
}
- bool get_gps_found(void)
- {
- return _gps_ctrl->gps_detected();
- }
-
- uhd::sensor_value_t get_gps_lock(void)
- {
- return _gps_ctrl->get_sensor("gps_locked");
- }
-
- uhd::sensor_value_t get_gps_time(void)
- {
- return _gps_ctrl->get_sensor("gps_time");
- }
-
uhd::sensor_value_t get_ref_lock(void)
{
//PPSLOOP_LOCKED_MASK is asserted in the following cases:
@@ -322,22 +196,21 @@ public:
}
private:
- gps_ctrl::sptr _gps_ctrl;
global_regs::sptr _global_regs;
};
}}}
using namespace uhd::usrp::e300;
e300_sensor_manager::sptr e300_sensor_manager::make_local(
- uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs)
+ global_regs::sptr global_regs)
{
- return sptr(new e300_sensor_local(gps_ctrl, global_regs));
+ return sptr(new e300_sensor_local(global_regs));
}
#else
using namespace uhd::usrp::e300;
e300_sensor_manager::sptr e300_sensor_manager::make_local(
- uhd::gps_ctrl::sptr, global_regs::sptr)
+ global_regs::sptr)
{
throw uhd::assertion_error("e300_sensor_manager::make_local() !E300_NATIVE");
}
diff --git a/host/lib/usrp/e300/e300_sensor_manager.hpp b/host/lib/usrp/e300/e300_sensor_manager.hpp
index 09f889251..bfaf8e90c 100644
--- a/host/lib/usrp/e300/e300_sensor_manager.hpp
+++ b/host/lib/usrp/e300/e300_sensor_manager.hpp
@@ -39,25 +39,22 @@ struct sensor_transaction_t {
-enum sensor {ZYNQ_TEMP=0, GPS_FOUND=1, GPS_TIME=2,
- GPS_LOCK=3, REF_LOCK=4};
+enum sensor {ZYNQ_TEMP=0, REF_LOCK=4};
class e300_sensor_manager : boost::noncopyable
{
public:
typedef boost::shared_ptr<e300_sensor_manager> sptr;
- virtual bool get_gps_found(void) = 0;
virtual uhd::sensor_value_t get_sensor(const std::string &key) = 0;
virtual std::vector<std::string> get_sensors(void) = 0;
virtual uhd::sensor_value_t get_mb_temp(void) = 0;
- virtual uhd::sensor_value_t get_gps_lock(void) = 0;
- virtual uhd::sensor_value_t get_gps_time(void) = 0;
virtual uhd::sensor_value_t get_ref_lock(void) = 0;
+
static sptr make_proxy(uhd::transport::zero_copy_if::sptr xport);
- static sptr make_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs);
+ static sptr make_local(global_regs::sptr global_regs);
// Note: This is a hack
static boost::uint32_t pack_float_in_uint32_t(const float &v)
diff --git a/host/lib/usrp/e300/e300_ublox_control.hpp b/host/lib/usrp/e300/e300_ublox_control.hpp
deleted file mode 100644
index 8705d6c52..000000000
--- a/host/lib/usrp/e300/e300_ublox_control.hpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP
-#define INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP
-
-#include <boost/cstdint.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/asio.hpp>
-#include <uhd/config.hpp>
-#include <uhd/usrp/gps_ctrl.hpp>
-#include <uhd/types/sensors.hpp>
-
-#include "e300_async_serial.hpp"
-
-namespace uhd { namespace usrp { namespace gps {
-
-namespace ublox { namespace ubx {
-
-class control : public virtual uhd::gps_ctrl
-{
-public:
- typedef boost::shared_ptr<control> sptr;
-
- static sptr make(const std::string &node, const size_t baud_rate);
-
- virtual void configure_message_rate(
- const boost::uint16_t msg,
- const boost::uint8_t rate) = 0;
-
- virtual void configure_antenna(
- const boost::uint16_t flags,
- const boost::uint16_t pins) = 0;
-
- virtual void configure_pps(
- const boost::uint32_t interval,
- const boost::uint32_t length,
- const boost::int8_t status,
- const boost::uint8_t time_ref,
- const boost::uint8_t flags,
- const boost::int16_t antenna_delay,
- const boost::int16_t rf_group_delay,
- const boost::int32_t user_delay) = 0;
-
- virtual void configure_rates(
- boost::uint16_t meas_rate,
- boost::uint16_t nav_rate,
- boost::uint16_t time_ref) = 0;
-};
-}} // namespace ublox::ubx
-
-}}} // namespace
-#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP
diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.cpp b/host/lib/usrp/e300/e300_ublox_control_impl.cpp
deleted file mode 100644
index 389bf79fa..000000000
--- a/host/lib/usrp/e300/e300_ublox_control_impl.cpp
+++ /dev/null
@@ -1,505 +0,0 @@
-#include <boost/format.hpp>
-#include <boost/foreach.hpp>
-#include <boost/bind.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/assign/list_of.hpp>
-#include "boost/date_time/posix_time/posix_time.hpp"
-
-#include <iostream>
-
-
-#include <uhd/utils/byteswap.hpp>
-#include <uhd/utils/msg.hpp>
-#include <uhd/exception.hpp>
-
-#include "e300_ublox_control.hpp"
-
-#ifdef E300_NATIVE
-#include "e300_ublox_control_impl.hpp"
-
-
-namespace uhd { namespace usrp { namespace gps {
-
-namespace ublox { namespace ubx {
-
-control_impl::control_impl(const std::string &node, const size_t baud_rate)
-{
- _decode_init();
- _serial = boost::make_shared<async_serial>(node, baud_rate);
- _serial->set_read_callback(boost::bind(&control_impl::_rx_callback, this, _1, _2));
-
- _detect();
-
- configure_message_rate(MSG_GLL, 0);
- configure_message_rate(MSG_GSV, 0);
- configure_message_rate(MSG_GGA, 0);
- configure_message_rate(MSG_GSA, 0);
- configure_message_rate(MSG_RMC, 0);
- configure_message_rate(MSG_VTG, 0);
- configure_message_rate(MSG_NAV_TIMEUTC, 1);
- configure_message_rate(MSG_NAV_SOL, 1);
-
- configure_antenna(0x001b, 0x8251);
-
- configure_pps(0xf4240, 0x3d090, 1, 0 /* utc */, 1, 0, 0, 0);
-
- _sensors = boost::assign::list_of("gps_locked")("gps_time");
-}
-
-bool control_impl::gps_detected(void)
-{
- return _detected;
-}
-
-void control_impl::_detect(void)
-{
- _send_message(MSG_MON_VER, NULL, 0);
-}
-
-std::vector<std::string> control_impl::get_sensors(void)
-{
- return _sensors;
-}
-
-uhd::sensor_value_t control_impl::get_sensor(std::string key)
-{
- if (key == "gps_time") {
- bool lock;
- _locked.wait_and_see(lock);
- return sensor_value_t("GPS epoch time",
- lock ? int(_get_epoch_time()) : 0, "seconds");
- } else if (key == "gps_locked") {
- bool lock;
- _locked.wait_and_see(lock);
- return sensor_value_t("GPS lock status", lock, "locked", "unlocked");
- } else
- throw uhd::key_error(str(boost::format("sensor %s unknown.") % key));
-}
-
-std::time_t control_impl::_get_epoch_time(void)
-{
- boost::posix_time::ptime ptime;
- _ptime.wait_and_see(ptime);
- return (ptime - boost::posix_time::from_time_t(0)).total_seconds();
-}
-
-control_impl::~control_impl(void)
-{
-}
-
-void control_impl::_decode_init(void)
-{
- _decode_state = DECODE_SYNC1;
- _rx_ck_a = 0;
- _rx_ck_b = 0;
- _rx_payload_length = 0;
- _rx_payload_index = 0;
-}
-
-void control_impl::_add_byte_to_checksum(const boost::uint8_t b)
-{
- _rx_ck_a = _rx_ck_a + b;
- _rx_ck_b = _rx_ck_b + _rx_ck_a;
-}
-
-void control_impl::_calc_checksum(
- const boost::uint8_t *buffer,
- const boost::uint16_t length,
- checksum_t &checksum)
-{
- for (size_t i = 0; i < length; i++)
- {
- checksum.ck_a = checksum.ck_a + buffer[i];
- checksum.ck_b = checksum.ck_b + checksum.ck_a;
- }
-}
-
-void control_impl::configure_rates(
- boost::uint16_t meas_rate,
- boost::uint16_t nav_rate,
- boost::uint16_t time_ref)
-{
- payload_tx_cfg_rate_t cfg_rate;
- cfg_rate.meas_rate = uhd::htowx<boost::uint16_t>(meas_rate);
- cfg_rate.nav_rate = uhd::htowx<boost::uint16_t>(nav_rate);
- cfg_rate.time_ref = uhd::htowx<boost::uint16_t>(time_ref);
-
- _send_message(
- MSG_CFG_RATE,
- reinterpret_cast<const uint8_t*>(&cfg_rate),
- sizeof(cfg_rate));
-
- _wait_for_ack(MSG_CFG_RATE, 1.0);
-}
-
-void control_impl::configure_message_rate(
- const boost::uint16_t msg,
- const uint8_t rate)
-{
- payload_tx_cfg_msg_t cfg_msg;
- cfg_msg.msg = uhd::htowx<boost::uint16_t>(msg);
- cfg_msg.rate[0] = 0;//rate;
- cfg_msg.rate[1] = rate;
- cfg_msg.rate[2] = 0;//rate;
- cfg_msg.rate[3] = 0;//rate;
- cfg_msg.rate[4] = 0;//rate;
- cfg_msg.rate[5] = 0;//rate;
- _send_message(
- MSG_CFG_MSG,
- reinterpret_cast<const uint8_t*>(&cfg_msg),
- sizeof(cfg_msg));
-
- _wait_for_ack(MSG_CFG_MSG, 1.0);
-}
-
-void control_impl::configure_antenna(
- const boost::uint16_t flags,
- const boost::uint16_t pins)
-{
- payload_tx_cfg_ant_t cfg_ant;
- cfg_ant.pins = uhd::htowx<boost::uint16_t>(pins);
- cfg_ant.flags = uhd::htowx<boost::uint16_t>(flags);
- _send_message(
- MSG_CFG_ANT,
- reinterpret_cast<const uint8_t*>(&cfg_ant),
- sizeof(cfg_ant));
- if (_wait_for_ack(MSG_CFG_ANT, 1.0) < 0) {
- throw uhd::runtime_error("Didn't get an ACK for antenna configuration.");
- }
-
-}
-
-void control_impl::configure_pps(
- const boost::uint32_t interval,
- const boost::uint32_t length,
- const boost::int8_t status,
- const boost::uint8_t time_ref,
- const boost::uint8_t flags,
- const boost::int16_t antenna_delay,
- const boost::int16_t rf_group_delay,
- const boost::int32_t user_delay)
-{
- payload_tx_cfg_tp_t cfg_tp;
- cfg_tp.interval = uhd::htowx<boost::uint32_t>(interval);
- cfg_tp.length = uhd::htowx<boost::uint32_t>(length);
- cfg_tp.status = status;
- cfg_tp.time_ref = time_ref;
- cfg_tp.flags = flags;
- cfg_tp.antenna_delay = uhd::htowx<boost::int16_t>(antenna_delay);
- cfg_tp.rf_group_delay = uhd::htowx<boost::int16_t>(rf_group_delay);
- cfg_tp.user_delay = uhd::htowx<boost::int32_t>(user_delay);
- _send_message(
- MSG_CFG_TP,
- reinterpret_cast<const uint8_t*>(&cfg_tp),
- sizeof(cfg_tp));
- if (_wait_for_ack(MSG_CFG_TP, 1.0) < 0) {
- throw uhd::runtime_error("Didn't get an ACK for PPS configuration.");
- }
-}
-
-
-void control_impl::_rx_callback(const char *data, unsigned int len)
-{
- //std::cout << "IN RX CALLBACK" << std::flush << std::endl;
- std::vector<char> v(data, data+len);
- BOOST_FOREACH(const char &c, v)
- {
- _parse_char(c);
- }
-}
-
-void control_impl::_parse_char(const boost::uint8_t b)
-{
- int ret = 0;
-
- switch (_decode_state) {
-
- // we're expecting the first sync byte
- case DECODE_SYNC1:
- if (b == SYNC1) { // sync1 found goto next step
- _decode_state = DECODE_SYNC2;
- } // else stay around
- break;
-
- // we're expecting the second sync byte
- case DECODE_SYNC2:
- if (b == SYNC2) { // sync2 found goto next step
- _decode_state = DECODE_CLASS;
- } else {
- // failed, reset
- _decode_init();
- }
- break;
-
- // we're expecting the class byte
- case DECODE_CLASS:
- _add_byte_to_checksum(b);
- _rx_msg = b;
- _decode_state = DECODE_ID;
- break;
-
- // we're expecting the id byte
- case DECODE_ID:
- _add_byte_to_checksum(b);
- _rx_msg |= (b << 8);
- _decode_state = DECODE_LENGTH1;
- break;
-
- // we're expecting the first length byte
- case DECODE_LENGTH1:
- _add_byte_to_checksum(b);
- _rx_payload_length = b;
- _decode_state = DECODE_LENGTH2;
- break;
-
- // we're expecting the second length byte
- case DECODE_LENGTH2:
- _add_byte_to_checksum(b);
- _rx_payload_length |= (b << 8);
- if(_payload_rx_init()) {
- _decode_init(); // we failed, give up for this one
- } else {
- _decode_state = _rx_payload_length ?
- DECODE_PAYLOAD : DECODE_CHKSUM1;
- }
- break;
-
- // we're expecting payload
- case DECODE_PAYLOAD:
- _add_byte_to_checksum(b);
- switch(_rx_msg) {
- default:
- ret = _payload_rx_add(b);
- break;
- };
- if (ret < 0) {
- // we couldn't deal with the payload, discard the whole thing
- _decode_init();
- } else if (ret > 0) {
- // payload was complete, let's check the checksum;
- _decode_state = DECODE_CHKSUM1;
- } else {
- // more payload expected, don't move
- }
- ret = 0;
- break;
-
- case DECODE_CHKSUM1:
- if (_rx_ck_a != b) {
- // checksum didn't match, barf
- std::cout << boost::format("Failed checksum byte1 %lx != %lx")
- % int(_rx_ck_a) % int(b) << std::endl;
- _decode_init();
- } else {
- _decode_state = DECODE_CHKSUM2;
- }
- break;
-
- case DECODE_CHKSUM2:
- if (_rx_ck_b != b) {
- // checksum didn't match, barf
- std::cout << boost::format("Failed checksum byte2 %lx != %lx")
- % int(_rx_ck_b) % int(b) << std::endl;
-
- } else {
- ret = _payload_rx_done(); // payload done
- }
- _decode_init();
- break;
-
- default:
- break;
- };
-}
-
-int control_impl::_payload_rx_init(void)
-{
- int ret = 0;
-
- _rx_state = RXMSG_HANDLE; // by default handle
- switch(_rx_msg) {
-
- case MSG_NAV_SOL:
- if (not (_rx_payload_length == sizeof(payload_rx_nav_sol_t)))
- _rx_state = RXMSG_ERROR_LENGTH;
- break;
-
- case MSG_NAV_TIMEUTC:
- if (not (_rx_payload_length == sizeof(payload_rx_nav_timeutc_t)))
- _rx_state = RXMSG_ERROR_LENGTH;
- break;
-
- case MSG_MON_VER:
- break; // always take this one
-
- case MSG_ACK_ACK:
- if (not (_rx_payload_length == sizeof(payload_rx_ack_ack_t)))
- _rx_state = RXMSG_ERROR_LENGTH;
- break;
-
- case MSG_ACK_NAK:
- if (not (_rx_payload_length == sizeof(payload_rx_ack_nak_t)))
- _rx_state = RXMSG_ERROR_LENGTH;
- break;
-
- default:
- _rx_state = RXMSG_DISABLE;
- break;
- };
-
- switch (_rx_state) {
- case RXMSG_HANDLE: // handle message
- case RXMSG_IGNORE: // ignore message but don't report error
- ret = 0;
- break;
- case RXMSG_DISABLE: // ignore message but don't report error
- case RXMSG_ERROR_LENGTH: // the length doesn't match
- ret = -1;
- break;
- default: // invalid, error
- ret = -1;
- break;
- };
-
- return ret;
-}
-
-int control_impl::_payload_rx_add(const boost::uint8_t b)
-{
- int ret = 0;
- _buf.raw[_rx_payload_index] = b;
- if (++_rx_payload_index >= _rx_payload_length)
- ret = 1;
- return ret;
-}
-
-int control_impl::_payload_rx_done(void)
-{
- int ret = 0;
- if (_rx_state != RXMSG_HANDLE) {
- return 0;
- }
-
- switch (_rx_msg) {
- case MSG_MON_VER:
- _detected = true;
- break;
-
- case MSG_MON_HW:
- std::cout << "MON-HW" << std::endl;
- break;
-
- case MSG_ACK_ACK:
- if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_ack.msg == _ack_waiting_msg))
- _ack_state = ACK_GOT_ACK;
- break;
-
- case MSG_ACK_NAK:
- if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_nak.msg == _ack_waiting_msg))
- _ack_state = ACK_GOT_NAK;
-
- break;
-
- case MSG_CFG_ANT:
- break;
-
- case MSG_NAV_TIMEUTC:
- _ptime.update(boost::posix_time::ptime(
- boost::gregorian::date(
- boost::gregorian::greg_year(uhd::wtohx<boost::uint16_t>(
- _buf.payload_rx_nav_timeutc.year)),
- boost::gregorian::greg_month(_buf.payload_rx_nav_timeutc.month),
- boost::gregorian::greg_day(_buf.payload_rx_nav_timeutc.day)),
- (boost::posix_time::hours(_buf.payload_rx_nav_timeutc.hour)
- + boost::posix_time::minutes(_buf.payload_rx_nav_timeutc.min)
- + boost::posix_time::seconds(_buf.payload_rx_nav_timeutc.sec))));
- break;
-
- case MSG_NAV_SOL:
- _locked.update(_buf.payload_rx_nav_sol.gps_fix > 0);
- break;
-
- default:
- std::cout << boost::format("Got unknown message %lx , with good checksum [") % int(_rx_msg);
- for(size_t i = 0; i < _rx_payload_length; i++)
- std::cout << boost::format("%lx, ") % int(_buf.raw[i]);
- std::cout << "]"<< std::endl;
- break;
- };
- return ret;
-}
-
-void control_impl::_send_message(
- const boost::uint16_t msg,
- const boost::uint8_t *payload,
- const boost::uint16_t len)
-{
- header_t header = {SYNC1, SYNC2, msg, len};
- checksum_t checksum = {0, 0};
-
- // calculate checksums, first header without sync
- // then payload
- _calc_checksum(
- reinterpret_cast<boost::uint8_t*>(&header) + 2,
- sizeof(header) - 2, checksum);
- if (payload)
- _calc_checksum(payload, len, checksum);
-
- _serial->write(
- reinterpret_cast<const char*>(&header),
- sizeof(header));
-
- if (payload)
- _serial->write((const char *) payload, len);
-
- _serial->write(
- reinterpret_cast<const char*>(&checksum),
- sizeof(checksum));
-}
-
-int control_impl::_wait_for_ack(
- const boost::uint16_t msg,
- const double timeout)
-{
- int ret = -1;
-
- _ack_state = ACK_WAITING;
- _ack_waiting_msg = msg;
-
- boost::system_time timeout_time =
- boost::get_system_time() +
- boost::posix_time::milliseconds(timeout * 1000.0);
-
- do {
- if(_ack_state == ACK_GOT_ACK)
- return 0;
- else if (_ack_state == ACK_GOT_NAK) {
- return -1;
- }
- boost::this_thread::sleep(boost::posix_time::milliseconds(20));
- } while (boost::get_system_time() < timeout_time);
-
- // we get here ... it's a timeout
- _ack_state = ACK_IDLE;
- return ret;
-}
-
-
-}} // namespace ublox::ubx
-}}} // namespace
-
-using namespace uhd::usrp::gps::ublox::ubx;
-
-control::sptr control::make(const std::string &node, const size_t baud_rate)
-{
- return control::sptr(new control_impl(node, baud_rate));
-}
-#else
-using namespace uhd::usrp::gps::ublox::ubx;
-
-control::sptr control::make(const std::string& /* node */, const size_t /* baud_rate */)
-{
- throw uhd::assertion_error("control::sptr::make: !E300_NATIVE");
-}
-#endif // E300_NATIVE
diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.hpp b/host/lib/usrp/e300/e300_ublox_control_impl.hpp
deleted file mode 100644
index a1dcbfe6c..000000000
--- a/host/lib/usrp/e300/e300_ublox_control_impl.hpp
+++ /dev/null
@@ -1,457 +0,0 @@
-#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP
-#define INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP
-
-#include <boost/cstdint.hpp>
-#include <boost/noncopyable.hpp>
-#include <boost/asio.hpp>
-#include <uhd/config.hpp>
-#include <uhd/usrp/gps_ctrl.hpp>
-#include <uhd/types/sensors.hpp>
-
-#include "e300_async_serial.hpp"
-
-namespace uhd { namespace usrp { namespace gps {
-
-namespace ublox { namespace ubx {
-// ublox binary sync words
-static const boost::uint8_t SYNC1 = 0xB5;
-static const boost::uint8_t SYNC2 = 0x62;
-
-// message classes
-static const boost::uint8_t CLASS_NAV = 0x01;
-static const boost::uint8_t CLASS_ACK = 0x05;
-static const boost::uint8_t CLASS_CFG = 0x06;
-static const boost::uint8_t CLASS_MON = 0x0a;
-static const boost::uint8_t CLASS_NMEA = 0xf0;
-
-// Message IDs
-static const boost::uint8_t ID_NAV_POSLLH = 0x02;
-static const boost::uint8_t ID_NAV_SOL = 0x06;
-static const boost::uint8_t ID_NAV_PVT = 0x07;
-static const boost::uint8_t ID_NAV_VELNED = 0x12;
-static const boost::uint8_t ID_NAV_TIMEUTC = 0x21;
-static const boost::uint8_t ID_NAV_SVINFO = 0x30;
-static const boost::uint8_t ID_ACK_NAK = 0x00;
-static const boost::uint8_t ID_ACK_ACK = 0x01;
-static const boost::uint8_t ID_CFG_PRT = 0x00;
-static const boost::uint8_t ID_CFG_ANT = 0x13;
-static const boost::uint8_t ID_CFG_TP = 0x07;
-static const boost::uint8_t ID_CFG_MSG = 0x01;
-static const boost::uint8_t ID_CFG_RATE = 0x08;
-static const boost::uint8_t ID_CFG_NAV5 = 0x24;
-static const boost::uint8_t ID_MON_VER = 0x04;
-static const boost::uint8_t ID_MON_HW = 0x09;
-static const boost::uint8_t ID_GGA = 0x00;
-static const boost::uint8_t ID_GLL = 0x01;
-static const boost::uint8_t ID_GSA = 0x02;
-static const boost::uint8_t ID_GSV = 0x03;
-static const boost::uint8_t ID_RMC = 0x04;
-static const boost::uint8_t ID_VTG = 0x05;
-static const boost::uint8_t ID_GST = 0x07;
-
-// Message Classes & IDs //
-static const boost::uint16_t MSG_NAV_POSLLH
- = CLASS_NAV | (ID_NAV_POSLLH << 8);
-static const boost::uint16_t MSG_NAV_SOL
- = CLASS_NAV | (ID_NAV_SOL << 8);
-static const boost::uint16_t MSG_NAV_PVT
- = CLASS_NAV | (ID_NAV_PVT << 8);
-static const boost::uint16_t MSG_NAV_VELNED
- = CLASS_NAV | (ID_NAV_VELNED << 8);
-static const boost::uint16_t MSG_NAV_TIMEUTC
- = CLASS_NAV | (ID_NAV_TIMEUTC << 8);
-static const boost::uint16_t MSG_NAV_SVINFO
- = CLASS_NAV | (ID_NAV_SVINFO << 8);
-static const boost::uint16_t MSG_ACK_NAK
- = CLASS_ACK | (ID_ACK_NAK << 8);
-static const boost::uint16_t MSG_ACK_ACK
- = CLASS_ACK | (ID_ACK_ACK << 8);
-static const boost::uint16_t MSG_CFG_PRT
- = CLASS_CFG | (ID_CFG_PRT << 8);
-static const boost::uint16_t MSG_CFG_ANT
- = CLASS_CFG | (ID_CFG_ANT << 8);
-static const boost::uint16_t MSG_CFG_TP
- = CLASS_CFG | (ID_CFG_TP << 8);
-static const boost::uint16_t MSG_CFG_MSG
- = CLASS_CFG | (ID_CFG_MSG << 8);
-static const boost::uint16_t MSG_CFG_RATE
- = CLASS_CFG | (ID_CFG_RATE << 8);
-static const boost::uint16_t MSG_CFG_NAV5
- = CLASS_CFG | (ID_CFG_NAV5 << 8);
-static const boost::uint16_t MSG_MON_HW
- = CLASS_MON | (ID_MON_HW << 8);
-static const boost::uint16_t MSG_MON_VER
- = CLASS_MON | (ID_MON_VER << 8);
-
-// NMEA ones
-static const boost::uint16_t MSG_GGA
- = CLASS_NMEA | (ID_GGA << 8);
-static const boost::uint16_t MSG_GLL
- = CLASS_NMEA | (ID_GLL << 8);
-static const boost::uint16_t MSG_GSA
- = CLASS_NMEA | (ID_GSA << 8);
-static const boost::uint16_t MSG_GSV
- = CLASS_NMEA | (ID_GSV << 8);
-static const boost::uint16_t MSG_RMC
- = CLASS_NMEA | (ID_RMC << 8);
-static const boost::uint16_t MSG_VTG
- = CLASS_NMEA | (ID_VTG << 8);
-
-// header
-struct header_t
-{
- boost::uint8_t sync1;
- boost::uint8_t sync2;
- boost::uint16_t msg;
- boost::uint16_t length;
-};
-
-// checksum
-struct checksum_t
-{
- boost::uint8_t ck_a;
- boost::uint8_t ck_b;
-};
-
-// rx rx mon-hw (ubx6)
-struct payload_rx_mon_hw_t
-{
- boost::uint32_t pin_sel;
- boost::uint32_t pin_bank;
- boost::uint32_t pin_dir;
- boost::uint32_t pin_val;
- boost::uint16_t noise_per_ms;
- boost::uint16_t agc_cnt;
- boost::uint8_t a_status;
- boost::uint8_t a_power;
- boost::uint8_t flags;
- boost::uint8_t reserved1;
- boost::uint32_t used_mask;
- boost::uint8_t vp[25];
- boost::uint8_t jam_ind;
- boost::uint16_t reserved3;
- boost::uint32_t pin_irq;
- boost::uint32_t pullh;
- boost::uint32_t pulll;
-};
-
-// rx mon-ver
-struct payload_rx_mon_ver_part1_t
-{
- char sw_version[30];
- char hw_version[10];
-};
-
-struct payload_rx_mon_ver_part2_t
-{
- boost::uint8_t extension[30];
-};
-
-// rx ack-ack
-typedef union {
- boost::uint16_t msg;
- struct {
- boost::uint8_t cls_id;
- boost::uint8_t msg_id;
- };
-} payload_rx_ack_ack_t;
-
-// rx ack-nak
-typedef union {
- boost::uint16_t msg;
- struct {
- boost::uint8_t cls_id;
- boost::uint8_t msg_id;
- };
-} payload_rx_ack_nak_t;
-
-// tx cfg-prt (uart)
-struct payload_tx_cfg_prt_t
-{
- boost::uint8_t port_id;
- boost::uint8_t reserved0;
- boost::uint16_t tx_ready;
- boost::uint32_t mode;
- boost::uint32_t baud_rate;
- boost::uint16_t in_proto_mask;
- boost::uint16_t out_proto_mask;
- boost::uint16_t flags;
- boost::uint16_t reserved5;
-};
-
-// tx cfg-rate
-struct payload_tx_cfg_rate_t
-{
- boost::uint16_t meas_rate;
- boost::uint16_t nav_rate;
- boost::uint16_t time_ref;
-};
-
-// tx cfg-msg
-struct payload_tx_cfg_msg_t
-{
- boost::uint16_t msg;
- boost::uint8_t rate[6];
-};
-
-
-// tx cfg-ant
-struct payload_tx_cfg_ant_t
-{
- boost::uint16_t flags;
- boost::uint16_t pins;
-};
-
-// tx cfg-tp
-struct payload_tx_cfg_tp_t
-{
- boost::uint32_t interval;
- boost::uint32_t length;
- boost::int8_t status;
- boost::uint8_t time_ref;
- boost::uint8_t flags;
- boost::uint8_t reserved1;
- boost::int16_t antenna_delay;
- boost::int16_t rf_group_delay;
- boost::int32_t user_delay;
-};
-
-struct payload_rx_nav_sol_t
-{
- boost::uint32_t i_tow;
- boost::int32_t f_tow;
- boost::int16_t week;
- boost::uint8_t gps_fix;
- boost::uint8_t flags;
- boost::int32_t ecef_x;
- boost::int32_t ecef_y;
- boost::int32_t ecef_z;
- boost::uint32_t p_acc;
- boost::int32_t ecef_vx;
- boost::int32_t ecef_vy;
- boost::int32_t ecef_vz;
- boost::uint32_t s_acc;
- boost::uint16_t p_dop;
- boost::uint8_t reserved1;
- boost::uint8_t num_sv;
- boost::uint32_t reserved2;
-};
-
-struct payload_rx_nav_timeutc_t
-{
- boost::uint32_t i_tow;
- boost::uint32_t t_acc;
- boost::int32_t nano;
- boost::uint16_t year;
- boost::uint8_t month;
- boost::uint8_t day;
- boost::uint8_t hour;
- boost::uint8_t min;
- boost::uint8_t sec;
- boost::uint8_t valid;
-};
-
-typedef union {
- payload_rx_mon_hw_t payload_rx_mon_hw;
-
- payload_rx_mon_ver_part1_t payload_rx_mon_ver_part1;
- payload_rx_mon_ver_part2_t payload_rx_mon_ver_part2;
-
- payload_rx_ack_ack_t payload_rx_ack_ack;
- payload_rx_ack_nak_t payload_rx_ack_nak;
-
- payload_tx_cfg_prt_t payload_tx_cfg_prt;
- payload_tx_cfg_ant_t payload_tx_cfg_ant;
- payload_tx_cfg_rate_t payload_tx_cfg_rate;
-
- payload_tx_cfg_msg_t payload_tx_cfg_msg;
-
- payload_rx_nav_timeutc_t payload_rx_nav_timeutc;
- payload_rx_nav_sol_t payload_rx_nav_sol;
- boost::uint8_t raw[];
-} buf_t;
-
-
-template <typename T>
-class sensor_entry
-{
-public:
- sensor_entry() : _seen(false)
- {
- }
-
- void update(const T &val)
- {
- boost::mutex::scoped_lock l(_mutex);
- _value = val;
- _seen = false;
- l.unlock();
- _cond.notify_one();
- }
-
- bool seen() const
- {
- boost::mutex::scoped_lock l(_mutex);
- return _seen;
- }
-
- bool try_and_see(T &val)
- {
- boost::mutex::scoped_lock l(_mutex);
- if (_seen)
- return false;
-
- val = _value;
- _seen = true;
- return true;
- }
-
- void wait_and_see(T &val)
- {
- boost::mutex::scoped_lock l(_mutex);
- while(_seen)
- {
- _cond.wait(l);
- //std::cout << "Already seen ... " << std::endl;
- }
- val = _value;
- _seen = true;
- }
-
-private: // members
- T _value;
- boost::mutex _mutex;
- boost::condition_variable _cond;
- bool _seen;
-};
-
-class control_impl : public control
-{
-public:
- control_impl(const std::string &node, const size_t baud_rate);
-
- virtual ~control_impl(void);
-
- void configure_message_rate(
- const boost::uint16_t msg,
- const boost::uint8_t rate);
-
- void configure_antenna(
- const boost::uint16_t flags,
- const boost::uint16_t pins);
-
- void configure_pps(
- const boost::uint32_t interval,
- const boost::uint32_t length,
- const boost::int8_t status,
- const boost::uint8_t time_ref,
- const boost::uint8_t flags,
- const boost::int16_t antenna_delay,
- const boost::int16_t rf_group_delay,
- const boost::int32_t user_delay);
-
- void configure_rates(
- boost::uint16_t meas_rate,
- boost::uint16_t nav_rate,
- boost::uint16_t time_ref);
-
- // gps_ctrl interface
- bool gps_detected(void);
- std::vector<std::string> get_sensors(void);
- uhd::sensor_value_t get_sensor(std::string key);
-
-private: // types
- enum decoder_state_t {
- DECODE_SYNC1 = 0,
- DECODE_SYNC2,
- DECODE_CLASS,
- DECODE_ID,
- DECODE_LENGTH1,
- DECODE_LENGTH2,
- DECODE_PAYLOAD,
- DECODE_CHKSUM1,
- DECODE_CHKSUM2,
- };
-
- enum rxmsg_state_t {
- RXMSG_IGNORE = 0,
- RXMSG_HANDLE,
- RXMSG_DISABLE,
- RXMSG_ERROR_LENGTH
- };
-
- enum ack_state_t {
- ACK_IDLE = 0,
- ACK_WAITING,
- ACK_GOT_ACK,
- ACK_GOT_NAK
- };
-
-private: // methods
- std::time_t _get_epoch_time(void);
-
- void _decode_init(void);
-
- void _add_byte_to_checksum(const boost::uint8_t b);
-
- void _detect(void);
-
- void _send_message(
- const boost::uint16_t msg,
- const boost::uint8_t *payload,
- const boost::uint16_t len);
-
- int _wait_for_ack(
- const boost::uint16_t msg,
- const double timeout);
-
- void _calc_checksum(
- const boost::uint8_t *buffer,
- const boost::uint16_t length,
- checksum_t &checksum);
-
- void _rx_callback(const char *data, unsigned len);
-
- void _parse_char(const boost::uint8_t b);
-
- int _payload_rx_init(void);
-
- int _payload_rx_add(const boost::uint8_t b);
-
- int _payload_rx_done(void);
-
-private: // members
- // gps_ctrl stuff
- bool _detected;
- std::vector<std::string> _sensors;
-
- sensor_entry<bool> _locked;
- sensor_entry<boost::posix_time::ptime> _ptime;
-
- // decoder state
- decoder_state_t _decode_state;
- rxmsg_state_t _rxmsg_state;
-
- ack_state_t _ack_state;
- boost::uint16_t _ack_waiting_msg;
-
- boost::uint8_t _rx_ck_a;
- boost::uint8_t _rx_ck_b;
-
- boost::uint16_t _rx_payload_length;
- size_t _rx_payload_index;
- boost::uint16_t _rx_msg;
-
- rxmsg_state_t _rx_state;
-
- boost::shared_ptr<async_serial> _serial;
-
- // this has to be at the end of the
- // class to be valid C++
- buf_t _buf;
-};
-
-}} // namespace ublox::ubx
-
-}}} // namespace
-#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP
diff --git a/host/lib/usrp/gpsd_iface.cpp b/host/lib/usrp/gpsd_iface.cpp
new file mode 100644
index 000000000..e0a1dea05
--- /dev/null
+++ b/host/lib/usrp/gpsd_iface.cpp
@@ -0,0 +1,309 @@
+//
+// 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/>.
+//
+
+#include <cmath>
+
+#include <gps.h>
+
+#include <boost/assign/list_of.hpp>
+#include <boost/bind.hpp>
+#include <boost/cstdint.hpp>
+#include "boost/date_time/gregorian/gregorian.hpp"
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/thread/shared_mutex.hpp>
+#include <boost/thread.hpp>
+#include <boost/math/special_functions/fpclassify.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/types/dict.hpp>
+
+#include "gpsd_iface.hpp"
+
+namespace uhd { namespace usrp {
+
+static const size_t TIMEOUT = 240;
+static const size_t CLICK_RATE = 250000;
+
+class gpsd_iface_impl : public virtual gpsd_iface {
+public:
+ gpsd_iface_impl(const std::string &addr, boost::uint16_t port)
+ : _detected(false), _bthread(), _timeout_cnt(0)
+ {
+ boost::unique_lock<boost::shared_mutex> l(_d_mutex);
+
+ if (gps_open(addr.c_str(),
+ str(boost::format("%u") % port).c_str(),
+ &_gps_data) < 0) {
+ throw uhd::runtime_error(
+ str((boost::format("Failed to connect to gpsd: %s")
+ % gps_errstr(errno))));
+ }
+
+ // register for updates, we don't specify a specific device,
+ // therefore no WATCH_DEVICE
+ gps_stream(&_gps_data, WATCH_ENABLE, NULL);
+
+ // create background thread talking to gpsd
+ boost::thread t(boost::bind(&gpsd_iface_impl::_thread_fcn ,this));
+ _bthread.swap(t);
+
+
+ _sensors = boost::assign::list_of("gps_locked")("gps_time") \
+ ("gps_position")("gps_gpgga")("gps_gprmc");
+ }
+
+ virtual ~gpsd_iface_impl(void)
+ {
+ // interrupt the background thread and wait for it to finish
+ _bthread.interrupt();
+ _bthread.join();
+
+ // clean up ...
+ {
+ boost::unique_lock<boost::shared_mutex> l(_d_mutex);
+
+ gps_stream(&_gps_data, WATCH_DISABLE, NULL);
+ gps_close(&_gps_data);
+ }
+ }
+
+ uhd::sensor_value_t get_sensor(std::string key)
+ {
+ if (key == "gps_locked") {
+ return sensor_value_t(
+ "GPS lock status", _gps_locked(), "locked", "unlocked");
+ } else if (key == "gps_time") {
+ return sensor_value_t(
+ "GPS epoch time", int(_epoch_time()), "seconds");
+ } else if (key == "gps_gpgga") {
+ return sensor_value_t(
+ "GPGGA", _gps_gpgga(), "");
+ } else if (key == "gps_gprmc") {
+ return sensor_value_t(
+ "GPRMC", _gps_gprmc(), "");
+ } else if (key == "gps_position") {
+ return sensor_value_t(
+ "GPS Position", str(
+ boost::format("%s %s %s")
+ % _gps_position()["lat"]
+ % _gps_position()["lon"]
+ % _gps_position()["alt"]), "lat/lon/alt");
+ } else
+ throw uhd::key_error(
+ str(boost::format("sensor %s unknown.") % key));
+ }
+
+ bool gps_detected(void) { return _detected; };
+
+ std::vector<std::string> get_sensors(void) { return _sensors; };
+
+private: // member functions
+ void _thread_fcn()
+ {
+ while (not boost::this_thread::interruption_requested()) {
+ if (!gps_waiting(&_gps_data, CLICK_RATE)) {
+ if (TIMEOUT < _timeout_cnt++)
+ _detected = false;
+ } else {
+ boost::unique_lock<boost::shared_mutex> l(_d_mutex);
+
+ _timeout_cnt = 0;
+ _detected = true;
+
+ if (gps_read(&_gps_data) < 0)
+ throw std::runtime_error("error while reading");
+ }
+ }
+ }
+
+ bool _gps_locked(void)
+ {
+ boost::shared_lock<boost::shared_mutex> l(_d_mutex);
+ return _gps_data.fix.mode >= MODE_2D;
+ }
+
+ std::time_t _epoch_time(void)
+ {
+ boost::shared_lock<boost::shared_mutex> l(_d_mutex);
+ return (boost::posix_time::from_time_t(_gps_data.fix.time)
+ - boost::posix_time::from_time_t(0)).total_seconds();
+ }
+
+ boost::gregorian::date _gregorian_date(void)
+ {
+ boost::shared_lock<boost::shared_mutex> l(_d_mutex);
+ return boost::posix_time::from_time_t(_gps_data.fix.time).date();
+ }
+
+ uhd::dict<std::string, std::string> _gps_position(void)
+ {
+ boost::shared_lock<boost::shared_mutex> l(_d_mutex);
+
+ uhd::dict<std::string, std::string> tmp;
+ if (_gps_data.fix.mode >= MODE_2D) {
+ tmp["lon"] = str(boost::format("%f deg")
+ % _gps_data.fix.longitude);
+ tmp["lat"] = str(boost::format("%f deg")
+ % _gps_data.fix.latitude);
+ tmp["alt"] = str(boost::format("%fm")
+ % _gps_data.fix.altitude);
+ } else {
+ tmp["lon"] = "n/a";
+ tmp["lat"] = "n/a";
+ tmp["alt"] = "n/a";
+ }
+ return tmp;
+ }
+
+ float _zeroize(float x)
+ {
+ return boost::math::isnan(x) ? 0.0 : x;
+ }
+
+ int _nmea_checksum(const std::string &s)
+ {
+ if ((s.at(0) != '$'))
+ return 0;
+
+ boost::uint8_t sum = '\0';
+ for (size_t i = 1; i < s.size(); i++)
+ sum ^= static_cast<boost::uint8_t>(s.at(i));
+
+ return sum;
+ }
+
+ double _deg_to_dm(double angle)
+ {
+ double fraction, integer;
+ fraction = std::modf(angle, &integer);
+ return std::floor(angle) * 100 + fraction * 60;
+ }
+
+ std::string _gps_gprmc(void)
+ {
+ struct tm tm;
+ time_t intfixtime;
+
+ boost::shared_lock<boost::shared_mutex> l(_d_mutex);
+
+ tm.tm_mday = tm.tm_mon = tm.tm_year = 0;
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+
+ if (boost::math::isnan(_gps_data.fix.time) == 0) {
+ intfixtime = (time_t) _gps_data.fix.time;
+ (void)gmtime_r(&intfixtime, &tm);
+ tm.tm_mon++;
+ tm.tm_year %= 100;
+ }
+ std::string string = str(boost::format(
+ "$GPRMC,%02d%02d%02d,%c,%09.4f,%c,%010.4f,%c,%.4f,%.3f,%02d%02d%02d,,")
+ % tm.tm_hour
+ % tm.tm_min
+ % tm.tm_sec
+ % (_gps_data.status ? 'A' : 'V')
+ % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.latitude)))
+ % ((_gps_data.fix.latitude > 0) ? 'N' : 'S')
+ % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.longitude)))
+ % ((_gps_data.fix.longitude > 0) ? 'E' : 'W')
+ % _zeroize(_gps_data.fix.speed * MPS_TO_KNOTS)
+ % _zeroize(_gps_data.fix.track)
+ % tm.tm_mday % tm.tm_mon % tm.tm_year);
+
+ string.append(str(
+ boost::format("*%02X") % _nmea_checksum(string)));
+
+ return string;
+ }
+
+ std::string _gps_gpgga(void)
+ {
+ struct tm tm;
+ time_t intfixtime;
+
+ // currently not supported, make it blank
+ float mag_var = NAN;
+
+ boost::shared_lock<boost::shared_mutex> l(_d_mutex);
+
+ intfixtime = (time_t) _gps_data.fix.time;
+ (void) gmtime_r(&intfixtime, &tm);
+
+ std::string string = str(boost::format(
+ "$GPGGA,%02d%02d%02d,%09.4f,%c,%010.4f,%c,%d,%02d,")
+ % tm.tm_hour
+ % tm.tm_min
+ % tm.tm_sec
+ % _deg_to_dm(std::fabs(_gps_data.fix.latitude))
+ % ((_gps_data.fix.latitude > 0) ? 'N' : 'S')
+ % _deg_to_dm(std::fabs(_gps_data.fix.longitude))
+ % ((_gps_data.fix.longitude > 0) ? 'E' : 'W')
+ % _gps_data.status
+ % _gps_data.satellites_used);
+
+ if (boost::math::isnan(_gps_data.dop.hdop))
+ string.append(",");
+ else
+ string.append(
+ str(boost::format("%.2f,") % _gps_data.dop.hdop));
+
+ if (boost::math::isnan(_gps_data.fix.altitude))
+ string.append(",");
+ else
+ string.append(
+ str(boost::format("%.2f,M,") % _gps_data.fix.altitude));
+
+ if (boost::math::isnan(_gps_data.separation))
+ string.append(",");
+ else
+ string.append(
+ str(boost::format("%.3f,M,") % _gps_data.separation));
+
+ if (boost::math::isnan(mag_var))
+ string.append(",");
+ else {
+ string.append(
+ str(boost::format("%3.2f,%s") % std::fabs(mag_var)
+ % (mag_var > 0 ? "E" : "W")));
+ }
+
+ string.append(str(
+ boost::format("*%02X") % _nmea_checksum(string)));
+
+ return string;
+ }
+
+private: // members
+ std::vector<std::string> _sensors;
+ bool _detected;
+
+ gps_data_t _gps_data;
+ boost::shared_mutex _d_mutex;
+ boost::thread _bthread;
+ size_t _timeout_cnt;
+};
+
+}} //namespace
+
+using namespace uhd::usrp;
+
+gpsd_iface::sptr gpsd_iface::make(const std::string &addr, const boost::uint16_t port)
+{
+ return gpsd_iface::sptr(new gpsd_iface_impl(addr, port));
+}
diff --git a/host/lib/usrp/gpsd_iface.hpp b/host/lib/usrp/gpsd_iface.hpp
new file mode 100644
index 000000000..7d934ae5c
--- /dev/null
+++ b/host/lib/usrp/gpsd_iface.hpp
@@ -0,0 +1,36 @@
+//
+// 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_GPSD_IFACE_HPP
+#define INCLUDED_GPSD_IFACE_HPP
+
+#include <boost/cstdint.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <uhd/usrp/gps_ctrl.hpp>
+
+namespace uhd { namespace usrp {
+
+class gpsd_iface : public virtual uhd::gps_ctrl {
+public:
+ typedef boost::shared_ptr<gpsd_iface> sptr;
+ static sptr make(const std::string &addr, boost::uint16_t port);
+};
+
+}};
+
+#endif /* INCLUDED_GPSD_IFACE_HPP */
diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp
index 68c084589..f60182c76 100644
--- a/host/lib/usrp/mboard_eeprom.cpp
+++ b/host/lib/usrp/mboard_eeprom.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2013 Ettus Research LLC
+// Copyright 2010-2013,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
@@ -16,6 +16,7 @@
//
#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/types/byte_vector.hpp>
#include <uhd/types/mac_addr.hpp>
#include <uhd/utils/byteswap.hpp>
#include <boost/asio/ip/address_v4.hpp>
@@ -39,32 +40,6 @@ static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN;
* Utility functions
**********************************************************************/
-//! A wrapper around std::copy that takes ranges instead of iterators.
-template<typename RangeSrc, typename RangeDst> inline
-void byte_copy(const RangeSrc &src, RangeDst &dst){
- std::copy(boost::begin(src), boost::end(src), boost::begin(dst));
-}
-
-//! create a string from a byte vector, return empty if invalid ascii
-static const std::string bytes_to_string(const byte_vector_t &bytes){
- std::string out;
- BOOST_FOREACH(boost::uint8_t byte, bytes){
- if (byte < 32 or byte > 127) return out;
- out += byte;
- }
- return out;
-}
-
-//! create a byte vector from a string, null terminate unless max length
-static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){
- byte_vector_t bytes;
- for (size_t i = 0; i < std::min(string.size(), max_length); i++){
- bytes.push_back(string[i]);
- }
- if (bytes.size() < max_length - 1) bytes.push_back('\0');
- return bytes;
-}
-
//! convert a string to a byte vector to write to eeprom
static byte_vector_t string_to_uint16_bytes(const std::string &num_str){
const boost::uint16_t num = boost::lexical_cast<boost::uint16_t>(num_str);
@@ -238,7 +213,8 @@ struct x300_eeprom_map
//indentifying numbers
unsigned char revision[2];
unsigned char product[2];
- boost::uint8_t _pad0[4];
+ unsigned char revision_compat[2];
+ boost::uint8_t _pad0[2];
//all the mac addrs
boost::uint8_t mac_addr0[6];
@@ -264,6 +240,11 @@ static void load_x300(mboard_eeprom_t &mb_eeprom, i2c_iface &iface)
iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), 2)
);
+ //extract the revision compat number
+ mb_eeprom["revision_compat"] = uint16_bytes_to_string(
+ iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), 2)
+ );
+
//extract the product code
mb_eeprom["product"] = uint16_bytes_to_string(
iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), 2)
@@ -310,6 +291,12 @@ static void store_x300(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface)
string_to_uint16_bytes(mb_eeprom["revision"])
);
+ //parse the revision compat number
+ if (mb_eeprom.has_key("revision_compat")) iface.write_eeprom(
+ X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat),
+ string_to_uint16_bytes(mb_eeprom["revision_compat"])
+ );
+
//parse the product code
if (mb_eeprom.has_key("product")) iface.write_eeprom(
X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product),
diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp
index 794438b90..1866255c9 100644
--- a/host/lib/usrp/multi_usrp.cpp
+++ b/host/lib/usrp/multi_usrp.cpp
@@ -30,6 +30,8 @@
#include <boost/thread.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
+#include <boost/algorithm/string.hpp>
+#include <algorithm>
#include <cmath>
using namespace uhd;
@@ -431,6 +433,9 @@ public:
******************************************************************/
void set_master_clock_rate(double rate, size_t mboard){
if (mboard != ALL_MBOARDS){
+ if (_tree->exists(mb_root(mboard) / "auto_tick_rate")) {
+ _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false);
+ }
_tree->access<double>(mb_root(mboard) / "tick_rate").set(rate);
return;
}
@@ -821,6 +826,26 @@ public:
}
void set_rx_gain(double gain, const std::string &name, size_t chan){
+ /* Check if any AGC mode is enable and if so warn the user */
+ if (chan != ALL_CHANS) {
+ if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc")) {
+ bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get();
+ if(agc) {
+ UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl;
+ }
+ }
+ } else {
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ if (_tree->exists(rx_rf_fe_root(c) / "gain" / "agc")) {
+ bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get();
+ if(agc) {
+ UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl;
+ }
+ }
+ }
+ }
+ /* Apply gain setting.
+ * If device is in AGC mode it will ignore the setting. */
try {
return rx_gain_group(chan)->set_value(gain, name);
} catch (uhd::key_error &) {
@@ -828,6 +853,32 @@ public:
}
}
+ void set_normalized_rx_gain(double gain, size_t chan = 0)
+ {
+ if (gain > 1.0 || gain < 0.0) {
+ throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1].");
+ }
+ gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan);
+ double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start();
+ set_rx_gain(abs_gain, ALL_GAINS, chan);
+ }
+
+ void set_rx_agc(bool enable, size_t chan = 0)
+ {
+ if (chan != ALL_CHANS){
+ if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")) {
+ _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").set(enable);
+ } else {
+ UHD_MSG(warning) << "AGC is not available on this device." << std::endl;
+ }
+ return;
+ }
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ this->set_rx_agc(enable, c);
+ }
+
+ }
+
double get_rx_gain(const std::string &name, size_t chan){
try {
return rx_gain_group(chan)->get_value(name);
@@ -836,6 +887,21 @@ public:
}
}
+ double get_normalized_rx_gain(size_t chan)
+ {
+ gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan);
+ double gain_range_width = gain_range.stop() - gain_range.start();
+ // In case we have a device without a range of gains:
+ if (gain_range_width == 0.0) {
+ return 0;
+ }
+ double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width;
+ // Avoid rounding errors:
+ if (norm_gain > 1.0) return 1.0;
+ if (norm_gain < 0.0) return 0.0;
+ return norm_gain;
+ }
+
gain_range_t get_rx_gain_range(const std::string &name, size_t chan){
try {
return rx_gain_group(chan)->get_range(name);
@@ -888,6 +954,9 @@ public:
if (chan != ALL_CHANS){
if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "enable")) {
_tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb);
+ } else if (_tree->exists(rx_rf_fe_root(chan) / "dc_offset" / "enable")) {
+ /*For B2xx devices the dc-offset correction is implemented in the rf front-end*/
+ _tree->access<bool>(rx_rf_fe_root(chan) / "dc_offset" / "enable").set(enb);
} else {
UHD_MSG(warning) << "Setting DC offset compensation is not possible on this device." << std::endl;
}
@@ -912,6 +981,20 @@ public:
}
}
+ void set_rx_iq_balance(const bool enb, size_t chan){
+ if (chan != ALL_CHANS){
+ if (_tree->exists(rx_rf_fe_root(chan) / "iq_balance" / "enable")) {
+ _tree->access<bool>(rx_rf_fe_root(chan) / "iq_balance" / "enable").set(enb);
+ } else {
+ UHD_MSG(warning) << "Setting IQ imbalance compensation is not possible on this device." << std::endl;
+ }
+ return;
+ }
+ for (size_t c = 0; c < get_rx_num_channels(); c++){
+ this->set_rx_iq_balance(enb, c);
+ }
+ }
+
void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){
if (chan != ALL_CHANS){
if (_tree->exists(rx_fe_root(chan) / "iq_balance" / "value")) {
@@ -926,6 +1009,87 @@ public:
}
}
+ std::vector<std::string> get_filter_names(const std::string &search_mask)
+ {
+ std::vector<std::string> ret;
+
+ for (size_t chan = 0; chan < get_rx_num_channels(); chan++){
+
+ if (_tree->exists(rx_rf_fe_root(chan) / "filters")) {
+ std::vector<std::string> names = _tree->list(rx_rf_fe_root(chan) / "filters");
+ for(size_t i = 0; i < names.size(); i++)
+ {
+ std::string name = rx_rf_fe_root(chan) / "filters" / names[i];
+ if((search_mask.empty()) or boost::contains(name, search_mask)) {
+ ret.push_back(name);
+ }
+ }
+ }
+ if (_tree->exists(rx_dsp_root(chan) / "filters")) {
+ std::vector<std::string> names = _tree->list(rx_dsp_root(chan) / "filters");
+ for(size_t i = 0; i < names.size(); i++)
+ {
+ std::string name = rx_dsp_root(chan) / "filters" / names[i];
+ if((search_mask.empty()) or (boost::contains(name, search_mask))) {
+ ret.push_back(name);
+ }
+ }
+ }
+
+ }
+
+ for (size_t chan = 0; chan < get_tx_num_channels(); chan++){
+
+ if (_tree->exists(tx_rf_fe_root(chan) / "filters")) {
+ std::vector<std::string> names = _tree->list(tx_rf_fe_root(chan) / "filters");
+ for(size_t i = 0; i < names.size(); i++)
+ {
+ std::string name = tx_rf_fe_root(chan) / "filters" / names[i];
+ if((search_mask.empty()) or (boost::contains(name, search_mask))) {
+ ret.push_back(name);
+ }
+ }
+ }
+ if (_tree->exists(rx_dsp_root(chan) / "filters")) {
+ std::vector<std::string> names = _tree->list(tx_dsp_root(chan) / "filters");
+ for(size_t i = 0; i < names.size(); i++)
+ {
+ std::string name = tx_dsp_root(chan) / "filters" / names[i];
+ if((search_mask.empty()) or (boost::contains(name, search_mask))) {
+ ret.push_back(name);
+ }
+ }
+ }
+
+ }
+
+ return ret;
+ }
+
+ filter_info_base::sptr get_filter(const std::string &path)
+ {
+ std::vector<std::string> possible_names = get_filter_names("");
+ std::vector<std::string>::iterator it;
+ it = find(possible_names.begin(), possible_names.end(), path);
+ if (it == possible_names.end()) {
+ throw uhd::runtime_error("Attempting to get non-existing filter: "+path);
+ }
+
+ return _tree->access<filter_info_base::sptr>(path / "value").get();
+ }
+
+ void set_filter(const std::string &path, filter_info_base::sptr filter)
+ {
+ std::vector<std::string> possible_names = get_filter_names("");
+ std::vector<std::string>::iterator it;
+ it = find(possible_names.begin(), possible_names.end(), path);
+ if (it == possible_names.end()) {
+ throw uhd::runtime_error("Attempting to set non-existing filter: "+path);
+ }
+
+ _tree->access<filter_info_base::sptr>(path / "value").set(filter);
+ }
+
/*******************************************************************
* TX methods
******************************************************************/
@@ -1029,6 +1193,17 @@ public:
}
}
+ void set_normalized_tx_gain(double gain, size_t chan = 0)
+ {
+ if (gain > 1.0 || gain < 0.0) {
+ throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1].");
+ }
+ gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan);
+ double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start();
+ set_tx_gain(abs_gain, ALL_GAINS, chan);
+ }
+
+
double get_tx_gain(const std::string &name, size_t chan){
try {
return tx_gain_group(chan)->get_value(name);
@@ -1037,6 +1212,21 @@ public:
}
}
+ double get_normalized_tx_gain(size_t chan)
+ {
+ gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan);
+ double gain_range_width = gain_range.stop() - gain_range.start();
+ // In case we have a device without a range of gains:
+ if (gain_range_width == 0.0) {
+ return 0.0;
+ }
+ double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width;
+ // Avoid rounding errors:
+ if (norm_gain > 1.0) return 1.0;
+ if (norm_gain < 0.0) return 0.0;
+ return norm_gain;
+ }
+
gain_range_t get_tx_gain_range(const std::string &name, size_t chan){
try {
return tx_gain_group(chan)->get_range(name);
diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt
index c6257c7fe..bd302895b 100644
--- a/host/lib/usrp/usrp2/CMakeLists.txt
+++ b/host/lib/usrp/usrp2/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012,2014 Ettus Research LLC
+# Copyright 2011-2012,2014-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
@@ -25,18 +25,6 @@
LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF)
IF(ENABLE_USRP2)
- ########################################################################
- # Define UHD_PKG_DATA_PATH for usrp2_iface.cpp
- ########################################################################
- FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH)
- STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH})
-
- SET_SOURCE_FILES_PROPERTIES(
- ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
- PROPERTIES COMPILE_DEFINITIONS
- "UHD_LIB_DIR=\"lib${LIB_SUFFIX}\""
- )
-
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
@@ -45,5 +33,6 @@ IF(ENABLE_USRP2)
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n200_image_loader.cpp
)
ENDIF(ENABLE_USRP2)
diff --git a/host/lib/usrp/usrp2/n200_image_loader.cpp b/host/lib/usrp/usrp2/n200_image_loader.cpp
new file mode 100644
index 000000000..ce956c22c
--- /dev/null
+++ b/host/lib/usrp/usrp2/n200_image_loader.cpp
@@ -0,0 +1,616 @@
+//
+// 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/>.
+//
+
+#include <cstring>
+#include <iostream>
+#include <fstream>
+
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/assign.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/algorithm/string/erase.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/types/dict.hpp>
+
+#include "fw_common.h"
+#include "usrp2_iface.hpp"
+#include "usrp2_impl.hpp"
+
+typedef boost::asio::ip::address_v4 ip_v4;
+
+namespace fs = boost::filesystem;
+using namespace boost::algorithm;
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+/*
+ * Constants
+ */
+
+#define N200_FLASH_DATA_PACKET_SIZE 256
+#define N200_UDP_FW_UPDATE_PORT 49154
+#define UDP_TIMEOUT 0.5
+
+#define N200_FW_MAX_SIZE_BYTES 31744
+#define N200_PROD_FW_IMAGE_ADDR 0x00300000
+#define N200_SAFE_FW_IMAGE_ADDR 0x003F0000
+
+#define N200_FPGA_MAX_SIZE_BYTES 1572864
+#define N200_PROD_FPGA_IMAGE_ADDR 0x00180000
+#define N200_SAFE_FPGA_IMAGE_ADDR 0x00000000
+
+/*
+ * Packet codes
+ */
+typedef enum {
+ UNKNOWN = ' ',
+
+ N200_QUERY = 'a',
+ N200_ACK = 'A',
+
+ GET_FLASH_INFO_CMD = 'f',
+ GET_FLASH_INFO_ACK = 'F',
+
+ ERASE_FLASH_CMD = 'e',
+ ERASE_FLASH_ACK = 'E',
+
+ CHECK_ERASING_DONE_CMD = 'd',
+ DONE_ERASING_ACK = 'D',
+ NOT_DONE_ERASING_ACK = 'B',
+
+ WRITE_FLASH_CMD = 'w',
+ WRITE_FLASH_ACK = 'W',
+
+ READ_FLASH_CMD = 'r',
+ READ_FLASH_ACK = 'R',
+
+ RESET_CMD = 's',
+ RESET_ACK = 'S',
+
+ GET_HW_REV_CMD = 'v',
+ GET_HW_REV_ACK = 'V',
+} n200_fw_update_id_t;
+
+/*
+ * Mapping revision numbers to names
+ */
+static const uhd::dict<boost::uint32_t, std::string> n200_filename_map = boost::assign::map_list_of
+ (0, "n2xx") // Is an N-Series, but the EEPROM value is invalid
+ (0xa, "n200_r3")
+ (0x100a, "n200_r4")
+ (0x10a, "n210_r3")
+ (0x110a, "n210_r4")
+;
+
+/*
+ * Packet structure
+ */
+typedef struct {
+ boost::uint32_t proto_ver;
+ boost::uint32_t id;
+ boost::uint32_t seq;
+ union {
+ boost::uint32_t ip_addr;
+ boost::uint32_t hw_rev;
+ struct {
+ boost::uint32_t flash_addr;
+ boost::uint32_t length;
+ boost::uint8_t data[256];
+ } flash_args;
+ struct {
+ boost::uint32_t sector_size_bytes;
+ boost::uint32_t memory_size_bytes;
+ } flash_info_args;
+ } data;
+} n200_fw_update_data_t;
+
+/*
+ * N-Series burn session
+ */
+typedef struct {
+ bool fw;
+ bool overwrite_safe;
+ bool reset;
+ uhd::device_addr_t dev_addr;
+ std::string burn_type;
+ std::string filepath;
+ boost::uint8_t data_in[udp_simple::mtu];
+ boost::uint32_t size;
+ boost::uint32_t max_size;
+ boost::uint32_t flash_addr;
+ udp_simple::sptr xport;
+} n200_session_t;
+
+/***********************************************************************
+ * uhd::image_loader functionality
+ **********************************************************************/
+
+static void print_usrp2_error(const image_loader::image_loader_args_t &image_loader_args){
+ #ifdef UHD_PLATFORM_WIN32
+ std::string usrp2_card_burner_gui = "\"";
+ const std::string nl = " ^\n ";
+ #else
+ std::string usrp2_card_burner_gui = "sudo \"";
+ const std::string nl = " \\\n ";
+ #endif
+
+ usrp2_card_burner_gui += find_utility("usrp2_card_burner_gui.py");
+ usrp2_card_burner_gui += "\"";
+
+ if(image_loader_args.load_firmware){
+ usrp2_card_burner_gui += str(boost::format("%s--fw=\"%s\"")
+ % nl
+ % ((image_loader_args.firmware_path == "")
+ ? find_image_path("usrp2_fw.bin")
+ : image_loader_args.firmware_path));
+ }
+ if(image_loader_args.load_fpga){
+ usrp2_card_burner_gui += str(boost::format("%s--fpga=\"%s\"")
+ % nl
+ % ((image_loader_args.fpga_path == "")
+ ? find_image_path("usrp2_fpga.bin")
+ : image_loader_args.fpga_path));
+ }
+
+ throw uhd::runtime_error(str(boost::format("The specified device is a USRP2, which is not supported by this utility.\n"
+ "Instead, plug the device's SD card into your machine and run this command:\n\n"
+ "%s"
+ ) % usrp2_card_burner_gui));
+}
+
+/*
+ * Ethernet communication functions
+ */
+static UHD_INLINE size_t n200_send_and_recv(udp_simple::sptr xport,
+ n200_fw_update_id_t pkt_code,
+ n200_fw_update_data_t *pkt_out,
+ boost::uint8_t* data){
+ pkt_out->proto_ver = htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM);
+ pkt_out->id = htonx<boost::uint32_t>(pkt_code);
+ xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out)));
+ return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT);
+}
+
+static UHD_INLINE bool n200_response_matches(const n200_fw_update_data_t *pkt_in,
+ n200_fw_update_id_t pkt_code,
+ size_t len){
+ return (len > offsetof(n200_fw_update_data_t, data) and
+ ntohl(pkt_in->id) == pkt_code);
+}
+
+static uhd::device_addr_t n200_find(const image_loader::image_loader_args_t &image_loader_args){
+ bool user_specified = image_loader_args.args.has_key("addr") or
+ image_loader_args.args.has_key("serial") or
+ image_loader_args.args.has_key("name");
+
+ uhd::device_addrs_t found = usrp2_find(image_loader_args.args);
+ if(found.size() > 0){
+ uhd::device_addr_t ret = found[0];
+
+ /*
+ * Make sure the device found is an N-Series and not a USRP2. A USRP2
+ * will not respond to this query. If the user supplied specific
+ * arguments that led to a USRP2, throw an error.
+ */
+ udp_simple::sptr rev_xport = udp_simple::make_connected(
+ ret["addr"],
+ BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT)
+ );
+
+ n200_fw_update_data_t pkt_out;
+ boost::uint8_t data_in[udp_simple::mtu];
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(data_in);
+
+ size_t len = n200_send_and_recv(rev_xport, GET_HW_REV_CMD, &pkt_out, data_in);
+ if(n200_response_matches(pkt_in, GET_HW_REV_ACK, len)){
+ boost::uint32_t rev = ntohl(pkt_in->data.hw_rev);
+ ret["hw_rev"] = n200_filename_map.get(rev, "n2xx");
+ return ret;
+ }
+ else if(len > offsetof(n200_fw_update_data_t, data) and ntohl(pkt_in->id) != GET_HW_REV_ACK){
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.")
+ % ntohl(pkt_in->id)));
+ }
+ else if(user_specified){
+ // At this point, we haven't received any response, so assume it's a USRP2
+ print_usrp2_error(image_loader_args);
+ }
+ }
+
+ return uhd::device_addr_t();
+}
+
+/*
+ * Validate and read firmware image
+ */
+static void n200_validate_firmware_image(n200_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ session.size = fs::file_size(session.filepath);
+ session.max_size = N200_FW_MAX_SIZE_BYTES;
+
+ if(session.size > session.max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % session.max_size));
+ }
+
+ // File must have proper header
+ std::ifstream image_file(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t test_bytes[4];
+ image_file.seekg(0, std::ios::beg);
+ image_file.read((char*)test_bytes,4);
+ image_file.close();
+ for(int i = 0; i < 4; i++) if(test_bytes[i] != 11){
+ throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid firmware image.")
+ % session.filepath));
+ }
+}
+
+/*
+ * Validate and validate FPGA image
+ */
+static void n200_validate_fpga_image(n200_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ session.size = fs::file_size(session.filepath);
+ session.max_size = N200_FPGA_MAX_SIZE_BYTES;
+
+ if(session.size > session.max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % session.max_size));
+ }
+
+ // File must have proper header
+ std::ifstream image_file(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t test_bytes[63];
+ image_file.seekg(0, std::ios::beg);
+ image_file.read((char*)test_bytes, 63);
+ bool is_good = false;
+ for(int i = 0; i < 63; i++){
+ if(test_bytes[i] == 255) continue;
+ else if(test_bytes[i] == 170 and
+ test_bytes[i+1] == 153){
+ is_good = true;
+ break;
+ }
+ }
+ image_file.close();
+ if(not is_good){
+ throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid FPGA image.")
+ % session.filepath));
+ }
+}
+
+/*
+ * Set up a session for burning an N-Series image. This session info
+ * will be passed into the erase, burn, and verify functions.
+ */
+static void n200_setup_session(n200_session_t &session,
+ const image_loader::image_loader_args_t &image_loader_args,
+ bool fw){
+
+
+ session.fw = fw;
+ session.reset = image_loader_args.args.has_key("reset");
+
+ /*
+ * If no filepath is given, attempt to determine the default image by
+ * querying the device for its revision. If the device has a corrupt
+ * EEPROM or is otherwise unable to provide its revision, this is
+ * impossible, and the user must manually provide a firmware file.
+ */
+ if((session.fw and image_loader_args.firmware_path == "") or
+ image_loader_args.fpga_path == ""){
+ if(session.dev_addr["hw_rev"] == "n2xx"){
+ throw uhd::runtime_error("This device's revision cannot be determined. "
+ "You must manually specify a filepath.");
+ }
+ else{
+ session.filepath = session.fw ? find_image_path(str(boost::format("usrp_%s_fw.bin")
+ % erase_tail_copy(session.dev_addr["hw_rev"],3)))
+ : find_image_path(str(boost::format("usrp_%s_fpga.bin")
+ % session.dev_addr["hw_rev"]));
+ }
+ }
+ else{
+ session.filepath = session.fw ? image_loader_args.firmware_path
+ : image_loader_args.fpga_path;
+ }
+ if(session.fw) n200_validate_firmware_image(session);
+ else n200_validate_fpga_image(session);
+
+ session.overwrite_safe = image_loader_args.args.has_key("overwrite-safe");
+ if(session.overwrite_safe){
+ session.flash_addr = session.fw ? N200_SAFE_FW_IMAGE_ADDR
+ : N200_SAFE_FPGA_IMAGE_ADDR;
+ session.burn_type = session.fw ? "firmware safe"
+ : "FPGA safe";
+ }
+ else{
+ session.flash_addr = session.fw ? N200_PROD_FW_IMAGE_ADDR
+ : N200_PROD_FPGA_IMAGE_ADDR;
+ session.burn_type = session.fw ? "firmware"
+ : "FPGA";
+ }
+
+ session.xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT));
+}
+
+static void n200_erase_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+
+ // Setting up UDP packet
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(session.flash_addr);
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(session.size);
+
+ // Begin erasing
+ size_t len = n200_send_and_recv(session.xport, ERASE_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, ERASE_FLASH_ACK, len)){
+ std::cout << boost::format("-- Erasing %s image...") % session.burn_type << std::flush;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != ERASE_FLASH_ACK){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Did not receive response from device.");
+ }
+
+ // Check for erase completion
+ while(true){
+ len = n200_send_and_recv(session.xport, CHECK_ERASING_DONE_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, DONE_ERASING_ACK, len)){
+ std::cout << "successful." << std::endl;
+ break;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != NOT_DONE_ERASING_ACK){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+ }
+}
+
+static void n200_write_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+ size_t len = 0;
+
+ // Write image
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+ boost::uint32_t current_addr = session.flash_addr;
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE);
+ for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+ memset(pkt_out.data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+ image.read((char*)pkt_out.data.flash_args.data, N200_FLASH_DATA_PACKET_SIZE);
+
+ len = n200_send_and_recv(session.xport, WRITE_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, WRITE_FLASH_ACK, len)){
+ std::cout << boost::format("\r-- Writing %s image (%d%%)")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::flush;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ image.close();
+ std::cout << boost::format("\r--Writing %s image..failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != WRITE_FLASH_ACK){
+ image.close();
+ std::cout << boost::format("\r--Writing %s image..failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+
+ current_addr += N200_FLASH_DATA_PACKET_SIZE;
+ }
+ std::cout << boost::format("\r-- Writing %s image...successful.")
+ % session.burn_type
+ << std::endl;
+
+ image.close();
+}
+
+static void n200_verify_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+ size_t len = 0;
+
+ // Read and verify image
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t image_part[N200_FLASH_DATA_PACKET_SIZE];
+ boost::uint32_t current_addr = session.flash_addr;
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE);
+ boost::uint16_t cmp_len = 0;
+ for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){
+ memset(image_part, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+ memset((void*)pkt_in->data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+ image.read((char*)image_part, N200_FLASH_DATA_PACKET_SIZE);
+ cmp_len = image.gcount();
+
+ len = n200_send_and_recv(session.xport, READ_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, READ_FLASH_ACK, len)){
+ std::cout << boost::format("\r-- Verifying %s image (%d%%)")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::flush;
+
+ if(memcmp(image_part, pkt_in->data.flash_args.data, cmp_len)){
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Failed to verify %s image.")
+ % session.burn_type));
+ }
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ image.close();
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != READ_FLASH_ACK){
+ image.close();
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+
+ current_addr += N200_FLASH_DATA_PACKET_SIZE;
+ }
+ std::cout << boost::format("\r-- Verifying %s image...successful.") % session.burn_type
+ << std::endl;
+
+ image.close();
+}
+
+static void n200_reset(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+
+ // There should be no response
+ std::cout << "-- Resetting device..." << std::flush;
+ size_t len = n200_send_and_recv(session.xport, RESET_CMD, &pkt_out, session.data_in);
+ if(len > 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset N200.");
+ }
+ std::cout << "successful." << std::endl;
+}
+
+// n210_r4 -> N210 r4
+static std::string nice_name(const std::string &fw_rev){
+ std::string ret = fw_rev;
+ ret[0] = ::toupper(ret[0]);
+
+ size_t pos = 0;
+ if((pos = fw_rev.find("_")) != std::string::npos){
+ ret[pos] = ' ';
+ }
+
+ return ret;
+}
+
+static bool n200_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if any N2x0 with the given args is found
+ // This will throw if specific args lead to a USRP2
+ n200_session_t session;
+ session.dev_addr = n200_find(image_loader_args);
+ if(session.dev_addr.size() == 0 or (!image_loader_args.load_firmware and !image_loader_args.load_fpga)){
+ return false;
+ }
+
+ std::cout << boost::format("Unit: USRP %s (%s, %s)")
+ % nice_name(session.dev_addr.get("hw_rev"))
+ % session.dev_addr.get("serial")
+ % session.dev_addr.get("addr")
+ << std::endl;
+
+ if(image_loader_args.load_firmware){
+ n200_setup_session(session,
+ image_loader_args,
+ true
+ );
+
+ std::cout << "Firmware image: " << session.filepath << std::endl;
+
+ n200_erase_image(session);
+ n200_write_image(session);
+ n200_verify_image(session);
+ if(session.reset and !image_loader_args.load_fpga){
+ n200_reset(session);
+ }
+ }
+ if(image_loader_args.load_fpga){
+ n200_setup_session(session,
+ image_loader_args,
+ false
+ );
+
+ std::cout << "FPGA image: " << session.filepath << std::endl;
+
+ n200_erase_image(session);
+ n200_write_image(session);
+ n200_verify_image(session);
+ if(session.reset){
+ n200_reset(session);
+ }
+ }
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_n200_image_loader){
+ std::string recovery_instructions = "Aborting. Your USRP-N Series unit will likely be unusable.\n"
+ "Refer to http://files.ettus.com/manual/page_usrp2.html#usrp2_loadflash_brick\n"
+ "for details on restoring your device.";
+
+ image_loader::register_image_loader("usrp2", n200_image_loader, recovery_instructions);
+}
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
index 1d41173f8..2b382ae38 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.cpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -387,15 +387,15 @@ public:
//create the burner commands
if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){
- const std::string card_burner = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp2_card_burner_gui.py").string();
- const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);
+ const std::string card_burner = uhd::find_utility("usrp2_card_burner_gui.py");
+ const std::string card_burner_cmd = str(boost::format(" %s\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);
return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % card_burner_cmd);
}
else{
const std::string addr = _ctrl_transport->get_recv_addr();
- const std::string net_burner_path = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp_n2xx_simple_net_burner").string();
- const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr);
- return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % net_burner_cmd);
+ const std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+ const std::string image_loader_cmd = str(boost::format(" \"%s\" %s--args=\"type=usrp2,addr=%s\"") % image_loader_path % ml % addr);
+ return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % image_loader_cmd);
}
}
diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp
index 1acc1dad3..6073ec1c0 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.cpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.cpp
@@ -48,7 +48,7 @@ static const size_t DEFAULT_NUM_FRAMES = 32;
/***********************************************************************
* Discovery over the udp transport
**********************************************************************/
-static device_addrs_t usrp2_find(const device_addr_t &hint_){
+device_addrs_t usrp2_find(const device_addr_t &hint_){
//handle the multi-device discovery
device_addrs_t hints = separate_device_addr(hint_);
if (hints.size() > 1){
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index 701403029..07cd98b4c 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -42,6 +42,7 @@
#include <uhd/transport/vrt_if_packet.hpp>
#include <uhd/transport/udp_simple.hpp>
#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/dboard_manager.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <boost/weak_ptr.hpp>
@@ -55,6 +56,8 @@ static const boost::uint32_t USRP2_TX_ASYNC_SID = 2;
static const boost::uint32_t USRP2_RX_SID_BASE = 3;
static const std::string USRP2_EEPROM_MAP_KEY = "N100";
+uhd::device_addrs_t usrp2_find(const uhd::device_addr_t &hint_);
+
//! Make a usrp2 dboard interface.
uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface(
uhd::timed_wb_iface::sptr wb_iface,
diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt
index a588f901b..9a8601452 100644
--- a/host/lib/usrp/x300/CMakeLists.txt
+++ b/host/lib/usrp/x300/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2013 Ettus Research LLC
+# Copyright 2013,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
@@ -34,5 +34,8 @@ IF(ENABLE_X300)
${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_dac_utils.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c
)
ENDIF(ENABLE_X300)
diff --git a/host/lib/usrp/x300/cdecode.c b/host/lib/usrp/x300/cdecode.c
new file mode 100644
index 000000000..1d09cbe22
--- /dev/null
+++ b/host/lib/usrp/x300/cdecode.c
@@ -0,0 +1,80 @@
+/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include "cdecode.h"
+
+int base64_decode_value(char value_in){
+ static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+ static const char decoding_size = sizeof(decoding);
+ value_in -= 43;
+ if ((signed char)value_in < 0 || value_in > decoding_size) return -1;
+ return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in){
+ state_in->step = step_a;
+ state_in->plainchar = 0;
+}
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){
+ const char* codechar = code_in;
+ char* plainchar = plaintext_out;
+ char fragment;
+
+ *plainchar = state_in->plainchar;
+
+ switch (state_in->step){
+ while (1){
+ case step_a:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_a;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar = (fragment & 0x03f) << 2;
+
+ case step_b:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_b;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x030) >> 4;
+ *plainchar = (fragment & 0x00f) << 4;
+ case step_c:
+ do{
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_c;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03c) >> 2;
+ *plainchar = (fragment & 0x003) << 6;
+ case step_d:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_d;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03f);
+ }
+ }
+ /* control should not reach here */
+ return plainchar - plaintext_out;
+}
diff --git a/host/lib/usrp/x300/cdecode.h b/host/lib/usrp/x300/cdecode.h
new file mode 100644
index 000000000..b8da55aa1
--- /dev/null
+++ b/host/lib/usrp/x300/cdecode.h
@@ -0,0 +1,36 @@
+/*
+cdecode.h - c header for a base64 decoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CDECODE_H
+#define BASE64_CDECODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+typedef enum{
+ step_a, step_b, step_c, step_d
+} base64_decodestep;
+
+typedef struct{
+ base64_decodestep step;
+ char plainchar;
+} base64_decodestate;
+
+void base64_init_decodestate(base64_decodestate* state_in);
+
+int base64_decode_value(char value_in);
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BASE64_CDECODE_H */
diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp
index b0e4e4b95..ce6102b35 100644
--- a/host/lib/usrp/x300/x300_adc_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp
@@ -55,8 +55,8 @@ public:
_ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS;
_ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT;
_ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT;
- _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26;
- _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26;
+ _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS4_26;
+ _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS4_26;
this->send_ads62p48_reg(0);
diff --git a/host/lib/usrp/x300/x300_adc_dac_utils.cpp b/host/lib/usrp/x300/x300_adc_dac_utils.cpp
new file mode 100644
index 000000000..2dadea26e
--- /dev/null
+++ b/host/lib/usrp/x300/x300_adc_dac_utils.cpp
@@ -0,0 +1,412 @@
+//
+// 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/>.
+//
+
+#include "x300_impl.hpp"
+#include <boost/date_time/posix_time/posix_time_io.hpp>
+
+/***********************************************************************
+ * DAC: Reset and synchronization operations
+ **********************************************************************/
+
+void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios)
+{
+ if (radios.size() < 2) return; //Nothing to synchronize
+
+ //**PRECONDITION**
+ //This function assumes that all the VITA times in "radios" are synchronized
+ //to a common reference. Currently, this function is called in get_tx_stream
+ //which also has the same precondition.
+
+ //Reinitialize and resync all DACs
+ for (size_t i = 0; i < radios.size(); i++) {
+ radios[i]->dac->reset_and_resync();
+ }
+
+ //Get a rough estimate of the cumulative command latency
+ boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time();
+ for (size_t i = 0; i < radios.size(); i++) {
+ radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call
+ }
+ boost::posix_time::time_duration t_elapsed =
+ boost::posix_time::microsec_clock::local_time() - t_start;
+
+ //Add 100% of headroom + uncertaintly to the command time
+ boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/;
+
+ //Pick radios[0] as the time reference.
+ uhd::time_spec_t sync_time =
+ radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6);
+
+ //Send the sync command
+ for (size_t i = 0; i < radios.size(); i++) {
+ radios[i]->ctrl->set_time(sync_time);
+ radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1); //Arm FRAMEP/N sync pulse
+ radios[i]->ctrl->set_time(uhd::time_spec_t(0.0)); //Clear command time
+ }
+
+ //Wait and check status
+ boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us));
+ for (size_t i = 0; i < radios.size(); i++) {
+ radios[i]->dac->verify_sync();
+ }
+}
+
+/***********************************************************************
+ * ADC: Self-test operations
+ **********************************************************************/
+
+static void check_adc(uhd::wb_iface::sptr iface, const boost::uint32_t val, const boost::uint32_t i)
+{
+ boost::uint32_t adc_rb = iface->peek32(RB32_RX);
+ adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA
+ if (val != adc_rb) {
+ throw uhd::runtime_error(
+ (boost::format("ADC self-test failed for Radio%d. (Exp=0x%x, Got=0x%x)")%i%val%adc_rb).str());
+ }
+}
+
+void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms) {
+ for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {
+ radio_perifs_t &perif = mb.radio_perifs[r];
+
+ //First test basic patterns
+ perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc,r);
+ perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000,r);
+ perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000,r);
+ perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc,r);
+ for (size_t k = 0; k < 14; k++)
+ {
+ perif.adc->set_test_word("zeros", "custom", 1 << k);
+ check_adc(perif.ctrl, 1 << (k+2),r);
+ }
+ for (size_t k = 0; k < 14; k++)
+ {
+ perif.adc->set_test_word("custom", "zeros", 1 << k);
+ check_adc(perif.ctrl, 1 << (k+18),r);
+ }
+
+ //Turn on ramp pattern test
+ perif.adc->set_test_word("ramp", "ramp");
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0);
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1);
+ }
+ boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms));
+
+ bool passed = true;
+ std::string status_str;
+ for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {
+ radio_perifs_t &perif = mb.radio_perifs[r];
+ perif.misc_ins->refresh();
+
+ std::string i_status, q_status;
+ if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED))
+ if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR))
+ i_status = "Bit Errors!";
+ else
+ i_status = "Good";
+ else
+ i_status = "Not Locked!";
+
+ if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED))
+ if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR))
+ q_status = "Bit Errors!";
+ else
+ q_status = "Good";
+ else
+ q_status = "Not Locked!";
+
+ passed = passed && (i_status == "Good") && (q_status == "Good");
+ status_str += (boost::format(", ADC%d_I=%s, ADC%d_Q=%s")%r%i_status%r%q_status).str();
+
+ //Return to normal mode
+ perif.adc->set_test_word("normal", "normal");
+ }
+
+ if (not passed) {
+ throw uhd::runtime_error(
+ (boost::format("ADC self-test failed! Ramp checker status: {%s}")%status_str.substr(2)).str());
+ }
+}
+
+void x300_impl::extended_adc_test(mboard_members_t& mb, double duration_s)
+{
+ static const size_t SECS_PER_ITER = 5;
+ UHD_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n")
+ % duration_s % SECS_PER_ITER;
+
+ size_t num_iters = static_cast<size_t>(ceil(duration_s/SECS_PER_ITER));
+ size_t num_failures = 0;
+ for (size_t iter = 0; iter < num_iters; iter++) {
+ //Print date and time
+ boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S");
+ std::ostringstream time_strm;
+ time_strm.imbue(std::locale(std::locale::classic(), facet));
+ time_strm << boost::posix_time::second_clock::local_time();
+ //Run self-test
+ UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1);
+ try {
+ self_test_adcs(mb, SECS_PER_ITER*1000);
+ UHD_MSG(status) << "passed" << std::endl;
+ } catch(std::exception &e) {
+ num_failures++;
+ UHD_MSG(status) << e.what() << std::endl;
+ }
+
+ }
+ if (num_failures == 0) {
+ UHD_MSG(status) << "Extended ADC Self-Test PASSED\n";
+ } else {
+ throw uhd::runtime_error(
+ (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") % num_failures % num_iters).str());
+ }
+}
+
+/***********************************************************************
+ * ADC: Self-calibration operations
+ **********************************************************************/
+
+void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status)
+{
+ radio_perifs_t& perif = mb.radio_perifs[radio_i];
+ if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush;
+
+ static const boost::uint32_t NUM_DELAY_STEPS = 32; //The IDELAYE2 element has 32 steps
+ static const boost::uint32_t NUM_RETRIES = 2; //Retry self-cal if it fails in warmup situations
+ static const boost::int32_t MIN_WINDOW_LEN = 4;
+
+ boost::int32_t win_start = -1, win_stop = -1;
+ boost::uint32_t iter = 0;
+ while (iter++ < NUM_RETRIES) {
+ for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) {
+ //Apply delay
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, dly_tap);
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1);
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0);
+
+ boost::uint32_t err_code = 0;
+
+ // -- Test I Channel --
+ //Put ADC in ramp test mode. Tie the other channel to all ones.
+ perif.adc->set_test_word("ramp", "ones");
+ //Turn on the pattern checker in the FPGA. It will lock when it sees a zero
+ //and count deviations from the expected value
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0);
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1);
+ //10ms @ 200MHz = 2 million samples
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_I_LOCKED)) {
+ err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_I_ERROR);
+ } else {
+ err_code += 100; //Increment error code by 100 to indicate no lock
+ }
+
+ // -- Test Q Channel --
+ //Put ADC in ramp test mode. Tie the other channel to all ones.
+ perif.adc->set_test_word("ones", "ramp");
+ //Turn on the pattern checker in the FPGA. It will lock when it sees a zero
+ //and count deviations from the expected value
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0);
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1);
+ //10ms @ 200MHz = 2 million samples
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_Q_LOCKED)) {
+ err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_Q_ERROR);
+ } else {
+ err_code += 100; //Increment error code by 100 to indicate no lock
+ }
+
+ if (err_code == 0) {
+ if (win_start == -1) { //This is the first window
+ win_start = dly_tap;
+ win_stop = dly_tap;
+ } else { //We are extending the window
+ win_stop = dly_tap;
+ }
+ } else {
+ if (win_start != -1) { //A valid window turned invalid
+ if (win_stop - win_start >= MIN_WINDOW_LEN) {
+ break; //Valid window found
+ } else {
+ win_start = -1; //Reset window
+ }
+ }
+ }
+ //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code);
+ }
+
+ //Retry the self-cal if it fails
+ if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) && iter < NUM_RETRIES /*not last iteration*/) {
+ win_start = -1;
+ win_stop = -1;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(2000));
+ } else {
+ break;
+ }
+ }
+ perif.adc->set_test_word("normal", "normal");
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0);
+
+ if (win_start == -1) {
+ throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error.");
+ }
+
+ if (win_stop-win_start < MIN_WINDOW_LEN) {
+ throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Valid window too narrow.");
+ }
+
+ boost::uint32_t ideal_tap = (win_stop + win_start) / 2;
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, ideal_tap);
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1);
+ perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0);
+
+ if (print_status) {
+ double tap_delay = (1.0e12 / mb.clock->get_master_clock_rate()) / (2*32); //in ps
+ UHD_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter;
+ }
+}
+
+double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay)
+{
+ UHD_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush;
+
+ //Effective resolution of the self-cal.
+ static const size_t NUM_DELAY_STEPS = 100;
+
+ double master_clk_period = (1.0e9 / mb.clock->get_master_clock_rate()); //in ns
+ double delay_start = 0.0;
+ double delay_range = 2 * master_clk_period;
+ double delay_incr = delay_range / NUM_DELAY_STEPS;
+
+ UHD_MSG(status) << "Measuring..." << std::flush;
+ double cached_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_ADC0);
+ double fpga_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_FPGA);
+
+ //Iterate through several values of delays and measure ADC data integrity
+ std::vector< std::pair<double,bool> > results;
+ for (size_t i = 0; i < NUM_DELAY_STEPS; i++) {
+ //Delay the ADC clock (will set both Ch0 and Ch1 delays)
+ double delay = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start);
+ wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1);
+
+ boost::uint32_t err_code = 0;
+ for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {
+ //Test each channel (I and Q) individually so as to not accidentally trigger
+ //on the data from the other channel if there is a swap
+
+ // -- Test I Channel --
+ //Put ADC in ramp test mode. Tie the other channel to all ones.
+ mb.radio_perifs[r].adc->set_test_word("ramp", "ones");
+ //Turn on the pattern checker in the FPGA. It will lock when it sees a zero
+ //and count deviations from the expected value
+ mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0);
+ mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1);
+ //50ms @ 200MHz = 10 million samples
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) {
+ err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR);
+ } else {
+ err_code += 100; //Increment error code by 100 to indicate no lock
+ }
+
+ // -- Test Q Channel --
+ //Put ADC in ramp test mode. Tie the other channel to all ones.
+ mb.radio_perifs[r].adc->set_test_word("ones", "ramp");
+ //Turn on the pattern checker in the FPGA. It will lock when it sees a zero
+ //and count deviations from the expected value
+ mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0);
+ mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1);
+ //50ms @ 200MHz = 10 million samples
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) {
+ err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR);
+ } else {
+ err_code += 100; //Increment error code by 100 to indicate no lock
+ }
+ }
+ //UHD_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % delay % err_code);
+ results.push_back(std::pair<double,bool>(delay, err_code==0));
+ }
+
+ //Calculate the valid window
+ int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1;
+ for (size_t i = 0; i < results.size(); i++) {
+ std::pair<double,bool>& item = results[i];
+ if (item.second) { //If data is stable
+ if (cur_start_idx == -1) { //This is the first window
+ cur_start_idx = i;
+ cur_stop_idx = i;
+ } else { //We are extending the window
+ cur_stop_idx = i;
+ }
+ } else {
+ if (cur_start_idx == -1) { //We haven't yet seen valid data
+ //Do nothing
+ } else if (win_start_idx == -1) { //We passed the first valid window
+ win_start_idx = cur_start_idx;
+ win_stop_idx = cur_stop_idx;
+ } else { //Update cached window if current window is larger
+ double cur_win_len = results[cur_stop_idx].first - results[cur_start_idx].first;
+ double cached_win_len = results[win_stop_idx].first - results[win_start_idx].first;
+ if (cur_win_len > cached_win_len) {
+ win_start_idx = cur_start_idx;
+ win_stop_idx = cur_stop_idx;
+ }
+ }
+ //Reset current window
+ cur_start_idx = -1;
+ cur_stop_idx = -1;
+ }
+ }
+ if (win_start_idx == -1) {
+ throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Convergence error.");
+ }
+
+ double win_center = (results[win_stop_idx].first + results[win_start_idx].first) / 2.0;
+ double win_length = results[win_stop_idx].first - results[win_start_idx].first;
+ if (win_length < master_clk_period/4) {
+ throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow.");
+ }
+
+ //Cycle slip the relative delay by a clock cycle to prevent sample misalignment
+ //fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need
+ bool cycle_slip = (win_center-fpga_clk_delay >= master_clk_period);
+ if (cycle_slip) {
+ win_center -= master_clk_period;
+ }
+
+ if (apply_delay) {
+ UHD_MSG(status) << "Validating..." << std::flush;
+ //Apply delay
+ win_center = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center); //Sets ADC0 and ADC1
+ wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1);
+ //Validate
+ self_test_adcs(mb, 2000);
+ } else {
+ //Restore delay
+ mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, cached_clk_delay); //Sets ADC0 and ADC1
+ }
+
+ //Teardown
+ for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {
+ mb.radio_perifs[r].adc->set_test_word("normal", "normal");
+ mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0);
+ }
+ UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") %
+ (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length);
+
+ return win_center;
+}
diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp
index 6450686dd..d5687f5cc 100644
--- a/host/lib/usrp/x300/x300_clock_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp
@@ -21,6 +21,7 @@
#include <uhd/utils/math.hpp>
#include <boost/cstdint.hpp>
#include <boost/format.hpp>
+#include <boost/math/special_functions/round.hpp>
#include <stdexcept>
#include <cmath>
#include <cstdlib>
@@ -29,6 +30,30 @@ static const double X300_REF_CLK_OUT_RATE = 10e6;
static const boost::uint16_t X300_MAX_CLKOUT_DIV = 1045;
static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6;
+struct x300_clk_delays {
+ x300_clk_delays() :
+ fpga_dly_ns(0.0),adc_dly_ns(0.0),dac_dly_ns(0.0),db_rx_dly_ns(0.0),db_tx_dly_ns(0.0)
+ {}
+ x300_clk_delays(double fpga, double adc, double dac, double db_rx, double db_tx) :
+ fpga_dly_ns(fpga),adc_dly_ns(adc),dac_dly_ns(dac),db_rx_dly_ns(db_rx),db_tx_dly_ns(db_tx)
+ {}
+
+ double fpga_dly_ns;
+ double adc_dly_ns;
+ double dac_dly_ns;
+ double db_rx_dly_ns;
+ double db_tx_dly_ns;
+};
+
+// Tune the FPGA->ADC clock delay to ensure a safe ADC_SSCLK -> RADIO_CLK crossing.
+// If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC
+// because the data interface clock is generated from FPGA_CLK.
+static const x300_clk_delays X300_REV0_6_CLK_DELAYS = x300_clk_delays(
+ /*fpga=*/0.000, /*adc=*/2.200, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000);
+
+static const x300_clk_delays X300_REV7_CLK_DELAYS = x300_clk_delays(
+ /*fpga=*/0.000, /*adc=*/0.000, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000);
+
using namespace uhd;
x300_clock_ctrl::~x300_clock_ctrl(void){
@@ -213,6 +238,187 @@ public:
_spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32);
}
+ double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) {
+ //All dividers have are delayed by 5 taps by default. The delay
+ //set by this function is relative to the 5 tap delay
+ static const boost::uint16_t DDLY_MIN_TAPS = 5;
+ static const boost::uint16_t DDLY_MAX_TAPS = 522; //Extended mode
+
+ //The resolution and range of the analog delay is fixed
+ static const double ADLY_RES_NS = 0.025;
+ static const double ADLY_MIN_NS = 0.500;
+ static const double ADLY_MAX_NS = 0.975;
+
+ //Each digital tap delays the clock by one VCO period
+ double vco_period_ns = 1.0e9/_vco_freq;
+ double half_vco_period_ns = vco_period_ns/2.0;
+
+ //Implement as much of the requested delay using digital taps. Whatever is leftover
+ //will be made up using the analog delay element and the half-cycle digital tap.
+ //A caveat here is that the analog delay starts at ADLY_MIN_NS, so we need to back off
+ //by that much when coming up with the digital taps so that the difference can be made
+ //up using the analog delay.
+ boost::uint16_t ddly_taps = 0;
+ if (delay_ns < ADLY_MIN_NS) {
+ ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns)/vco_period_ns));
+ } else {
+ ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns-ADLY_MIN_NS)/vco_period_ns));
+ }
+ double leftover_delay = delay_ns - (vco_period_ns * ddly_taps);
+
+ //Compute settings
+ boost::uint16_t ddly_value = ddly_taps + DDLY_MIN_TAPS;
+ bool adly_en = false;
+ boost::uint8_t adly_value = 0;
+ boost::uint8_t half_shift_en = 0;
+
+ if (ddly_value > DDLY_MAX_TAPS) {
+ throw uhd::value_error("set_clock_delay: Requested delay is out of range.");
+ }
+
+ double coerced_delay = (vco_period_ns * ddly_taps);
+ if (leftover_delay > ADLY_MAX_NS) {
+ //The VCO is running too slowly for us to compensate the digital delay difference using
+ //analog delay. Do the best we can.
+ adly_en = true;
+ adly_value = static_cast<boost::uint8_t>(boost::math::round((ADLY_MAX_NS-ADLY_MIN_NS)/ADLY_RES_NS));
+ coerced_delay += ADLY_MAX_NS;
+ } else if (leftover_delay >= ADLY_MIN_NS && leftover_delay <= ADLY_MAX_NS) {
+ //The leftover delay can be compensated by the analog delay up to the analog delay resolution
+ adly_en = true;
+ adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay-ADLY_MIN_NS)/ADLY_RES_NS));
+ coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value);
+ } else if (leftover_delay >= (ADLY_MIN_NS - half_vco_period_ns) && leftover_delay < ADLY_MIN_NS) {
+ //The leftover delay if less than the minimum supported analog delay but if we move the digital
+ //delay back by half a VCO cycle then it will be in the range of the analog delay. So do that!
+ adly_en = true;
+ adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay+half_vco_period_ns-ADLY_MIN_NS)/ADLY_RES_NS));
+ half_shift_en = 1;
+ coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value)-half_vco_period_ns;
+ } else {
+ //Even after moving the digital delay back by half a cycle, we cannot make up the difference
+ //so give up on compensating for the difference from the digital delay tap.
+ //If control reaches here then the value of leftover_delay is possible very small and will still
+ //be close to what the client requested.
+ }
+
+ UHD_LOGV(often)
+ << boost::format("x300_clock_ctrl::set_clock_delay: Which=%d, Requested=%f, Digital Taps=%d, Half Shift=%d, Analog Delay=%d (%s), Coerced Delay=%fns"
+ ) % which % delay_ns % ddly_value % (half_shift_en?"ON":"OFF") % ((int)adly_value) % (adly_en?"ON":"OFF") % coerced_delay << std::endl;
+
+ //Apply settings
+ switch (which)
+ {
+ case X300_CLOCK_WHICH_FPGA:
+ _lmk04816_regs.CLKout0_1_DDLY = ddly_value;
+ _lmk04816_regs.CLKout0_1_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout0_1_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_PD;
+ }
+ write_regs(0);
+ write_regs(6);
+ _delays.fpga_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_DB0_RX:
+ case X300_CLOCK_WHICH_DB1_RX:
+ _lmk04816_regs.CLKout2_3_DDLY = ddly_value;
+ _lmk04816_regs.CLKout2_3_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout2_3_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_PD;
+ }
+ write_regs(1);
+ write_regs(6);
+ _delays.db_rx_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_DB0_TX:
+ case X300_CLOCK_WHICH_DB1_TX:
+ _lmk04816_regs.CLKout4_5_DDLY = ddly_value;
+ _lmk04816_regs.CLKout4_5_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout4_5_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_PD;
+ }
+ write_regs(2);
+ write_regs(7);
+ _delays.db_tx_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_DAC0:
+ case X300_CLOCK_WHICH_DAC1:
+ _lmk04816_regs.CLKout6_7_DDLY = ddly_value;
+ _lmk04816_regs.CLKout6_7_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout6_7_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_PD;
+ }
+ write_regs(3);
+ write_regs(7);
+ _delays.dac_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_ADC0:
+ case X300_CLOCK_WHICH_ADC1:
+ _lmk04816_regs.CLKout8_9_DDLY = ddly_value;
+ _lmk04816_regs.CLKout8_9_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout8_9_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_PD;
+ }
+ write_regs(4);
+ write_regs(8);
+ _delays.adc_dly_ns = coerced_delay;
+ break;
+ default:
+ throw uhd::value_error("set_clock_delay: Requested source is invalid.");
+ }
+
+ //Delays are applied only on a sync event
+ if (resync) sync_clocks();
+
+ return coerced_delay;
+ }
+
+ double get_clock_delay(const x300_clock_which_t which) {
+ switch (which)
+ {
+ case X300_CLOCK_WHICH_FPGA:
+ return _delays.fpga_dly_ns;
+ case X300_CLOCK_WHICH_DB0_RX:
+ case X300_CLOCK_WHICH_DB1_RX:
+ return _delays.db_rx_dly_ns;
+ case X300_CLOCK_WHICH_DB0_TX:
+ case X300_CLOCK_WHICH_DB1_TX:
+ return _delays.db_tx_dly_ns;
+ case X300_CLOCK_WHICH_DAC0:
+ case X300_CLOCK_WHICH_DAC1:
+ return _delays.dac_dly_ns;
+ case X300_CLOCK_WHICH_ADC0:
+ case X300_CLOCK_WHICH_ADC1:
+ return _delays.adc_dly_ns;
+ default:
+ throw uhd::value_error("get_clock_delay: Requested source is invalid.");
+ }
+ }
private:
@@ -409,7 +615,6 @@ private:
_lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP;
this->write_regs(0);
_lmk04816_regs.CLKout0_1_DIV = master_clock_div;
- _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X;
this->write_regs(0);
// Register 1
@@ -433,9 +638,6 @@ private:
_lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS
_lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX
_lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX
- // Analog delay of 900ps to synchronize the radio clock with the source synchronous ADC clocks.
- // This delay may need to vary due to temperature. Tested and verified at room temperature only.
- _lmk04816_regs.CLKout0_1_ADLY = 0x10;
// Register 7
_lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX
@@ -501,6 +703,19 @@ private:
// PLL2_P_30 set in individual cases above
// PLL2_N_30 set in individual cases above
+ if (_hw_rev >= 7) {
+ _delays = X300_REV7_CLK_DELAYS;
+ } else {
+ _delays = X300_REV0_6_CLK_DELAYS;
+ }
+
+ //Apply delay values
+ set_clock_delay(X300_CLOCK_WHICH_FPGA, _delays.fpga_dly_ns, false);
+ set_clock_delay(X300_CLOCK_WHICH_DB0_RX, _delays.db_rx_dly_ns, false); //Sets both Ch0 and Ch1
+ set_clock_delay(X300_CLOCK_WHICH_DB0_TX, _delays.db_tx_dly_ns, false); //Sets both Ch0 and Ch1
+ set_clock_delay(X300_CLOCK_WHICH_ADC0, _delays.adc_dly_ns, false); //Sets both Ch0 and Ch1
+ set_clock_delay(X300_CLOCK_WHICH_DAC0, _delays.dac_dly_ns, false); //Sets both Ch0 and Ch1
+
/* Write the configuration values into the LMK */
for (size_t i = 1; i <= 16; ++i) {
this->write_regs(i);
@@ -512,13 +727,14 @@ private:
this->sync_clocks();
}
- const spi_iface::sptr _spiface;
- const size_t _slaveno;
- const size_t _hw_rev;
- const double _master_clock_rate;
- const double _system_ref_rate;
- lmk04816_regs_t _lmk04816_regs;
- double _vco_freq;
+ const spi_iface::sptr _spiface;
+ const size_t _slaveno;
+ const size_t _hw_rev;
+ const double _master_clock_rate;
+ const double _system_ref_rate;
+ lmk04816_regs_t _lmk04816_regs;
+ double _vco_freq;
+ x300_clk_delays _delays;
};
x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface,
diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp
index 9c08aa356..160a14e6d 100644
--- a/host/lib/usrp/x300/x300_clock_ctrl.hpp
+++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp
@@ -33,7 +33,7 @@ enum x300_clock_which_t
X300_CLOCK_WHICH_DB0_TX,
X300_CLOCK_WHICH_DB1_RX,
X300_CLOCK_WHICH_DB1_TX,
- X300_CLOCK_WHICH_TEST,
+ X300_CLOCK_WHICH_FPGA,
};
class x300_clock_ctrl : boost::noncopyable
@@ -94,6 +94,22 @@ public:
*/
virtual void set_ref_out(const bool) = 0;
+ /*! Set the clock delay for the given clock divider.
+ * \param which which clock
+ * \param rate the delay in nanoseconds
+ * \param resync resync clocks to apply delays
+ * \return the actual delay value set
+ * \throw exception when which invalid or delay_ns out of range
+ */
+ virtual double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) = 0;
+
+ /*! Get the clock delay for the given clock divider.
+ * \param which which clock
+ * \return the actual delay value set
+ * \throw exception when which invalid
+ */
+ virtual double get_clock_delay(const x300_clock_which_t which) = 0;
+
/*! Reset the clocks.
* Should be called if the reference clock changes
* to reduce the time required to achieve a lock.
diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp
index d3bcb8644..bb41146b6 100644
--- a/host/lib/usrp/x300/x300_dac_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp
@@ -129,12 +129,16 @@ public:
_check_pll();
// Configure digital interface settings
- write_ad9146_reg(0x16, 0x02); // Skew DCI signal by 615ps to find stable data eye
- write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface
- //fpga wants I,Q in the sample word:
- //first transaction goes into low bits
- //second transaction goes into high bits
- //therefore, we want Q to go first (bit 6 == 1)
+ // Bypass DCI delay. We center the clock edge in the data
+ // valid window in the FPGA by phase shifting the DCI going
+ // to the DAC.
+ write_ad9146_reg(0x16, 0x04);
+ // 2's comp, I first, byte wide interface
+ write_ad9146_reg(0x03, 0x00);
+ // FPGA wants I,Q in the sample word:
+ // - First transaction goes into low bits
+ // - Second transaction goes into high bits
+ // therefore, we want Q to go first (bit 6 == 1)
write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode
// Configure interpolation filters
diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h
index 76531f921..6493e938d 100644
--- a/host/lib/usrp/x300/x300_fw_common.h
+++ b/host/lib/usrp/x300/x300_fw_common.h
@@ -29,10 +29,11 @@
extern "C" {
#endif
-#define X300_MAX_HW_REV 6
-#define X300_FW_COMPAT_MAJOR 3
+#define X300_REVISION_COMPAT 7
+#define X300_REVISION_MIN 2
+#define X300_FW_COMPAT_MAJOR 4
#define X300_FW_COMPAT_MINOR 0
-#define X300_FPGA_COMPAT_MAJOR 9
+#define X300_FPGA_COMPAT_MAJOR 13
//shared memory sections - in between the stack and the program space
#define X300_FW_SHMEM_BASE 0x6000
diff --git a/host/lib/usrp/x300/x300_image_loader.cpp b/host/lib/usrp/x300/x300_image_loader.cpp
new file mode 100644
index 000000000..321309868
--- /dev/null
+++ b/host/lib/usrp/x300/x300_image_loader.cpp
@@ -0,0 +1,402 @@
+//
+// 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/>.
+//
+
+#include <fstream>
+#include <vector>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/transport/nirio/niusrprio_session.h>
+#include <uhd/transport/nirio/status.h>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "x300_impl.hpp"
+#include "x300_fw_common.h"
+#include "cdecode.h"
+
+namespace fs = boost::filesystem;
+
+using namespace boost::algorithm;
+using namespace uhd;
+using namespace uhd::transport;
+
+/*
+ * Constants
+ */
+#define X300_FPGA_BIN_SIZE_BYTES 15877916
+#define X300_FPGA_BIT_SIZE_BYTES 15878032
+#define X300_FPGA_PROG_UDP_PORT 49157
+#define X300_FLASH_SECTOR_SIZE 131072
+#define X300_PACKET_SIZE_BYTES 256
+#define X300_FPGA_SECTOR_START 32
+#define X300_MAX_RESPONSE_BYTES 128
+#define UDP_TIMEOUT 3
+#define FPGA_LOAD_TIMEOUT 15
+
+/*
+ * Packet structure
+ */
+typedef struct {
+ boost::uint32_t flags;
+ boost::uint32_t sector;
+ boost::uint32_t index;
+ boost::uint32_t size;
+ union {
+ boost::uint8_t data8[X300_PACKET_SIZE_BYTES];
+ boost::uint16_t data16[X300_PACKET_SIZE_BYTES/2];
+ };
+} x300_fpga_update_data_t;
+
+/*
+ * X-Series burn session
+ */
+typedef struct {
+ bool found;
+ bool ethernet;
+ bool configure; // Reload FPGA after burning to flash (Ethernet only)
+ bool verify; // Device will verify the download along the way (Ethernet only)
+ bool lvbitx;
+ uhd::device_addr_t dev_addr;
+ std::string ip_addr;
+ std::string fpga_type;
+ std::string resource;
+ std::string filepath;
+ std::string rpc_port;
+ boost::uint32_t size;
+ udp_simple::sptr xport;
+ std::vector<char> bitstream; // .bin image extracted from .lvbitx file
+ boost::uint8_t data_in[udp_simple::mtu];
+} x300_session_t;
+
+/*
+ * Extract the .bin image from the given LVBITX file.
+ */
+static void extract_from_lvbitx(x300_session_t &session){
+ boost::property_tree::ptree pt;
+ boost::property_tree::xml_parser::read_xml(session.filepath.c_str(), pt,
+ boost::property_tree::xml_parser::no_comments |
+ boost::property_tree::xml_parser::trim_whitespace);
+ const std::string encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream"));
+ std::vector<char> decoded_bitstream(encoded_bitstream.size());
+
+ base64_decodestate decode_state;
+ base64_init_decodestate(&decode_state);
+ const size_t decoded_size = base64_decode_block(encoded_bitstream.c_str(),
+ encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state);
+ decoded_bitstream.resize(decoded_size);
+ session.bitstream.swap(decoded_bitstream);
+
+ session.size = session.bitstream.size();
+}
+
+/*
+ * Validate X300 image and extract if LVBITX.
+ */
+static void x300_validate_image(x300_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ std::string extension = fs::extension(session.filepath);
+ session.lvbitx = (extension == ".lvbitx");
+
+ if(session.lvbitx){
+ extract_from_lvbitx(session);
+ if(session.size > X300_FPGA_BIN_SIZE_BYTES){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % X300_FPGA_BIN_SIZE_BYTES));
+ }
+
+ /*
+ * PCIe burning just takes a filepath, even for a .lvbitx file,
+ * so just extract it to validate the size.
+ */
+ if(!session.ethernet) session.bitstream.clear();
+ }
+ else if(extension == ".bin" or extension == ".bit"){
+ boost::uint32_t max_size = (extension == ".bin") ? X300_FPGA_BIN_SIZE_BYTES
+ : X300_FPGA_BIT_SIZE_BYTES;
+
+ session.size = fs::file_size(session.filepath);
+ if(session.size > max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % max_size));
+ return;
+ }
+ }
+ else{
+ throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .bin, .bit, or .lvbitx.")
+ % extension));
+ }
+}
+
+static void x300_setup_session(x300_session_t &session,
+ const device_addr_t &args,
+ const std::string &filepath){
+ device_addr_t find_args;
+ find_args["type"] = "x300";
+ if(args.has_key("name")) find_args["name"] = args["name"];
+ if(args.has_key("serial")) find_args["serial"] = args["serial"];
+ if(args.has_key("ip-addr")) find_args["addr"] = args["ip-addr"];
+ else if(args.has_key("resource")) find_args["resource"] = args["resource"];
+
+ device_addrs_t devs = x300_find(args);
+ session.found = (devs.size() > 0);
+ if(!session.found) return;
+
+ session.dev_addr = devs[0];
+ session.ethernet = session.dev_addr.has_key("addr");
+ if(session.ethernet){
+ session.ip_addr = session.dev_addr["addr"];
+ session.configure = args.has_key("configure");
+ session.xport = udp_simple::make_connected(session.ip_addr,
+ BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT));
+ session.verify = args.has_key("verify");
+ }
+ else{
+ session.resource = session.dev_addr["resource"];
+ session.rpc_port = args.get("rpc-port", "5444");
+ }
+
+ /*
+ * The user can specify an FPGA type (1G, HGS, XGS), rather than a filename. If the user
+ * does not specify one, this will default to the type currently on the device. If this
+ * cannot be determined, then the user is forced to specify a filename.
+ */
+ session.fpga_type = args.get("fpga", session.dev_addr.get("fpga", ""));
+ if(filepath == ""){
+ if(!session.dev_addr.has_key("product") or session.fpga_type == ""){
+ throw uhd::runtime_error("Found a device but could not auto-generate an image filename.");
+ }
+ else session.filepath = find_image_path(str(boost::format("usrp_%s_fpga_%s.bit")
+ % (to_lower_copy(session.dev_addr["product"]))
+ % session.fpga_type));
+ }
+ else session.filepath = filepath;
+
+ // Validate image
+ x300_validate_image(session);
+}
+
+/*
+ * Ethernet communication functions
+ */
+static UHD_INLINE size_t x300_send_and_recv(udp_simple::sptr xport,
+ boost::uint32_t pkt_code,
+ x300_fpga_update_data_t *pkt_out,
+ boost::uint8_t* data){
+ pkt_out->flags = uhd::htonx<boost::uint32_t>(pkt_code);
+ xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out)));
+ return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT);
+}
+
+static UHD_INLINE bool x300_recv_ok(const x300_fpga_update_data_t *pkt_in,
+ size_t len){
+ return (len > 0 and
+ ((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR));
+}
+
+// Image data needs to be bitswapped
+static UHD_INLINE void x300_bitswap(boost::uint8_t *num){
+ *num = ((*num & 0xF0) >> 4) | ((*num & 0x0F) << 4);
+ *num = ((*num & 0xCC) >> 2) | ((*num & 0x33) << 2);
+ *num = ((*num & 0xAA) >> 1) | ((*num & 0x55) << 1);
+}
+
+static void x300_ethernet_load(x300_session_t &session){
+
+ // UDP receive buffer
+ x300_fpga_update_data_t pkt_out;
+ const x300_fpga_update_data_t *pkt_in = reinterpret_cast<const x300_fpga_update_data_t*>(session.data_in);
+
+ // Initialize write session
+ boost::uint32_t flags = X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT;
+ size_t len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(x300_recv_ok(pkt_in, len)){
+ std::cout << "-- Initializing FPGA loading..." << std::flush;
+ }
+ else if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error during initialization.");
+ }
+
+ std::cout << "successful." << std::endl;
+ if(session.verify){
+ std::cout << "-- NOTE: Device is verifying the image it is receiving, increasing the loading time." << std::endl;
+ }
+
+ size_t current_pos = 0;
+ size_t sectors = (session.size / X300_FLASH_SECTOR_SIZE);
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+
+ // Each sector
+ for(size_t i = 0; i < session.size; i += X300_FLASH_SECTOR_SIZE){
+
+ // Print progress percentage at beginning of each sector
+ std::cout << boost::format("\r-- Loading %s FPGA image: %d%% (%d/%d sectors)")
+ % session.fpga_type
+ % (int(double(i) / double(session.size) * 100.0))
+ % (i / X300_FLASH_SECTOR_SIZE)
+ % sectors
+ << std::flush;
+
+ // Each packet
+ for(size_t j = i; (j < session.size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){
+ flags = X300_FPGA_PROG_FLAGS_ACK;
+ if(j == i) flags |= X300_FPGA_PROG_FLAGS_ERASE; // Erase at beginning of sector
+ if(session.verify) flags |= X300_FPGA_PROG_FLAGS_VERIFY;
+
+ // Set burn location
+ pkt_out.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE));
+ pkt_out.index = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2);
+ pkt_out.size = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2);
+
+ // Read next piece of image
+ memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES);
+ if(session.lvbitx){
+ memcpy(pkt_out.data8, &session.bitstream[current_pos], X300_PACKET_SIZE_BYTES);
+ current_pos += X300_PACKET_SIZE_BYTES;
+ }
+ else{
+ image.read((char*)pkt_out.data8, X300_PACKET_SIZE_BYTES);
+ }
+
+ // Data must be bitswapped and byteswapped
+ for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){
+ x300_bitswap(&pkt_out.data8[k]);
+ }
+ for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){
+ pkt_out.data16[k] = htonx<boost::uint16_t>(pkt_out.data16[k]);
+ }
+
+ len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(len == 0){
+ if(!session.lvbitx) image.close();
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ if(!session.lvbitx) image.close();
+ throw uhd::runtime_error("Device reported an error.");
+ }
+ }
+ }
+ if(!session.lvbitx){
+ image.close();
+ }
+
+ std::cout << boost::format("\r-- Loading %s FPGA image: 100%% (%d/%d sectors)")
+ % session.fpga_type
+ % sectors
+ % sectors
+ << std::endl;
+
+ // Cleanup
+ if(!session.lvbitx) image.close();
+ flags = (X300_FPGA_PROG_FLAGS_CLEANUP | X300_FPGA_PROG_FLAGS_ACK);
+ pkt_out.sector = pkt_out.index = pkt_out.size = 0;
+ memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES);
+ std::cout << "-- Finalizing image load..." << std::flush;
+ len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error during cleanup.");
+ }
+ else std::cout << "successful." << std::endl;
+
+ // Save new FPGA image (if option set)
+ if(session.configure){
+ flags = (X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK);
+ x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ std::cout << "-- Saving image onto device..." << std::flush;
+ if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error while saving the image.");
+ }
+ else std::cout << "successful." << std::endl;
+ }
+}
+
+static void x300_pcie_load(x300_session_t &session){
+
+ std::cout << boost::format("\r-- Loading %s FPGA image (this will take 5-10 minutes)...")
+ % session.fpga_type
+ << std::flush;
+
+ nirio_status status = NiRio_Status_Success;
+ niusrprio::niusrprio_session fpga_session(session.resource, session.rpc_port);
+ nirio_status_chain(fpga_session.download_bitstream_to_flash(session.filepath), status);
+
+ if(nirio_status_fatal(status)){
+ std::cout << "failed." << std::endl;
+ niusrprio::nirio_status_to_exception(status, "NI-RIO reported the following error:");
+ }
+ else std::cout << "successful." << std::endl;
+}
+
+static bool x300_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if any X3x0 with the given args is found
+ device_addrs_t devs = x300_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ x300_session_t session;
+ x300_setup_session(session,
+ image_loader_args.args,
+ image_loader_args.fpga_path
+ );
+ if(!session.found) return false;
+
+ std::cout << boost::format("Unit: USRP %s (%s, %s)\nFPGA Image: %s\n")
+ % session.dev_addr["product"]
+ % session.dev_addr["serial"]
+ % session.dev_addr[session.ethernet ? "addr" : "resource"]
+ % session.filepath;
+
+ if(session.ethernet) x300_ethernet_load(session);
+ else x300_pcie_load(session);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_x300_image_loader){
+ std::string recovery_instructions = "Aborting. Your USRP X-Series device will likely be unusable. Visit\n"
+ "http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_load_fpga_imgs_jtag\n"
+ "for details on restoring your device.";
+
+ image_loader::register_image_loader("x300", x300_image_loader, recovery_instructions);
+}
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index aff150acb..c9cc0cabc 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-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
@@ -16,7 +16,6 @@
//
#include "x300_impl.hpp"
-#include "x300_regs.hpp"
#include "x300_lvbitx.hpp"
#include "x310_lvbitx.hpp"
#include <boost/algorithm/string.hpp>
@@ -41,7 +40,7 @@
#define NIUSRPRIO_DEFAULT_RPC_PORT "5444"
-#define X300_REV(x) (x - "A" + 1)
+#define X300_REV(x) ((x) - "A" + 1)
using namespace uhd;
using namespace uhd::usrp;
@@ -236,7 +235,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu
return addrs;
}
-static device_addrs_t x300_find(const device_addr_t &hint_)
+device_addrs_t x300_find(const device_addr_t &hint_)
{
//handle the multi-device discovery
device_addrs_t hints = separate_device_addr(hint_);
@@ -400,7 +399,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
default:
nirio_status_to_exception(status, "Motherboard detection error. Please ensure that you \
have a valid USRP X3x0, NI USRP-294xR or NI USRP-295xR device and that all the device \
- driver have been loaded.");
+ drivers have loaded successfully.");
}
//Load the lvbitx onto the device
UHD_MSG(status) << boost::format("Using LVBITX bitfile %s...\n") % lvbitx->get_bitfile_path();
@@ -410,7 +409,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
//Tell the quirks object which FIFOs carry TX stream data
const boost::uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3};
- mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos);
+ mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos, 2);
_tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE);
}
@@ -508,9 +507,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
x300_load_fw(mb.zpu_ctrl, x300_fw_image);
}
- //check compat -- good place to do after conditional loading
+ //check compat numbers
+ //check fpga compat before fw compat because the fw is a subset of the fpga image
+ this->check_fpga_compat(mb_path, mb);
this->check_fw_compat(mb_path, mb.zpu_ctrl);
- this->check_fpga_compat(mb_path, mb.zpu_ctrl);
//store which FPGA image is loaded
mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl);
@@ -558,6 +558,13 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
.set(mb_eeprom)
.subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1));
+ bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom");
+ if (recover_mb_eeprom) {
+ UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version "
+ "checks.\nOperating in this mode may cause hardware damage and unstable "
+ "radio performance!"<< std::endl;
+ }
+
////////////////////////////////////////////////////////////////////
// parse the product number
////////////////////////////////////////////////////////////////////
@@ -570,7 +577,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
product_name = "X310";
break;
default:
- break;
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("Unrecognized product type.\n"
+ "Either the software does not support this device in which case please update your driver software to the latest version and retry OR\n"
+ "The product code in the EEPROM is corrupt and may require reprogramming.");
}
_tree->create<std::string>(mb_path / "name").set(product_name);
_tree->create<std::string>(mb_path / "codename").set("Yetti");
@@ -602,36 +612,57 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
}
////////////////////////////////////////////////////////////////////
- // create clock control objects
+ // read hardware revision and compatibility number
////////////////////////////////////////////////////////////////////
- UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl;
-
mb.hw_rev = 0;
if(mb_eeprom.has_key("revision") and not mb_eeprom["revision"].empty()) {
try {
mb.hw_rev = boost::lexical_cast<size_t>(mb_eeprom["revision"]);
} catch(...) {
- UHD_MSG(warning) << "Revision in EEPROM is invalid! Please reprogram your EEPROM." << std::endl;
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("Revision in EEPROM is invalid! Please reprogram your EEPROM.");
}
} else {
- UHD_MSG(warning) << "No revision detected MB EEPROM must be reprogrammed!" << std::endl;
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("No revision detected. MB EEPROM must be reprogrammed!");
}
- if(mb.hw_rev == 0) {
- UHD_MSG(warning) << "Defaulting to X300 RevD Clock Settings. This will result in non-optimal lock times." << std::endl;
- mb.hw_rev = X300_REV("D");
+ size_t hw_rev_compat = 0;
+ if (mb.hw_rev >= 7) { //Revision compat was added with revision 7
+ if (mb_eeprom.has_key("revision_compat") and not mb_eeprom["revision_compat"].empty()) {
+ try {
+ hw_rev_compat = boost::lexical_cast<size_t>(mb_eeprom["revision_compat"]);
+ } catch(...) {
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("Revision compat in EEPROM is invalid! Please reprogram your EEPROM.");
+ }
+ } else {
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("No revision compat detected. MB EEPROM must be reprogrammed!");
+ }
+ } else {
+ //For older HW just assume that revision_compat = revision
+ hw_rev_compat = mb.hw_rev;
}
- if (mb.hw_rev > X300_MAX_HW_REV) {
- throw uhd::runtime_error(str(
- boost::format("Unsupported board revision number: %d.\n"
- "The maximum board revision number supported in this version is %d.\n"
- "Please update your UHD version.")
- % mb.hw_rev % X300_MAX_HW_REV
- ));
+ if (hw_rev_compat > X300_REVISION_COMPAT) {
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error(str(boost::format(
+ "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.")
+ % mb.hw_rev));
+ } else if (mb.hw_rev < X300_REVISION_MIN) { //Compare min against the revision (and not compat) to give us more leeway for partial support for a compat
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error(str(boost::format(
+ "Software is too new for this hardware. Please downgrade to a driver that supports hardware revision %d.")
+ % mb.hw_rev));
}
- //Create clock control. NOTE: This does not configure the LMK yet.
+ ////////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////////
+ UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl;
+
+ //Initialize clock control registers. NOTE: This does not configure the LMK yet.
initialize_clock_control(mb);
mb.clock = x300_clock_ctrl::make(mb.zpu_spi,
1 /*slaveno*/,
@@ -696,23 +727,33 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
////////////////////////////////////////////////////////////////////
// setup radios
////////////////////////////////////////////////////////////////////
- UHD_MSG(status) << "Initialize Radio control..." << std::endl;
- this->setup_radio(mb_i, "A");
- this->setup_radio(mb_i, "B");
+ this->setup_radio(mb_i, "A", dev_addr);
+ this->setup_radio(mb_i, "B", dev_addr);
+
+ ////////////////////////////////////////////////////////////////////
+ // ADC test and cal
+ ////////////////////////////////////////////////////////////////////
+ if (dev_addr.has_key("self_cal_adc_delay")) {
+ self_cal_adc_xfer_delay(mb, true /* Apply ADC delay */);
+ }
+ if (dev_addr.has_key("ext_adc_self_test")) {
+ extended_adc_test(mb, dev_addr.cast<double>("ext_adc_self_test", 30));
+ } else {
+ self_test_adcs(mb);
+ }
////////////////////////////////////////////////////////////////////
// front panel gpio
////////////////////////////////////////////////////////////////////
mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO);
- const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX");
- BOOST_FOREACH(const std::string &attr, GPIO_ATTRS)
+ BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)
{
- _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr)
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)
.set(0)
- .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1));
+ .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1));
}
_tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK")
- .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK"));
+ .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio));
////////////////////////////////////////////////////////////////////
// register the time keepers - only one can be the highlander
@@ -745,8 +786,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
////////////////////////////////////////////////////////////////////
_tree->create<std::string>(mb_path / "clock_source" / "value")
.set("internal")
- .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1))
- .subscribe(boost::bind(&x300_impl::reset_radios, this, boost::ref(mb)));
+ .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1));
static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo");
_tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_source_options);
@@ -829,8 +869,13 @@ x300_impl::~x300_impl(void)
{
BOOST_FOREACH(mboard_members_t &mb, _mb)
{
- mb.radio_perifs[0].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC
- mb.radio_perifs[1].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC
+ //Disable/reset ADC/DAC
+ mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1);
+ mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0);
+ mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0);
+ mb.radio_perifs[0].misc_outs->flush();
+ mb.radio_perifs[1].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0);
+ mb.radio_perifs[1].misc_outs->flush();
//kill the claimer task and unclaim the device
mb.claimer_task.reset();
@@ -850,15 +895,7 @@ x300_impl::~x300_impl(void)
}
}
-static void check_adc(wb_iface::sptr iface, const boost::uint32_t val)
-{
- boost::uint32_t adc_rb = iface->peek32(RB32_RX);
- adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA
- //UHD_MSG(status) << "adc_rb " << std::hex << adc_rb << " val " << std::hex << val << std::endl;
- UHD_ASSERT_THROW(adc_rb == val);
-}
-
-void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
+void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, const uhd::device_addr_t &dev_addr)
{
const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i);
UHD_ASSERT_THROW(mb_i < _mb.size());
@@ -866,6 +903,8 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
const size_t radio_index = mb.get_radio_index(slot_name);
radio_perifs_t &perif = mb.radio_perifs[radio_index];
+ UHD_MSG(status) << boost::format("Initialize Radio%d control...") % radio_index << std::endl;
+
////////////////////////////////////////////////////////////////////
// radio control
////////////////////////////////////////////////////////////////////
@@ -873,39 +912,59 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
boost::uint32_t ctrl_sid;
both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid);
perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name);
- perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //reset adc + dac
- perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 1) | (1 << 0)); //out of reset + dac enable
+
+ perif.misc_outs = boost::make_shared<radio_misc_outs_reg>();
+ perif.misc_ins = boost::make_shared<radio_misc_ins_reg>();
+ perif.misc_outs->initialize(*perif.ctrl, true);
+ perif.misc_ins->initialize(*perif.ctrl);
+
+ //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1
+ if (radio_index == 0) {
+ perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1);
+ perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0);
+ perif.misc_outs->flush();
+ perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 0);
+ perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 1);
+ perif.misc_outs->flush();
+ }
+ perif.misc_outs->write(radio_misc_outs_reg::DAC_ENABLED, 1);
this->register_loopback_self_test(perif.ctrl);
+ ////////////////////////////////////////////////////////////////
+ // Setup peripherals
+ ////////////////////////////////////////////////////////////////
perif.spi = spi_core_3000::make(perif.ctrl, TOREG(SR_SPI), RB32_SPI);
perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN);
perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate());
perif.leds = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_LEDS));
+ perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT));
+ perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE);
+ perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT));
+ perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
+ perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));
+ perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP));
+ perif.ddc->set_link_rate(10e9/8); //whatever
+ perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
+ perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
+ perif.duc->set_link_rate(10e9/8); //whatever
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time_core_3000::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_now = RB64_TIME_NOW;
+ time64_rb_bases.rb_pps = RB64_TIME_PPS;
+ perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+
+ //Capture delays are calibrated every time. The status is only printed is the user
+ //asks to run the xfer self cal using "self_cal_adc_delay"
+ self_cal_adc_capture_delay(mb, radio_index, dev_addr.has_key("self_cal_adc_delay"));
_tree->access<time_spec_t>(mb_path / "time" / "cmd")
.subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1));
- _tree->access<double>(mb_path / "tick_rate")
- .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1));
-
- ////////////////////////////////////////////////////////////////
- // ADC self test
- ////////////////////////////////////////////////////////////////
- perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc);
- perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000);
- perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000);
- perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc);
- for (size_t k = 0; k < 14; k++)
- {
- perif.adc->set_test_word("zeros", "custom", 1 << k);
- check_adc(perif.ctrl, 1 << (k+2));
- }
- for (size_t k = 0; k < 14; k++)
- {
- perif.adc->set_test_word("custom", "zeros", 1 << k);
- check_adc(perif.ctrl, 1 << (k+18));
- }
- perif.adc->set_test_word("normal", "normal");
////////////////////////////////////////////////////////////////
// create codec control objects
@@ -922,80 +981,28 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
////////////////////////////////////////////////////////////////////
// front end corrections
////////////////////////////////////////////////////////////////////
- perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT));
- const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name;
- _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value")
- .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
- _tree->create<bool>(rx_fe_path / "dc_offset" / "enable")
- .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1))
- .set(true);
- _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value")
- .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
-
- perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT));
- const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name;
- _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value")
- .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
- _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value")
- .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1))
- .set(std::complex<double>(0.0, 0.0));
+ perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name));
+ perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name));
////////////////////////////////////////////////////////////////////
- // create rx dsp control objects
+ // connect rx dsp control objects
////////////////////////////////////////////////////////////////////
- perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));
- perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP));
- perif.ddc->set_link_rate(10e9/8); //whatever
- _tree->access<double>(mb_path / "tick_rate")
- .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1))
- .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1));
const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % radio_index);
- _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range")
- .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc));
- _tree->create<double>(rx_dsp_path / "rate" / "value")
- .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1))
+ perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path));
+ _tree->access<double>(rx_dsp_path / "rate" / "value")
.subscribe(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), radio_index, _1))
- .set(1e6);
- _tree->create<double>(rx_dsp_path / "freq" / "value")
- .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1))
- .set(0.0);
- _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range")
- .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc));
+ ;
_tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
.subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));
////////////////////////////////////////////////////////////////////
- // create tx dsp control objects
+ // connect tx dsp control objects
////////////////////////////////////////////////////////////////////
- perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
- perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
- perif.duc->set_link_rate(10e9/8); //whatever
- _tree->access<double>(mb_path / "tick_rate")
- .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))
- .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));
const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % radio_index);
- _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range")
- .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc));
- _tree->create<double>(tx_dsp_path / "rate" / "value")
- .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1))
+ perif.duc->populate_subtree(_tree->subtree(tx_dsp_path));
+ _tree->access<double>(tx_dsp_path / "rate" / "value")
.subscribe(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), radio_index, _1))
- .set(1e6);
- _tree->create<double>(tx_dsp_path / "freq" / "value")
- .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1))
- .set(0.0);
- _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range")
- .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc));
-
- ////////////////////////////////////////////////////////////////////
- // create time control objects
- ////////////////////////////////////////////////////////////////////
- time_core_3000::readback_bases_type time64_rb_bases;
- time64_rb_bases.rb_now = RB64_TIME_NOW;
- time64_rb_bases.rb_pps = RB64_TIME_PPS;
- perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+ ;
////////////////////////////////////////////////////////////////////
// create RF frontend interfacing
@@ -1309,8 +1316,14 @@ void x300_impl::update_atr_leds(gpio_core_200_32wo::sptr leds, const std::string
void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate)
{
- BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs)
+ BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) {
+ perif.ctrl->set_tick_rate(rate);
perif.time64->set_tick_rate(rate);
+ perif.framer->set_tick_rate(rate);
+ perif.ddc->set_tick_rate(rate);
+ perif.deframer->set_tick_rate(rate);
+ perif.duc->set_tick_rate(rate);
+ }
}
void x300_impl::register_loopback_self_test(wb_iface::sptr iface)
@@ -1365,7 +1378,8 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou
//Optimize for the case when the current source is internal and we are trying
//to set it to internal. This is the only case where we are guaranteed that
//the clock has not gone away so we can skip setting the MUX and reseting the LMK.
- if (not (mb.current_refclk_src == "internal" and source == "internal")) {
+ const bool reconfigure_clks = (mb.current_refclk_src != "internal") or (source != "internal");
+ if (reconfigure_clks) {
//Update the clock MUX on the motherboard to select the requested source
mb.clock_control_regs_clock_source = 0;
mb.clock_control_regs_tcxo_enb = 0;
@@ -1394,10 +1408,10 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou
//The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may
//lead to locking issues. So, disable the ref-locked check for older (unsupported) boards.
if (mb.hw_rev > 4) {
- if (not wait_for_ref_locked(mb.zpu_ctrl, timeout)) {
+ if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, timeout)) {
//failed to lock on reference
if (mb.initialization_done) {
- throw uhd::runtime_error((boost::format("Reference Clock failed to lock to %s source.") % source).str());
+ throw uhd::runtime_error((boost::format("Reference Clock PLL failed to lock to %s source.") % source).str());
} else {
//TODO: Re-enable this warning when we figure out a reliable lock time
//UHD_MSG(warning) << "Reference clock failed to lock to " + source + " during device initialization. " <<
@@ -1406,6 +1420,41 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou
}
}
+ if (reconfigure_clks) {
+ //Reset the radio clock PLL in the FPGA
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL);
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
+
+ //Wait for radio clock PLL to lock
+ if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK, 0.01)) {
+ throw uhd::runtime_error((boost::format("Reference Clock PLL in FPGA failed to lock to %s source.") % source).str());
+ }
+
+ //Reset the IDELAYCTRL used to calibrate the data interface delays
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL);
+ mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0);
+
+ //Wait for the ADC IDELAYCTRL to be ready
+ if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK, 0.01)) {
+ throw uhd::runtime_error((boost::format("ADC Calibration Clock in FPGA failed to lock to %s source.") % source).str());
+ }
+
+ // Reset ADCs and DACs
+ for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {
+ radio_perifs_t &perif = mb.radio_perifs[r];
+ if (perif.misc_outs && r==0) { //ADC/DAC reset lines only exist in Radio0
+ perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1);
+ perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0);
+ perif.misc_outs->flush();
+ perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 0);
+ perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 1);
+ perif.misc_outs->flush();
+ }
+ if (perif.adc) perif.adc->reset();
+ if (perif.dac) perif.dac->reset();
+ }
+ }
+
//Update cache value
mb.current_refclk_src = source;
}
@@ -1432,24 +1481,29 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour
}
}
-bool x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout)
+static bool get_clk_locked(wb_iface::sptr ctrl, boost::uint32_t which)
+{
+ return (ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & which) != 0;
+}
+
+bool x300_impl::wait_for_clk_locked(wb_iface::sptr ctrl, boost::uint32_t which, double timeout)
{
boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::milliseconds(timeout * 1000.0);
- do
- {
- if (get_ref_locked(ctrl).to_bool())
+ do {
+ if (get_clk_locked(ctrl, which))
return true;
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
} while (boost::get_system_time() < timeout_time);
- //failed to lock on reference
- return false;
+ //Check one last time
+ return get_clk_locked(ctrl, which);
}
sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl)
{
- boost::uint32_t clk_status = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS));
- const bool lock = ((clk_status & ZPU_RB_CLK_STATUS_LMK_LOCK) != 0);
+ const bool lock = get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK) &&
+ get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK) &&
+ get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK);
return sensor_value_t("Ref", lock, "locked", "unlocked");
}
@@ -1469,63 +1523,6 @@ bool x300_impl::is_pps_present(wb_iface::sptr ctrl)
}
/***********************************************************************
- * reset and synchronization logic
- **********************************************************************/
-
-void x300_impl::reset_radios(mboard_members_t &mb)
-{
- // Reset ADCs and DACs
- BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs)
- {
- perif.adc->reset();
- perif.dac->reset();
- }
-}
-
-void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios)
-{
- if (radios.size() < 2) return; //Nothing to synchronize
-
- //**PRECONDITION**
- //This function assumes that all the VITA times in "radios" are synchronized
- //to a common reference. Currently, this function is called in get_tx_stream
- //which also has the same precondition.
-
- //Reinitialize and resync all DACs
- for (size_t i = 0; i < radios.size(); i++) {
- radios[i]->dac->reset_and_resync();
- }
-
- //Get a rough estimate of the cumulative command latency
- boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time();
- for (size_t i = 0; i < radios.size(); i++) {
- radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call
- }
- boost::posix_time::time_duration t_elapsed =
- boost::posix_time::microsec_clock::local_time() - t_start;
-
- //Add 100% of headroom + uncertaintly to the command time
- boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/;
-
- //Pick radios[0] as the time reference.
- uhd::time_spec_t sync_time =
- radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6);
-
- //Send the sync command
- for (size_t i = 0; i < radios.size(); i++) {
- radios[i]->ctrl->set_time(sync_time);
- radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1); //Arm FRAMEP/N sync pulse
- radios[i]->ctrl->set_time(uhd::time_spec_t(0.0)); //Clear command time
- }
-
- //Wait and check status
- boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us));
- for (size_t i = 0; i < radios.size(); i++) {
- radios[i]->dac->verify_sync();
- }
-}
-
-/***********************************************************************
* eeprom
**********************************************************************/
@@ -1544,20 +1541,24 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep
* front-panel GPIO
**********************************************************************/
-boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &)
+boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio)
{
return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));
}
-void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value)
+void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value)
{
- if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value);
- if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value);
- if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value);
- if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value);
- if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value);
- if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value);
- if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ switch (attr)
+ {
+ case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value);
+ case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value);
+ case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value);
+ case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value);
+ case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value);
+ case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value);
+ case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
}
/***********************************************************************
@@ -1688,25 +1689,33 @@ void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface)
% compat_major % compat_minor));
}
-void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface)
+void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t &members)
{
- boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
+ boost::uint32_t compat_num = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
boost::uint32_t compat_major = (compat_num >> 16);
boost::uint32_t compat_minor = (compat_num & 0xffff);
if (compat_major != X300_FPGA_COMPAT_MAJOR)
{
+ std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+ std::string image_loader_cmd = str(boost::format("\"%s\" --args=\"type=x300,%s=%s\"")
+ % image_loader_path
+ % (members.xport_path == "eth" ? "addr"
+ : "resource")
+ % members.addr);
+
throw uhd::runtime_error(str(boost::format(
"Expected FPGA compatibility number %d, but got %d:\n"
"The FPGA image on your device is not compatible with this host code build.\n"
"Download the appropriate FPGA images for this version of UHD.\n"
"%s\n\n"
"Then burn a new image to the on-board flash storage of your\n"
- "USRP X3xx device using the burner utility. %s\n\n"
+ "USRP X3xx device using the image loader utility. Use this command:\n\n%s\n\n"
"For more information, refer to the UHD manual:\n\n"
" http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_flash"
) % int(X300_FPGA_COMPAT_MAJOR) % compat_major
- % print_utility_error("uhd_images_downloader.py") % print_utility_error("usrp_x3xx_fpga_burner")));
+ % print_utility_error("uhd_images_downloader.py")
+ % image_loader_cmd));
}
_tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")
% compat_major % compat_minor));
@@ -1727,17 +1736,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& res
if (nirio_status_not_fatal(status)) {
//The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping
switch (pid) {
- case X300_USRP_PCIE_SSID:
+ case X300_USRP_PCIE_SSID_ADC_33:
+ case X300_USRP_PCIE_SSID_ADC_18:
mb_type = USRP_X300_MB; break;
- case X310_USRP_PCIE_SSID:
- case X310_2940R_PCIE_SSID:
- case X310_2942R_PCIE_SSID:
- case X310_2943R_PCIE_SSID:
- case X310_2944R_PCIE_SSID:
- case X310_2950R_PCIE_SSID:
- case X310_2952R_PCIE_SSID:
- case X310_2953R_PCIE_SSID:
- case X310_2954R_PCIE_SSID:
+ case X310_USRP_PCIE_SSID_ADC_33:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_33:
+ case X310_USRP_PCIE_SSID_ADC_18:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_18:
mb_type = USRP_X310_MB; break;
default:
mb_type = UNKNOWN; break;
@@ -1762,17 +1793,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo
switch (product_num) {
//The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping
- case X300_USRP_PCIE_SSID:
+ case X300_USRP_PCIE_SSID_ADC_33:
+ case X300_USRP_PCIE_SSID_ADC_18:
mb_type = USRP_X300_MB; break;
- case X310_USRP_PCIE_SSID:
- case X310_2940R_PCIE_SSID:
- case X310_2942R_PCIE_SSID:
- case X310_2943R_PCIE_SSID:
- case X310_2944R_PCIE_SSID:
- case X310_2950R_PCIE_SSID:
- case X310_2952R_PCIE_SSID:
- case X310_2953R_PCIE_SSID:
- case X310_2954R_PCIE_SSID:
+ case X310_USRP_PCIE_SSID_ADC_33:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_33:
+ case X310_USRP_PCIE_SSID_ADC_18:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_18:
mb_type = USRP_X310_MB; break;
default:
UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl;
@@ -1781,4 +1834,3 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo
}
return mb_type;
}
-
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index 890ef7bcb..20cd4d754 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-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
@@ -49,6 +49,8 @@
#include <uhd/transport/nirio/niusrprio_session.h>
#include <uhd/transport/vrt_if_packet.hpp>
#include "recv_packet_demuxer_3000.hpp"
+#include <uhd/utils/soft_register.hpp>
+#include "x300_regs.hpp"
static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin";
@@ -140,6 +142,8 @@ uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp);
uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy);
+uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_);
+
class x300_impl : public uhd::device
{
public:
@@ -169,9 +173,43 @@ public:
private:
boost::shared_ptr<async_md_type> _async_md;
+ class radio_misc_outs_reg : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET, /*width*/ 1, /*shift*/ 2); //[2]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB, /*width*/ 1, /*shift*/ 3); //[3]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL, /*width*/ 5, /*shift*/ 4); //[8:4]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED, /*width*/ 1, /*shift*/ 9); //[9]
+
+ radio_misc_outs_reg(): uhd::soft_reg32_wo_t(TOREG(SR_MISC_OUTS)) {
+ //Initial values
+ set(DAC_ENABLED, 0);
+ set(DAC_RESET_N, 0);
+ set(ADC_RESET, 0);
+ set(ADC_DATA_DLY_STB, 0);
+ set(ADC_DATA_DLY_VAL, 16);
+ set(ADC_CHECKER_ENABLED, 0);
+ }
+ };
+ class radio_misc_ins_reg : public uhd::soft_reg32_ro_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2); //[2]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3); //[3]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR, /*width*/ 1, /*shift*/ 4); //[4]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR, /*width*/ 1, /*shift*/ 5); //[5]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR, /*width*/ 1, /*shift*/ 6); //[6]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR, /*width*/ 1, /*shift*/ 7); //[7]
+
+ radio_misc_ins_reg(): uhd::soft_reg32_ro_t(RB32_MISC_INS) { }
+ };
+
//perifs in the radio core
struct radio_perifs_t
{
+ //Interfaces
radio_ctrl_core_3000::sptr ctrl;
spi_core_3000::sptr spi;
x300_adc_ctrl::sptr adc;
@@ -184,6 +222,9 @@ private:
gpio_core_200_32wo::sptr leds;
rx_frontend_core_200::sptr rx_fe;
tx_frontend_core_200::sptr tx_fe;
+ //Registers
+ radio_misc_outs_reg::sptr misc_outs;
+ radio_misc_ins_reg::sptr misc_ins;
};
//overflow recovery impl
@@ -211,7 +252,8 @@ private:
i2c_core_100_wb32::sptr zpu_i2c;
//perifs in each radio
- radio_perifs_t radio_perifs[2]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B
+ static const size_t NUM_RADIOS = 2;
+ radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B
uhd::usrp::dboard_eeprom_t db_eeproms[8];
//! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs
size_t get_radio_index(const std::string &slot_name) {
@@ -259,7 +301,7 @@ private:
* \param mb_i Motherboard index
* \param slot_name Slot name (A or B).
*/
- void setup_radio(const size_t, const std::string &slot_name);
+ void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr);
size_t _sid_framer;
struct sid_config_t
@@ -348,21 +390,26 @@ private:
void set_time_source_out(mboard_members_t&, const bool);
void update_clock_source(mboard_members_t&, const std::string &);
void update_time_source(mboard_members_t&, const std::string &);
- void reset_radios(mboard_members_t&);
uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr);
- bool wait_for_ref_locked(uhd::wb_iface::sptr, double timeout = 0.0);
+ bool wait_for_clk_locked(uhd::wb_iface::sptr, boost::uint32_t which, double timeout);
bool is_pps_present(uhd::wb_iface::sptr);
void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &);
void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &);
void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);
- void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);
+ void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members);
void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant);
- boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &);
- void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t);
+ boost::uint32_t get_fp_gpio(gpio_core_200::sptr);
+ void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t);
+
+ void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false);
+ double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false);
+ void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100);
+
+ void extended_adc_test(mboard_members_t& mb, double duration_s);
//**PRECONDITION**
//This function assumes that all the VITA times in "radios" are synchronized
diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp
index 334ae8168..e3515af0c 100644
--- a/host/lib/usrp/x300/x300_io_impl.cpp
+++ b/host/lib/usrp/x300/x300_io_impl.cpp
@@ -23,6 +23,7 @@
#include <uhd/transport/nirio_zero_copy.hpp>
#include "async_packet_handler.hpp"
#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/chdr.hpp>
#include <boost/bind.hpp>
#include <uhd/utils/tasks.hpp>
#include <uhd/utils/log.hpp>
@@ -124,41 +125,6 @@ void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i,
/***********************************************************************
- * VITA stuff
- **********************************************************************/
-static void x300_if_hdr_unpack_be(
- const boost::uint32_t *packet_buff,
- vrt::if_packet_info_t &if_packet_info
-){
- if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
- return vrt::if_hdr_unpack_be(packet_buff, if_packet_info);
-}
-
-static void x300_if_hdr_pack_be(
- boost::uint32_t *packet_buff,
- vrt::if_packet_info_t &if_packet_info
-){
- if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
- return vrt::if_hdr_pack_be(packet_buff, if_packet_info);
-}
-
-static void x300_if_hdr_unpack_le(
- const boost::uint32_t *packet_buff,
- vrt::if_packet_info_t &if_packet_info
-){
- if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
- return vrt::if_hdr_unpack_le(packet_buff, if_packet_info);
-}
-
-static void x300_if_hdr_pack_le(
- boost::uint32_t *packet_buff,
- vrt::if_packet_info_t &if_packet_info
-){
- if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
- return vrt::if_hdr_pack_le(packet_buff, if_packet_info);
-}
-
-/***********************************************************************
* RX flow control handler
**********************************************************************/
static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args)
@@ -209,9 +175,9 @@ static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xpo
//load header
if (big_endian)
- x300_if_hdr_pack_be(pkt, packet_info);
+ vrt::chdr::if_hdr_pack_be(pkt, packet_info);
else
- x300_if_hdr_pack_le(pkt, packet_info);
+ vrt::chdr::if_hdr_pack_le(pkt, packet_info);
//load payload
pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0);
@@ -276,12 +242,12 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero
{
if (big_endian)
{
- x300_if_hdr_unpack_be(packet_buff, if_packet_info);
+ vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info);
endian_conv = uhd::ntohx;
}
else
{
- x300_if_hdr_unpack_le(packet_buff, if_packet_info);
+ vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info);
endian_conv = uhd::wtohx;
}
}
@@ -430,10 +396,10 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_)
//init some streamer stuff
std::string conv_endianness;
if (mb.if_pkt_is_big_endian) {
- my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be);
+ my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be);
conv_endianness = "be";
} else {
- my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le);
+ my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le);
conv_endianness = "le";
}
@@ -594,10 +560,10 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
std::string conv_endianness;
if (mb.if_pkt_is_big_endian) {
- my_streamer->set_vrt_packer(&x300_if_hdr_pack_be);
+ my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be);
conv_endianness = "be";
} else {
- my_streamer->set_vrt_packer(&x300_if_hdr_pack_le);
+ my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le);
conv_endianness = "le";
}
diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp
index f920b5ae2..6e92a6dbc 100644
--- a/host/lib/usrp/x300/x300_regs.hpp
+++ b/host/lib/usrp/x300/x300_regs.hpp
@@ -25,101 +25,134 @@
#define localparam static const int
-localparam SR_DACSYNC = 5;
-localparam SR_LOOPBACK = 6;
-localparam SR_TEST = 7;
-localparam SR_SPI = 8;
-localparam SR_GPIO = 16;
-localparam SR_MISC_OUTS = 24;
-localparam SR_READBACK = 32;
-localparam SR_TX_CTRL = 64;
-localparam SR_RX_CTRL = 96;
-localparam SR_TIME = 128;
-localparam SR_RX_DSP = 144;
-localparam SR_TX_DSP = 184;
-localparam SR_LEDS = 195;
-localparam SR_FP_GPIO = 200;
-localparam SR_RX_FRONT = 208;
-localparam SR_TX_FRONT = 216;
-
-localparam RB32_GPIO = 0;
-localparam RB32_SPI = 4;
-localparam RB64_TIME_NOW = 8;
-localparam RB64_TIME_PPS = 16;
-localparam RB32_TEST = 24;
-localparam RB32_RX = 28;
-localparam RB32_FP_GPIO = 32;
-
-localparam BL_ADDRESS = 0;
-localparam BL_DATA = 1;
+localparam SR_DACSYNC = 5;
+localparam SR_LOOPBACK = 6;
+localparam SR_TEST = 7;
+localparam SR_SPI = 8;
+localparam SR_GPIO = 16;
+localparam SR_MISC_OUTS = 24;
+localparam SR_READBACK = 32;
+localparam SR_TX_CTRL = 64;
+localparam SR_RX_CTRL = 96;
+localparam SR_TIME = 128;
+localparam SR_RX_DSP = 144;
+localparam SR_TX_DSP = 184;
+localparam SR_LEDS = 195;
+localparam SR_FP_GPIO = 200;
+localparam SR_RX_FRONT = 208;
+localparam SR_TX_FRONT = 216;
+
+localparam RB32_GPIO = 0;
+localparam RB32_SPI = 4;
+localparam RB64_TIME_NOW = 8;
+localparam RB64_TIME_PPS = 16;
+localparam RB32_TEST = 24;
+localparam RB32_RX = 28;
+localparam RB32_FP_GPIO = 32;
+localparam RB32_MISC_INS = 36;
+
+localparam BL_ADDRESS = 0;
+localparam BL_DATA = 1;
//wishbone settings map - relevant to host code
-#define SET0_BASE 0xa000
-#define SETXB_BASE 0xb000
-#define BOOT_LDR_BASE 0xFA00
-#define I2C0_BASE 0xfe00
-#define I2C1_BASE 0xff00
+#define SET0_BASE 0xa000
+#define SETXB_BASE 0xb000
+#define BOOT_LDR_BASE 0xfa00
+#define I2C0_BASE 0xfe00
+#define I2C1_BASE 0xff00
#define SR_ADDR(base, offset) ((base) + (offset)*4)
localparam ZPU_SR_LEDS = 00;
-localparam ZPU_SR_PHY_RST = 01;
+localparam ZPU_SR_SW_RST = 01;
localparam ZPU_SR_CLOCK_CTRL = 02;
localparam ZPU_SR_XB_LOCAL = 03;
localparam ZPU_SR_SPI = 32;
localparam ZPU_SR_ETHINT0 = 40;
localparam ZPU_SR_ETHINT1 = 56;
+//reset bits
+#define ZPU_SR_SW_RST_ETH_PHY (1<<0)
+#define ZPU_SR_SW_RST_RADIO_RST (1<<1)
+#define ZPU_SR_SW_RST_RADIO_CLK_PLL (1<<2)
+#define ZPU_SR_SW_RST_ADC_IDELAYCTRL (1<<3)
+
//clock controls
-#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00
-#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02
-#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO 0x03
-#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00
-#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02
-#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO 0x03
-
-localparam ZPU_RB_SPI = 2;
+#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00
+#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02
+#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO 0x03
+#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00
+#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02
+#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO 0x03
+
+localparam ZPU_RB_SPI = 2;
localparam ZPU_RB_CLK_STATUS = 3;
localparam ZPU_RB_COMPAT_NUM = 6;
localparam ZPU_RB_ETH_TYPE0 = 4;
localparam ZPU_RB_ETH_TYPE1 = 5;
//clock status
-#define ZPU_RB_CLK_STATUS_LMK_STATUS (0x3 << 0)
-#define ZPU_RB_CLK_STATUS_LMK_LOCK (0x1 << 2)
-#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER (0x1 << 3)
-#define ZPU_RB_CLK_STATUS_PPS_DETECT (0x1 << 4)
+#define ZPU_RB_CLK_STATUS_LMK_STATUS (0x3 << 0)
+#define ZPU_RB_CLK_STATUS_LMK_LOCK (0x1 << 2)
+#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER (0x1 << 3)
+#define ZPU_RB_CLK_STATUS_PPS_DETECT (0x1 << 4)
+#define ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK (0x1 << 5)
+#define ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK (0x1 << 6)
//spi slaves on radio
-#define DB_DAC_SEN (1 << 7)
-#define DB_ADC_SEN (1 << 6)
+#define DB_DAC_SEN (1 << 7)
+#define DB_ADC_SEN (1 << 6)
#define DB_RX_LSADC_SEN (1 << 5)
#define DB_RX_LSDAC_SEN (1 << 4)
#define DB_TX_LSADC_SEN (1 << 3)
#define DB_TX_LSDAC_SEN (1 << 2)
-#define DB_RX_SEN (1 << 1)
-#define DB_TX_SEN (1 << 0)
+#define DB_RX_SEN (1 << 1)
+#define DB_TX_SEN (1 << 0)
//-------------------------------------------------------------------
// PCIe Registers
//-------------------------------------------------------------------
-static const uint32_t X300_PCIE_VID = 0x1093;
-static const uint32_t X300_PCIE_PID = 0xC4C4;
-static const uint32_t X300_USRP_PCIE_SSID = 0x7736;
-static const uint32_t X310_USRP_PCIE_SSID = 0x76CA;
-static const uint32_t X310_2940R_PCIE_SSID = 0x772B;
-static const uint32_t X310_2942R_PCIE_SSID = 0x772C;
-static const uint32_t X310_2943R_PCIE_SSID = 0x772D;
-static const uint32_t X310_2944R_PCIE_SSID = 0x772E;
-static const uint32_t X310_2950R_PCIE_SSID = 0x772F;
-static const uint32_t X310_2952R_PCIE_SSID = 0x7730;
-static const uint32_t X310_2953R_PCIE_SSID = 0x7731;
-static const uint32_t X310_2954R_PCIE_SSID = 0x7732;
+static const uint32_t X300_PCIE_VID = 0x1093;
+static const uint32_t X300_PCIE_PID = 0xC4C4;
+//Rev 0-6 motherboard/PCIe IDs (ADC driven at 3.3V)
+static const uint32_t X300_USRP_PCIE_SSID_ADC_33 = 0x7736;
+static const uint32_t X310_USRP_PCIE_SSID_ADC_33 = 0x76CA;
+static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_33 = 0x772B;
+static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_33 = 0x77FB;
+static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_33 = 0x772C;
+static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_33 = 0x77FC;
+static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_33 = 0x772D;
+static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_33 = 0x77FD;
+static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_33 = 0x772E;
+static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_33 = 0x772F;
+static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_33 = 0x77FE;
+static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_33 = 0x7730;
+static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_33 = 0x77FF;
+static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_33 = 0x7731;
+static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_33 = 0x7800;
+static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_33 = 0x7732;
+//Rev 7+ motherboard/PCIe IDs (ADCs driven at 1.8V)
+static const uint32_t X300_USRP_PCIE_SSID_ADC_18 = 0x7861;
+static const uint32_t X310_USRP_PCIE_SSID_ADC_18 = 0x7862;
+static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_18 = 0x7853;
+static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_18 = 0x785B;
+static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_18 = 0x7854;
+static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_18 = 0x785C;
+static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_18 = 0x7855;
+static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_18 = 0x785D;
+static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_18 = 0x7856;
+static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_18 = 0x7857;
+static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_18 = 0x785E;
+static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_18 = 0x7858;
+static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_18 = 0x785F;
+static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_18 = 0x7859;
+static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_18 = 0x7860;
+static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_18 = 0x785A;
static const uint32_t FPGA_X3xx_SIG_VALUE = 0x58333030;
static const uint32_t PCIE_FPGA_ADDR_BASE = 0xC0000;
-#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + X)
+#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + (X))
static const uint32_t FPGA_PCIE_SIG_REG = PCIE_FPGA_REG(0x0000);
static const uint32_t FPGA_CNTR_LO_REG = PCIE_FPGA_REG(0x0004);
@@ -140,8 +173,8 @@ static const uint32_t DMA_FRAME_SIZE_REG = 0x4;
static const uint32_t DMA_SAMPLE_COUNT_REG = 0x8;
static const uint32_t DMA_PKT_COUNT_REG = 0xC;
-#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG)
-#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG)
+#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG))
+#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG))
static const uint32_t DMA_CTRL_DISABLED = 0x00000000;
static const uint32_t DMA_CTRL_ENABLED = 0x00000002;
@@ -154,15 +187,15 @@ static const uint32_t DMA_STATUS_ERROR = 0x00000001;
static const uint32_t DMA_STATUS_BUSY = 0x00000002;
static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500);
-#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X)
+#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + (X))
static const uint32_t PCIE_ZPU_DATA_BASE = 0x30000;
static const uint32_t PCIE_ZPU_READ_BASE = 0x20000; //Trig and Status share the same base
static const uint32_t PCIE_ZPU_STATUS_BASE = 0x20000;
-#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + X)
-#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + X)
-#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + X)
+#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + (X))
+#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + (X))
+#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + (X))
static const uint32_t PCIE_ZPU_READ_START = 0x0;
static const uint32_t PCIE_ZPU_READ_CLOBBER = 0x80000000;