diff options
Diffstat (limited to 'host/lib/usrp/cores')
22 files changed, 1462 insertions, 250 deletions
diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index f28ae040f..404fc6137 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -37,7 +37,11 @@ LIBUHD_APPEND_SOURCES(      ${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}/dsp_core_utils.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 +    ${CMAKE_CURRENT_SOURCE_DIR}/gpio_atr_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_3000.cpp  ) diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.cpp b/host/lib/usrp/cores/dma_fifo_core_3000.cpp new file mode 100644 index 000000000..1a9d5dd5c --- /dev/null +++ b/host/lib/usrp/cores/dma_fifo_core_3000.cpp @@ -0,0 +1,397 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "dma_fifo_core_3000.hpp" +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <uhd/utils/soft_register.hpp> +#include <uhd/utils/msg.hpp> + +using namespace uhd; + +#define SR_DRAM_BIST_BASE 16 + +dma_fifo_core_3000::~dma_fifo_core_3000(void) { +    /* NOP */ +} + +class dma_fifo_core_3000_impl : public dma_fifo_core_3000 +{ +protected: +    class rb_addr_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ADDR, /*width*/ 3, /*shift*/ 0);  //[2:0] + +        static const boost::uint32_t RB_FIFO_STATUS     = 0; +        static const boost::uint32_t RB_BIST_STATUS     = 1; +        static const boost::uint32_t RB_BIST_XFER_CNT   = 2; +        static const boost::uint32_t RB_BIST_CYC_CNT    = 3; + +        rb_addr_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 0) +        { +            //Initial values +            set(ADDR, RB_FIFO_STATUS); +        } +    }; + +    class fifo_ctrl_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(CLEAR_FIFO,           /*width*/  1, /*shift*/  0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_EN,       /*width*/  1, /*shift*/  1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(BURST_TIMEOUT,        /*width*/ 12, /*shift*/  4);  //[15:4] +        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_THRESH,   /*width*/ 16, /*shift*/ 16);  //[31:16] + +        fifo_ctrl_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 4) +        { +            //Initial values +            set(CLEAR_FIFO, 1); +            set(RD_SUPPRESS_EN, 0); +            set(BURST_TIMEOUT, 256); +            set(RD_SUPPRESS_THRESH, 0); +        } +    }; + +    class base_addr_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(BASE_ADDR, /*width*/ 30, /*shift*/ 0);  //[29:0] + +        base_addr_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 8) +        { +            //Initial values +            set(BASE_ADDR, 0x00000000); +        } +    }; + +    class addr_mask_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ADDR_MASK, /*width*/ 30, /*shift*/ 0);  //[29:0] + +        addr_mask_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 12) +        { +            //Initial values +            set(ADDR_MASK, 0xFF000000); +        } +    }; + +    class bist_ctrl_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(GO,               /*width*/ 1, /*shift*/ 0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(CONTINUOUS_MODE,  /*width*/ 1, /*shift*/ 1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(TEST_PATT,        /*width*/ 2, /*shift*/ 4);  //[5:4] + +        static const boost::uint32_t TEST_PATT_ZERO_ONE     = 0; +        static const boost::uint32_t TEST_PATT_CHECKERBOARD = 1; +        static const boost::uint32_t TEST_PATT_COUNT        = 2; +        static const boost::uint32_t TEST_PATT_COUNT_INV    = 3; + +        bist_ctrl_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 16) +        { +            //Initial values +            set(GO, 0); +            set(CONTINUOUS_MODE, 0); +            set(TEST_PATT, TEST_PATT_ZERO_ONE); +        } +    }; + +    class bist_cfg_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKTS,         /*width*/ 18, /*shift*/ 0);  //[17:0] +        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKT_SIZE,     /*width*/ 13, /*shift*/ 18); //[30:18] +        UHD_DEFINE_SOFT_REG_FIELD(PKT_SIZE_RAMP,    /*width*/ 1,  /*shift*/ 31); //[31] + +        bist_cfg_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 20) +        { +            //Initial values +            set(MAX_PKTS, 0); +            set(MAX_PKT_SIZE, 0); +            set(PKT_SIZE_RAMP, 0); +        } +    }; + +    class bist_delay_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(TX_PKT_DELAY,     /*width*/ 16, /*shift*/ 0);  //[15:0] +        UHD_DEFINE_SOFT_REG_FIELD(RX_SAMP_DELAY,    /*width*/  8, /*shift*/ 16); //[23:16] + +        bist_delay_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 24) +        { +            //Initial values +            set(TX_PKT_DELAY, 0); +            set(RX_SAMP_DELAY, 0); +        } +    }; + +    class bist_sid_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SID,     /*width*/ 32, /*shift*/ 0);  //[31:0] + +        bist_sid_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 28) +        { +            //Initial values +            set(SID, 0); +        } +    }; + +public: +    class fifo_readback { +    public: +        fifo_readback(wb_iface::sptr iface,  const size_t base, const size_t rb_addr) : +            _iface(iface), _addr_reg(base), _rb_addr(rb_addr) +        { +            _addr_reg.initialize(*iface, true); +        } + +        bool is_fifo_instantiated() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x80000000; +        } + +        boost::uint32_t get_occupied_cnt() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x7FFFFFF; +        } + +        boost::uint32_t is_fifo_busy() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x40000000; +        } + +        struct bist_status_t { +            bool running; +            bool finished; +            boost::uint8_t error; +        }; + +        bist_status_t get_bist_status() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); +            boost::uint32_t st32 = _iface->peek32(_rb_addr) & 0xF; +            bist_status_t status; +            status.running = st32 & 0x1; +            status.finished = st32 & 0x2; +            status.error = static_cast<boost::uint8_t>((st32>>2) & 0x3); +            return status; +        } + +        bool is_ext_bist_supported() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); +            return _iface->peek32(_rb_addr) & 0x80000000; +        } + +        double get_xfer_ratio() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            boost::uint32_t xfer_cnt = 0, cyc_cnt = 0; +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_XFER_CNT); +            xfer_cnt = _iface->peek32(_rb_addr); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_CYC_CNT); +            cyc_cnt = _iface->peek32(_rb_addr); +            return (static_cast<double>(xfer_cnt)/cyc_cnt); +        } + +    private: +        wb_iface::sptr  _iface; +        rb_addr_reg_t   _addr_reg; +        const size_t    _rb_addr; +        boost::mutex    _mutex; +    }; + +public: +    dma_fifo_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback): +        _iface(iface), _base(base), _fifo_readback(iface, base, readback), +        _fifo_ctrl_reg(base), _base_addr_reg(base), _addr_mask_reg(base), +        _bist_ctrl_reg(base), _bist_cfg_reg(base), _bist_delay_reg(base), _bist_sid_reg(base) +    { +        _fifo_ctrl_reg.initialize(*iface, true); +        _base_addr_reg.initialize(*iface, true); +        _addr_mask_reg.initialize(*iface, true); +        _bist_ctrl_reg.initialize(*iface, true); +        _bist_cfg_reg.initialize(*iface, true); +        _has_ext_bist = _fifo_readback.is_ext_bist_supported(); +        if (_has_ext_bist) { +            _bist_delay_reg.initialize(*iface, true); +            _bist_sid_reg.initialize(*iface, true); +        } +        flush(); +    } + +    virtual void flush() { +        //Clear the FIFO and hold it in that state +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); +        //Re-arm the FIFO +        _wait_for_fifo_empty(); +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 0); +    } + +    virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) { +        //Validate parameters +        if (size < 8192) throw uhd::runtime_error("DMA FIFO must be larger than 8KiB"); +        boost::uint32_t size_mask = size - 1; +        if (size & size_mask) throw uhd::runtime_error("DMA FIFO size must be a power of 2"); + +        //Clear the FIFO and hold it in that state +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); +        //Write base address and mask +        _base_addr_reg.write(base_addr_reg_t::BASE_ADDR, base_addr); +        _addr_mask_reg.write(addr_mask_reg_t::ADDR_MASK, ~size_mask); + +        //Re-arm the FIFO +        flush(); +    } + +    virtual boost::uint32_t get_bytes_occupied() { +        return _fifo_readback.get_occupied_cnt() * 8; +    } + +    virtual bool ext_bist_supported() { +        return _fifo_readback.is_ext_bist_supported(); +    } + +    virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) { +        return run_ext_bist(finite, 0, 0, 0, timeout_ms); +    } + +    virtual boost::uint8_t run_ext_bist( +        bool finite, +        boost::uint32_t rx_samp_delay, +        boost::uint32_t tx_pkt_delay, +        boost::uint32_t sid, +        boost::uint32_t timeout_ms = 500 +    ) { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        _wait_for_bist_done(timeout_ms, true);          //Stop previous BIST and wait (if running) +        _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0);   //Reset + +        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKTS, (2^18)-1); +        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKT_SIZE, 8000); +        _bist_cfg_reg.set(bist_cfg_reg_t::PKT_SIZE_RAMP, 0); +        _bist_cfg_reg.flush(); + +        if (_has_ext_bist) { +            _bist_delay_reg.set(bist_delay_reg_t::RX_SAMP_DELAY, rx_samp_delay); +            _bist_delay_reg.set(bist_delay_reg_t::TX_PKT_DELAY, tx_pkt_delay); +            _bist_delay_reg.flush(); + +            _bist_sid_reg.write(bist_sid_reg_t::SID, sid); +        } else { +            if (rx_samp_delay != 0 || tx_pkt_delay != 0 || sid != 0) { +                throw uhd::not_implemented_error( +                    "dma_fifo_core_3000: Runtime delay and SID support only available on FPGA images with extended BIST enabled"); +            } +        } + +        _bist_ctrl_reg.set(bist_ctrl_reg_t::TEST_PATT, bist_ctrl_reg_t::TEST_PATT_COUNT); +        _bist_ctrl_reg.set(bist_ctrl_reg_t::CONTINUOUS_MODE, finite ? 0 : 1); +        _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 1); + +        if (!finite) { +            boost::this_thread::sleep(boost::posix_time::milliseconds(timeout_ms)); +        } + +        _wait_for_bist_done(timeout_ms, !finite); +        if (!_fifo_readback.get_bist_status().finished) { +            throw uhd::runtime_error("dma_fifo_core_3000: DRAM BIST state machine is in a bad state."); +        } + +        return _fifo_readback.get_bist_status().error; +    } + +    virtual double get_bist_throughput(double fifo_clock_rate) { +        if (_has_ext_bist) { +            _wait_for_bist_done(1000); +            static const double BYTES_PER_CYC = 8; +            return _fifo_readback.get_xfer_ratio() * fifo_clock_rate * BYTES_PER_CYC; +        } else { +            throw uhd::not_implemented_error( +                "dma_fifo_core_3000: Throughput counter only available on FPGA images with extended BIST enabled"); +        } +    } + +private: +    void _wait_for_fifo_empty() +    { +        boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +        boost::posix_time::time_duration elapsed; + +        while (_fifo_readback.is_fifo_busy()) { +            boost::this_thread::sleep(boost::posix_time::microsec(1000)); +            elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +            if (elapsed.total_milliseconds() > 100) break; +        } +    } + +    void _wait_for_bist_done(boost::uint32_t timeout_ms, bool force_stop = false) +    { +        boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +        boost::posix_time::time_duration elapsed; + +        while (_fifo_readback.get_bist_status().running) { +            if (force_stop) { +                _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0); +                force_stop = false; +            } +            boost::this_thread::sleep(boost::posix_time::microsec(1000)); +            elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +            if (elapsed.total_milliseconds() > timeout_ms) break; +        } +    } + +private: +    wb_iface::sptr  _iface; +    const size_t    _base; +    boost::mutex    _mutex; +    bool            _has_ext_bist; + +    fifo_readback       _fifo_readback; +    fifo_ctrl_reg_t     _fifo_ctrl_reg; +    base_addr_reg_t     _base_addr_reg; +    addr_mask_reg_t     _addr_mask_reg; +    bist_ctrl_reg_t     _bist_ctrl_reg; +    bist_cfg_reg_t      _bist_cfg_reg; +    bist_delay_reg_t    _bist_delay_reg; +    bist_sid_reg_t      _bist_sid_reg; +}; + +// +// Static make function +// +dma_fifo_core_3000::sptr dma_fifo_core_3000::make(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) +{ +    if (check(iface, set_base, rb_addr)) { +        return sptr(new dma_fifo_core_3000_impl(iface, set_base, rb_addr)); +    } else { +        throw uhd::runtime_error(""); +    } +} + +bool dma_fifo_core_3000::check(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) +{ +    dma_fifo_core_3000_impl::fifo_readback fifo_rb(iface, set_base, rb_addr); +    return fifo_rb.is_fifo_instantiated(); +} diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.hpp b/host/lib/usrp/cores/dma_fifo_core_3000.hpp new file mode 100644 index 000000000..41430e5c3 --- /dev/null +++ b/host/lib/usrp/cores/dma_fifo_core_3000.hpp @@ -0,0 +1,86 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <uhd/types/wb_iface.hpp> + + +class dma_fifo_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<dma_fifo_core_3000> sptr; +    virtual ~dma_fifo_core_3000(void) = 0; + +    /*! +     * Create a DMA FIFO controller using the given bus, settings and readback base +     * Throws uhd::runtime_error if a DMA FIFO is not instantiated in the FPGA +     */ +    static sptr make(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Check if a DMA FIFO is instantiated in the FPGA +     */ +    static bool check(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Flush the DMA FIFO. Will clear all contents. +     */ +    virtual void flush() = 0; + +    /*! +     * Resize and rebase the DMA FIFO. Will clear all contents. +     */ +    virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) = 0; + +    /*! +     * Get the (approx) number of bytes currently in the DMA FIFO +     */ +    virtual boost::uint32_t get_bytes_occupied() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO +     */ +    virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) = 0; + +    /*! +     * Is extended BIST supported +     */ +    virtual bool ext_bist_supported() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO (extended BIST only) +     */ +    virtual boost::uint8_t run_ext_bist( +        bool finite, +        boost::uint32_t rx_samp_delay, +        boost::uint32_t tx_pkt_delay, +        boost::uint32_t sid, +        boost::uint32_t timeout_ms = 500) = 0; + +    /*! +     * Get the throughput measured from the last invocation of the BIST (extended BIST only) +     */ +    virtual double get_bist_throughput(double fifo_clock_rate) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/dsp_core_utils.cpp b/host/lib/usrp/cores/dsp_core_utils.cpp new file mode 100644 index 000000000..aea809ae8 --- /dev/null +++ b/host/lib/usrp/cores/dsp_core_utils.cpp @@ -0,0 +1,66 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 "dsp_core_utils.hpp" +#include <uhd/utils/math.hpp> +#include <uhd/exception.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> + +static const int32_t MAX_FREQ_WORD = boost::numeric::bounds<boost::int32_t>::highest(); +static const int32_t MIN_FREQ_WORD = boost::numeric::bounds<boost::int32_t>::lowest(); + +void get_freq_and_freq_word( +        const double requested_freq, +        const double tick_rate, +        double &actual_freq, +        int32_t &freq_word +) { +    //correct for outside of rate (wrap around) +    double freq = std::fmod(requested_freq, tick_rate); +    if (std::abs(freq) > tick_rate/2.0) +        freq -= boost::math::sign(freq) * tick_rate; + +    //confirm that the target frequency is within range of the CORDIC +    UHD_ASSERT_THROW(std::abs(freq) <= tick_rate/2.0); + +    /* Now calculate the frequency word. It is possible for this calculation +     * to cause an overflow. As the requested DSP frequency approaches the +     * master clock rate, that ratio multiplied by the scaling factor (2^32) +     * will generally overflow within the last few kHz of tunable range. +     * Thus, we check to see if the operation will overflow before doing it, +     * and if it will, we set it to the integer min or max of this system. +     */ +    freq_word = 0; + +    static const double scale_factor = std::pow(2.0, 32); +    if ((freq / tick_rate) >= (MAX_FREQ_WORD / scale_factor)) { +        /* Operation would have caused a positive overflow of int32. */ +        freq_word = MAX_FREQ_WORD; + +    } else if ((freq / tick_rate) <= (MIN_FREQ_WORD / scale_factor)) { +        /* Operation would have caused a negative overflow of int32. */ +        freq_word = MIN_FREQ_WORD; + +    } else { +        /* The operation is safe. Perform normally. */ +        freq_word = int32_t(boost::math::round((freq / tick_rate) * scale_factor)); +    } + +    actual_freq = (double(freq_word) / scale_factor) * tick_rate; +} + diff --git a/host/lib/usrp/cores/dsp_core_utils.hpp b/host/lib/usrp/cores/dsp_core_utils.hpp new file mode 100644 index 000000000..d5d43f236 --- /dev/null +++ b/host/lib/usrp/cores/dsp_core_utils.hpp @@ -0,0 +1,33 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_DSP_CORE_UTILS_HPP +#define INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP + +#include <stdint.h> + +/*! For a requested frequency and sampling rate, return the + *  correct frequency word (to set the CORDIC) and the actual frequency. + */ +void get_freq_and_freq_word( +        const double requested_freq, +        const double tick_rate, +        double &actual_freq, +        int32_t &freq_word +); + +#endif /* INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP */ diff --git a/host/lib/usrp/cores/gpio_atr_3000.cpp b/host/lib/usrp/cores/gpio_atr_3000.cpp new file mode 100644 index 000000000..5844af601 --- /dev/null +++ b/host/lib/usrp/cores/gpio_atr_3000.cpp @@ -0,0 +1,341 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "gpio_atr_3000.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/utils/soft_register.hpp> + +using namespace uhd; +using namespace usrp; + +//------------------------------------------------------------- +// gpio_atr_3000 +//------------------------------------------------------------- + +#define REG_ATR_IDLE_OFFSET     (base + 0) +#define REG_ATR_RX_OFFSET       (base + 4) +#define REG_ATR_TX_OFFSET       (base + 8) +#define REG_ATR_FDX_OFFSET      (base + 12) +#define REG_DDR_OFFSET          (base + 16) +#define REG_ATR_DISABLE_OFFSET  (base + 20) + +namespace uhd { namespace usrp { namespace gpio_atr { + +class gpio_atr_3000_impl : public gpio_atr_3000{ +public: +    gpio_atr_3000_impl( +        wb_iface::sptr iface, +        const wb_iface::wb_addr_type base, +        const wb_iface::wb_addr_type rb_addr = READBACK_DISABLED +    ): +        _iface(iface), _rb_addr(rb_addr), +        _atr_idle_reg(REG_ATR_IDLE_OFFSET, _atr_disable_reg), +        _atr_rx_reg(REG_ATR_RX_OFFSET), +        _atr_tx_reg(REG_ATR_TX_OFFSET), +        _atr_fdx_reg(REG_ATR_FDX_OFFSET), +        _ddr_reg(REG_DDR_OFFSET), +        _atr_disable_reg(REG_ATR_DISABLE_OFFSET) +    { +        _atr_idle_reg.initialize(*_iface, true); +        _atr_rx_reg.initialize(*_iface, true); +        _atr_tx_reg.initialize(*_iface, true); +        _atr_fdx_reg.initialize(*_iface, true); +        _ddr_reg.initialize(*_iface, true); +        _atr_disable_reg.initialize(*_iface, true); +    } + +    virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask) +    { +        //Each bit in the "ATR Disable" register determines whether the respective bit in the GPIO +        //output bus is driven by the ATR engine or a static register. +        //For each bit position, a 1 means that the bit is static and 0 means that the bit +        //is driven by the ATR state machine. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        _atr_disable_reg.set_with_mask((mode==MODE_ATR) ? ~MASK_SET_ALL : MASK_SET_ALL, mask); +        _atr_disable_reg.flush(); +    } + +    virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask) +    { +        //Each bit in the "DDR" register determines whether the respective bit in the GPIO +        //bus is an input or an output. +        //For each bit position, a 1 means that the bit is an output and 0 means that the bit +        //is an input. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        _ddr_reg.set_with_mask((dir==DDR_INPUT) ? ~MASK_SET_ALL : MASK_SET_ALL, mask); +        _ddr_reg.flush(); +    } + +    virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) +    { +        //Set the value of the specified ATR register. For bits with ATR Disable set to 1, +        //the IDLE register will hold the output state +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        masked_reg_t* reg = NULL; +        switch (atr) { +            case ATR_REG_IDLE:          reg = &_atr_idle_reg; break; +            case ATR_REG_RX_ONLY:       reg = &_atr_rx_reg;   break; +            case ATR_REG_TX_ONLY:       reg = &_atr_tx_reg;   break; +            case ATR_REG_FULL_DUPLEX:   reg = &_atr_fdx_reg;  break; +            default:                    reg = &_atr_idle_reg; break; +        } +        //For protection we only write to bits that have the mode ATR by masking the user +        //specified "mask" with ~atr_disable. +        reg->set_with_mask(value, mask); +        reg->flush(); +    } + +    virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) { +        //Set the value of the specified GPIO output register. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. + +        //For protection we only write to bits that have the mode GPIO by masking the user +        //specified "mask" with atr_disable. +        _atr_idle_reg.set_gpio_out_with_mask(value, mask); +        _atr_idle_reg.flush(); +    } + +    virtual boost::uint32_t read_gpio() +    { +        //Read the state of the GPIO pins +        //If a pin is configured as an input, reads the actual value of the pin +        //If a pin is configured as an output, reads the last value written to the pin +        if (_rb_addr != READBACK_DISABLED) { +            return _iface->peek32(_rb_addr); +        } else { +            throw uhd::runtime_error("read_gpio not supported for write-only interface."); +        } +    } + +    inline virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value) +    { +        //An attribute based API to configure all settings for the GPIO bus in one function +        //call. This API does not have a mask so it configures all bits at the same time. +        switch (attr) +        { +        case GPIO_CTRL: +            set_atr_mode(MODE_ATR, value);   //Configure mode=ATR for all bits that are set +            set_atr_mode(MODE_GPIO, ~value); //Configure mode=GPIO for all bits that are unset +            break; +        case GPIO_DDR: +            set_gpio_ddr(DDR_OUTPUT, value); //Configure as output for all bits that are set +            set_gpio_ddr(DDR_INPUT, ~value); //Configure as input for all bits that are unset +            break; +        case GPIO_OUT: +            //Only set bits that are driven statically +            set_gpio_out(value); +            break; +        case GPIO_ATR_0X: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_IDLE, value); +            break; +        case GPIO_ATR_RX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_RX_ONLY, value); +            break; +        case GPIO_ATR_TX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_TX_ONLY, value); +            break; +        case GPIO_ATR_XX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_FULL_DUPLEX, value); +            break; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +protected: +    //Special RB addr value to indicate no readback +    //This value is invalid as a real address because it is not a multiple of 4 +    static const wb_iface::wb_addr_type READBACK_DISABLED = 0xFFFFFFFF; + +    class masked_reg_t : public uhd::soft_reg32_wo_t { +    public: +        masked_reg_t(const wb_iface::wb_addr_type offset): uhd::soft_reg32_wo_t(offset) { +            uhd::soft_reg32_wo_t::set(REGISTER, 0); +        } + +        virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            uhd::soft_reg32_wo_t::set(REGISTER, +                (value&mask)|(uhd::soft_reg32_wo_t::get(REGISTER)&(~mask))); +        } + +        virtual boost::uint32_t get() { +            return uhd::soft_reg32_wo_t::get(uhd::soft_reg32_wo_t::REGISTER); +        } + +        virtual void flush() { +            uhd::soft_reg32_wo_t::flush(); +        } +    }; + +    class atr_idle_reg_t : public masked_reg_t { +    public: +        atr_idle_reg_t(const wb_iface::wb_addr_type offset, masked_reg_t& atr_disable_reg): +            masked_reg_t(offset), +            _atr_idle_cache(0), _gpio_out_cache(0), +            _atr_disable_reg(atr_disable_reg) +        { } + +        virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            _atr_idle_cache = (value&mask)|(_atr_idle_cache&(~mask)); +        } + +        virtual boost::uint32_t get() { +            return _atr_idle_cache; +        } + +        void set_gpio_out_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            _gpio_out_cache = (value&mask)|(_gpio_out_cache&(~mask)); +        } + +        virtual boost::uint32_t get_gpio_out() { +            return _gpio_out_cache; +        } + +        virtual void flush() { +            set(REGISTER, +                (_atr_idle_cache & (~_atr_disable_reg.get())) | +                (_gpio_out_cache & _atr_disable_reg.get()) +            ); +            masked_reg_t::flush(); +        } + +    private: +        boost::uint32_t _atr_idle_cache; +        boost::uint32_t _gpio_out_cache; +        masked_reg_t&   _atr_disable_reg; +    }; + +    wb_iface::sptr          _iface; +    wb_iface::wb_addr_type  _rb_addr; +    atr_idle_reg_t          _atr_idle_reg; +    masked_reg_t            _atr_rx_reg; +    masked_reg_t            _atr_tx_reg; +    masked_reg_t            _atr_fdx_reg; +    masked_reg_t            _ddr_reg; +    masked_reg_t            _atr_disable_reg; +}; + +gpio_atr_3000::sptr gpio_atr_3000::make( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr +) { +    return sptr(new gpio_atr_3000_impl(iface, base, rb_addr)); +} + +gpio_atr_3000::sptr gpio_atr_3000::make_write_only( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base +) { +    gpio_atr_3000::sptr gpio_iface(new gpio_atr_3000_impl(iface, base)); +    gpio_iface->set_gpio_ddr(DDR_OUTPUT, MASK_SET_ALL); +    return gpio_iface; +} + +//------------------------------------------------------------- +// db_gpio_atr_3000 +//------------------------------------------------------------- + +class db_gpio_atr_3000_impl : public gpio_atr_3000_impl, public db_gpio_atr_3000 { +public: +    db_gpio_atr_3000_impl(wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr): +        gpio_atr_3000_impl(iface, base, rb_addr) { /* NOP */ } + +    inline void set_pin_ctrl(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_atr_mode(MODE_ATR,  compute_mask(unit, value&mask)); +        gpio_atr_3000_impl::set_atr_mode(MODE_GPIO, compute_mask(unit, (~value)&mask)); +    } + +    inline boost::uint32_t get_pin_ctrl(const db_unit_t unit) +    { +        return (~_atr_disable_reg.get()) >> compute_shift(unit); +    } + +    inline void set_gpio_ddr(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_gpio_ddr(DDR_OUTPUT, compute_mask(unit, value&mask)); +        gpio_atr_3000_impl::set_gpio_ddr(DDR_INPUT,  compute_mask(unit, (~value)&mask)); +    } + +    inline boost::uint32_t get_gpio_ddr(const db_unit_t unit) +    { +        return _ddr_reg.get() >> compute_shift(unit); +    } + +    inline void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_atr_reg(atr, value << compute_shift(unit), compute_mask(unit, mask)); +    } + +    inline boost::uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr) +    { +        masked_reg_t* reg = NULL; +        switch (atr) { +            case ATR_REG_IDLE:          reg = &_atr_idle_reg; break; +            case ATR_REG_RX_ONLY:       reg = &_atr_rx_reg;   break; +            case ATR_REG_TX_ONLY:       reg = &_atr_tx_reg;   break; +            case ATR_REG_FULL_DUPLEX:   reg = &_atr_fdx_reg;  break; +            default:                    reg = &_atr_idle_reg; break; +        } +        return (reg->get() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit); +    } + +    inline void set_gpio_out(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_gpio_out( +            static_cast<boost::uint32_t>(value) << compute_shift(unit), +            compute_mask(unit, mask)); +    } + +    inline boost::uint32_t get_gpio_out(const db_unit_t unit) +    { +        return (_atr_idle_reg.get_gpio_out() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit); +    } + +    inline boost::uint32_t read_gpio(const db_unit_t unit) +    { +        return (gpio_atr_3000_impl::read_gpio() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit); +    } + +private: +    inline boost::uint32_t compute_shift(const db_unit_t unit) { +        switch (unit) { +        case dboard_iface::UNIT_RX: return 0; +        case dboard_iface::UNIT_TX: return 16; +        default:                    return 0; +        } +    } + +    inline boost::uint32_t compute_mask(const db_unit_t unit, const boost::uint32_t mask) { +        boost::uint32_t tmp_mask = (unit == dboard_iface::UNIT_BOTH) ? mask : (mask & 0xFFFF); +        return tmp_mask << (compute_shift(unit)); +    } +}; + +db_gpio_atr_3000::sptr db_gpio_atr_3000::make( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr +) { +    return sptr(new db_gpio_atr_3000_impl(iface, base, rb_addr)); +} + +}}} diff --git a/host/lib/usrp/cores/gpio_atr_3000.hpp b/host/lib/usrp/cores/gpio_atr_3000.hpp new file mode 100644 index 000000000..7b90429fe --- /dev/null +++ b/host/lib/usrp/cores/gpio_atr_3000.hpp @@ -0,0 +1,183 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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_GPIO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +namespace uhd { namespace usrp { namespace gpio_atr { + +class gpio_atr_3000 : boost::noncopyable { +public: +    typedef boost::shared_ptr<gpio_atr_3000> sptr; + +    static const boost::uint32_t MASK_SET_ALL = 0xFFFFFFFF; + +    virtual ~gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param base readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Create a write-only GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     */ +    static sptr make_write_only( +        uhd::wb_iface::sptr iface, const uhd::wb_iface::wb_addr_type base); + +    /*! +     * Select the ATR mode for all bits in the mask +     * +     * \param mode the mode to apply {ATR = outputs driven by ATR state machine, GPIO = outputs static} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask) = 0; + +    /*! +     * Select the data direction for all bits in the mask +     * +     * \param dir the direction {OUTPUT, INPUT} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask) = 0; + +    /*! +     * Write the specified (masked) value to the ATR register +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Write to a static GPIO output +     * +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \return the value read back +     */ +    virtual boost::uint32_t read_gpio() = 0; + +    /*! +     * Set a GPIO attribute +     * +     * \param attr the attribute to set +     * \param value the value to write to the attribute +     */ +    virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value) = 0; +}; + +class db_gpio_atr_3000 { +public: +    typedef boost::shared_ptr<db_gpio_atr_3000> sptr; + +    typedef uhd::usrp::dboard_iface::unit_t db_unit_t; + +    virtual ~db_gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object for a daughterboard connector +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param base readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Configure the GPIO mode for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is in ATR mode otherwise it is in GPIO mode +     */ +    virtual void set_pin_ctrl(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_pin_ctrl(const db_unit_t unit) = 0; + +    /*! +     * Configure the direction for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is an output otherwise it is an input +     */ +    virtual void set_gpio_ddr(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_gpio_ddr(const db_unit_t unit) = 0; + +    /*! +     * Write the specified value to the ATR register (all bits) +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value the value to write +     */ +    virtual void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr) = 0; + +    /*! +     * Write the specified value to the GPIO register (all bits) +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     */ +    virtual void set_gpio_out(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_gpio_out(const db_unit_t unit) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \return the value read back +     */ +    virtual boost::uint32_t read_gpio(const db_unit_t unit) = 0; +}; + +}}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 704a71d5f..8223a0bbf 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -27,6 +27,11 @@  using namespace uhd;  using namespace usrp; +template <typename T> +static void shadow_it(T &shadow, const T &value, const T &mask){ +    shadow = (shadow & ~mask) | (value & mask); +} +  gpio_core_200::~gpio_core_200(void){      /* NOP */  } @@ -36,13 +41,20 @@ public:      gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr):          _iface(iface), _base(base), _rb_addr(rb_addr), _first_atr(true) { /* NOP */ } -    void set_pin_ctrl(const unit_t unit, const boost::uint16_t value){ -        _pin_ctrl[unit] = value; //shadow +    void set_pin_ctrl(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_pin_ctrl[unit], value, mask);          update(); //full update      } -    void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value){ -        _atr_regs[unit][atr] = value;  //shadow +    boost::uint16_t get_pin_ctrl(unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _pin_ctrl[unit]; +    } + +    void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_atr_regs[unit][atr], value, mask);          if (_first_atr)          {              // To preserve legacy behavior, update all registers the first time @@ -53,20 +65,38 @@ public:              update(atr);      } -    void set_gpio_ddr(const unit_t unit, const boost::uint16_t value){ -        _gpio_ddr[unit] = value; //shadow +    boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _atr_regs[unit][reg]; +    } + +    void set_gpio_ddr(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_gpio_ddr[unit], value, mask);          _iface->poke32(REG_GPIO_DDR, //update the 32 bit register              (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) |              (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX))          );      } -    void set_gpio_out(const unit_t unit, const boost::uint16_t value){ -        _gpio_out[unit] = value; //shadow +    boost::uint16_t get_gpio_ddr(unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _gpio_ddr[unit]; +    } + +    void set_gpio_out(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_gpio_out[unit], value, mask);          this->update(); //full update      } +    boost::uint16_t get_gpio_out(unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _gpio_out[unit]; +    } +      boost::uint16_t read_gpio(const unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");          return boost::uint16_t(_iface->peek32(_rb_addr) >> shift_by_unit(unit));      } @@ -85,26 +115,26 @@ private:      }      void update(void){ -        update(dboard_iface::ATR_REG_IDLE); -        update(dboard_iface::ATR_REG_TX_ONLY); -        update(dboard_iface::ATR_REG_RX_ONLY); -        update(dboard_iface::ATR_REG_FULL_DUPLEX); +        update(gpio_atr::ATR_REG_IDLE); +        update(gpio_atr::ATR_REG_TX_ONLY); +        update(gpio_atr::ATR_REG_RX_ONLY); +        update(gpio_atr::ATR_REG_FULL_DUPLEX);      }      void update(const atr_reg_t atr){          size_t addr;          switch (atr)          { -        case dboard_iface::ATR_REG_IDLE: +        case gpio_atr::ATR_REG_IDLE:              addr = REG_GPIO_IDLE;              break; -        case dboard_iface::ATR_REG_TX_ONLY: +        case gpio_atr::ATR_REG_TX_ONLY:              addr = REG_GPIO_TX_ONLY;              break; -        case dboard_iface::ATR_REG_RX_ONLY: +        case gpio_atr::ATR_REG_RX_ONLY:              addr = REG_GPIO_RX_ONLY;              break; -        case dboard_iface::ATR_REG_FULL_DUPLEX: +        case gpio_atr::ATR_REG_FULL_DUPLEX:              addr = REG_GPIO_BOTH;              break;          default: @@ -148,23 +178,23 @@ public:      }      void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){ -        if (atr == dboard_iface::ATR_REG_IDLE) +        if (atr == gpio_atr::ATR_REG_IDLE)              _iface->poke32(REG_GPIO_IDLE, value); -        else if (atr == dboard_iface::ATR_REG_TX_ONLY) +        else if (atr == gpio_atr::ATR_REG_TX_ONLY)              _iface->poke32(REG_GPIO_TX_ONLY, value); -        else if (atr == dboard_iface::ATR_REG_RX_ONLY) +        else if (atr == gpio_atr::ATR_REG_RX_ONLY)              _iface->poke32(REG_GPIO_RX_ONLY, value); -        else if (atr == dboard_iface::ATR_REG_FULL_DUPLEX) +        else if (atr == gpio_atr::ATR_REG_FULL_DUPLEX)              _iface->poke32(REG_GPIO_BOTH, value);          else              UHD_THROW_INVALID_CODE_PATH();      }      void set_all_regs(const boost::uint32_t value){ -        set_atr_reg(dboard_iface::ATR_REG_IDLE,        value); -        set_atr_reg(dboard_iface::ATR_REG_TX_ONLY,     value); -        set_atr_reg(dboard_iface::ATR_REG_RX_ONLY,     value); -        set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, value); +        set_atr_reg(gpio_atr::ATR_REG_IDLE,        value); +        set_atr_reg(gpio_atr::ATR_REG_TX_ONLY,     value); +        set_atr_reg(gpio_atr::ATR_REG_RX_ONLY,     value); +        set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, value);      }  private: diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index e22834fd9..67aa8bde8 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp>  #include <boost/assign.hpp>  #include <boost/cstdint.hpp>  #include <boost/utility.hpp> @@ -27,28 +28,6 @@  #include <uhd/types/wb_iface.hpp>  #include <map> -typedef enum { -    GPIO_CTRL, -    GPIO_DDR, -    GPIO_OUT, -    GPIO_ATR_0X, -    GPIO_ATR_RX, -    GPIO_ATR_TX, -    GPIO_ATR_XX -} gpio_attr_t; - -typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t; -static const gpio_attr_map_t gpio_attr_map = -    boost::assign::map_list_of -        (GPIO_CTRL,   "CTRL") -        (GPIO_DDR,    "DDR") -        (GPIO_OUT,    "OUT") -        (GPIO_ATR_0X, "ATR_0X") -        (GPIO_ATR_RX, "ATR_RX") -        (GPIO_ATR_TX, "ATR_TX") -        (GPIO_ATR_XX, "ATR_XX") -; -  class gpio_core_200 : boost::noncopyable{  public:      typedef boost::shared_ptr<gpio_core_200> sptr; @@ -59,20 +38,32 @@ public:      virtual ~gpio_core_200(void) = 0;      //! makes a new GPIO core from iface and slave base -    static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr); +    static sptr make( +        uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr);      //! 1 = ATR -    virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0; +    virtual void set_pin_ctrl( +        const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0; + +    virtual boost::uint16_t get_pin_ctrl(unit_t unit) = 0; + +    virtual void set_atr_reg( +        const unit_t unit, const atr_reg_t atr, const boost::uint16_t value, const boost::uint16_t mask) = 0; -    virtual void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value) = 0; +    virtual boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg) = 0;      //! 1 = OUTPUT -    virtual void set_gpio_ddr(const unit_t unit, const boost::uint16_t value) = 0; +    virtual void set_gpio_ddr( +        const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0; -    virtual void set_gpio_out(const unit_t unit, const boost::uint16_t value) = 0; +    virtual boost::uint16_t get_gpio_ddr(unit_t unit) = 0; -    virtual boost::uint16_t read_gpio(const unit_t unit) = 0; +    virtual void set_gpio_out( +        const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0; + +    virtual boost::uint16_t get_gpio_out(unit_t unit) = 0; +    virtual boost::uint16_t read_gpio(const unit_t unit) = 0;  };  //! Simple wrapper for 32 bit write only diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index b899085c0..e51862d3b 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -16,6 +16,7 @@  //  #include "rx_dsp_core_200.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp> @@ -24,7 +25,6 @@  #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 <boost/numeric/conversion/bounds.hpp>  #include <algorithm>  #include <cmath> @@ -223,42 +223,11 @@ public:          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; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq){ +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);          _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word)); -          return actual_freq;      } diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 035bc6a3f..eedbbef95 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -16,6 +16,7 @@  //  #include "rx_dsp_core_3000.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp> @@ -24,7 +25,6 @@  #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> @@ -69,6 +69,7 @@ public:          _scaling_adjustment = 1.0;          _dsp_extra_scaling = 1.0;          _tick_rate = 1.0; +        _dsp_freq_offset = 0.0;      }      ~rx_dsp_core_3000_impl(void) @@ -79,17 +80,41 @@ public:          )      } -    void set_mux(const std::string &mode, const bool fe_swapped, const bool invert_i, const bool invert_q){ -        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) -            | (invert_i ? FLAG_DSP_RX_MUX_INVERT_I : 0) -            | (invert_q ? FLAG_DSP_RX_MUX_INVERT_Q : 0)); +    void set_mux(const uhd::usrp::fe_connection_t& fe_conn){ +        boost::uint32_t reg_val = 0; +        switch (fe_conn.get_sampling_mode()) { +        case uhd::usrp::fe_connection_t::REAL: +        case uhd::usrp::fe_connection_t::HETERODYNE: +            reg_val = FLAG_DSP_RX_MUX_REAL_MODE; +            break; +        default: +            reg_val = 0; +            break; +        } + +        if (fe_conn.is_iq_swapped()) reg_val |= FLAG_DSP_RX_MUX_SWAP_IQ; +        if (fe_conn.is_i_inverted()) reg_val |= FLAG_DSP_RX_MUX_INVERT_I; +        if (fe_conn.is_q_inverted()) reg_val |= FLAG_DSP_RX_MUX_INVERT_Q; + +        _iface->poke32(REG_DSP_RX_MUX, reg_val); + +        if (fe_conn.get_sampling_mode() == uhd::usrp::fe_connection_t::HETERODYNE) { +            //1. Remember the sign of the IF frequency. +            //   It will be discarded in the next step +            int if_freq_sign = boost::math::sign(fe_conn.get_if_freq()); +            //2. Map IF frequency to the range [0, _tick_rate) +            double if_freq = std::abs(std::fmod(fe_conn.get_if_freq(), _tick_rate)); +            //3. Map IF frequency to the range [-_tick_rate/2, _tick_rate/2) +            //   This is the aliased frequency +            if (if_freq > (_tick_rate / 2.0)) { +                if_freq -= _tick_rate; +            } +            //4. Set DSP offset to spin the signal in the opposite +            //   direction as the aliased frequency +            _dsp_freq_offset = if_freq * (-if_freq_sign); +        } else { +            _dsp_freq_offset = 0.0; +        }      }      void set_tick_rate(const double rate){ @@ -209,47 +234,18 @@ public:          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; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq){ +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq + _dsp_freq_offset, _tick_rate, actual_freq, freq_word);          _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)); +        //Too keep the DSP range symmetric about 0, we use abs(_dsp_freq_offset) +        const double offset = std::abs<double>(_dsp_freq_offset); +        return uhd::meta_range_t(-(_tick_rate-offset)/2, +(_tick_rate-offset)/2, _tick_rate/std::pow(2.0, 32));      }      void setup(const uhd::stream_args_t &stream_args){ @@ -284,18 +280,18 @@ public:      void populate_subtree(property_tree::sptr subtree)      {          subtree->create<meta_range_t>("rate/range") -            .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, this)) +            .set_publisher(boost::bind(&rx_dsp_core_3000::get_host_rates, this))          ;          subtree->create<double>("rate/value")              .set(DEFAULT_RATE) -            .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1)) +            .set_coercer(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1))          ;          subtree->create<double>("freq/value")              .set(DEFAULT_CORDIC_FREQ) -            .coerce(boost::bind(&rx_dsp_core_3000::set_freq, this, _1)) +            .set_coercer(boost::bind(&rx_dsp_core_3000::set_freq, this, _1))          ;          subtree->create<meta_range_t>("freq/range") -            .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, this)) +            .set_publisher(boost::bind(&rx_dsp_core_3000::get_freq_range, this))          ;      } @@ -305,6 +301,7 @@ private:      const bool _is_b200;    //TODO: Obsolete this when we switch to the new DDC on the B200      double _tick_rate, _link_rate;      double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; +    double _dsp_freq_offset;  };  rx_dsp_core_3000::sptr rx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base, const bool is_b200 /* = false */) diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp index 65801de1d..41b328357 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -24,6 +24,7 @@  #include <uhd/types/stream_cmd.hpp>  #include <uhd/types/wb_iface.hpp>  #include <uhd/property_tree.hpp> +#include <uhd/usrp/fe_connection.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp>  #include <string> @@ -43,7 +44,7 @@ public:          const bool is_b200 = false  //TODO: Obsolete this when we switch to the new DDC on the B200      ); -    virtual void set_mux(const std::string &mode, const bool fe_swapped = false, const bool invert_i = false, const bool invert_q = false) = 0; +    virtual void set_mux(const uhd::usrp::fe_connection_t& fe_conn) = 0;      virtual void set_tick_rate(const double rate) = 0; diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index 7ac920553..0a60bf87c 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -83,15 +83,15 @@ public:      {          subtree->create<std::complex<double> >("dc_offset/value")              .set(DEFAULT_DC_OFFSET_VALUE) -            .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1)) +            .set_coercer(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1))          ;          subtree->create<bool>("dc_offset/enable")              .set(DEFAULT_DC_OFFSET_ENABLE) -            .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1)) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1))          ;          subtree->create<std::complex<double> >("iq_balance/value")              .set(DEFAULT_IQ_BALANCE_VALUE) -            .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1)) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1))          ;      } diff --git a/host/lib/usrp/cores/rx_vita_core_3000.cpp b/host/lib/usrp/cores/rx_vita_core_3000.cpp index f61da7cc3..52121837e 100644 --- a/host/lib/usrp/cores/rx_vita_core_3000.cpp +++ b/host/lib/usrp/cores/rx_vita_core_3000.cpp @@ -20,6 +20,8 @@  #include <uhd/utils/safe_call.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/tuple/tuple.hpp> +#include <boost/date_time.hpp> +#include <boost/thread.hpp>  #define REG_FRAMER_MAXLEN    _base + 4*4 + 0  #define REG_FRAMER_SID       _base + 4*4 + 4 @@ -63,6 +65,17 @@ struct rx_vita_core_3000_impl : rx_vita_core_3000      void configure_flow_control(const size_t window_size)      { +        // The window needs to be disabled in the case where this object is +        // uncleanly destroyed and the FC window is left enabled +        _iface->poke32(REG_FC_ENABLE, 0); + +        // Sleep for a large amount of time to allow the source flow control +        // module in the FPGA to flush all the packets buffered upstream. +        // At 1 ms * 200 MHz = 200k cycles, 8 bytes * 200k cycles = 1.6 MB +        // of flushed data, when the typical amount of data buffered +        // is on the order of kilobytes +        boost::this_thread::sleep(boost::posix_time::milliseconds(1.0)); +          _iface->poke32(REG_FC_WINDOW, window_size-1);          _iface->poke32(REG_FC_ENABLE, window_size?1:0);      } diff --git a/host/lib/usrp/cores/spi_core_3000.cpp b/host/lib/usrp/cores/spi_core_3000.cpp index 0656d910a..d33624b0d 100644 --- a/host/lib/usrp/cores/spi_core_3000.cpp +++ b/host/lib/usrp/cores/spi_core_3000.cpp @@ -34,7 +34,7 @@ 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) +        _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0), _divider_cache(0)      {          this->set_divider(30);      } @@ -46,7 +46,21 @@ public:          size_t num_bits,          bool readback      ){ -        boost::mutex::scoped_lock lock(_mutex); +        boost::lock_guard<boost::mutex> lock(_mutex); + +        //load SPI divider +        size_t spi_divider = _div; +        if (config.use_custom_divider) { +            //The resulting SPI frequency will be f_system/(2*(divider+1)) +            //This math ensures the frequency will be equal to or less than the target +            spi_divider = (config.divider-1)/2; +        } + +        //conditionally send SPI divider +        if (spi_divider != _divider_cache) { +            _iface->poke32(SPI_DIV, spi_divider); +            _divider_cache = spi_divider; +        }          //load control word          boost::uint32_t ctrl_word = 0; @@ -55,17 +69,16 @@ public:          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;          } +        //load data word (must be in upper bits) +        const boost::uint32_t data_out = data << (32 - num_bits); +          //send data word          _iface->poke32(SPI_DATA, data_out); @@ -91,6 +104,7 @@ private:      boost::uint32_t _ctrl_word_cache;      boost::mutex _mutex;      size_t _div; +    size_t _divider_cache;  };  spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback) diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index 2ef9f4406..4c456a10d 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -16,13 +16,13 @@  //  #include "tx_dsp_core_200.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp>  #include <uhd/utils/msg.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> @@ -163,42 +163,11 @@ public:          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; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq){ +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);          _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word)); -          return actual_freq;      } diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp index 7e447ae7d..3889bbdc4 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -16,13 +16,13 @@  //  #include "tx_dsp_core_3000.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp>  #include <uhd/utils/msg.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> @@ -136,42 +136,11 @@ public:          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; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq) { +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);          _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word)); -          return actual_freq;      } @@ -211,18 +180,18 @@ public:      void populate_subtree(property_tree::sptr subtree)      {          subtree->create<meta_range_t>("rate/range") -            .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, this)) +            .set_publisher(boost::bind(&tx_dsp_core_3000::get_host_rates, this))          ;          subtree->create<double>("rate/value")              .set(DEFAULT_RATE) -            .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1)) +            .set_coercer(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1))          ;          subtree->create<double>("freq/value")              .set(DEFAULT_CORDIC_FREQ) -            .coerce(boost::bind(&tx_dsp_core_3000::set_freq, this, _1)) +            .set_coercer(boost::bind(&tx_dsp_core_3000::set_freq, this, _1))          ;          subtree->create<meta_range_t>("freq/range") -            .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, this)) +            .set_publisher(boost::bind(&tx_dsp_core_3000::get_freq_range, this))          ;      } diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index 0fa028571..be4f77f39 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -79,11 +79,11 @@ public:      {          subtree->create< std::complex<double> >("dc_offset/value")              .set(DEFAULT_DC_OFFSET_VALUE) -            .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1)) +            .set_coercer(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1))          ;          subtree->create< std::complex<double> >("iq_balance/value")              .set(DEFAULT_IQ_BALANCE_VALUE) -            .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1)) +            .add_coerced_subscriber(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1))          ;      } diff --git a/host/lib/usrp/cores/tx_vita_core_3000.cpp b/host/lib/usrp/cores/tx_vita_core_3000.cpp index 71a2b7e21..c76b384d9 100644 --- a/host/lib/usrp/cores/tx_vita_core_3000.cpp +++ b/host/lib/usrp/cores/tx_vita_core_3000.cpp @@ -18,9 +18,11 @@  #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 +#define REG_CTRL_ERROR_POLICY       (_base + 0) +#define REG_FC_PRE_RADIO_RESP_BASE  (_base + 2*4) +#define REG_FC_PRE_FIFO_RESP_BASE   (_base + 4*4) +#define REG_CTRL_FC_CYCLE_OFFSET    (0*4) +#define REG_CTRL_FC_PACKET_OFFSET   (1*4)  using namespace uhd; @@ -32,12 +34,22 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000  {      tx_vita_core_3000_impl(          wb_iface::sptr iface, -        const size_t base +        const size_t base, +        fc_monitor_loc fc_location      ):          _iface(iface), -        _base(base) +        _base(base), +        _fc_base((fc_location==FC_PRE_RADIO or fc_location==FC_DEFAULT) ? +                    REG_FC_PRE_RADIO_RESP_BASE : REG_FC_PRE_FIFO_RESP_BASE), +        _fc_location(fc_location)      { -        this->set_tick_rate(1); //init to non zero +        if (fc_location != FC_DEFAULT) { +            //Turn off the other FC monitoring module +            const size_t other_fc_base = (fc_location==FC_PRE_RADIO) ? +                    REG_FC_PRE_FIFO_RESP_BASE : REG_FC_PRE_RADIO_RESP_BASE; +            _iface->poke32(other_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0); +            _iface->poke32(other_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0); +        }          this->set_underflow_policy("next_packet");          this->clear();      } @@ -56,11 +68,6 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000          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") @@ -89,23 +96,35 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000      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 (cycs_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0); +        else _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, (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)); +        if (pkts_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0); +        else _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, (1 << 31) | ((pkts_per_up) & 0xffff));      } -    wb_iface::sptr _iface; -    const size_t _base; -    double _tick_rate; -    std::string _policy; +    wb_iface::sptr  _iface; +    const size_t    _base; +    const size_t    _fc_base; +    std::string     _policy; +    fc_monitor_loc  _fc_location; +  };  tx_vita_core_3000::sptr tx_vita_core_3000::make(      wb_iface::sptr iface, +    const size_t base, +    fc_monitor_loc fc_location +) +{ +    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, fc_location)); +} + +tx_vita_core_3000::sptr tx_vita_core_3000::make_no_radio_buff( +    wb_iface::sptr iface,      const size_t base  )  { -    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base)); +    //No internal radio buffer so only pre-radio monitoring is supported. +    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, FC_DEFAULT));  } diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp index 4c0052d4f..bd0f20ba4 100644 --- a/host/lib/usrp/cores/tx_vita_core_3000.hpp +++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp @@ -32,17 +32,27 @@ class tx_vita_core_3000 : boost::noncopyable  public:      typedef boost::shared_ptr<tx_vita_core_3000> sptr; +    enum fc_monitor_loc { +        FC_DEFAULT, +        FC_PRE_RADIO, +        FC_PRE_FIFO +    }; +      virtual ~tx_vita_core_3000(void) = 0;      static sptr make(          uhd::wb_iface::sptr iface, +        const size_t base, +        fc_monitor_loc fc_location = FC_PRE_RADIO +    ); + +    static sptr make_no_radio_buff( +        uhd::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; diff --git a/host/lib/usrp/cores/user_settings_core_3000.cpp b/host/lib/usrp/cores/user_settings_core_3000.cpp new file mode 100644 index 000000000..549264f57 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_3000.cpp @@ -0,0 +1,85 @@ +// +// Copyright 2012 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 "user_settings_core_3000.hpp" +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> + +using namespace uhd; + +#define REG_USER_SR_ADDR   _sr_base_addr + 0 +#define REG_USER_SR_DATA   _sr_base_addr + 4 +#define REG_USER_RB_ADDR   _sr_base_addr + 8 + +class user_settings_core_3000_impl : public user_settings_core_3000 { +public: +    user_settings_core_3000_impl( +        wb_iface::sptr iface, +        const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr): +        _iface(iface), _sr_base_addr(sr_base_addr), _rb_reg_addr(rb_reg_addr) +    { +    } + +    void poke64(const wb_addr_type offset, const boost::uint64_t value) +    { +        if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("poke64: Incorrect address alignment"); +        poke32(offset, static_cast<boost::uint32_t>(value)); +        poke32(offset + 4, static_cast<boost::uint32_t>(value >> 32)); +    } + +    boost::uint64_t peek64(const wb_addr_type offset) +    { +        if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("peek64: Incorrect address alignment"); + +        boost::unique_lock<boost::mutex> lock(_mutex); +        _iface->poke32(REG_USER_RB_ADDR, offset >> 3);  //Translate byte offset to 64-bit offset +        return _iface->peek64(_rb_reg_addr); +    } + +    void poke32(const wb_addr_type offset, const boost::uint32_t value) +    { +        if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("poke32: Incorrect address alignment"); + +        boost::unique_lock<boost::mutex> lock(_mutex); +        _iface->poke32(REG_USER_SR_ADDR, offset >> 2);   //Translate byte offset to 64-bit offset +        _iface->poke32(REG_USER_SR_DATA, value); +    } + +    boost::uint32_t peek32(const wb_addr_type offset) +    { +        if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("peek32: Incorrect address alignment"); + +        boost::uint64_t value = peek64((offset >> 3) << 3); +        if ((offset & 0x7) == 0) { +            return static_cast<boost::uint32_t>(value); +        } else { +            return static_cast<boost::uint32_t>(value >> 32); +        } +    } + +private: +    wb_iface::sptr      _iface; +    const wb_addr_type  _sr_base_addr; +    const wb_addr_type  _rb_reg_addr; +    boost::mutex        _mutex; +}; + +wb_iface::sptr user_settings_core_3000::make(wb_iface::sptr iface, +    const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr) +{ +    return sptr(new user_settings_core_3000_impl(iface, sr_base_addr, rb_reg_addr)); +} diff --git a/host/lib/usrp/cores/user_settings_core_3000.hpp b/host/lib/usrp/cores/user_settings_core_3000.hpp new file mode 100644 index 000000000..6891b9e81 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_3000.hpp @@ -0,0 +1,35 @@ +// +// Copyright 2012 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_USER_SETTINGS_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class user_settings_core_3000 : public uhd::wb_iface { +public: +    virtual ~user_settings_core_3000() {} + +    static sptr make( +        wb_iface::sptr iface, +        const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr); +}; + +#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP */  | 
