diff options
Diffstat (limited to 'host/lib')
| -rw-r--r-- | host/lib/usrp/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/b200/CMakeLists.txt | 34 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_iface.cpp | 561 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_iface.hpp | 81 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_impl.cpp | 885 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_impl.hpp | 196 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_io_impl.cpp | 377 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_regs.hpp | 119 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_uart.cpp | 117 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_uart.hpp | 36 | 
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 */ | 
