diff options
28 files changed, 2583 insertions, 9 deletions
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index fa07e3d1d..1728b63f9 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2011-2012 Ettus Research LLC +# Copyright 2011-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 @@ -29,6 +29,8 @@ ENDIF(ENABLE_USB)  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})  LIBUHD_APPEND_SOURCES( +    ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp new file mode 100644 index 000000000..ae7cc6f9b --- /dev/null +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -0,0 +1,165 @@ +// +// 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 "ad9361_ctrl.hpp" +#include "ad9361_transaction.h" +#include <uhd/exception.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/format.hpp> +#include <cstring> + +using namespace uhd; + +struct ad9361_ctrl_impl : public ad9361_ctrl +{ +    ad9361_ctrl_impl(ad9361_ctrl_iface_sptr iface): +        _iface(iface), _seq(0) +    { +        ad9361_transaction_t request; + +        request.action = AD9361_ACTION_ECHO; +        this->do_transaction(request); + +        request.action = AD9361_ACTION_INIT; +        this->do_transaction(request); +    } + +    double set_gain(const std::string &which, const double value) +    { +        ad9361_transaction_t request; + +        if (which == "RX1") request.action = AD9361_ACTION_SET_RX1_GAIN; +        if (which == "RX2") request.action = AD9361_ACTION_SET_RX2_GAIN; +        if (which == "TX1") request.action = AD9361_ACTION_SET_TX1_GAIN; +        if (which == "TX2") request.action = AD9361_ACTION_SET_TX2_GAIN; + +        ad9361_double_pack(value, request.value.gain); +        const ad9361_transaction_t reply = this->do_transaction(request); +        return ad9361_double_unpack(reply.value.gain); +    } + +    //! set a new clock rate, return the exact value +    double set_clock_rate(const double rate) +    { +        //warning for known trouble rates +        if (rate > 56e6) UHD_MSG(warning) << boost::format( +            "The requested clock rate %f MHz may cause slow configuration.\n" +            "The driver recommends a master clock rate less than %f MHz.\n" +        ) % (rate/1e6) % 56.0 << std::endl; + +        //clip to known bounds +        const meta_range_t clock_rate_range(250e3, 61.44e6); +        const double clipped_rate = clock_rate_range.clip(rate); + +        ad9361_transaction_t request; +        request.action = AD9361_ACTION_SET_CLOCK_RATE; +        ad9361_double_pack(clipped_rate, request.value.rate); +        const ad9361_transaction_t reply = this->do_transaction(request); +        return ad9361_double_unpack(reply.value.rate); +    } + +    //! set which RX and TX chains/antennas are active +    void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) +    { +        boost::uint32_t mask = 0; +        if (tx1) mask |= (1 << 0); +        if (tx2) mask |= (1 << 1); +        if (rx1) mask |= (1 << 2); +        if (rx2) mask |= (1 << 3); + +        ad9361_transaction_t request; +        request.action = AD9361_ACTION_SET_ACTIVE_CHAINS; +        request.value.enable_mask = mask; +        this->do_transaction(request); +    } + +    //! tune the given frontend, return the exact value +    double tune(const std::string &which, const double freq) +    { +        //clip to known bounds +        const meta_range_t freq_range(50e6, 6e9); +        const double clipped_freq = freq_range.clip(freq); + +        ad9361_transaction_t request; + +        if (which[0] == 'R') request.action = AD9361_ACTION_SET_RX_FREQ; +        if (which[0] == 'T') request.action = AD9361_ACTION_SET_TX_FREQ; + +        const double value = ad9361_ctrl::get_rf_freq_range().clip(clipped_freq); +        ad9361_double_pack(value, request.value.freq); +        const ad9361_transaction_t reply = this->do_transaction(request); +        return ad9361_double_unpack(reply.value.freq); +    } + +    //! turn on/off Catalina's data port loopback +    void data_port_loopback(const bool on) +    { +        ad9361_transaction_t request; +        request.action = AD9361_ACTION_SET_CODEC_LOOP; +        request.value.codec_loop = on? 1 : 0; +        this->do_transaction(request); +    } + +    ad9361_transaction_t do_transaction(const ad9361_transaction_t &request) +    { +        boost::mutex::scoped_lock lock(_mutex); + +        //declare in/out buffers +        unsigned char in_buff[64] = {}; +        unsigned char out_buff[64] = {}; + +        //copy the input transaction +        std::memcpy(in_buff, &request, sizeof(request)); +     +        //fill in other goodies +        ad9361_transaction_t *in = (ad9361_transaction_t *)in_buff; +        in->version = AD9361_TRANSACTION_VERSION; +        in->sequence = _seq++; + +        //transact +        _iface->ad9361_transact(in_buff, out_buff); +        ad9361_transaction_t *out = (ad9361_transaction_t *)out_buff; + +        //sanity checks +        UHD_ASSERT_THROW(out->version == in->version); +        UHD_ASSERT_THROW(out->sequence == in->sequence); + +        //handle errors +        const size_t len = strnlen(out->error_msg, AD9361_TRANSACTION_MAX_ERROR_MSG); +        const std::string error_msg(out->error_msg, len); +        if (not error_msg.empty()) throw uhd::runtime_error("ad9361 do transaction: " + error_msg); + +        //return result done! +        return *out; +    } + +    ad9361_ctrl_iface_sptr _iface; +    size_t _seq; +    boost::mutex _mutex; + +}; + + +/*********************************************************************** + * Make an instance of the implementation + **********************************************************************/ +ad9361_ctrl::sptr ad9361_ctrl::make(ad9361_ctrl_iface_sptr iface) +{ +    return sptr(new ad9361_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp new file mode 100644 index 000000000..6d79ef3e9 --- /dev/null +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -0,0 +1,127 @@ +// +// 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_AD9361_CTRL_HPP +#define INCLUDED_AD9361_CTRL_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <boost/function.hpp> +#include <vector> +#include <string> + + +struct ad9361_ctrl_iface_type +{ +    virtual void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) = 0; +}; +typedef boost::shared_ptr<ad9361_ctrl_iface_type> ad9361_ctrl_iface_sptr; + + +struct ad9361_ctrl_over_zc : ad9361_ctrl_iface_type +{ +    ad9361_ctrl_over_zc(uhd::transport::zero_copy_if::sptr xport) +    { +        _xport = xport; +    } + +    void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) +    { +        { +            uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); +            if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc send timeout"); +            std::memcpy(buff->cast<void *>(), in_buff, 64); +            buff->commit(64); +        } +        { +            uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); +            if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc recv timeout"); +            std::memcpy(out_buff, buff->cast<const void *>(), 64); +        } +    } + +    uhd::transport::zero_copy_if::sptr _xport; +}; + + +class ad9361_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<ad9361_ctrl> sptr; + +    //! make a new codec control object +    static sptr make(ad9361_ctrl_iface_sptr iface); + +    //! Get a list of gain names for RX or TX +    static std::vector<std::string> get_gain_names(const std::string &/*which*/) +    { +        return std::vector<std::string>(1, "PGA"); +    } + +    //! get the gain range for a particular gain element +    static uhd::meta_range_t get_gain_range(const std::string &which) +    { +        if(which[0] == 'R') { +            return uhd::meta_range_t(0.0, 73.0, 1.0); +        } else { +            return uhd::meta_range_t(0.0, 89.75, 0.25); +        } +    } + +    //! get the freq range for the frontend which +    static uhd::meta_range_t get_rf_freq_range(void) +    { +        return uhd::meta_range_t(30e6, 6e9); +    } + +    //! get the filter range for the frontend which +    static uhd::meta_range_t get_bw_filter_range(const std::string &/*which*/) +    { +        return uhd::meta_range_t(200e3, 56e6); +    } + +    //! get the filter range for the frontend which +    static uhd::meta_range_t get_samp_rate_range(void) +    { +        return uhd::meta_range_t(220e3, 61.44e6); +    } + +    //! set the filter bandwidth for the frontend +    double set_bw_filter(const std::string &/*which*/, const double /*bw*/) +    { +        return 56e6; //TODO +    } + +    //! set the gain for a particular gain element +    virtual double set_gain(const std::string &which, const double value) = 0; + +    //! set a new clock rate, return the exact value +    virtual double set_clock_rate(const double rate) = 0; + +    //! set which RX and TX chains/antennas are active +    virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0; + +    //! tune the given frontend, return the exact value +    virtual double tune(const std::string &which, const double value) = 0; + +    //! turn on/off Catalina's data port loopback +    virtual void data_port_loopback(const bool on) = 0; +}; + +#endif /* INCLUDED_AD9361_CTRL_HPP */ diff --git a/host/lib/usrp/common/ad9361_transaction.h b/host/lib/usrp/common/ad9361_transaction.h new file mode 100644 index 000000000..7b41b811f --- /dev/null +++ b/host/lib/usrp/common/ad9361_transaction.h @@ -0,0 +1,102 @@ +// +// 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_AD9361_TRANSACTION_H +#define INCLUDED_AD9361_TRANSACTION_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +//various constants +#define AD9361_TRANSACTION_VERSION 0x4 +#define AD9361_TRANSACTION_MAX_ERROR_MSG 40 + +//action types +#define AD9361_ACTION_ECHO 0 +#define AD9361_ACTION_INIT 1 +#define AD9361_ACTION_SET_RX1_GAIN 2 +#define AD9361_ACTION_SET_TX1_GAIN 3 +#define AD9361_ACTION_SET_RX2_GAIN 4 +#define AD9361_ACTION_SET_TX2_GAIN 5 +#define AD9361_ACTION_SET_RX_FREQ 6 +#define AD9361_ACTION_SET_TX_FREQ 7 +#define AD9361_ACTION_SET_CODEC_LOOP 8 +#define AD9361_ACTION_SET_CLOCK_RATE 9 +#define AD9361_ACTION_SET_ACTIVE_CHAINS 10 + +static inline void ad9361_double_pack(const double input, uint32_t output[2]) +{ +    const uint32_t *p = (const uint32_t *)&input; +    output[0] = p[0]; +    output[1] = p[1]; +} + +static inline double ad9361_double_unpack(const uint32_t input[2]) +{ +    double output = 0.0; +    uint32_t *p = (uint32_t *)&output; +    p[0] = input[0]; +    p[1] = input[1]; +    return output; +} + +typedef struct +{ +    //version is expected to be AD9361_TRANSACTION_VERSION +    //check otherwise for compatibility +    uint32_t version; + +    //sequence number - increment every call for sanity +    uint32_t sequence; + +    //action tells us what to do, see AD9361_ACTION_* +    uint32_t action; + +    union +    { +        //enable mask for chains +        uint32_t enable_mask; + +        //true to enable codec internal loopback +        uint32_t codec_loop; + +        //freq holds request LO freq and result from tune +        uint32_t freq[2]; + +        //gain holds request gain and result from action +        uint32_t gain[2]; + +        //rate holds request clock rate and result from action +        uint32_t rate[2]; + +    } value; + +    //error message comes back as a reply - +    //set to null string for no error \0 +    char error_msg[]; + +} ad9361_transaction_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_AD9361_TRANSACTION_H */ diff --git a/host/lib/usrp/common/adf4001_ctrl.cpp b/host/lib/usrp/common/adf4001_ctrl.cpp new file mode 100644 index 000000000..46171c7ce --- /dev/null +++ b/host/lib/usrp/common/adf4001_ctrl.cpp @@ -0,0 +1,151 @@ +// +// Copyright 2013 Ettus Research LLC +// +// Original ADF4001 driver written by: bistromath +//                                     Mar 1, 2013 +// +// Re-used and re-licensed with permission. +// +// 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 "adf4001_ctrl.hpp" + +#include <uhd/utils/msg.hpp> +#include <iostream> +#include <iomanip> + +using namespace uhd; +using namespace uhd::usrp; + +adf4001_regs_t::adf4001_regs_t(void) { +    ref_counter = 0; +    n = 0; +    charge_pump_current_1 = 0; +    charge_pump_current_2 = 0; +    anti_backlash_width = ANTI_BACKLASH_WIDTH_2_9NS; +    lock_detect_precision = LOCK_DETECT_PRECISION_3CYC; +    charge_pump_gain = CHARGE_PUMP_GAIN_1; +    counter_reset = COUNTER_RESET_NORMAL; +    power_down = POWER_DOWN_NORMAL; +    muxout = MUXOUT_TRISTATE_OUT; +    phase_detector_polarity = PHASE_DETECTOR_POLARITY_NEGATIVE; +    charge_pump_mode = CHARGE_PUMP_TRISTATE; +    fastlock_mode = FASTLOCK_MODE_DISABLED; +    timer_counter_control = TIMEOUT_3CYC; +} + + +boost::uint32_t adf4001_regs_t::get_reg(boost::uint8_t addr) { +    boost::uint32_t reg = 0; +    switch (addr) { +    case 0: +        reg |= (boost::uint32_t(ref_counter)         & 0x003FFF) << 2; +        reg |= (boost::uint32_t(anti_backlash_width) & 0x000003) << 16; +        reg |= (boost::uint32_t(lock_detect_precision) & 0x000001) << 20; +        break; +    case 1: +        reg |= (boost::uint32_t(n) & 0x001FFF) << 8; +        reg |= (boost::uint32_t(charge_pump_gain) & 0x000001) << 21; +        break; +    case 2: +        reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2; +        reg |= (boost::uint32_t(power_down) & 0x000001) << 3; +        reg |= (boost::uint32_t(muxout) & 0x000007) << 4; +        reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7; +        reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8; +        reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9; +        reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11; +        reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15; +        reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18; +        reg |= (boost::uint32_t(power_down) & 0x000002) << 21; +        break; +    case 3: +        reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2; +        reg |= (boost::uint32_t(power_down) & 0x000001) << 3; +        reg |= (boost::uint32_t(muxout) & 0x000007) << 4; +        reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7; +        reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8; +        reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9; +        reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11; +        reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15; +        reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18; +        reg |= (boost::uint32_t(power_down) & 0x000002) << 21; +        break; +    default: +        break; +    } + +    reg |= (boost::uint32_t(addr) & 0x03); + +    return reg; +} + + +adf4001_ctrl::adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno): +    spi_iface(_spi), +    slaveno(slaveno) +    { + +    spi_config.mosi_edge = spi_config_t::EDGE_RISE; + +    //set defaults +    adf4001_regs.ref_counter = 1; +    adf4001_regs.n = 4; +    adf4001_regs.charge_pump_current_1 = 7; +    adf4001_regs.charge_pump_current_2 = 7; +    adf4001_regs.muxout = adf4001_regs_t::MUXOUT_DLD; +    adf4001_regs.counter_reset = adf4001_regs_t::COUNTER_RESET_NORMAL; +    adf4001_regs.phase_detector_polarity = adf4001_regs_t::PHASE_DETECTOR_POLARITY_POSITIVE; +    adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE; + +    //everything else should be defaults + +    program_regs(); +} + +void adf4001_ctrl::set_lock_to_ext_ref(bool external) { +    if(external) { +        adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_NORMAL; +    } else { +        adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE; +    } + +    program_regs(); +} + +void adf4001_ctrl::program_regs(void) { +    //no control over CE, only LE, therefore we use the initialization latch method +    write_reg(3); +    boost::this_thread::sleep(boost::posix_time::microseconds(1)); + +    //write R counter latch (0) +    write_reg(0); +    boost::this_thread::sleep(boost::posix_time::microseconds(1)); + +    //write N counter latch (1) +    write_reg(1); +    boost::this_thread::sleep(boost::posix_time::microseconds(1)); +} + + +void adf4001_ctrl::write_reg(boost::uint8_t addr) { +    boost::uint32_t reg = adf4001_regs.get_reg(addr); //load the reg data + +        spi_iface->transact_spi(slaveno, +                                spi_config, +                                reg, +                                24, +                                false); +} diff --git a/host/lib/usrp/common/adf4001_ctrl.hpp b/host/lib/usrp/common/adf4001_ctrl.hpp new file mode 100644 index 000000000..a16cff3fa --- /dev/null +++ b/host/lib/usrp/common/adf4001_ctrl.hpp @@ -0,0 +1,142 @@ +// +// Copyright 2013 Ettus Research LLC +// +// Original ADF4001 driver written by: bistromath +//                                     Mar 1, 2013 +// +// Re-used and re-licensed with permission. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP + +#include "spi_core_3000.hpp" +#include <uhd/types/serial.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread/thread.hpp> + +namespace uhd { namespace usrp { + +class adf4001_regs_t { +public: + +    /* Function prototypes */ +    boost::uint32_t get_reg(boost::uint8_t addr); +    adf4001_regs_t(void); + +    /* Register values / addresses */ +    boost::uint16_t ref_counter; //14 bits +    boost::uint16_t n; //13 bits +    boost::uint8_t charge_pump_current_1; //3 bits +    boost::uint8_t charge_pump_current_2; //3 bits + +    enum anti_backlash_width_t { +        ANTI_BACKLASH_WIDTH_2_9NS = 0, +        ANTI_BACKLASH_WIDTH_1_3NS = 1, +        ANTI_BACKLASH_WIDTH_6_0NS = 2, +        ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3 +    }; +    anti_backlash_width_t anti_backlash_width; + +    enum lock_detect_precision_t { +        LOCK_DETECT_PRECISION_3CYC = 0, +        LOCK_DETECT_PRECISION_5CYC = 1 +    }; +    lock_detect_precision_t lock_detect_precision; +    enum charge_pump_gain_t { +        CHARGE_PUMP_GAIN_1 = 0, +        CHARGE_PUMP_GAIN_2 = 1 +    }; +    charge_pump_gain_t charge_pump_gain; +    enum counter_reset_t { +        COUNTER_RESET_NORMAL = 0, +        COUNTER_RESET_RESET = 1 +    }; +    counter_reset_t    counter_reset; +    enum power_down_t { +        POWER_DOWN_NORMAL = 0, +        POWER_DOWN_ASYNC = 1, +        POWER_DOWN_SYNC = 3 +    }; +    power_down_t power_down; +    enum muxout_t { +        MUXOUT_TRISTATE_OUT = 0, +        MUXOUT_DLD = 1, +        MUXOUT_NDIV = 2, +        MUXOUT_AVDD = 3, +        MUXOUT_RDIV = 4, +        MUXOUT_NCH_OD_ALD = 5, +        MUXOUT_SDO = 6, +        MUXOUT_GND = 7 +    }; +    muxout_t muxout; +    enum phase_detector_polarity_t { +        PHASE_DETECTOR_POLARITY_NEGATIVE = 0, +        PHASE_DETECTOR_POLARITY_POSITIVE = 1 +    }; +    phase_detector_polarity_t phase_detector_polarity; +    enum charge_pump_mode_t { +        CHARGE_PUMP_NORMAL = 0, +        CHARGE_PUMP_TRISTATE = 1 +    }; +    charge_pump_mode_t charge_pump_mode; +    enum fastlock_mode_t { +        FASTLOCK_MODE_DISABLED = 0, +        FASTLOCK_MODE_1 = 1, +        FASTLOCK_MODE_2 = 2 +    }; +    fastlock_mode_t fastlock_mode; +    enum timer_counter_control_t { +        TIMEOUT_3CYC = 0, +        TIMEOUT_7CYC = 1, +        TIMEOUT_11CYC = 2, +        TIMEOUT_15CYC = 3, +        TIMEOUT_19CYC = 4, +        TIMEOUT_23CYC = 5, +        TIMEOUT_27CYC = 6, +        TIMEOUT_31CYC = 7, +        TIMEOUT_35CYC = 8, +        TIMEOUT_39CYC = 9, +        TIMEOUT_43CYC = 10, +        TIMEOUT_47CYC = 11, +        TIMEOUT_51CYC = 12, +        TIMEOUT_55CYC = 13, +        TIMEOUT_59CYC = 14, +        TIMEOUT_63CYC = 15, +    }; +    timer_counter_control_t timer_counter_control; +}; + + +class adf4001_ctrl { +public: + +    adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno); +    void set_lock_to_ext_ref(bool external); + +private: +    spi_core_3000::sptr spi_iface; +    int slaveno; +    spi_config_t spi_config; +    adf4001_regs_t adf4001_regs; + +    void program_regs(void); +    void write_reg(boost::uint8_t addr); +}; + +}} + +#endif diff --git a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp index 9de3666dd..4fb6c4604 100644 --- a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp +++ b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp @@ -92,7 +92,10 @@ namespace uhd{ namespace usrp{                      if (new_sid != sid)                      {                          boost::mutex::scoped_lock l(mutex); -                        _queues[new_sid].push(buff); +                        if (_queues.count(new_sid) == 0) UHD_MSG(error) +                            << "recv packet demuxer unexpected sid 0x" << std::hex << new_sid << std::dec +                            << std::endl; +                        else _queues[new_sid].push(buff);                          buff.reset();                      }                  } @@ -102,6 +105,15 @@ namespace uhd{ namespace usrp{              return buff;          } +        void realloc_sid(const boost::uint32_t sid) +        { +            boost::mutex::scoped_lock l(mutex); +            while(not _queues[sid].empty()) //allocated and clears if already allocated +            { +                _queues[sid].pop(); +            } +        } +          typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t;          std::map<boost::uint32_t, queue_type_t> _queues;          transport::zero_copy_if::sptr _xport; diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index 3192b0774..f526319bc 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2011-2012 Ettus Research LLC +# Copyright 2011-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 @@ -22,6 +22,7 @@  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})  LIBUHD_APPEND_SOURCES( +    ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_200.cpp @@ -32,4 +33,12 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_200.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/rx_vita_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/tx_vita_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/time_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100_wb32.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_core_3000.cpp  ) diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index cdab70b8d..51c23aa4b 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -104,3 +104,35 @@ private:  gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base, const size_t rb_addr){      return sptr(new gpio_core_200_impl(iface, base, rb_addr));  } + +class gpio_core_200_32wo_impl : public gpio_core_200_32wo{ +public: +    gpio_core_200_32wo_impl(wb_iface::sptr iface, const size_t base): +        _iface(iface), _base(base) +    { +        _iface->poke32(REG_GPIO_DDR, 0xffffffff); +    } + +    void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){ +        if (atr == dboard_iface::ATR_REG_IDLE)        _iface->poke32(REG_GPIO_IDLE, value); +        if (atr == dboard_iface::ATR_REG_TX_ONLY)     _iface->poke32(REG_GPIO_TX_ONLY, value); +        if (atr == dboard_iface::ATR_REG_RX_ONLY)     _iface->poke32(REG_GPIO_RX_ONLY, value); +        if (atr == dboard_iface::ATR_REG_FULL_DUPLEX) _iface->poke32(REG_GPIO_BOTH, value); +    } + +    void set_all_regs(const boost::uint32_t value){ +        this->set_atr_reg(dboard_iface::ATR_REG_IDLE,        value); +        this->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY,     value); +        this->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY,     value); +        this->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, value); +    } + +private: +    wb_iface::sptr _iface; +    const size_t _base; + +}; + +gpio_core_200_32wo::sptr gpio_core_200_32wo::make(wb_iface::sptr iface, const size_t base){ +    return sptr(new gpio_core_200_32wo_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 278575874..a3edf5454 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -49,4 +49,18 @@ public:  }; +//! Simple wrapper for 32 bit write only +class gpio_core_200_32wo : boost::noncopyable{ +public: +    typedef boost::shared_ptr<gpio_core_200_32wo> sptr; + +    typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; + +    static sptr make(wb_iface::sptr iface, const size_t); + +    virtual void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value) = 0; + +    virtual void set_all_regs(const boost::uint32_t value) = 0; +}; +  #endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/i2c_core_100_wb32.cpp b/host/lib/usrp/cores/i2c_core_100_wb32.cpp new file mode 100644 index 000000000..b38d5b4bc --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_100_wb32.cpp @@ -0,0 +1,144 @@ +// +// Copyright 2011-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 "i2c_core_100_wb32.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep + +#define REG_I2C_PRESCALER_LO _base + 0 +#define REG_I2C_PRESCALER_HI _base + 4 +#define REG_I2C_CTRL         _base + 8 +#define REG_I2C_DATA         _base + 12 +#define REG_I2C_CMD_STATUS   _base + 16 + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define	I2C_CTRL_EN	(1 << 7)	// core enable +#define	I2C_CTRL_IE	(1 << 6)	// interrupt enable + +#define	I2C_CMD_START	(1 << 7)	// generate (repeated) start condition +#define I2C_CMD_STOP	(1 << 6)	// generate stop condition +#define	I2C_CMD_RD	(1 << 5)	// read from slave +#define I2C_CMD_WR	(1 << 4)	// write to slave +#define	I2C_CMD_NACK	(1 << 3)	// when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2	(1 << 2)	// reserved +#define	I2C_CMD_RSVD_1	(1 << 1)	// reserved +#define I2C_CMD_IACK	(1 << 0)	// set to clear pending interrupt + +#define I2C_ST_RXACK	(1 << 7)	// Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define	I2C_ST_BUSY	(1 << 6)	// 1 after START signal detected; 0 after STOP signal detected +#define	I2C_ST_AL	(1 << 5)	// Arbitration lost.  1 when core lost arbitration +#define	I2C_ST_RSVD_4	(1 << 4)	// reserved +#define	I2C_ST_RSVD_3	(1 << 3)	// reserved +#define	I2C_ST_RSVD_2	(1 << 2)	// reserved +#define I2C_ST_TIP	(1 << 1)	// Transfer-in-progress +#define	I2C_ST_IP	(1 << 0)	// Interrupt pending + +using namespace uhd; + +class i2c_core_100_wb32_wb32_impl : public i2c_core_100_wb32{ +public: +    i2c_core_100_wb32_wb32_impl(wb_iface::sptr iface, const size_t base): +        _iface(iface), _base(base) +    { +        //init I2C FPGA interface. +        _iface->poke32(REG_I2C_CTRL, 0x0000); +        _iface->poke32(REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core +    } + +    void set_clock_rate(const double rate) +    { +        static const boost::uint32_t i2c_datarate = 400000; +        boost::uint16_t prescaler = rate / (i2c_datarate*5) - 1; +        _iface->poke32(REG_I2C_PRESCALER_LO, prescaler & 0xFF); +        _iface->poke32(REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF); +    } + +    void write_i2c( +        boost::uint8_t addr, +        const byte_vector_t &bytes +    ){ +        _iface->poke32(REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) +        _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + +        //wait for previous transfer to complete +        if (not wait_chk_ack()) { +            _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP); +            return; +        } + +        for (size_t i = 0; i < bytes.size(); i++) { +            _iface->poke32(REG_I2C_DATA, bytes[i]); +            _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); +            if(!wait_chk_ack()) { +                _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP); +                return; +            } +        } +    } + +    byte_vector_t read_i2c( +        boost::uint8_t addr, +        size_t num_bytes +    ){ +        byte_vector_t bytes; +        if (num_bytes == 0) return bytes; + +        while (_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_BUSY){ +            /* NOP */ +        } + +        _iface->poke32(REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1) +        _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START); +        //wait for previous transfer to complete +        if (not wait_chk_ack()) { +            _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP); +        } +        for (size_t i = 0; i < num_bytes; i++) { +            _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); +            i2c_wait(); +            bytes.push_back(boost::uint8_t(_iface->peek32(REG_I2C_DATA))); +        } +        return bytes; +    } + +private: +    void i2c_wait(void) { +        for (size_t i = 0; i < 10; i++) +        { +            if ((_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_TIP) == 0) return; +            boost::this_thread::sleep(boost::posix_time::milliseconds(1)); +        } +        UHD_MSG(error) << "i2c_core_100_wb32: i2c_wait timeout" << std::endl; +    } + +    bool wait_chk_ack(void){ +        i2c_wait(); +        return (_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0; +    } + +    wb_iface::sptr _iface; +    const size_t _base; +}; + +i2c_core_100_wb32::sptr i2c_core_100_wb32::make(wb_iface::sptr iface, const size_t base) +{ +    return sptr(new i2c_core_100_wb32_wb32_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/i2c_core_100_wb32.hpp b/host/lib/usrp/cores/i2c_core_100_wb32.hpp new file mode 100644 index 000000000..f2ac98292 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_100_wb32.hpp @@ -0,0 +1,37 @@ +// +// Copyright 2011-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_LIBUHD_USRP_I2C_CORE_100_WB32_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class i2c_core_100_wb32 : boost::noncopyable, public uhd::i2c_iface{ +public: +    typedef boost::shared_ptr<i2c_core_100_wb32> sptr; + +    //! makes a new i2c core from iface and slave base +    static sptr make(wb_iface::sptr iface, const size_t base); + +    virtual void set_clock_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP */ diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp new file mode 100644 index 000000000..616903920 --- /dev/null +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -0,0 +1,312 @@ +// +// 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 "radio_ctrl_core_3000.hpp" +#include "async_packet_handler.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/bind.hpp> +#include <queue> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const size_t SR_READBACK  = 32; + +class radio_ctrl_core_3000_impl : public radio_ctrl_core_3000 +{ +public: + +    radio_ctrl_core_3000_impl( +        vrt::if_packet_info_t::link_type_t link_type, +        uhd::transport::zero_copy_if::sptr ctrl_xport, +        uhd::transport::zero_copy_if::sptr resp_xport, +        const boost::uint32_t sid, +        const std::string &name +    ): +        _link_type(link_type), +        _packet_type(vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), +        _bige(link_type == vrt::if_packet_info_t::LINK_TYPE_VRLP), +        _ctrl_xport(ctrl_xport), +        _resp_xport(resp_xport), +        _sid(sid), +        _name(name), +        _seq_out(0), +        _timeout(ACK_TIMEOUT), +        _resp_queue(128/*max response msgs*/), +        _resp_queue_size(_resp_xport? _resp_xport->get_num_recv_frames() : 3) +    { +        UHD_LOG << "radio_ctrl_core_3000_impl() " << _name << std::endl; +        if (resp_xport) +        { +            while (resp_xport->get_recv_buff(0.0)){} //flush +        } +        this->set_time(uhd::time_spec_t(0.0)); +        this->set_tick_rate(1.0); //something possible but bogus +    } + +    ~radio_ctrl_core_3000_impl(void) +    { +        UHD_LOG << "~radio_ctrl_core_3000_impl() " << _name << std::endl; +        _timeout = ACK_TIMEOUT; //reset timeout to something small +        UHD_SAFE_CALL( +            this->peek32(0); //dummy peek with the purpose of ack'ing all packets +            _async_task.reset(); //now its ok to release the task +        ) +    } + +    /******************************************************************* +     * Peek and poke 32 bit implementation +     ******************************************************************/ +    void poke32(const wb_addr_type addr, const boost::uint32_t data) +    { +        boost::mutex::scoped_lock lock(_mutex); +        UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << " data 0x" << data << std::dec << std::endl; + +        this->send_pkt(addr/4, data); +        this->wait_for_ack(false); +    } + +    boost::uint32_t peek32(const wb_addr_type addr) +    { +        boost::mutex::scoped_lock lock(_mutex); +        UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; + +        this->send_pkt(SR_READBACK, addr/8); +        this->wait_for_ack(false); + +        this->send_pkt(0); +        const boost::uint64_t res = this->wait_for_ack(true); +        const boost::uint32_t lo = boost::uint32_t(res & 0xffffffff); +        const boost::uint32_t hi = boost::uint32_t(res >> 32); +        return ((addr/4) & 0x1)? hi : lo; +    } + +    boost::uint64_t peek64(const wb_addr_type addr) +    { +        boost::mutex::scoped_lock lock(_mutex); +        UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; + +        this->send_pkt(SR_READBACK, addr/8); +        this->wait_for_ack(false); + +        this->send_pkt(0); +        return this->wait_for_ack(true); +    } + +    /******************************************************************* +     * Update methods for time +     ******************************************************************/ +    void set_time(const uhd::time_spec_t &time) +    { +        boost::mutex::scoped_lock lock(_mutex); +        _time = time; +        _use_time = _time != uhd::time_spec_t(0.0); +        if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout +    } + +    void set_tick_rate(const double rate) +    { +        boost::mutex::scoped_lock lock(_mutex); +        _tick_rate = rate; +    } + +private: + +    /******************************************************************* +     * Primary control and interaction private methods +     ******************************************************************/ +    UHD_INLINE void send_pkt(const boost::uint32_t addr, const boost::uint32_t data = 0) +    { +        managed_send_buffer::sptr buff = _ctrl_xport->get_send_buff(0.0); +        if (not buff){ +            throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); +        } +        boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + +        //load packet info +        vrt::if_packet_info_t packet_info; +        packet_info.link_type = _link_type; +        packet_info.packet_type = _packet_type; +        packet_info.num_payload_words32 = 2; +        packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); +        packet_info.packet_count = _seq_out; +        packet_info.tsf = _time.to_ticks(_tick_rate); +        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 = _use_time; +        packet_info.has_tlr = false; + +        //load header +        if (_bige) vrt::if_hdr_pack_be(pkt, packet_info); +        else vrt::if_hdr_pack_le(pkt, packet_info); + +        //load payload +        pkt[packet_info.num_header_words32+0] = (_bige)? uhd::htonx(addr) : uhd::htowx(addr); +        pkt[packet_info.num_header_words32+1] = (_bige)? uhd::htonx(data) : uhd::htowx(data); +        //UHD_MSG(status) << boost::format("0x%08x, 0x%08x\n") % addr % data; + +        //send the buffer over the interface +        _outstanding_seqs.push(_seq_out); +        buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); + +        _seq_out++; //inc seq for next call +    } + +    UHD_INLINE boost::uint64_t wait_for_ack(const bool readback) +    { +        while (readback or (_outstanding_seqs.size() >= _resp_queue_size)) +        { +            UHD_LOGV(always) << _name << " wait_for_ack: " << "readback = " << readback << " outstanding_seqs.size() " << _outstanding_seqs.size() << std::endl; + +            //get seq to ack from outstanding packets list +            UHD_ASSERT_THROW(not _outstanding_seqs.empty()); +            const size_t seq_to_ack = _outstanding_seqs.front(); +            _outstanding_seqs.pop(); + +            //parse the packet +            vrt::if_packet_info_t packet_info; +            resp_buff_type resp_buff; +            boost::uint32_t const *pkt = NULL; +            managed_recv_buffer::sptr buff; + +            //get buffer from response endpoint - or die in timeout +            if (_resp_xport) +            { +                buff = _resp_xport->get_recv_buff(_timeout); +                try +                { +                    UHD_ASSERT_THROW(bool(buff)); +                    UHD_ASSERT_THROW(bool(buff->size())); +                } +                catch(const std::exception &ex) +                { +                    throw uhd::io_error(str(boost::format("Radio ctrl (%s) no response packet - %s") % _name % ex.what())); +                } +                pkt = buff->cast<const boost::uint32_t *>(); +                packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +            } + +            //get buffer from response endpoint - or die in timeout +            else +            { +                UHD_ASSERT_THROW(_resp_queue.pop_with_timed_wait(resp_buff, _timeout)); +                pkt = resp_buff.data; +                packet_info.num_packet_words32 = sizeof(resp_buff)/sizeof(boost::uint32_t); +            } + +            //parse the buffer +            try +            { +                packet_info.link_type = _link_type; +                if (_bige) vrt::if_hdr_unpack_be(pkt, packet_info); +                else vrt::if_hdr_unpack_le(pkt, packet_info); +            } +            catch(const std::exception &ex) +            { +                UHD_MSG(error) << "Radio ctrl bad VITA packet: " << ex.what() << std::endl; +                UHD_VAR(buff->size()); +                UHD_MSG(status) << std::hex << pkt[0] << std::dec << std::endl; +                UHD_MSG(status) << std::hex << pkt[1] << std::dec << std::endl; +                UHD_MSG(status) << std::hex << pkt[2] << std::dec << std::endl; +                UHD_MSG(status) << std::hex << pkt[3] << std::dec << std::endl; +            } + +            //check the buffer +            try +            { +                UHD_ASSERT_THROW(packet_info.has_sid); +                UHD_ASSERT_THROW(packet_info.sid == boost::uint32_t((_sid >> 16) | (_sid << 16))); +                UHD_ASSERT_THROW(packet_info.packet_count == (seq_to_ack & 0xfff)); +                UHD_ASSERT_THROW(packet_info.num_payload_words32 == 2); +                UHD_ASSERT_THROW(packet_info.packet_type == _packet_type); +            } +            catch(const std::exception &ex) +            { +                throw uhd::io_error(str(boost::format("Radio ctrl (%s) packet parse error - %s") % _name % ex.what())); +            } + +            //return the readback value +            if (readback and _outstanding_seqs.empty()) +            { +                const boost::uint64_t hi = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+0]) : uhd::wtohx(pkt[packet_info.num_header_words32+0]); +                const boost::uint64_t lo = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+1]) : uhd::wtohx(pkt[packet_info.num_header_words32+1]); +                return ((hi << 32) | lo); +            } +        } +        return 0; +    } + +    void push_response(const boost::uint32_t *buff) +    { +        resp_buff_type resp_buff; +        std::memcpy(resp_buff.data, buff, sizeof(resp_buff)); +        _resp_queue.push_with_haste(resp_buff); +    } + +    void hold_task(boost::shared_ptr<void> task) +    { +        _async_task = task; +    } + +    const vrt::if_packet_info_t::link_type_t _link_type; +    const vrt::if_packet_info_t::packet_type_t _packet_type; +    const bool _bige; +    const uhd::transport::zero_copy_if::sptr _ctrl_xport; +    const uhd::transport::zero_copy_if::sptr _resp_xport; +    boost::shared_ptr<void> _async_task; +    const boost::uint32_t _sid; +    const std::string _name; +    boost::mutex _mutex; +    size_t _seq_out; +    uhd::time_spec_t _time; +    bool _use_time; +    double _tick_rate; +    double _timeout; +    std::queue<size_t> _outstanding_seqs; +    struct resp_buff_type +    { +        boost::uint32_t data[8]; +    }; +    bounded_buffer<resp_buff_type> _resp_queue; +    const size_t _resp_queue_size; +}; + + +radio_ctrl_core_3000::sptr radio_ctrl_core_3000::make( +    vrt::if_packet_info_t::link_type_t link_type, +    zero_copy_if::sptr ctrl_xport, +    zero_copy_if::sptr resp_xport, +    const boost::uint32_t sid, +    const std::string &name +) +{ +    return sptr(new radio_ctrl_core_3000_impl(link_type, ctrl_xport, resp_xport, sid, name)); +} diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.hpp b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp new file mode 100644 index 000000000..6ef484296 --- /dev/null +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp @@ -0,0 +1,59 @@ +// +// 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_LIBUHD_USRP_RADIO_CTRL_3000_HPP +#define INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + +/*! + * Provide access to peek, poke for the radio ctrl module + */ +class radio_ctrl_core_3000 : public wb_iface +{ +public: +    typedef boost::shared_ptr<radio_ctrl_core_3000> sptr; + +    //! Make a new control object +    static sptr make( +        uhd::transport::vrt::if_packet_info_t::link_type_t link_type, +        uhd::transport::zero_copy_if::sptr ctrl_xport, +        uhd::transport::zero_copy_if::sptr resp_xport, +        const boost::uint32_t sid, +        const std::string &name = "0" +    ); + +    //! Hold a ref to a task thats feeding push response +    virtual void hold_task(boost::shared_ptr<void> task) = 0; + +    //! Push a response externall (resp_xport is NULL) +    virtual void push_response(const boost::uint32_t *buff) = 0; + +    //! Set the command time that will activate +    virtual void set_time(const uhd::time_spec_t &time) = 0; + +    //! Set the tick rate (converting time into ticks) +    virtual void set_tick_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp new file mode 100644 index 000000000..36d9af5bc --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -0,0 +1,204 @@ +// +// Copyright 2011-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 "rx_dsp_core_3000.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/algorithm.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/thread/thread.hpp> //thread sleep +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> +#include <algorithm> +#include <cmath> + +#define REG_DSP_RX_FREQ       _dsp_base + 0 +#define REG_DSP_RX_SCALE_IQ   _dsp_base + 4 +#define REG_DSP_RX_DECIM      _dsp_base + 8 +#define REG_DSP_RX_MUX        _dsp_base + 12 + +#define FLAG_DSP_RX_MUX_SWAP_IQ   (1 << 0) +#define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1) + +template <class T> T ceil_log2(T num){ +    return std::ceil(std::log(num)/std::log(T(2))); +} + +using namespace uhd; + +class rx_dsp_core_3000_impl : public rx_dsp_core_3000{ +public: +    rx_dsp_core_3000_impl( +        wb_iface::sptr iface, +        const size_t dsp_base +    ): +        _iface(iface), _dsp_base(dsp_base) +    { +        //init to something so update method has reasonable defaults +        _scaling_adjustment = 1.0; +        _dsp_extra_scaling = 1.0; +        this->set_tick_rate(1.0); +    } + +    ~rx_dsp_core_3000_impl(void) +    { +        UHD_SAFE_CALL +        ( +            //NOP +        ) +    } + +    void set_mux(const std::string &mode, const bool fe_swapped){ +        static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of +            ("IQ", 0) +            ("QI", FLAG_DSP_RX_MUX_SWAP_IQ) +            ("I", FLAG_DSP_RX_MUX_REAL_MODE) +            ("Q", FLAG_DSP_RX_MUX_SWAP_IQ | FLAG_DSP_RX_MUX_REAL_MODE) +        ; +        _iface->poke32(REG_DSP_RX_MUX, mode_to_mux[mode] ^ (fe_swapped? FLAG_DSP_RX_MUX_SWAP_IQ : 0)); +    } + +    void set_tick_rate(const double rate){ +        _tick_rate = rate; +    } + +    void set_link_rate(const double rate){ +        //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s +        _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) +    } + +    uhd::meta_range_t get_host_rates(void){ +        meta_range_t range; +        for (int rate = 512; rate > 256; rate -= 4){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 256; rate > 128; rate -= 2){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        return range; +    } + +    double set_host_rate(const double rate){ +        const size_t decim_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); +        size_t decim = decim_rate; + +        //determine which half-band filters are activated +        int hb0 = 0, hb1 = 0; +        if (decim % 2 == 0){ +            hb0 = 1; +            decim /= 2; +        } +        if (decim % 2 == 0){ +            hb1 = 1; +            decim /= 2; +        } + +        _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff)); + +        if (decim > 1 and hb0 == 0 and hb1 == 0) +        { +            UHD_MSG(warning) << boost::format( +                "The requested decimation is odd; the user should expect CIC rolloff.\n" +                "Select an even decimation to ensure that a halfband filter is enabled.\n" +                "decimation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" +            ) % decim_rate % (_tick_rate/1e6) % (rate/1e6); +        } + +        // Calculate CIC decimation (i.e., without halfband decimators) +        // Calculate closest multiplier constant to reverse gain absent scale multipliers +        const double rate_pow = std::pow(double(decim & 0xff), 4); +        _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); +        this->update_scalar(); + +        return _tick_rate/decim_rate; +    } + +    void update_scalar(void){ +        const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0); +        const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor; +        const boost::int32_t actual_scalar = boost::math::iround(target_scalar); +        _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small +        _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar); +    } + +    double get_scaling_adjustment(void){ +        return _fxpt_scalar_correction*_host_extra_scaling/32767.; +    } + +    double set_freq(const double freq_){ +        //correct for outside of rate (wrap around) +        double freq = std::fmod(freq_, _tick_rate); +        if (std::abs(freq) > _tick_rate/2.0) +            freq -= boost::math::sign(freq)*_tick_rate; + +        //calculate the freq register word (signed) +        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); +        static const double scale_factor = std::pow(2.0, 32); +        const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); + +        //update the actual frequency +        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; + +        _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word)); + +        return actual_freq; +    } + +    uhd::meta_range_t get_freq_range(void){ +        return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32)); +    } + +    void setup(const uhd::stream_args_t &stream_args){ + +        //unsigned format_word = 0; +        if (stream_args.otw_format == "sc16"){ +            //format_word = 0; +            _dsp_extra_scaling = 1.0; +            _host_extra_scaling = 1.0; +        } +        /* +        else if (stream_args.otw_format == "sc8"){ +            format_word = (1 << 0); +            double peak = stream_args.args.cast<double>("peak", 1.0); +            peak = std::max(peak, 1.0/256); +            _host_extra_scaling = peak*256; +            _dsp_extra_scaling = peak*256; +        } +        */ +        else throw uhd::value_error("USRP RX cannot handle requested wire format: " + stream_args.otw_format); + +        _host_extra_scaling *= stream_args.args.cast<double>("fullscale", 1.0); + +        this->update_scalar(); +    } + +private: +    wb_iface::sptr _iface; +    const size_t _dsp_base; +    double _tick_rate, _link_rate; +    double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; +}; + +rx_dsp_core_3000::sptr rx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base) +{ +    return sptr(new rx_dsp_core_3000_impl(iface, dsp_base)); +} diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp new file mode 100644 index 000000000..23b12b9b7 --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -0,0 +1,58 @@ +// +// Copyright 2011-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_LIBUHD_USRP_RX_DSP_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include "wb_iface.hpp" +#include <string> + +class rx_dsp_core_3000 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<rx_dsp_core_3000> sptr; + +    static sptr make( +        wb_iface::sptr iface, +        const size_t dsp_base +    ); + +    virtual void set_mux(const std::string &mode, const bool fe_swapped = false) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual double get_scaling_adjustment(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_vita_core_3000.cpp b/host/lib/usrp/cores/rx_vita_core_3000.cpp new file mode 100644 index 000000000..d6c3250a5 --- /dev/null +++ b/host/lib/usrp/cores/rx_vita_core_3000.cpp @@ -0,0 +1,153 @@ +// +// 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 "rx_vita_core_3000.hpp" +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/tuple/tuple.hpp> + +#define REG_FRAMER_MAXLEN    _base + 4*4 + 0 +#define REG_FRAMER_SID       _base + 4*4 + 4 + +#define REG_CTRL_CMD           _base + 0 +#define REG_CTRL_TIME_HI       _base + 4 +#define REG_CTRL_TIME_LO       _base + 8 + +#define REG_FC_WINDOW       _base + 6*4 + 0 +#define REG_FC_ENABLE       _base + 6*4 + 4 + +using namespace uhd; + +struct rx_vita_core_3000_impl : rx_vita_core_3000 +{ +    rx_vita_core_3000_impl( +        wb_iface::sptr iface, +        const size_t base +    ): +        _iface(iface), +        _base(base), +        _continuous_streaming(false), +        _is_setup(false) +    { +        this->set_tick_rate(1); //init to non zero +        this->set_nsamps_per_packet(100); //init to non zero +        this->clear(); +    } + +    ~rx_vita_core_3000_impl(void) +    { +        UHD_SAFE_CALL +        ( +            this->clear(); +        ) +    } + +    void configure_flow_control(const size_t window_size) +    { +        _iface->poke32(REG_FC_WINDOW, window_size-1); +        _iface->poke32(REG_FC_ENABLE, window_size?1:0); +    } + +    void clear(void) +    { +        this->configure_flow_control(0); //disable fc +    } + +    void set_nsamps_per_packet(const size_t nsamps) +    { +        _iface->poke32(REG_FRAMER_MAXLEN, nsamps); +    } + +    void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) +    { +        if (not _is_setup) +        { +            UHD_MSG(warning) << "rx vita core 3000 issue stream command - not setup yet!"; +            return; +        } +        UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff); +        _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + +        //setup the mode to instruction flags +        typedef boost::tuple<bool, bool, bool, bool> inst_t; +        static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of +                                                                //reload, chain, samps, stop +            (stream_cmd_t::STREAM_MODE_START_CONTINUOUS,   inst_t(true,  true,  false, false)) +            (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS,    inst_t(false, false, false, true)) +            (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true,  false)) +            (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true,  true,  false)) +        ; + +        //setup the instruction flag values +        bool inst_reload, inst_chain, inst_samps, inst_stop; +        boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode]; + +        //calculate the word from flags and length +        boost::uint32_t cmd_word = 0; +        cmd_word |= boost::uint32_t((stream_cmd.stream_now)? 1 : 0) << 31; +        cmd_word |= boost::uint32_t((inst_chain)?            1 : 0) << 30; +        cmd_word |= boost::uint32_t((inst_reload)?           1 : 0) << 29; +        cmd_word |= boost::uint32_t((inst_stop)?             1 : 0) << 28; +        cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1); + +        //issue the stream command +        _iface->poke32(REG_CTRL_CMD, cmd_word); +        const boost::uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(_tick_rate); +        _iface->poke32(REG_CTRL_TIME_HI, boost::uint32_t(ticks >> 32)); +        _iface->poke32(REG_CTRL_TIME_LO, boost::uint32_t(ticks >> 0)); //latches the command +    } + +    void set_tick_rate(const double rate) +    { +        _tick_rate = rate; +    } + +    void set_sid(const boost::uint32_t sid) +    { +        _iface->poke32(REG_FRAMER_SID, sid); +    } + +    void handle_overflow(void) +    { +        if (_continuous_streaming) this->issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    } + +    void setup(const uhd::stream_args_t &) +    { +        _is_setup = true; +    } + +    bool in_continuous_streaming_mode(void) +    { +        return _continuous_streaming; +    } + +    wb_iface::sptr _iface; +    const size_t _base; +    double _tick_rate; +    bool _continuous_streaming; +    bool _is_setup; +}; + +rx_vita_core_3000::sptr rx_vita_core_3000::make( +    wb_iface::sptr iface, +    const size_t base +) +{ +    return rx_vita_core_3000::sptr(new rx_vita_core_3000_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/rx_vita_core_3000.hpp b/host/lib/usrp/cores/rx_vita_core_3000.hpp new file mode 100644 index 000000000..b011a7388 --- /dev/null +++ b/host/lib/usrp/cores/rx_vita_core_3000.hpp @@ -0,0 +1,59 @@ +// +// 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_LIBUHD_USRP_RX_VITA_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include "wb_iface.hpp" +#include <string> + +class rx_vita_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<rx_vita_core_3000> sptr; + +    static sptr make( +        wb_iface::sptr iface, +        const size_t base +    ); + +    virtual void clear(void) = 0; + +    virtual void set_nsamps_per_packet(const size_t nsamps) = 0; + +    virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_sid(const boost::uint32_t sid) = 0; + +    virtual void handle_overflow(void) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void configure_flow_control(const size_t window_size) = 0; + +    virtual bool in_continuous_streaming_mode(void) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/spi_core_3000.cpp b/host/lib/usrp/cores/spi_core_3000.cpp new file mode 100644 index 000000000..b7503064a --- /dev/null +++ b/host/lib/usrp/cores/spi_core_3000.cpp @@ -0,0 +1,96 @@ +// +// 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 "spi_core_3000.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep + +#define SPI_DIV _base + 0 +#define SPI_CTRL _base + 4 +#define SPI_DATA _base + 8 + +using namespace uhd; + +class spi_core_3000_impl : public spi_core_3000 +{ +public: +    spi_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback): +        _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0) +    { +        this->set_divider(30); +    } + +    boost::uint32_t transact_spi( +        int which_slave, +        const spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits, +        bool readback +    ){ +        boost::mutex::scoped_lock lock(_mutex); + +        //load control word +        boost::uint32_t ctrl_word = 0; +        ctrl_word |= ((which_slave & 0xffffff) << 0); +        ctrl_word |= ((num_bits & 0x3f) << 24); +        if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); +        if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + +        //load data word (must be in upper bits) +        const boost::uint32_t data_out = data << (32 - num_bits); + +        //conditionally send control word +        if (_ctrl_word_cache != ctrl_word) +        { +            _iface->poke32(SPI_DIV, _div); +            _iface->poke32(SPI_CTRL, ctrl_word); +            _ctrl_word_cache = ctrl_word; +        } + +        //send data word +        _iface->poke32(SPI_DATA, data_out); + +        //conditional readback +        if (readback) +        { +            return _iface->peek32(_readback); +        } + +        return 0; +    } + +    void set_divider(const double div) +    { +        _div = size_t((div/2) - 0.5); +    } + +private: + +    wb_iface::sptr _iface; +    const size_t _base; +    const size_t _readback; +    boost::uint32_t _ctrl_word_cache; +    boost::mutex _mutex; +    size_t _div; +}; + +spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback) +{ +    return sptr(new spi_core_3000_impl(iface, base, readback)); +} + diff --git a/host/lib/usrp/cores/spi_core_3000.hpp b/host/lib/usrp/cores/spi_core_3000.hpp new file mode 100644 index 000000000..995ad59db --- /dev/null +++ b/host/lib/usrp/cores/spi_core_3000.hpp @@ -0,0 +1,39 @@ +// +// 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_LIBUHD_USRP_SPI_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class spi_core_3000 : boost::noncopyable, public uhd::spi_iface +{ +public: +    typedef boost::shared_ptr<spi_core_3000> sptr; + +    //! makes a new spi core from iface and slave base +    static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback); + +    //! Set the spi clock divider to something usable +    virtual void set_divider(const double div) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/time_core_3000.cpp b/host/lib/usrp/cores/time_core_3000.cpp new file mode 100644 index 000000000..8b5ad927a --- /dev/null +++ b/host/lib/usrp/cores/time_core_3000.cpp @@ -0,0 +1,136 @@ +// +// 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 "time_core_3000.hpp" +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> + +#define REG_TIME_HI       _base + 0 +#define REG_TIME_LO       _base + 4 +#define REG_TIME_CTRL     _base + 8 + +#define CTRL_LATCH_TIME_PPS (1 << 2) +#define CTRL_LATCH_TIME_NOW (1 << 1) +#define CTRL_SELECT_EXT_PPS (1 << 0) + +using namespace uhd; + +struct time_core_3000_impl : time_core_3000 +{ +    time_core_3000_impl( +        wb_iface::sptr iface, const size_t base, +        const readback_bases_type &readback_bases +    ): +        _iface(iface), +        _base(base), +        _readback_bases(readback_bases) +    { +        this->set_tick_rate(1); //init to non zero +        this->set_time_source("internal"); +    } + +    ~time_core_3000_impl(void) +    { +        UHD_SAFE_CALL +        ( +            //NOP +        ) +    } + +    void set_tick_rate(const double rate) +    { +        _tick_rate = rate; +    } + +    void self_test(void) +    { +        const size_t sleep_millis = 100; +        UHD_MSG(status) << "Performing timer loopback test... " << std::flush; +        const time_spec_t time0 = this->get_time_now(); +        boost::this_thread::sleep(boost::posix_time::milliseconds(sleep_millis)); +        const time_spec_t time1 = this->get_time_now(); +        const double approx_secs = (time1 - time0).get_real_secs(); +        const bool test_fail = (approx_secs > 0.15) or (approx_secs < 0.05); +        UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; + +        //useful warning for debugging actual rate +        const size_t ticks_elapsed = _tick_rate*approx_secs; +        const size_t appox_rate = ticks_elapsed/(sleep_millis/1e3); +        if (test_fail) UHD_MSG(warning) +            << "Expecting clock rate: " << (_tick_rate/1e6) << " MHz\n" +            << "Appoximate clock rate: " << (appox_rate/1e6) << " MHz\n" +        << std::endl; +    } + +    uhd::time_spec_t get_time_now(void) +    { +        const boost::uint64_t ticks = _iface->peek64(_readback_bases.rb_now); +        return time_spec_t::from_ticks(ticks, _tick_rate); +    } + +    uhd::time_spec_t get_time_last_pps(void) +    { +        const boost::uint64_t ticks = _iface->peek64(_readback_bases.rb_pps); +        return time_spec_t::from_ticks(ticks, _tick_rate); +    } + +    void set_time_now(const uhd::time_spec_t &time) +    { +        const boost::uint64_t ticks = time.to_ticks(_tick_rate); +        _iface->poke32(REG_TIME_HI, boost::uint32_t(ticks >> 32)); +        _iface->poke32(REG_TIME_LO, boost::uint32_t(ticks >> 0)); +        _iface->poke32(REG_TIME_CTRL, CTRL_LATCH_TIME_NOW); +    } + +    void set_time_next_pps(const uhd::time_spec_t &time) +    { +        const boost::uint64_t ticks = time.to_ticks(_tick_rate); +        _iface->poke32(REG_TIME_HI, boost::uint32_t(ticks >> 32)); +        _iface->poke32(REG_TIME_LO, boost::uint32_t(ticks >> 0)); +        _iface->poke32(REG_TIME_CTRL, (_use_ext_pps?CTRL_SELECT_EXT_PPS:0) | CTRL_LATCH_TIME_PPS); +    } + +    void set_time_source(const std::string &source) +    { +        if (source == "internal") _use_ext_pps = false; +        else if (source == "external") _use_ext_pps = true; +        else throw uhd::runtime_error("time_core_3000: set_time_source unknown source: " + source); +    } + +    std::vector<std::string> get_time_sources(void) +    { +        std::vector<std::string> sources; +        sources.push_back("internal"); +        sources.push_back("external"); +        return sources; +    } + +    wb_iface::sptr _iface; +    const size_t _base; +    const readback_bases_type _readback_bases; +    double _tick_rate; +    bool _use_ext_pps; +}; + +time_core_3000::sptr time_core_3000::make( +    wb_iface::sptr iface, const size_t base, +    const readback_bases_type &readback_bases +) +{ +    return time_core_3000::sptr(new time_core_3000_impl(iface, base, readback_bases)); +} diff --git a/host/lib/usrp/cores/time_core_3000.hpp b/host/lib/usrp/cores/time_core_3000.hpp new file mode 100644 index 000000000..f2b684ccf --- /dev/null +++ b/host/lib/usrp/cores/time_core_3000.hpp @@ -0,0 +1,64 @@ +// +// 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_LIBUHD_USRP_TIME_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" +#include <string> +#include <vector> + +class time_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<time_core_3000> sptr; + +    struct readback_bases_type +    { +        size_t rb_now; +        size_t rb_pps; +    }; + +    //! makes a new time core from iface and slave base +    static sptr make( +        wb_iface::sptr iface, const size_t base, +        const readback_bases_type &readback_bases +    ); + +    virtual void self_test(void) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual uhd::time_spec_t get_time_now(void) = 0; + +    virtual uhd::time_spec_t get_time_last_pps(void) = 0; + +    virtual void set_time_now(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_source(const std::string &source) = 0; + +    virtual std::vector<std::string> get_time_sources(void) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp new file mode 100644 index 000000000..ff4392a13 --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -0,0 +1,181 @@ +// +// Copyright 2011-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 "tx_dsp_core_3000.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/algorithm.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <algorithm> +#include <cmath> + +#define REG_DSP_TX_FREQ          _dsp_base + 0 +#define REG_DSP_TX_SCALE_IQ      _dsp_base + 4 +#define REG_DSP_TX_INTERP        _dsp_base + 8 + +template <class T> T ceil_log2(T num){ +    return std::ceil(std::log(num)/std::log(T(2))); +} + +using namespace uhd; + +class tx_dsp_core_3000_impl : public tx_dsp_core_3000{ +public: +    tx_dsp_core_3000_impl( +        wb_iface::sptr iface, +        const size_t dsp_base +    ): +        _iface(iface), _dsp_base(dsp_base) +    { +        //init to something so update method has reasonable defaults +        _scaling_adjustment = 1.0; +        _dsp_extra_scaling = 1.0; +        this->set_tick_rate(1.0); +    } + +    void set_tick_rate(const double rate){ +        _tick_rate = rate; +    } + +    void set_link_rate(const double rate){ +        //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s +        _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) +    } + +    uhd::meta_range_t get_host_rates(void){ +        meta_range_t range; +        for (int rate = 512; rate > 256; rate -= 4){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 256; rate > 128; rate -= 2){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        return range; +    } + +    double set_host_rate(const double rate){ +        const size_t interp_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); +        size_t interp = interp_rate; + +        //determine which half-band filters are activated +        int hb0 = 0, hb1 = 0; +        if (interp % 2 == 0){ +            hb0 = 1; +            interp /= 2; +        } +        if (interp % 2 == 0){ +            hb1 = 1; +            interp /= 2; +        } + +        _iface->poke32(REG_DSP_TX_INTERP, (hb1 << 9) | (hb0 << 8) | (interp & 0xff)); + +        if (interp > 1 and hb0 == 0 and hb1 == 0) +        { +            UHD_MSG(warning) << boost::format( +                "The requested interpolation is odd; the user should expect CIC rolloff.\n" +                "Select an even interpolation to ensure that a halfband filter is enabled.\n" +                "interpolation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" +            ) % interp_rate % (_tick_rate/1e6) % (rate/1e6); +        } + +        // Calculate CIC interpolation (i.e., without halfband interpolators) +        // Calculate closest multiplier constant to reverse gain absent scale multipliers +        const double rate_pow = std::pow(double(interp & 0xff), 3); +        _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); +        this->update_scalar(); + +        return _tick_rate/interp_rate; +    } + +    void update_scalar(void){ +        const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0); +        const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor; +        const boost::int32_t actual_scalar = boost::math::iround(target_scalar); +        _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small +        _iface->poke32(REG_DSP_TX_SCALE_IQ, actual_scalar); +    } + +    double get_scaling_adjustment(void){ +        return _fxpt_scalar_correction*_host_extra_scaling*32767.; +    } + +    double set_freq(const double freq_){ +        //correct for outside of rate (wrap around) +        double freq = std::fmod(freq_, _tick_rate); +        if (std::abs(freq) > _tick_rate/2.0) +            freq -= boost::math::sign(freq)*_tick_rate; + +        //calculate the freq register word (signed) +        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); +        static const double scale_factor = std::pow(2.0, 32); +        const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); + +        //update the actual frequency +        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; + +        _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word)); + +        return actual_freq; +    } + +    uhd::meta_range_t get_freq_range(void){ +        return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32)); +    } + +    void setup(const uhd::stream_args_t &stream_args){ + +        //unsigned format_word = 0; +        if (stream_args.otw_format == "sc16"){ +            //format_word = 0; +            _dsp_extra_scaling = 1.0; +            _host_extra_scaling = 1.0; +        } +        /* +        else if (stream_args.otw_format == "sc8"){ +            format_word = (1 << 0); +            double peak = stream_args.args.cast<double>("peak", 1.0); +            peak = std::max(peak, 1.0/256); +            _host_extra_scaling = 1.0/peak/256; +            _dsp_extra_scaling = 1.0/peak; +        } +        else throw uhd::value_error("USRP TX cannot handle requested wire format: " + stream_args.otw_format); +        */ + +        _host_extra_scaling /= stream_args.args.cast<double>("fullscale", 1.0); + +        this->update_scalar(); +    } + +private: +    wb_iface::sptr _iface; +    const size_t _dsp_base; +    double _tick_rate, _link_rate; +    double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; +}; + +tx_dsp_core_3000::sptr tx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base) +{ +    return sptr(new tx_dsp_core_3000_impl(iface, dsp_base)); +} diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp new file mode 100644 index 000000000..eb5ffaf0f --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp @@ -0,0 +1,54 @@ +// +// Copyright 2011-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_LIBUHD_USRP_TX_DSP_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class tx_dsp_core_3000 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<tx_dsp_core_3000> sptr; + +    static sptr make( +        wb_iface::sptr iface, +        const size_t dsp_base +    ); + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual double get_scaling_adjustment(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_vita_core_3000.cpp b/host/lib/usrp/cores/tx_vita_core_3000.cpp new file mode 100644 index 000000000..38eb6afb5 --- /dev/null +++ b/host/lib/usrp/cores/tx_vita_core_3000.cpp @@ -0,0 +1,107 @@ +// +// 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 "tx_vita_core_3000.hpp" +#include <uhd/utils/safe_call.hpp> + +#define REG_CTRL_ERROR_POLICY           _base + 0 +#define REG_DEFRAMER_CYCLE_FC_UPS       _base + 2*4 + 0 +#define REG_DEFRAMER_PACKET_FC_UPS      _base + 2*4 + 4 + +using namespace uhd; + +struct tx_vita_core_3000_impl : tx_vita_core_3000 +{ +    tx_vita_core_3000_impl( +        wb_iface::sptr iface, +        const size_t base +    ): +        _iface(iface), +        _base(base) +    { +        this->set_tick_rate(1); //init to non zero +        this->set_underflow_policy("next_packet"); +        this->clear(); +    } + +    ~tx_vita_core_3000_impl(void) +    { +        UHD_SAFE_CALL +        ( +            this->clear(); +        ) +    } + +    void clear(void) +    { +        this->configure_flow_control(0, 0); +        this->set_underflow_policy(_policy); //clears the seq +    } + +    void set_tick_rate(const double rate) +    { +        _tick_rate = rate; +    } + +    void set_underflow_policy(const std::string &policy) +    { +        if (policy == "next_packet") +        { +            _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 1)); +        } +        else if (policy == "next_burst") +        { +            _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 2)); +        } +        else if (policy == "wait") +        { +            _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 0)); +        } +        else throw uhd::value_error("USRP TX cannot handle requested underflow policy: " + policy); +        _policy = policy; +    } + +    void setup(const uhd::stream_args_t &stream_args) +    { +        if (stream_args.args.has_key("underflow_policy")) +        { +            this->set_underflow_policy(stream_args.args["underflow_policy"]); +        } +    } + +    void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) +    { +        if (cycs_per_up == 0) _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, 0); +        else _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, (1 << 31) | ((cycs_per_up) & 0xffffff)); + +        if (pkts_per_up == 0) _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, 0); +        else _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, (1 << 31) | ((pkts_per_up) & 0xffff)); +    } + +    wb_iface::sptr _iface; +    const size_t _base; +    double _tick_rate; +    std::string _policy; +}; + +tx_vita_core_3000::sptr tx_vita_core_3000::make( +    wb_iface::sptr iface, +    const size_t base +) +{ +    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp new file mode 100644 index 000000000..2070936ce --- /dev/null +++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp @@ -0,0 +1,49 @@ +// +// 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_LIBUHD_USRP_TX_VITA_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include "wb_iface.hpp" +#include <string> + +class tx_vita_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<tx_vita_core_3000> sptr; + +    static sptr make( +        wb_iface::sptr iface, +        const size_t base +    ); + +    virtual void clear(void) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/wb_iface.cpp b/host/lib/usrp/cores/wb_iface.cpp new file mode 100644 index 000000000..9aa6d18d4 --- /dev/null +++ b/host/lib/usrp/cores/wb_iface.cpp @@ -0,0 +1,51 @@ +// +// 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 "wb_iface.hpp" +#include <uhd/exception.hpp> + +using namespace uhd; + +void wb_iface::poke64(const wb_iface::wb_addr_type, const boost::uint64_t) +{ +    throw uhd::not_implemented_error("poke64 not implemented"); +} + +boost::uint64_t wb_iface::peek64(const wb_iface::wb_addr_type) +{ +    throw uhd::not_implemented_error("peek64 not implemented"); +} + +void wb_iface::poke32(const wb_iface::wb_addr_type, const boost::uint32_t) +{ +    throw uhd::not_implemented_error("poke32 not implemented"); +} + +boost::uint32_t wb_iface::peek32(const wb_iface::wb_addr_type) +{ +    throw uhd::not_implemented_error("peek32 not implemented"); +} + +void wb_iface::poke16(const wb_iface::wb_addr_type, const boost::uint16_t) +{ +    throw uhd::not_implemented_error("poke16 not implemented"); +} + +boost::uint16_t wb_iface::peek16(const wb_iface::wb_addr_type) +{ +    throw uhd::not_implemented_error("peek16 not implemented"); +} diff --git a/host/lib/usrp/cores/wb_iface.hpp b/host/lib/usrp/cores/wb_iface.hpp index 982594b21..197788180 100644 --- a/host/lib/usrp/cores/wb_iface.hpp +++ b/host/lib/usrp/cores/wb_iface.hpp @@ -1,5 +1,5 @@  // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -22,38 +22,53 @@  #include <boost/cstdint.hpp>  #include <boost/shared_ptr.hpp> -class wb_iface{ +class /*UHD_API*/ wb_iface +{  public:      typedef boost::shared_ptr<wb_iface> sptr;      typedef boost::uint32_t wb_addr_type;      /*! +     * Write a register (64 bits) +     * \param addr the address +     * \param data the 64bit data +     */ +    virtual void poke64(const wb_addr_type addr, const boost::uint64_t data); + +    /*! +     * Read a register (64 bits) +     * \param addr the address +     * \return the 64bit data +     */ +    virtual boost::uint64_t peek64(const wb_addr_type addr); + +    /*!       * Write a register (32 bits)       * \param addr the address       * \param data the 32bit data       */ -    virtual void poke32(wb_addr_type addr, boost::uint32_t data) = 0; +    virtual void poke32(const wb_addr_type addr, const boost::uint32_t data);      /*!       * Read a register (32 bits)       * \param addr the address       * \return the 32bit data       */ -    virtual boost::uint32_t peek32(wb_addr_type addr) = 0; +    virtual boost::uint32_t peek32(const wb_addr_type addr);      /*!       * Write a register (16 bits)       * \param addr the address       * \param data the 16bit data       */ -    virtual void poke16(wb_addr_type addr, boost::uint16_t data) = 0; +    virtual void poke16(const wb_addr_type addr, const boost::uint16_t data);      /*!       * Read a register (16 bits)       * \param addr the address       * \return the 16bit data       */ -    virtual boost::uint16_t peek16(wb_addr_type addr) = 0; +    virtual boost::uint16_t peek16(const wb_addr_type addr);  };  | 
