aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp')
-rw-r--r--host/lib/usrp/CMakeLists.txt1
-rw-r--r--host/lib/usrp/b200/CMakeLists.txt34
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp561
-rw-r--r--host/lib/usrp/b200/b200_iface.hpp81
-rw-r--r--host/lib/usrp/b200/b200_impl.cpp885
-rw-r--r--host/lib/usrp/b200/b200_impl.hpp196
-rw-r--r--host/lib/usrp/b200/b200_io_impl.cpp377
-rw-r--r--host/lib/usrp/b200/b200_regs.hpp119
-rw-r--r--host/lib/usrp/b200/b200_uart.cpp117
-rw-r--r--host/lib/usrp/b200/b200_uart.hpp36
10 files changed, 2407 insertions, 0 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
index d251fbaeb..f8c817df5 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -37,3 +37,4 @@ INCLUDE_SUBDIRECTORY(usrp1)
INCLUDE_SUBDIRECTORY(usrp2)
INCLUDE_SUBDIRECTORY(b100)
INCLUDE_SUBDIRECTORY(e100)
+INCLUDE_SUBDIRECTORY(b200)
diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt
new file mode 100644
index 000000000..3d8aad052
--- /dev/null
+++ b/host/lib/usrp/b200/CMakeLists.txt
@@ -0,0 +1,34 @@
+#
+# Copyright 2012-2013 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the B100 support
+########################################################################
+LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)
+
+IF(ENABLE_B200)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_uart.cpp
+ )
+ENDIF(ENABLE_B200)
diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp
new file mode 100644
index 000000000..5173beacb
--- /dev/null
+++ b/host/lib/usrp/b200/b200_iface.cpp
@@ -0,0 +1,561 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "b200_iface.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/format.hpp>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <cstring>
+#include <iomanip>
+#include <libusb.h>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+static const bool load_img_msg = true;
+
+const static boost::uint8_t FX3_FIRMWARE_LOAD = 0xA0;
+const static boost::uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR
+ | LIBUSB_ENDPOINT_OUT);
+const static boost::uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR
+ | LIBUSB_ENDPOINT_IN);
+const static boost::uint8_t B200_VREQ_FPGA_START = 0x02;
+const static boost::uint8_t B200_VREQ_FPGA_DATA = 0x12;
+const static boost::uint8_t B200_VREQ_GET_COMPAT = 0x15;
+const static boost::uint8_t B200_VREQ_SET_FPGA_HASH = 0x1C;
+const static boost::uint8_t B200_VREQ_GET_FPGA_HASH = 0x1D;
+const static boost::uint8_t B200_VREQ_SET_FW_HASH = 0x1E;
+const static boost::uint8_t B200_VREQ_GET_FW_HASH = 0x1F;
+const static boost::uint8_t B200_VREQ_LOOP = 0x22;
+const static boost::uint8_t B200_VREQ_SPI_WRITE = 0x32;
+const static boost::uint8_t B200_VREQ_SPI_READ = 0x42;
+const static boost::uint8_t B200_VREQ_FPGA_RESET = 0x62;
+const static boost::uint8_t B200_VREQ_GPIF_RESET = 0x72;
+const static boost::uint8_t B200_VREQ_GET_USB = 0x80;
+const static boost::uint8_t B200_VREQ_GET_STATUS = 0x83;
+const static boost::uint8_t B200_VREQ_AD9361_CTRL_WRITE = 0x90;
+const static boost::uint8_t B200_VREQ_AD9361_CTRL_READ = 0x91;
+const static boost::uint8_t B200_VREQ_FX3_RESET = 0x99;
+const static boost::uint8_t B200_VREQ_EEPROM_WRITE = 0xBA;
+const static boost::uint8_t B200_VREQ_EEPROM_READ = 0xBB;
+
+const static boost::uint8_t FX3_STATE_FPGA_READY = 0x00;
+const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x01;
+const static boost::uint8_t FX3_STATE_BUSY = 0x02;
+const static boost::uint8_t FX3_STATE_RUNNING = 0x03;
+
+typedef boost::uint32_t hash_type;
+
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * Create a file hash
+ * The hash will be used to identify the loaded firmware and fpga image
+ * \param filename file used to generate hash value
+ * \return hash value in a size_t type
+ */
+static hash_type generate_hash(const char *filename)
+{
+ std::ifstream file(filename);
+ if (not file){
+ throw uhd::io_error(std::string("cannot open input file ") + filename);
+ }
+
+ size_t hash = 0;
+
+ char ch;
+ long long count = 0;
+ while (file.get(ch)) {
+ count++;
+ boost::hash_combine(hash, ch);
+ }
+
+ if (count == 0){
+ throw uhd::io_error(std::string("empty input file ") + filename);
+ }
+
+ if (not file.eof()){
+ throw uhd::io_error(std::string("file error ") + filename);
+ }
+
+ file.close();
+ return hash_type(hash);
+}
+
+
+/*!
+ * Verify checksum of a Intel HEX record
+ * \param record a line from an Intel HEX file
+ * \return true if record is valid, false otherwise
+ */
+bool checksum(std::string *record) {
+
+ size_t len = record->length();
+ unsigned int i;
+ unsigned char sum = 0;
+ unsigned int val;
+
+ for (i = 1; i < len; i += 2) {
+ std::istringstream(record->substr(i, 2)) >> std::hex >> val;
+ sum += val;
+ }
+
+ if (sum == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/*!
+ * Parse Intel HEX record
+ *
+ * \param record a line from an Intel HEX file
+ * \param len output length of record
+ * \param addr output address
+ * \param type output type
+ * \param data output data
+ * \return true if record is sucessfully read, false on error
+ */
+bool parse_record(std::string *record, boost::uint16_t &len, \
+ boost::uint16_t &addr, boost::uint16_t &type, unsigned char* data) {
+
+ unsigned int i;
+ std::string _data;
+ unsigned int val;
+
+ if (record->substr(0, 1) != ":")
+ return false;
+
+ std::istringstream(record->substr(1, 2)) >> std::hex >> len;
+ std::istringstream(record->substr(3, 4)) >> std::hex >> addr;
+ std::istringstream(record->substr(7, 2)) >> std::hex >> type;
+
+ for (i = 0; i < len; i++) {
+ std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val;
+ data[i] = (unsigned char) val;
+ }
+
+ return true;
+}
+
+
+/***********************************************************************
+ * The implementation class
+ **********************************************************************/
+class b200_iface_impl : public b200_iface{
+public:
+
+ b200_iface_impl(usb_control::sptr usb_ctrl):
+ _usb_ctrl(usb_ctrl)
+ {
+ //NOP
+ }
+
+
+ int fx3_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length,
+ boost::int32_t timeout = 0)
+ {
+ return _usb_ctrl->submit(VRT_VENDOR_OUT, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length, // wLength
+ timeout); // timeout
+ }
+
+
+ int fx3_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length,
+ boost::int32_t timeout = 0)
+ {
+ return _usb_ctrl->submit(VRT_VENDOR_IN, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length, // wLength
+ timeout); // timeout
+ }
+
+
+ void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes)
+ {
+ throw uhd::not_implemented_error("b200 write i2c");
+ }
+
+
+ byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes)
+ {
+ throw uhd::not_implemented_error("b200 read i2c");
+ }
+
+ void write_eeprom(boost::uint8_t addr, boost::uint8_t offset,
+ const byte_vector_t &bytes)
+ {
+ fx3_control_write(B200_VREQ_EEPROM_WRITE,
+ 0, offset | (boost::uint16_t(addr) << 8),
+ (unsigned char *) &bytes[0],
+ bytes.size());
+ }
+
+ byte_vector_t read_eeprom(
+ boost::uint8_t addr,
+ boost::uint8_t offset,
+ size_t num_bytes
+ ){
+ byte_vector_t recv_bytes(num_bytes);
+ fx3_control_read(B200_VREQ_EEPROM_READ,
+ 0, offset | (boost::uint16_t(addr) << 8),
+ (unsigned char*) &recv_bytes[0],
+ num_bytes);
+ return recv_bytes;
+ }
+
+ void transact_spi(
+ unsigned char *tx_data,
+ size_t num_tx_bits,
+ unsigned char *rx_data,
+ size_t num_rx_bits
+ ){
+ int ret = 0;
+ boost::uint16_t tx_length = num_tx_bits / 8;
+
+ if(tx_data[0] & 0x80) {
+ ret = fx3_control_write(B200_VREQ_SPI_WRITE, 0x00, \
+ 0x00, tx_data, tx_length);
+ } else {
+ ret = fx3_control_write(B200_VREQ_SPI_READ, 0x00, \
+ 0x00, tx_data, tx_length);
+ }
+
+ if(ret < 0) {
+ throw uhd::io_error("transact_spi: fx3_control_write failed!");
+ }
+
+
+ if(num_rx_bits) {
+ boost::uint16_t total_length = num_rx_bits / 8;
+
+ ret = fx3_control_read(B200_VREQ_LOOP, 0x00, \
+ 0x00, rx_data, total_length);
+
+ if(ret < 0) {
+ throw uhd::io_error("transact_spi: readback failed!");
+ }
+ }
+ }
+
+ void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) {
+ fx3_control_write(B200_VREQ_AD9361_CTRL_WRITE, 0x00, 0x00, (unsigned char *)in_buff, 64);
+ int ret = 0;
+ for (size_t i = 0; i < 10; i++)
+ {
+ ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, 64, 1000);
+ if (ret == 64) return;
+ }
+ throw uhd::io_error(str(boost::format("ad9361_transact failed with usb error: %d") % ret));
+ }
+
+
+ void load_firmware(const std::string filestring, bool force = false)
+ {
+ const char *filename = filestring.c_str();
+
+ /* Fields used in each USB control transfer. */
+ boost::uint16_t len = 0;
+ boost::uint16_t type = 0;
+ boost::uint16_t lower_address_bits = 0x0000;
+ unsigned char data[512];
+
+ /* Can be set by the Intel HEX record 0x04, used for all 0x00 records
+ * thereafter. Note this field takes the place of the 'index' parameter in
+ * libusb calls, and is necessary for FX3's 32-bit addressing. */
+ boost::uint16_t upper_address_bits = 0x0000;
+
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if(!file.good()) {
+ throw uhd::io_error("fx3_load_firmware: cannot open firmware input file");
+ }
+
+ if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " \
+ << filestring << "..." << std::flush;
+
+ while (!file.eof()) {
+ boost::int32_t ret = 0;
+ std::string record;
+ file >> record;
+
+ /* Check for valid Intel HEX record. */
+ if (!checksum(&record) || !parse_record(&record, len, \
+ lower_address_bits, type, data)) {
+ throw uhd::io_error("fx3_load_firmware: bad intel hex record checksum");
+ }
+
+ /* Type 0x00: Data. */
+ if (type == 0x00) {
+ ret = fx3_control_write(FX3_FIRMWARE_LOAD, \
+ lower_address_bits, upper_address_bits, data, len);
+
+ if (ret < 0) {
+ throw uhd::io_error("usrp_load_firmware: usrp_control_write failed");
+ }
+ }
+
+ /* Type 0x01: EOF. */
+ else if (type == 0x01) {
+ if (lower_address_bits != 0x0000 || len != 0 ) {
+ throw uhd::io_error("fx3_load_firmware: For EOF record, address must be 0, length must be 0.");
+ }
+
+ //TODO
+ //usrp_set_firmware_hash(hash); //set hash before reset
+
+ /* Successful termination! */
+ file.close();
+
+ /* Let the system settle. */
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+ return;
+ }
+
+ /* Type 0x04: Extended Linear Address Record. */
+ else if (type == 0x04) {
+ if (lower_address_bits != 0x0000 || len != 2 ) {
+ throw uhd::io_error("fx3_load_firmware: For ELA record, address must be 0, length must be 2.");
+ }
+
+ upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[1] & 0x00FF));
+ }
+
+ /* Type 0x05: Start Linear Address Record. */
+ else if (type == 0x05) {
+ if (lower_address_bits != 0x0000 || len != 4 ) {
+ throw uhd::io_error("fx3_load_firmware: For SLA record, address must be 0, length must be 4.");
+ }
+
+ /* The firmware load is complete. We now need to tell the CPU
+ * to jump to an execution address start point, now contained within
+ * the data field. Parse these address bits out, and then push the
+ * instruction. */
+ upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[1] & 0x00FF));
+ lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\
+ + ((boost::uint16_t)(data[3] & 0x00FF));
+
+ fx3_control_write(FX3_FIRMWARE_LOAD, lower_address_bits, \
+ upper_address_bits, 0, 0);
+
+ if (load_img_msg) UHD_MSG(status) << " done" << std::endl;
+ }
+
+ /* If we receive an unknown record type, error out. */
+ else {
+ throw uhd::io_error("fx3_load_firmware: unsupported record type.");
+ }
+ }
+
+ /* There was no valid EOF. */
+ throw uhd::io_error("fx3_load_firmware: No EOF record found.");
+ }
+
+
+ void reset_fx3(void) {
+ unsigned char data[4];
+ memset(data, 0x00, sizeof(data));
+
+ fx3_control_write(B200_VREQ_FX3_RESET, 0x00, 0x00, data, 4);
+ }
+
+ void reset_gpif(void) {
+ unsigned char data[4];
+ memset(data, 0x00, sizeof(data));
+
+ fx3_control_write(B200_VREQ_GPIF_RESET, 0x00, 0x00, data, 4);
+ }
+
+ void set_fpga_reset_pin(const bool reset)
+ {
+ unsigned char data[4];
+ memset(data, (reset)? 0xFF : 0x00, sizeof(data));
+
+ UHD_THROW_INVALID_CODE_PATH();
+
+ fx3_control_write(B200_VREQ_FPGA_RESET, 0x00, 0x00, data, 4);
+ }
+
+ boost::uint8_t get_usb_speed(void) {
+
+ unsigned char rx_data[1];
+
+ fx3_control_read(B200_VREQ_GET_USB, 0x00, 0x00, rx_data, 1);
+
+ return boost::lexical_cast<boost::uint8_t>(rx_data[0]);
+ }
+
+
+ boost::uint8_t get_fx3_status(void) {
+
+ unsigned char rx_data[1];
+
+ fx3_control_read(B200_VREQ_GET_STATUS, 0x00, 0x00, rx_data, 1);
+
+ return boost::lexical_cast<boost::uint8_t>(rx_data[0]);
+ }
+
+ boost::uint16_t get_compat_num(void) {
+
+ unsigned char rx_data[2];
+
+ fx3_control_read(B200_VREQ_GET_COMPAT , 0x00, 0x00, rx_data, 2);
+
+ boost::uint16_t compat = 0x0000;
+ compat |= (((uint16_t) rx_data[0]) << 8);
+ compat |= (rx_data[1] & 0x00FF);
+
+ return compat;
+ }
+
+ void usrp_get_firmware_hash(hash_type &hash) {
+ fx3_control_read(B200_VREQ_GET_FW_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4, 500);
+ }
+
+ void usrp_set_firmware_hash(hash_type hash) {
+ fx3_control_write(B200_VREQ_SET_FW_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4);
+ }
+
+ void usrp_get_fpga_hash(hash_type &hash) {
+ fx3_control_read(B200_VREQ_GET_FPGA_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4, 500);
+ }
+
+ void usrp_set_fpga_hash(hash_type hash) {
+ fx3_control_write(B200_VREQ_SET_FPGA_HASH, 0x00, 0x00,
+ (unsigned char*) &hash, 4);
+ }
+
+ void load_fpga(const std::string filestring) {
+
+ boost::uint8_t fx3_state = 0;
+
+ const char *filename = filestring.c_str();
+
+ hash_type hash = generate_hash(filename);
+ hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
+ if (hash == loaded_hash) return;
+
+ size_t file_size = 0;
+ {
+ std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate);
+ file_size = file.tellg();
+ }
+
+ std::ifstream file;
+ file.open(filename, std::ios::in | std::ios::binary);
+
+ if(!file.good()) {
+ throw uhd::io_error("load_fpga: cannot open FPGA input file.");
+ }
+
+ do {
+ fx3_state = get_fx3_status();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ } while(fx3_state != FX3_STATE_FPGA_READY);
+
+ if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " \
+ << filestring << "..." << std::flush;
+
+ unsigned char out_buff[64];
+ memset(out_buff, 0x00, sizeof(out_buff));
+ fx3_control_write(B200_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000);
+
+ do {
+ fx3_state = get_fx3_status();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ } while(fx3_state != FX3_STATE_CONFIGURING_FPGA);
+
+
+ size_t bytes_sent = 0;
+ while(!file.eof()) {
+ file.read((char *) out_buff, sizeof(out_buff));
+ const std::streamsize n = file.gcount();
+ if(n == 0) continue;
+
+ boost::uint16_t transfer_count = boost::uint16_t(n);
+
+ /* Send the data to the device. */
+ fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000);
+
+ if (load_img_msg)
+ {
+ if (bytes_sent == 0) UHD_MSG(status) << " 0%" << std::flush;
+ const size_t percent_before = size_t((bytes_sent*100)/file_size);
+ bytes_sent += transfer_count;
+ const size_t percent_after = size_t((bytes_sent*100)/file_size);
+ if (percent_before/10 != percent_after/10)
+ {
+ UHD_MSG(status) << "\b\b\b\b" << std::setw(3) << percent_after << "%" << std::flush;
+ }
+ }
+ }
+
+ file.close();
+
+ do {
+ fx3_state = get_fx3_status();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ } while(fx3_state != FX3_STATE_RUNNING);
+
+ usrp_set_fpga_hash(hash);
+
+ if (load_img_msg) UHD_MSG(status) << "\b\b\b\b done" << std::endl;
+ }
+
+private:
+ usb_control::sptr _usb_ctrl;
+};
+
+/***********************************************************************
+ * Make an instance of the implementation
+ **********************************************************************/
+b200_iface::sptr b200_iface::make(usb_control::sptr usb_ctrl)
+{
+ return sptr(new b200_iface_impl(usb_ctrl));
+}
diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp
new file mode 100644
index 000000000..bf2e006a9
--- /dev/null
+++ b/host/lib/usrp/b200/b200_iface.hpp
@@ -0,0 +1,81 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B200_IFACE_HPP
+#define INCLUDED_B200_IFACE_HPP
+
+#include <stdint.h>
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/types/serial.hpp> //i2c iface
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include "ad9361_ctrl.hpp"
+
+class b200_iface: boost::noncopyable, public uhd::i2c_iface,
+ public ad9361_ctrl_iface_type {
+public:
+ typedef boost::shared_ptr<b200_iface> sptr;
+
+ /*!
+ * Make a b200 interface object from a control transport
+ * \param usb_ctrl a USB control transport
+ * \return a new b200 interface object
+ */
+ static sptr make(uhd::transport::usb_control::sptr usb_ctrl);
+
+ //! query the device USB speed (2, 3)
+ virtual boost::uint8_t get_usb_speed(void) = 0;
+
+ //! get the current status of the FX3
+ virtual boost::uint8_t get_fx3_status(void) = 0;
+
+ //! get the current status of the FX3
+ virtual boost::uint16_t get_compat_num(void) = 0;
+
+ //! load a firmware image
+ virtual void load_firmware(const std::string filestring, bool force=false) = 0;
+
+ //! reset the FX3
+ virtual void reset_fx3(void) = 0;
+
+ //! reset the GPIF state machine
+ virtual void reset_gpif(void) = 0;
+
+ //! set the FPGA_RESET line
+ virtual void set_fpga_reset_pin(const bool reset) = 0;
+
+ //! load an FPGA image
+ virtual void load_fpga(const std::string filestring) = 0;
+
+ //! Read and Write to/from the EEPROM on the motherboard
+ virtual void write_i2c(boost::uint8_t addr, const uhd::byte_vector_t &bytes) = 0;
+ virtual uhd::byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) = 0;
+
+ //! Overload the virtual read_eeprom function from i2c_iface, which won't
+ // work on the B200 since the i2c controller is actually the FX3.
+ virtual void write_eeprom(boost::uint8_t addr, boost::uint8_t offset,
+ const uhd::byte_vector_t &bytes) = 0;
+ virtual uhd::byte_vector_t read_eeprom(boost::uint8_t addr, boost::uint8_t offset,
+ size_t num_bytes) = 0;
+
+ //! send SPI through the FX3
+ virtual void transact_spi( unsigned char *tx_data, size_t num_tx_bits, \
+ unsigned char *rx_data, size_t num_rx_bits) = 0;
+};
+
+
+#endif /* INCLUDED_B200_IFACE_HPP */
diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp
new file mode 100644
index 000000000..01db182e0
--- /dev/null
+++ b/host/lib/usrp/b200/b200_impl.cpp
@@ -0,0 +1,885 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "b200_impl.hpp"
+#include "b200_regs.hpp"
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/images.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <boost/format.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/functional/hash.hpp>
+#include <cstdio>
+#include <ctime>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+const boost::uint16_t B200_VENDOR_ID = 0x2500;
+const boost::uint16_t B200_PRODUCT_ID = 0x0020;
+const boost::uint16_t INIT_PRODUCT_ID = 0x00f0;
+
+static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000);
+
+//! mapping of frontend to radio perif index
+static const size_t FE1 = 1;
+static const size_t FE2 = 0;
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+static device_addrs_t b200_find(const device_addr_t &hint)
+{
+ device_addrs_t b200_addrs;
+
+ //return an empty list of addresses when type is set to non-b200
+ if (hint.has_key("type") and hint["type"] != "b200") return b200_addrs;
+
+ //Return an empty list of addresses when an address is specified,
+ //since an address is intended for a different, non-USB, device.
+ if (hint.has_key("addr")) return b200_addrs;
+
+ unsigned int vid, pid;
+
+ if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
+ sscanf(hint.get("vid").c_str(), "%x", &vid);
+ sscanf(hint.get("pid").c_str(), "%x", &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)) {
+ //extract the firmware path for the b200
+ std::string b200_fw_image;
+ try{
+ b200_fw_image = find_image_path(hint.get("fw", B200_FW_FILE_NAME));
+ }
+ catch(...){
+ UHD_MSG(warning) << boost::format(
+ "Could not locate B200 firmware.\n"
+ "Please install the images package.\n"
+ );
+ return b200_addrs;
+ }
+ UHD_LOG << "the firmware image: " << b200_fw_image << std::endl;
+
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ //check if fw was already loaded
+ if (handle->get_manufacturer() != "Ettus Research LLC")
+ {
+ b200_iface::make(control)->load_firmware(b200_fw_image);
+ }
+
+ found++;
+ }
+
+ const boost::system_time timeout_time = boost::get_system_time() + REENUMERATION_TIMEOUT_MS;
+
+ //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))
+ {
+ usb_control::sptr control;
+ try{control = usb_control::make(handle, 0);}
+ catch(const uhd::exception &){continue;} //ignore claimed
+
+ b200_iface::sptr iface = b200_iface::make(control);
+ const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*iface, "B200");
+
+ device_addr_t new_addr;
+ new_addr["type"] = "b200";
+ new_addr["name"] = mb_eeprom["name"];
+ new_addr["serial"] = handle->get_serial();
+ //this is a found b200 when the hint serial and name match or blank
+ if (
+ (not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"])
+ ){
+ b200_addrs.push_back(new_addr);
+ }
+ }
+ }
+
+ return b200_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static device::sptr b200_make(const device_addr_t &device_addr)
+{
+ return device::sptr(new b200_impl(device_addr));
+}
+
+UHD_STATIC_BLOCK(register_b200_device)
+{
+ device::register_device(&b200_find, &b200_make);
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+b200_impl::b200_impl(const device_addr_t &device_addr)
+{
+ _tree = property_tree::make();
+ const fs_path mb_path = "/mboards/0";
+
+ //try to match the given device address with something on the USB bus
+ std::vector<usb_device_handle::sptr> device_list =
+ usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID);
+
+ //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;
+ break;
+ }
+ }
+ UHD_ASSERT_THROW(handle.get() != NULL); //better be found
+
+ //create control objects
+ usb_control::sptr control = usb_control::make(handle, 0);
+ _iface = b200_iface::make(control);
+ this->check_fw_compat(); //check after making
+
+ ////////////////////////////////////////////////////////////////////
+ // setup the mboard eeprom
+ ////////////////////////////////////////////////////////////////////
+ const mboard_eeprom_t mb_eeprom(*_iface, "B200");
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(mb_eeprom)
+ .subscribe(boost::bind(&b200_impl::set_mb_eeprom, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // Load the FPGA image, then reset GPIF
+ ////////////////////////////////////////////////////////////////////
+ std::string default_file_name;
+ if (not mb_eeprom["product"].empty())
+ {
+ switch (boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]))
+ {
+ case 0x0001: default_file_name = B200_FPGA_FILE_NAME; break;
+ case 0x0002: default_file_name = B210_FPGA_FILE_NAME; break;
+ default: throw uhd::runtime_error("b200 unknown product code: " + mb_eeprom["product"]);
+ }
+ }
+ if (default_file_name.empty())
+ {
+ UHD_ASSERT_THROW(device_addr.has_key("fpga"));
+ }
+
+ //extract the FPGA path for the B200
+ std::string b200_fpga_image = find_image_path(
+ device_addr.has_key("fpga")? device_addr["fpga"] : default_file_name
+ );
+
+ _iface->load_fpga(b200_fpga_image);
+ _iface->reset_gpif();
+
+ ////////////////////////////////////////////////////////////////////
+ // Create control transport
+ ////////////////////////////////////////////////////////////////////
+ boost::uint8_t usb_speed = _iface->get_usb_speed();
+ UHD_MSG(status) << "Operating over USB " << (int) usb_speed << "." << std::endl;
+ const std::string min_frame_size = (usb_speed == 3) ? "1024" : "512";
+
+ device_addr_t ctrl_xport_args;
+ ctrl_xport_args["recv_frame_size"] = min_frame_size;
+ ctrl_xport_args["num_recv_frames"] = "16";
+ ctrl_xport_args["send_frame_size"] = min_frame_size;
+ ctrl_xport_args["num_send_frames"] = "16";
+
+ _ctrl_transport = usb_zero_copy::make(
+ handle,
+ 4, 8, //interface, endpoint
+ 3, 4, //interface, endpoint
+ ctrl_xport_args
+ );
+ while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport
+
+ ////////////////////////////////////////////////////////////////////
+ // Async task structure
+ ////////////////////////////////////////////////////////////////////
+ _async_task_data.reset(new AsyncTaskData());
+ _async_task_data->async_md.reset(new async_md_type(1000/*messages deep*/));
+ _async_task = uhd::task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data));
+
+ ////////////////////////////////////////////////////////////////////
+ // Local control endpoint
+ ////////////////////////////////////////////////////////////////////
+ _local_ctrl = radio_ctrl_core_3000::make(vrt::if_packet_info_t::LINK_TYPE_CHDR, _ctrl_transport, zero_copy_if::sptr()/*null*/, B200_LOCAL_CTRL_SID);
+ _local_ctrl->hold_task(_async_task);
+ _async_task_data->local_ctrl = _local_ctrl; //weak
+ this->check_fpga_compat();
+
+ /* Initialize the GPIOs, set the default bandsels to the lower range. Note
+ * that calling update_bandsel calls update_gpio_state(). */
+ _gpio_state = gpio_state();
+ update_bandsel("RX", 800e6);
+ update_bandsel("TX", 850e6);
+
+ ////////////////////////////////////////////////////////////////////
+ // Create the GPSDO control
+ ////////////////////////////////////////////////////////////////////
+ _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID);
+ _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200);
+ _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup
+ boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation
+
+ if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE)
+ {
+ UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush;
+ try
+ {
+ _gps = gps_ctrl::make(_async_task_data->gpsdo_uart);
+ }
+ catch(std::exception &e)
+ {
+ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl;
+ }
+ if (_gps and _gps->gps_detected())
+ {
+ //UHD_MSG(status) << "found" << std::endl;
+ BOOST_FOREACH(const std::string &name, _gps->get_sensors())
+ {
+ _tree->create<sensor_value_t>(mb_path / "sensors" / name)
+ .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name));
+ }
+ }
+ else
+ {
+ UHD_MSG(status) << "not found" << std::endl;
+ _local_ctrl->poke32(TOREG(SR_CORE_GPSDO_ST), B200_GPSDO_ST_NONE);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Initialize the properties tree
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<std::string>("/name").set("B-Series Device");
+ _tree->create<std::string>(mb_path / "name").set("B200");
+ _tree->create<std::string>(mb_path / "codename").set("Sasquatch");
+
+ ////////////////////////////////////////////////////////////////////
+ // Create data transport
+ // This happens after FPGA ctrl instantiated so any junk that might
+ // be in the FPGAs buffers doesn't get pulled into the transport
+ // before being cleared.
+ ////////////////////////////////////////////////////////////////////
+ device_addr_t data_xport_args;
+ data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "8192");
+ data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16");
+ 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");
+
+ _data_transport = usb_zero_copy::make(
+ handle, // identifier
+ 2, 6, // IN interface, endpoint
+ 1, 2, // OUT interface, endpoint
+ data_xport_args // param hints
+ );
+ while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport
+ _demux.reset(new recv_packet_demuxer_3000(_data_transport));
+
+ ////////////////////////////////////////////////////////////////////
+ // Init codec - turns on clocks
+ ////////////////////////////////////////////////////////////////////
+ UHD_MSG(status) << "Initialize CODEC control..." << std::endl;
+ _codec_ctrl = ad9361_ctrl::make(_iface);
+ this->reset_codec_dcm();
+
+ ////////////////////////////////////////////////////////////////////
+ // create codec control objects
+ ////////////////////////////////////////////////////////////////////
+ {
+ const fs_path codec_path = mb_path / ("rx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set("B200 RX dual ADC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+ {
+ const fs_path codec_path = mb_path / ("tx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set("B200 TX dual DAC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<double>(mb_path / "tick_rate")
+ .coerce(boost::bind(&b200_impl::set_tick_rate, this, _1))
+ .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");
+
+ ////////////////////////////////////////////////////////////////////
+ // and do the misc mboard sensors
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked")
+ .publish(boost::bind(&b200_impl::get_ref_locked, this));
+
+ ////////////////////////////////////////////////////////////////////
+ // create frontend mapping
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .set(subdev_spec_t())
+ .subscribe(boost::bind(&b200_impl::update_rx_subdev_spec, this, _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .set(subdev_spec_t())
+ .subscribe(boost::bind(&b200_impl::update_tx_subdev_spec, this, _1));
+
+ ////////////////////////////////////////////////////////////////////
+ // setup radio control
+ ////////////////////////////////////////////////////////////////////
+ UHD_MSG(status) << "Initialize Radio control..." << std::endl;
+ const size_t num_radio_chains = ((_local_ctrl->peek32(RB32_CORE_STATUS) >> 8) & 0xff);
+ 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);
+
+ //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_ctrl->data_port_loopback(false);
+
+ ////////////////////////////////////////////////////////////////////
+ // create time and clock control objects
+ ////////////////////////////////////////////////////////////////////
+ _spi_iface = spi_core_3000::make(_local_ctrl, TOREG(SR_CORE_SPI), RB32_CORE_SPI);
+ _spi_iface->set_divider(B200_BUS_CLOCK_RATE/ADF4001_SPI_RATE);
+ _adf4001_iface = boost::shared_ptr<adf4001_ctrl>(new adf4001_ctrl(_spi_iface, ADF4001_SLAVENO));
+
+ //register time now and pps onto available radio cores
+ _tree->create<time_spec_t>(mb_path / "time" / "now")
+ .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64));
+ _tree->create<time_spec_t>(mb_path / "time" / "pps")
+ .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64));
+ for (size_t i = 0; i < _radio_perifs.size(); i++)
+ {
+ _tree->access<time_spec_t>(mb_path / "time" / "now")
+ .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[i].time64, _1));
+ _tree->access<time_spec_t>(mb_path / "time" / "pps")
+ .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[i].time64, _1));
+ }
+
+ //setup time source props
+ _tree->create<std::string>(mb_path / "time_source" / "value")
+ .subscribe(boost::bind(&b200_impl::update_time_source, this, _1));
+ static const std::vector<std::string> time_sources = boost::assign::list_of("none")("external")("gpsdo");
+ _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")
+ .subscribe(boost::bind(&b200_impl::update_clock_source, this, _1));
+ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo");
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources);
+
+ ////////////////////////////////////////////////////////////////////
+ // dboard eeproms but not really
+ ////////////////////////////////////////////////////////////////////
+ dboard_eeprom_t db_eeprom;
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom").set(db_eeprom);
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom").set(db_eeprom);
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom);
+
+ ////////////////////////////////////////////////////////////////////
+ // do some post-init tasks
+ ////////////////////////////////////////////////////////////////////
+
+ //init the clock rate to something reasonable
+ _tree->access<double>(mb_path / "tick_rate").set(
+ device_addr.cast<double>("master_clock_rate", B200_DEFAULT_TICK_RATE));
+
+ //subdev spec contains full width of selections
+ subdev_spec_t rx_spec, tx_spec;
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends"))
+ {
+ rx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends"))
+ {
+ tx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ _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);
+
+ //init to internal clock and time source
+ _tree->access<std::string>(mb_path / "clock_source/value").set("internal");
+ _tree->access<std::string>(mb_path / "time_source/value").set("none");
+
+ //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");
+ _tree->access<std::string>(mb_path / "clock_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));
+ }
+
+}
+
+b200_impl::~b200_impl(void)
+{
+ UHD_SAFE_CALL
+ (
+ _async_task.reset();
+ )
+}
+
+/***********************************************************************
+ * setup radio control objects
+ **********************************************************************/
+
+void b200_impl::setup_radio(const size_t dspno)
+{
+ radio_perifs_t &perif = _radio_perifs[dspno];
+ const fs_path mb_path = "/mboards/0";
+
+ ////////////////////////////////////////////////////////////////////
+ // radio control
+ ////////////////////////////////////////////////////////////////////
+ const boost::uint32_t sid = (dspno == 0)? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID;
+ perif.ctrl = radio_ctrl_core_3000::make(vrt::if_packet_info_t::LINK_TYPE_CHDR, _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")
+ .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, 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
+ ////////////////////////////////////////////////////////////////////
+ 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") % 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))
+ .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _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
+ ////////////////////////////////////////////////////////////////////
+ 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))
+ .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _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
+ ////////////////////////////////////////////////////////////////////
+ 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"); //empty TODO
+ 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(0.0);
+ }
+ _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")
+ .set(0.0)
+ .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
+ .subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1));
+ _tree->create<meta_range_t>(rf_fe_path / "freq" / "range")
+ .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range));
+
+ //setup antenna stuff
+ if (key[0] == 'R')
+ {
+ 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");
+ }
+ if (key[0] == 'T')
+ {
+ 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");
+ }
+
+ }
+}
+
+/***********************************************************************
+ * loopback tests
+ **********************************************************************/
+
+void b200_impl::register_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ UHD_MSG(status) << "Performing register loopback test... " << std::flush;
+ size_t hash = time(NULL);
+ for (size_t i = 0; i < 100; i++)
+ {
+ boost::hash_combine(hash, i);
+ iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash));
+ test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash);
+ if (test_fail) break; //exit loop on any failure
+ }
+ UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl;
+}
+
+void b200_impl::codec_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ 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);
+}
+
+/***********************************************************************
+ * Sample and tick rate comprehension below
+ **********************************************************************/
+double b200_impl::set_tick_rate(const double raw_rate)
+{
+ //clip rate (which can be doubled by factor) to possible bounds
+ const double rate = ad9361_ctrl::get_samp_rate_range().clip(raw_rate);
+
+ UHD_MSG(status) << "Asking for clock rate " << rate/1e6 << " MHz\n";
+ _tick_rate = _codec_ctrl->set_clock_rate(rate);
+ UHD_MSG(status) << "Actually got clock rate " << _tick_rate/1e6 << " MHz\n";
+
+ //reset after clock rate change
+ this->reset_codec_dcm();
+
+ BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
+ {
+ perif.time64->set_tick_rate(_tick_rate);
+ perif.time64->self_test();
+ }
+ return _tick_rate;
+}
+
+/***********************************************************************
+ * compat checks
+ **********************************************************************/
+
+void b200_impl::check_fw_compat(void)
+{
+ boost::uint16_t compat_num = _iface->get_compat_num();
+ boost::uint32_t compat_major = (boost::uint32_t) (compat_num >> 8);
+ boost::uint32_t compat_minor = (boost::uint32_t) (compat_num & 0xFF);
+
+ if (compat_major != B200_FW_COMPAT_NUM_MAJOR){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected firmware compatibility number 0x%x, but got 0x%x.%x:\n"
+ "The firmware build is not compatible with the host code build.\n"
+ "%s"
+ ) % int(B200_FW_COMPAT_NUM_MAJOR) % compat_major % compat_minor
+ % print_images_error()));
+ }
+ _tree->create<std::string>("/mboards/0/fw_version").set(str(boost::format("%u.%u")
+ % compat_major % compat_minor));
+}
+
+void b200_impl::check_fpga_compat(void)
+{
+ const boost::uint64_t compat = _local_ctrl->peek64(0);
+ const boost::uint32_t signature = boost::uint32_t(compat >> 32);
+ const boost::uint16_t compat_major = boost::uint16_t(compat >> 16);
+ const boost::uint16_t compat_minor = boost::uint16_t(compat & 0xffff);
+ if (signature != 0xACE0BA5E) throw uhd::runtime_error(
+ "b200::check_fpga_compat signature register readback failed");
+
+ if (compat_major != B200_FPGA_COMPAT_NUM){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected FPGA compatibility number 0x%x, but got 0x%x.%x:\n"
+ "The FPGA build is not compatible with the host code build.\n"
+ "%s"
+ ) % int(B200_FPGA_COMPAT_NUM) % compat_major % compat_minor
+ % print_images_error()));
+ }
+ _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u")
+ % compat_major % compat_minor));
+}
+
+void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom)
+{
+ mb_eeprom.commit(*_iface, "B200");
+}
+
+
+/***********************************************************************
+ * Reference time and clock
+ **********************************************************************/
+
+void b200_impl::update_clock_source(const std::string &source)
+{
+ if (source == "internal"){
+ _adf4001_iface->set_lock_to_ext_ref(false);
+ }
+ else if ((source == "external")
+ or (source == "gpsdo")){
+
+ _adf4001_iface->set_lock_to_ext_ref(true);
+ } else {
+ throw uhd::key_error("update_clock_source: unknown source: " + source);
+ }
+
+ _gpio_state.ref_sel = (source == "gpsdo")? 1 : 0;
+ this->update_gpio_state();
+}
+
+void b200_impl::update_time_source(const std::string &source)
+{
+ if (source == "none"){}
+ else if (source == "external"){}
+ else if (source == "gpsdo"){}
+ else throw uhd::key_error("update_time_source: unknown source: " + source);
+ for (size_t i = 0; i < _radio_perifs.size(); i++)
+ {
+ _radio_perifs[i].time64->set_time_source((source == "external")? "external" : "internal");
+ }
+ this->update_gpio_state();
+}
+
+/***********************************************************************
+ * GPIO setup
+ **********************************************************************/
+
+void b200_impl::update_bandsel(const std::string& which, double freq)
+{
+ if(which[0] == 'R') {
+ if(freq < 2.2e9) {
+ _gpio_state.rx_bandsel_a = 0;
+ _gpio_state.rx_bandsel_b = 0;
+ _gpio_state.rx_bandsel_c = 1;
+ } else if((freq >= 2.2e9) && (freq < 4e9)) {
+ _gpio_state.rx_bandsel_a = 0;
+ _gpio_state.rx_bandsel_b = 1;
+ _gpio_state.rx_bandsel_c = 0;
+ } else if((freq >= 4e9) && (freq <= 6e9)) {
+ _gpio_state.rx_bandsel_a = 1;
+ _gpio_state.rx_bandsel_b = 0;
+ _gpio_state.rx_bandsel_c = 0;
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ } else if(which[0] == 'T') {
+ if(freq < 2.5e9) {
+ _gpio_state.tx_bandsel_a = 0;
+ _gpio_state.tx_bandsel_b = 1;
+ } else if((freq >= 2.5e9) && (freq <= 6e9)) {
+ _gpio_state.tx_bandsel_a = 1;
+ _gpio_state.tx_bandsel_b = 0;
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ update_gpio_state();
+}
+
+void b200_impl::update_gpio_state(void)
+{
+ const boost::uint32_t misc_word = 0
+ | (_gpio_state.tx_bandsel_a << 7)
+ | (_gpio_state.tx_bandsel_b << 6)
+ | (_gpio_state.rx_bandsel_a << 5)
+ | (_gpio_state.rx_bandsel_b << 4)
+ | (_gpio_state.rx_bandsel_c << 3)
+ | (_gpio_state.codec_arst << 2)
+ | (_gpio_state.mimo << 1)
+ | (_gpio_state.ref_sel << 0)
+ ;
+
+ _local_ctrl->poke32(TOREG(RB32_CORE_MISC), misc_word);
+}
+
+void b200_impl::reset_codec_dcm(void)
+{
+ _gpio_state.codec_arst = 1;
+ this->update_gpio_state();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ _gpio_state.codec_arst = 0;
+ this->update_gpio_state();
+}
+
+void b200_impl::update_atrs(void)
+{
+ if (_radio_perifs.size() > FE1 and _radio_perifs[FE1].atr)
+ {
+ radio_perifs_t &perif = _radio_perifs[FE1];
+ const bool enb_rx = bool(perif.rx_streamer.lock());
+ const bool enb_tx = bool(perif.tx_streamer.lock());
+ const bool is_rx2 = perif.ant_rx2;
+ const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX1_RX2 : STATE_RX1_TXRX) : STATE_OFF;
+ const size_t txonly = (enb_tx)? (STATE_TX1_TXRX) : STATE_OFF;
+ size_t fd = STATE_OFF;
+ if (enb_rx and enb_tx) fd = STATE_FDX1_TXRX;
+ if (enb_rx and not enb_tx) fd = rxonly;
+ if (not enb_rx and enb_tx) fd = txonly;
+ gpio_core_200_32wo::sptr atr = perif.atr;
+ atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF);
+ atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd);
+ }
+ if (_radio_perifs.size() > FE2 and _radio_perifs[FE2].atr)
+ {
+ radio_perifs_t &perif = _radio_perifs[FE2];
+ const bool enb_rx = bool(perif.rx_streamer.lock());
+ const bool enb_tx = bool(perif.tx_streamer.lock());
+ const bool is_rx2 = perif.ant_rx2;
+ const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX2_RX2 : STATE_RX2_TXRX) : STATE_OFF;
+ const size_t txonly = (enb_tx)? (STATE_TX2_TXRX) : STATE_OFF;
+ size_t fd = STATE_OFF;
+ if (enb_rx and enb_tx) fd = STATE_FDX2_TXRX;
+ if (enb_rx and not enb_tx) fd = rxonly;
+ if (not enb_rx and enb_tx) fd = txonly;
+ gpio_core_200_32wo::sptr atr = perif.atr;
+ atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF);
+ atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly);
+ atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd);
+ }
+}
+
+void b200_impl::update_antenna_sel(const size_t which, const std::string &ant)
+{
+ if (ant != "TX/RX" and ant != "RX2") throw uhd::value_error("b200: unknown RX antenna option: " + ant);
+ _radio_perifs[which].ant_rx2 = (ant == "RX2");
+ this->update_atrs();
+}
+
+void b200_impl::update_enables(void)
+{
+ //extract settings from state variables
+ const bool enb_tx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].tx_streamer.lock());
+ const bool enb_rx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].rx_streamer.lock());
+ const bool enb_tx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].tx_streamer.lock());
+ const bool enb_rx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].rx_streamer.lock());
+ const size_t num_rx = (enb_rx1?1:0) + (enb_rx2?1:0);
+ const size_t num_tx = (enb_tx1?1:0) + (enb_tx2?1:0);
+ const bool mimo = num_rx == 2 or num_tx == 2;
+
+ //setup the active chains in the codec
+ _codec_ctrl->set_active_chains(enb_tx1, enb_tx2, enb_rx1, enb_rx2);
+ if ((num_rx + num_tx) == 0) _codec_ctrl->set_active_chains(true, false, true, false); //enable something
+ this->reset_codec_dcm(); //set_active_chains could cause a clock rate change - reset dcm
+
+ //figure out if mimo is enabled based on new state
+ _gpio_state.mimo = (mimo)? 1 : 0;
+ update_gpio_state();
+
+ //atrs change based on enables
+ this->update_atrs();
+}
+
+sensor_value_t b200_impl::get_ref_locked(void)
+{
+ const bool lock = (_local_ctrl->peek32(RB32_CORE_MISC) & 0x1) == 0x1;
+ return sensor_value_t("Ref", lock, "locked", "unlocked");
+}
diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp
new file mode 100644
index 000000000..d8a2232e0
--- /dev/null
+++ b/host/lib/usrp/b200/b200_impl.hpp
@@ -0,0 +1,196 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B200_IMPL_HPP
+#define INCLUDED_B200_IMPL_HPP
+
+#include "b200_iface.hpp"
+#include "b200_uart.hpp"
+#include "ad9361_ctrl.hpp"
+#include "adf4001_ctrl.hpp"
+#include "rx_vita_core_3000.hpp"
+#include "tx_vita_core_3000.hpp"
+#include "time_core_3000.hpp"
+#include "gpio_core_200.hpp"
+#include "radio_ctrl_core_3000.hpp"
+#include "rx_dsp_core_3000.hpp"
+#include "tx_dsp_core_3000.hpp"
+#include <uhd/device.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/utils/pimpl.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/clock_config.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/weak_ptr.hpp>
+#include "recv_packet_demuxer_3000.hpp"
+
+static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex";
+static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin";
+static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin";
+static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x02;
+static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00;
+static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x01;
+static const double B200_LINK_RATE_BPS = (5e9)/8; //practical link rate (5 Gbps)
+static const double B200_BUS_CLOCK_RATE = 100e6;
+static const double B200_DEFAULT_TICK_RATE = 32e6;
+static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83;
+
+#define FLIP_SID(sid) (((sid)<<16)|((sid)>>16))
+
+static const boost::uint32_t B200_CTRL0_MSG_SID = 0x00000010;
+static const boost::uint32_t B200_RESP0_MSG_SID = FLIP_SID(B200_CTRL0_MSG_SID);
+
+static const boost::uint32_t B200_CTRL1_MSG_SID = 0x00000020;
+static const boost::uint32_t B200_RESP1_MSG_SID = FLIP_SID(B200_CTRL1_MSG_SID);
+
+static const boost::uint32_t B200_TX_DATA0_SID = 0x00000050;
+static const boost::uint32_t B200_TX_MSG0_SID = FLIP_SID(B200_TX_DATA0_SID);
+
+static const boost::uint32_t B200_TX_DATA1_SID = 0x00000060;
+static const boost::uint32_t B200_TX_MSG1_SID = FLIP_SID(B200_TX_DATA1_SID);
+
+static const boost::uint32_t B200_RX_DATA0_SID = 0x000000A0;
+static const boost::uint32_t B200_RX_DATA1_SID = 0x000000B0;
+
+static const boost::uint32_t B200_TX_GPS_UART_SID = 0x00000030;
+static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SID);
+
+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
+ **********************************************************************/
+
+//! Implementation guts
+struct b200_impl : public uhd::device
+{
+ //structors
+ b200_impl(const uhd::device_addr_t &);
+ ~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);
+
+ uhd::property_tree::sptr _tree;
+
+ //controllers
+ b200_iface::sptr _iface;
+ radio_ctrl_core_3000::sptr _local_ctrl;
+ ad9361_ctrl::sptr _codec_ctrl;
+ spi_core_3000::sptr _spi_iface;
+ boost::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface;
+ uhd::gps_ctrl::sptr _gps;
+
+ //transports
+ uhd::transport::zero_copy_if::sptr _data_transport;
+ uhd::transport::zero_copy_if::sptr _ctrl_transport;
+ boost::shared_ptr<uhd::usrp::recv_packet_demuxer_3000> _demux;
+
+ //device properties interface
+ uhd::property_tree::sptr get_tree(void) const
+ {
+ return _tree;
+ }
+
+ boost::weak_ptr<uhd::rx_streamer> _rx_streamer;
+ boost::weak_ptr<uhd::tx_streamer> _tx_streamer;
+
+ //async ctrl + msgs
+ uhd::task::sptr _async_task;
+ typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
+ struct AsyncTaskData
+ {
+ boost::shared_ptr<async_md_type> async_md;
+ boost::weak_ptr<radio_ctrl_core_3000> local_ctrl;
+ boost::weak_ptr<radio_ctrl_core_3000> radio_ctrl[2];
+ b200_uart::sptr gpsdo_uart;
+ };
+ boost::shared_ptr<AsyncTaskData> _async_task_data;
+ void handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>);
+
+ void register_loopback_self_test(wb_iface::sptr iface);
+ void codec_loopback_self_test(wb_iface::sptr iface);
+ void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);
+ void check_fw_compat(void);
+ void check_fpga_compat(void);
+ void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void update_time_source(const std::string &);
+ void update_clock_source(const std::string &);
+ void update_bandsel(const std::string& which, double freq);
+ void update_antenna_sel(const size_t which, const std::string &ant);
+ uhd::sensor_value_t get_ref_locked(void);
+
+ //perifs in the radio core
+ struct radio_perifs_t
+ {
+ radio_ctrl_core_3000::sptr ctrl;
+ gpio_core_200_32wo::sptr atr;
+ time_core_3000::sptr time64;
+ rx_vita_core_3000::sptr framer;
+ rx_dsp_core_3000::sptr ddc;
+ tx_vita_core_3000::sptr deframer;
+ tx_dsp_core_3000::sptr duc;
+ boost::weak_ptr<uhd::rx_streamer> rx_streamer;
+ boost::weak_ptr<uhd::tx_streamer> tx_streamer;
+ bool ant_rx2;
+ };
+ std::vector<radio_perifs_t> _radio_perifs;
+ void setup_radio(const size_t which_radio);
+ void handle_overflow(const size_t index);
+
+ struct gpio_state {
+ boost::uint32_t tx_bandsel_a, tx_bandsel_b, rx_bandsel_a, rx_bandsel_b, rx_bandsel_c, codec_arst, mimo, ref_sel;
+
+ gpio_state() {
+ tx_bandsel_a = 0;
+ tx_bandsel_b = 0;
+ rx_bandsel_a = 0;
+ rx_bandsel_b = 0;
+ rx_bandsel_c = 0;
+ codec_arst = 0;
+ mimo = 0;
+ ref_sel = 0;
+ }
+ } _gpio_state;
+
+ void update_gpio_state(void);
+ void reset_codec_dcm(void);
+
+ void update_enables(void);
+ void update_atrs(void);
+
+ void update_tick_rate(const double);
+ void update_rx_samp_rate(const size_t, const double);
+ void update_tx_samp_rate(const size_t, const double);
+
+ double _tick_rate;
+ double get_tick_rate(void){return _tick_rate;}
+ double set_tick_rate(const double rate);
+};
+
+#endif /* INCLUDED_B200_IMPL_HPP */
diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp
new file mode 100644
index 000000000..bb71fa5b6
--- /dev/null
+++ b/host/lib/usrp/b200/b200_io_impl.cpp
@@ -0,0 +1,377 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "b200_regs.hpp"
+#include "b200_impl.hpp"
+#include "validate_subdev_spec.hpp"
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include "async_packet_handler.hpp"
+#include <boost/bind.hpp>
+#include <boost/make_shared.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+/***********************************************************************
+ * update streamer rates
+ **********************************************************************/
+void b200_impl::update_tick_rate(const double 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);
+ }
+ 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);
+ }
+}
+
+void b200_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 (not my_streamer) return;
+ my_streamer->set_samp_rate(rate);
+ const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate)
+{
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock());
+ if (not my_streamer) return;
+ my_streamer->set_samp_rate(rate);
+ const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+/***********************************************************************
+ * frontend selection
+ **********************************************************************/
+void b200_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec)
+{
+ //sanity checking
+ if (spec.size()) validate_subdev_spec(_tree, spec, "rx");
+ UHD_ASSERT_THROW(spec.size() <= _radio_perifs.size());
+
+ if (spec.size() > 0)
+ {
+ UHD_ASSERT_THROW(spec[0].db_name == "A");
+ UHD_ASSERT_THROW(spec[0].sd_name == "A");
+ }
+ if (spec.size() > 1)
+ {
+ //TODO we can support swapping at a later date, only this combo is supported
+ UHD_ASSERT_THROW(spec[1].db_name == "A");
+ UHD_ASSERT_THROW(spec[1].sd_name == "B");
+ }
+
+ this->update_enables();
+}
+
+void b200_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec)
+{
+ //sanity checking
+ if (spec.size()) validate_subdev_spec(_tree, spec, "tx");
+ UHD_ASSERT_THROW(spec.size() <= _radio_perifs.size());
+
+ if (spec.size() > 0)
+ {
+ UHD_ASSERT_THROW(spec[0].db_name == "A");
+ UHD_ASSERT_THROW(spec[0].sd_name == "A");
+ }
+ if (spec.size() > 1)
+ {
+ //TODO we can support swapping at a later date, only this combo is supported
+ UHD_ASSERT_THROW(spec[1].db_name == "A");
+ UHD_ASSERT_THROW(spec[1].sd_name == "B");
+ }
+
+ this->update_enables();
+}
+
+static void b200_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 b200_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);
+}
+
+/***********************************************************************
+ * Async Data
+ **********************************************************************/
+bool b200_impl::recv_async_msg(
+ async_metadata_t &async_metadata, double timeout
+){
+ return _async_task_data->async_md->pop_with_timed_wait(async_metadata, timeout);
+}
+
+void b200_impl::handle_async_task(
+ uhd::transport::zero_copy_if::sptr xport,
+ boost::shared_ptr<AsyncTaskData> data
+)
+{
+ managed_recv_buffer::sptr buff = xport->get_recv_buff();
+ if (not buff or buff->size() < 8) return;
+ const boost::uint32_t sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]);
+ switch (sid)
+ {
+
+ //if the packet is a control response
+ case B200_RESP0_MSG_SID:
+ case B200_RESP1_MSG_SID:
+ case B200_LOCAL_RESP_SID:
+ {
+ radio_ctrl_core_3000::sptr ctrl;
+ if (sid == B200_RESP0_MSG_SID) ctrl = data->radio_ctrl[0].lock();
+ if (sid == B200_RESP1_MSG_SID) ctrl = data->radio_ctrl[1].lock();
+ if (sid == B200_LOCAL_RESP_SID) ctrl = data->local_ctrl.lock();
+ if (ctrl) ctrl->push_response(buff->cast<const boost::uint32_t *>());
+ break;
+ }
+
+ //if the packet is a uart message
+ case B200_RX_GPS_UART_SID:
+ {
+ data->gpsdo_uart->handle_uart_packet(buff);
+ break;
+ }
+
+ //or maybe the packet is a TX async message
+ case B200_TX_MSG0_SID:
+ case B200_TX_MSG1_SID:
+ {
+ const size_t i = (sid == B200_TX_MSG0_SID)? 0 : 1;
+
+ //extract packet info
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+
+ //unpacking can fail
+ try
+ {
+ b200_if_hdr_unpack_le(packet_buff, if_packet_info);
+ }
+ catch(const std::exception &ex)
+ {
+ UHD_MSG(error) << "Error parsing ctrl packet: " << ex.what() << std::endl;
+ break;
+ }
+
+ //fill in the async metadata
+ async_metadata_t metadata;
+ load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, packet_buff, _tick_rate, i);
+ data->async_md->push_with_pop_on_full(metadata);
+ standard_async_msg_prints(metadata);
+ break;
+ }
+
+ //doh!
+ default:
+ UHD_MSG(error) << "Got a ctrl packet with unknown SID " << sid << std::endl;
+ }
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_)
+{
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (not args.otw_format.empty() and args.otw_format != "sc16")
+ {
+ throw uhd::value_error("b200_impl::get_rx_stream only supports otw_format sc16");
+ }
+ args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer;
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+ const size_t chan = args.channels[stream_i];
+ radio_perifs_t &perif = _radio_perifs[chan];
+ const boost::uint32_t sid = chan?B200_RX_DATA1_SID:B200_RX_DATA0_SID;
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+
+ //init some streamer stuff
+ my_streamer->set_vrt_unpacker(&b200_if_hdr_unpack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item32_le";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.framer->clear();
+ perif.framer->set_nsamps_per_packet(spp);
+ perif.framer->set_sid(sid);
+ perif.framer->setup(args);
+ perif.ddc->setup(args);
+ _demux->realloc_sid(sid);
+ my_streamer->set_xport_chan_get_buff(stream_i, boost::bind(
+ &recv_packet_demuxer_3000::get_recv_buff, _demux, sid, _1
+ ), true /*flush*/);
+ my_streamer->set_overflow_handler(stream_i, boost::bind(
+ &b200_impl::handle_overflow, this, chan
+ ));
+ my_streamer->set_issue_stream_cmd(stream_i, boost::bind(
+ &rx_vita_core_3000::issue_stream_command, perif.framer, _1
+ ));
+ perif.rx_streamer = my_streamer; //store weak pointer
+
+ //sets all tick and samp rates on this streamer
+ this->update_tick_rate(this->get_tick_rate());
+ _tree->access<double>(str(boost::format("/mboards/0/rx_dsps/%u/rate/value") % chan)).update();
+ }
+ this->update_enables();
+
+ return my_streamer;
+}
+
+void b200_impl::handle_overflow(const size_t i)
+{
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[i].rx_streamer.lock());
+ if (my_streamer->get_num_channels() == 2) //MIMO time
+ {
+ //find out if we were in continuous mode before stopping
+ const bool in_continuous_streaming_mode = _radio_perifs[i].framer->in_continuous_streaming_mode();
+ //stop streaming
+ my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+ //flush demux
+ _demux->realloc_sid(B200_RX_DATA0_SID);
+ _demux->realloc_sid(B200_RX_DATA1_SID);
+ //flush actual transport
+ while (_data_transport->get_recv_buff(0.001)){}
+ //restart streaming
+ if (in_continuous_streaming_mode)
+ {
+ stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ stream_cmd.stream_now = false;
+ stream_cmd.time_spec = _radio_perifs[i].time64->get_time_now() + time_spec_t(0.01);
+ my_streamer->issue_stream_cmd(stream_cmd);
+ }
+ }
+ else _radio_perifs[i].framer->handle_overflow();
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_)
+{
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (not args.otw_format.empty() and args.otw_format != "sc16")
+ {
+ throw uhd::value_error("b200_impl::get_rx_stream only supports otw_format sc16");
+ }
+ args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer;
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+ const size_t chan = args.channels[stream_i];
+ radio_perifs_t &perif = _radio_perifs[chan];
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size;
+ const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format);
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+
+ //init some streamer stuff
+ my_streamer->set_vrt_packer(&b200_if_hdr_pack_le);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = 1;
+ id.output_format = args.otw_format + "_item32_le";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.deframer->clear();
+ perif.deframer->setup(args);
+ perif.duc->setup(args);
+
+ my_streamer->set_xport_chan_get_buff(stream_i, boost::bind(
+ &zero_copy_if::get_send_buff, _data_transport, _1
+ ));
+ my_streamer->set_async_receiver(boost::bind(
+ &async_md_type::pop_with_timed_wait, _async_task_data->async_md, _1, _2
+ ));
+ my_streamer->set_xport_chan_sid(stream_i, true, chan?B200_TX_DATA1_SID:B200_TX_DATA0_SID);
+ my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet
+ perif.tx_streamer = my_streamer; //store weak pointer
+
+ //sets all tick and samp rates on this streamer
+ this->update_tick_rate(this->get_tick_rate());
+ _tree->access<double>(str(boost::format("/mboards/0/tx_dsps/%u/rate/value") % chan)).update();
+ }
+ this->update_enables();
+
+ return my_streamer;
+}
diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp
new file mode 100644
index 000000000..0e4cc82cd
--- /dev/null
+++ b/host/lib/usrp/b200/b200_regs.hpp
@@ -0,0 +1,119 @@
+//
+// Copyright 2012-2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B200_REGS_HPP
+#define INCLUDED_B200_REGS_HPP
+
+#include <boost/cstdint.hpp>
+
+#define TOREG(x) ((x)*4)
+
+#define localparam static const int
+
+localparam SR_CORE_SPI = 8;
+localparam SR_CORE_MISC = 16;
+localparam SR_CORE_COMPAT = 24;
+localparam SR_CORE_GPSDO_ST = 40;
+localparam RB32_CORE_SPI = 8;
+localparam RB32_CORE_MISC = 16;
+localparam RB32_CORE_STATUS = 20;
+
+localparam SR_SPI = 8;
+localparam SR_ATR = 12;
+localparam SR_TEST = 21;
+localparam SR_CODEC_IDLE = 22;
+localparam SR_READBACK = 32;
+localparam SR_TX_CTRL = 64;
+localparam SR_RX_CTRL = 96;
+localparam SR_RX_DSP = 144;
+localparam SR_TX_DSP = 184;
+localparam SR_TIME = 128;
+
+localparam RB32_TEST = 0;
+localparam RB64_TIME_NOW = 8;
+localparam RB64_TIME_PPS = 16;
+localparam RB64_CODEC_READBACK = 24;
+
+//pll constants
+static const int ADF4001_SLAVENO = (1 << 1);
+static const double ADF4001_SPI_RATE = 10e3; //slow for large time constant on spi lines
+
+/* ATR Control Bits */
+static const boost::uint32_t TX_ENABLE1 = (1 << 7);
+static const boost::uint32_t SFDX1_RX = (1 << 6);
+static const boost::uint32_t SFDX1_TX = (1 << 5);
+static const boost::uint32_t SRX1_RX = (1 << 4);
+static const boost::uint32_t SRX1_TX = (1 << 3);
+static const boost::uint32_t LED_RX1 = (1 << 2);
+static const boost::uint32_t LED_TXRX_RX1 = (1 << 1);
+static const boost::uint32_t LED_TXRX_TX1 = (1 << 0);
+
+static const boost::uint32_t TX_ENABLE2 = (1 << 7);
+static const boost::uint32_t SFDX2_RX = (1 << 6);
+static const boost::uint32_t SFDX2_TX = (1 << 5);
+static const boost::uint32_t SRX2_RX = (1 << 4);
+static const boost::uint32_t SRX2_TX = (1 << 3);
+static const boost::uint32_t LED_RX2 = (1 << 2);
+static const boost::uint32_t LED_TXRX_RX2 = (1 << 1);
+static const boost::uint32_t LED_TXRX_TX2 = (1 << 0);
+
+
+/* ATR State Definitions. */
+static const boost::uint32_t STATE_OFF = 0x00;
+
+///////////////////////// side 1 ///////////////////////////////////
+static const boost::uint32_t STATE_RX1_RX2 = (SFDX1_RX
+ | SFDX1_TX
+ | LED_RX1);
+
+static const boost::uint32_t STATE_RX1_TXRX = (SRX1_RX
+ | SRX1_TX
+ | LED_TXRX_RX1);
+
+static const boost::uint32_t STATE_FDX1_TXRX = (TX_ENABLE1
+ | SFDX1_RX
+ | SFDX1_TX
+ | LED_TXRX_TX1
+ | LED_RX1);
+
+static const boost::uint32_t STATE_TX1_TXRX = (TX_ENABLE1
+ | SFDX1_RX
+ | SFDX1_TX
+ | LED_TXRX_TX1);
+
+///////////////////////// side 2 ///////////////////////////////////
+static const boost::uint32_t STATE_RX2_RX2 = (SFDX2_RX
+ | SRX2_TX
+ | LED_RX2);
+
+static const boost::uint32_t STATE_RX2_TXRX = (SRX2_TX
+ | SRX2_RX
+ | LED_TXRX_RX2);
+
+static const boost::uint32_t STATE_FDX2_TXRX = (TX_ENABLE2
+ | SFDX2_RX
+ | SFDX2_TX
+ | LED_TXRX_TX2
+ | LED_RX2);
+
+static const boost::uint32_t STATE_TX2_TXRX = (TX_ENABLE2
+ | SFDX2_RX
+ | SFDX2_TX
+ | LED_TXRX_TX2);
+
+
+#endif /* INCLUDED_B200_REGS_HPP */
diff --git a/host/lib/usrp/b200/b200_uart.cpp b/host/lib/usrp/b200/b200_uart.cpp
new file mode 100644
index 000000000..4682a79b9
--- /dev/null
+++ b/host/lib/usrp/b200/b200_uart.cpp
@@ -0,0 +1,117 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "b200_uart.hpp"
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/exception.hpp>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+struct b200_uart_impl : b200_uart
+{
+ b200_uart_impl(zero_copy_if::sptr xport, const boost::uint32_t sid):
+ _xport(xport),
+ _sid(sid),
+ _count(0),
+ _char_queue(4096)
+ {
+ //this default baud divider is over 9000
+ this->set_baud_divider(9001);
+ }
+
+ void send_char(const char ch)
+ {
+ managed_send_buffer::sptr buff = _xport->get_send_buff();
+ UHD_ASSERT_THROW(bool(buff));
+
+ vrt::if_packet_info_t packet_info;
+ packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT;
+ packet_info.num_payload_words32 = 2;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = _count++;
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.sid = _sid;
+ packet_info.has_sid = true;
+ packet_info.has_cid = false;
+ packet_info.has_tsi = false;
+ packet_info.has_tsf = false;
+ packet_info.has_tlr = false;
+
+ boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>();
+ vrt::if_hdr_pack_le(packet_buff, packet_info);
+ packet_buff[packet_info.num_header_words32+0] = uhd::htowx(boost::uint32_t(_baud_div));
+ packet_buff[packet_info.num_header_words32+1] = uhd::htowx(boost::uint32_t(ch));
+ buff->commit(packet_info.num_packet_words32*sizeof(boost::uint32_t));
+ }
+
+ void write_uart(const std::string &buff)
+ {
+ for (size_t i = 0; i < buff.size(); i++)
+ {
+ if (buff[i] == '\n') this->send_char('\r');
+ this->send_char(buff[i]);
+ }
+ }
+
+ std::string read_uart(double timeout)
+ {
+ std::string line;
+ char ch = '\0';
+ while (_char_queue.pop_with_timed_wait(ch, timeout))
+ {
+ if (ch == '\r') continue;
+ line += std::string(&ch, 1);
+ if (ch == '\n') return line;
+ }
+ return line;
+ }
+
+ void handle_uart_packet(managed_recv_buffer::sptr buff)
+ {
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+ vrt::if_packet_info_t packet_info;
+ packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ vrt::if_hdr_unpack_le(packet_buff, packet_info);
+ const char ch = char(uhd::wtohx(packet_buff[packet_info.num_header_words32+1]));
+ _char_queue.push_with_pop_on_full(ch);
+ }
+
+ void set_baud_divider(const double baud_div)
+ {
+ _baud_div = size_t(baud_div + 0.5);
+ }
+
+ const zero_copy_if::sptr _xport;
+ const boost::uint32_t _sid;
+ size_t _count;
+ size_t _baud_div;
+ bounded_buffer<char> _char_queue;
+};
+
+
+b200_uart::sptr b200_uart::make(zero_copy_if::sptr xport, const boost::uint32_t sid)
+{
+ return b200_uart::sptr(new b200_uart_impl(xport, sid));
+}
diff --git a/host/lib/usrp/b200/b200_uart.hpp b/host/lib/usrp/b200/b200_uart.hpp
new file mode 100644
index 000000000..1c8e44ddc
--- /dev/null
+++ b/host/lib/usrp/b200/b200_uart.hpp
@@ -0,0 +1,36 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_B200_UART_HPP
+#define INCLUDED_B200_UART_HPP
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/serial.hpp> //uart iface
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+class b200_uart: boost::noncopyable, public uhd::uart_iface
+{
+public:
+ typedef boost::shared_ptr<b200_uart> sptr;
+ static sptr make(uhd::transport::zero_copy_if::sptr, const boost::uint32_t sid);
+ virtual void handle_uart_packet(uhd::transport::managed_recv_buffer::sptr buff) = 0;
+ virtual void set_baud_divider(const double baud_div) = 0;
+};
+
+
+#endif /* INCLUDED_B200_UART_HPP */