diff options
25 files changed, 1143 insertions, 370 deletions
| diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index a320f86cb..e14f49933 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -45,6 +45,8 @@ The user may set the receive antenna to be TX/RX or RX2.  However, when using an RFX board in full-duplex mode,  the receive antenna will always be set to RX2, regardless of the settings. +Recieve Gains: **PGA0**, Range: 0-45dB +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  XCVR 2450  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,6 +65,14 @@ The XCVR2450 uses a common LO for both receive and transmit.  Even though the API allows the RX and TX LOs to be individually set,  a change of one LO setting will be reflected in the other LO setting. +Transmit Gains:  + * **VGA**, Range: 0-30dB + * **BB**, Range: 0-5dB + +Recieve Gains:  + * **LNA**, Range: 0-30.5dB + * **VGA**, Range: 0-62dB +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  WBX Series  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,5 +81,9 @@ Transmit Antennas: **TX/RX**  Receive Antennas: **TX/RX** or **RX2**  The user may set the receive antenna to be TX/RX or RX2. -However, when using an RFX board in full-duplex mode, +However, when using an WBX board in full-duplex mode,  the receive antenna will always be set to RX2, regardless of the settings. + +Transmit Gains: **PGA0**, Range: 0-25dB + +Recieve Gains: **PGA0**, Range: 0-31.5dB diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index fea95145c..b23a4dc00 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -31,10 +31,11 @@  # pragma warning(disable: 4251) // class 'A<T>' needs to have dll-interface to be used by clients of class 'B'  //# pragma warning(disable: 4127) // conditional expression is constant  //# pragma warning(disable: 4290) // C++ exception specification ignored except to ... -//# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored +# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored  # pragma warning(disable: 4275) // non dll-interface class ... used as base for dll-interface class ...  //# pragma warning(disable: 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data  //# pragma warning(disable: 4511) // 'class' : copy constructor could not be generated +# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance  #endif  //define logical operators diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 4cefffa24..23a4aae94 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -17,6 +17,8 @@  INSTALL(FILES +    alignment_buffer.hpp +    bounded_buffer.hpp      convert_types.hpp      if_addrs.hpp      udp_simple.hpp diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp new file mode 100644 index 000000000..dc6ccc3ed --- /dev/null +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -0,0 +1,134 @@ +// +// Copyright 2010 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_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/shared_ptr.hpp> +#include <utility> +#include <vector> + +namespace uhd{ namespace transport{ + +    /*! +     * Imlement a templated alignment buffer: +     * Used for aligning asynchronously pushed elements with matching ids. +     */ +    template <typename elem_type, typename seq_type> class alignment_buffer{ +    public: +        typedef boost::shared_ptr<alignment_buffer<elem_type, seq_type> > sptr; + +        /*! +         * Make a new alignment buffer object. +         * \param capacity the maximum elements per index +         * \param width the number of elements to align +         */ +        static sptr make(size_t capacity, size_t width){ +            return sptr(new alignment_buffer(capacity, width)); +        } + +        /*! +         * Push an element with sequence id into the buffer at index. +         * \param elem the element to push +         * \param seq the sequence identifier +         * \param index the buffer index +         * \return true if the element fit without popping for space +         */ +        UHD_INLINE bool push_with_pop_on_full( +            const elem_type &elem, +            const seq_type &seq, +            size_t index +        ){ +            return _buffs[index]->push_with_pop_on_full(buff_contents_type(elem, seq)); +        } + +        /*! +         * Pop an aligned set of elements from this alignment buffer. +         * \param elems a collection to store the aligned elements +         * \param time the timeout time +         * \return false when the operation times out +         */ +        template <typename elems_type, typename time_type> +        bool pop_elems_with_timed_wait(elems_type &elems, const time_type &time){ +            buff_contents_type buff_contents_tmp; +            std::list<size_t> indexes_to_do(_all_indexes); + +            //do an initial pop to load an initial sequence id +            size_t index = indexes_to_do.front(); +            if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; +            elems[index] = buff_contents_tmp.first; +            seq_type expected_seq_id = buff_contents_tmp.second; +            indexes_to_do.pop_front(); + +            //get an aligned set of elements from the buffers: +            while(indexes_to_do.size() != 0){ +                //pop an element off for this index +                index = indexes_to_do.front(); +                if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; + +                //if the sequence id matches: +                //  store the popped element into the output, +                //  remove this index from the list and continue +                if (buff_contents_tmp.second == expected_seq_id){ +                    elems[index] = buff_contents_tmp.first; +                    indexes_to_do.pop_front(); +                    continue; +                } + +                //if the sequence id is older: +                //  continue with the same index to try again +                if (buff_contents_tmp.second < expected_seq_id){ +                    continue; +                } + +                //if the sequence id is newer: +                //  store the popped element into the output, +                //  add all other indexes back into the list +                if (buff_contents_tmp.second > expected_seq_id){ +                    elems[index] = buff_contents_tmp.first; +                    expected_seq_id = buff_contents_tmp.second; +                    indexes_to_do = _all_indexes; +                    indexes_to_do.remove(index); +                    continue; +                } +            } +            return true; +        } + +    private: +        //a vector of bounded buffers for each index +        typedef std::pair<elem_type, seq_type> buff_contents_type; +        typedef bounded_buffer<buff_contents_type> bounded_buffer_type; +        typedef boost::shared_ptr<bounded_buffer_type> bounded_buffer_sptr; +        std::vector<bounded_buffer_sptr> _buffs; +        std::list<size_t> _all_indexes; + +        //private constructor +        alignment_buffer(size_t capacity, size_t width){ +            for (size_t i = 0; i < width; i++){ +                _buffs.push_back(bounded_buffer_type::make(capacity)); +                _all_indexes.push_back(i); +            } +        } +    }; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP */ diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp new file mode 100644 index 000000000..baecd6382 --- /dev/null +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -0,0 +1,146 @@ +// +// Copyright 2010 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_UHD_TRANSPORT_BOUNDED_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP + +#include <uhd/config.hpp> +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/circular_buffer.hpp> +#include <boost/thread/condition.hpp> + +namespace uhd{ namespace transport{ + +    /*! +     * Imlement a templated bounded buffer: +     * Used for passing elements between threads in a producer-consumer model. +     * The bounded buffer implemented waits and timed waits with condition variables. +     * The pop operation blocks on the bounded_buffer to become non empty. +     * The push operation blocks on the bounded_buffer to become non full. +     */ +    template <typename elem_type> class bounded_buffer{ +    public: +        typedef boost::shared_ptr<bounded_buffer<elem_type> > sptr; + +        /*! +         * Make a new bounded buffer object. +         * \param capacity the bounded_buffer capacity +         */ +        static sptr make(size_t capacity){ +            return sptr(new bounded_buffer(capacity)); +        } + +        /*! +         * Push a new element into the bounded buffer. +         * If the buffer is full prior to the push, +         * make room by poping the oldest element. +         * \param elem the new element to push +         * \return true if the element fit without popping for space +         */ +        UHD_INLINE bool push_with_pop_on_full(const elem_type &elem){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            if(_buffer.full()){ +                _buffer.pop_back(); +                _buffer.push_front(elem); +                lock.unlock(); +                _empty_cond.notify_one(); +                return false; +            } +            else{ +                _buffer.push_front(elem); +                lock.unlock(); +                _empty_cond.notify_one(); +                return true; +            } +        } + +        /*! +         * Push a new element into the bounded_buffer. +         * Wait until the bounded_buffer becomes non-full. +         * \param elem the new element to push +         */ +        UHD_INLINE void push_with_wait(const elem_type &elem){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            _full_cond.wait(lock, boost::bind(&bounded_buffer<elem_type>::not_full, this)); +            _buffer.push_front(elem); +            lock.unlock(); +            _empty_cond.notify_one(); +        } + +        /*! +         * Push a new element into the bounded_buffer. +         * Wait until the bounded_buffer becomes non-full or timeout. +         * \param elem the new element to push +         * \param time the timeout time +         * \return false when the operation times out +         */ +        template<typename time_type> UHD_INLINE +        bool push_with_timed_wait(const elem_type &elem, const time_type &time){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer<elem_type>::not_full, this))) return false; +            _buffer.push_front(elem); +            lock.unlock(); +            _empty_cond.notify_one(); +            return true; +        } + +        /*! +         * Pop an element from the bounded_buffer. +         * Wait until the bounded_buffer becomes non-empty. +         * \param elem the element reference pop to +         */ +        UHD_INLINE void pop_with_wait(elem_type &elem){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            _empty_cond.wait(lock, boost::bind(&bounded_buffer<elem_type>::not_empty, this)); +            elem = _buffer.back(); _buffer.pop_back(); +            lock.unlock(); +            _full_cond.notify_one(); +        } + +        /*! +         * Pop an element from the bounded_buffer. +         * Wait until the bounded_buffer becomes non-empty or timeout. +         * \param elem the element reference pop to +         * \param time the timeout time +         * \return false when the operation times out +         */ +        template<typename time_type> UHD_INLINE +        bool pop_with_timed_wait(elem_type &elem, const time_type &time){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer<elem_type>::not_empty, this))) return false; +            elem = _buffer.back(); _buffer.pop_back(); +            lock.unlock(); +            _full_cond.notify_one(); +            return true; +        } + +    private: +        boost::mutex _mutex; +        boost::condition _empty_cond, _full_cond; +        boost::circular_buffer<elem_type> _buffer; + +        bool not_full(void) const{return not _buffer.full();} +        bool not_empty(void) const{return not _buffer.empty();} + +        //private constructor +        bounded_buffer(size_t capacity) : _buffer(capacity){} +    }; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP */ diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index 525606a9f..818709973 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -34,7 +34,7 @@ namespace uhd{ namespace transport{   * If no platform specific solution is available, make returns a boost asio   * implementation that wraps the functionality around a standard send/recv calls.   */ -class UHD_API udp_zero_copy : public zero_copy_if{ +class UHD_API udp_zero_copy : public virtual zero_copy_if{  public:      typedef boost::shared_ptr<udp_zero_copy> sptr; diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 52c6d4143..2815e3189 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -19,115 +19,201 @@  #define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP  #include <uhd/config.hpp> +#include <uhd/utils/pimpl.hpp>  #include <boost/asio/buffer.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp>  namespace uhd{ namespace transport{ -/*! - * A managed receive buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after reading. - */ -class UHD_API managed_recv_buffer : boost::noncopyable{ -public: -    typedef boost::shared_ptr<managed_recv_buffer> sptr; - -    /*! -     * Managed recv buffer destructor: -     * Signal to the transport that we are done with the buffer. -     * This should be called to release the buffer to the transport. -     * After calling, the referenced memory should be considered invalid. -     */ -    virtual ~managed_recv_buffer(void){}; - -    /*! -     * Get the size of the underlying buffer. -     * \return the number of bytes -     */ -    size_t size(void) const{ -        return boost::asio::buffer_size(this->get()); -    } - -    /*! -     * Get a pointer to the underlying buffer. -     * \return a pointer into memory -     */ -    template <class T> T cast(void) const{ -        return boost::asio::buffer_cast<T>(this->get()); -    } - -private: -    /*! -     * Get a reference to the internal const buffer. -     * The buffer has a reference to memory and a size. -     * \return a boost asio const buffer -     */ -    virtual const boost::asio::const_buffer &get(void) const = 0; -}; - -/*! - * A managed send buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after writing. - */ -class UHD_API managed_send_buffer : boost::noncopyable{ -public: -    typedef boost::shared_ptr<managed_send_buffer> sptr; - -    /*! -     * Signal to the transport that we are done with the buffer. -     * This should be called to commit the write to the transport object. -     * After calling, the referenced memory should be considered invalid. -     * \param num_bytes the number of bytes written into the buffer -     */ -    virtual void commit(size_t num_bytes) = 0; -      /*! -     * Get the size of the underlying buffer. -     * \return the number of bytes +     * A managed receive buffer: +     * Contains a reference to transport-managed memory, +     * and a method to release the memory after reading.       */ -    size_t size(void) const{ -        return boost::asio::buffer_size(this->get()); -    } +    class UHD_API managed_recv_buffer : boost::noncopyable{ +    public: +        typedef boost::shared_ptr<managed_recv_buffer> sptr; + +        /*! +         * Managed recv buffer destructor: +         * Signal to the transport that we are done with the buffer. +         * This should be called to release the buffer to the transport. +         * After calling, the referenced memory should be considered invalid. +         */ +        virtual ~managed_recv_buffer(void) = 0; + +        /*! +         * Get the size of the underlying buffer. +         * \return the number of bytes +         */ +        size_t size(void) const{ +            return boost::asio::buffer_size(this->get()); +        } + +        /*! +         * Get a pointer to the underlying buffer. +         * \return a pointer into memory +         */ +        template <class T> T cast(void) const{ +            return boost::asio::buffer_cast<T>(this->get()); +        } + +    private: +        /*! +         * Get a reference to the internal const buffer. +         * The buffer has a reference to memory and a size. +         * \return a boost asio const buffer +         */ +        virtual const boost::asio::const_buffer &get(void) const = 0; +    };      /*! -     * Get a pointer to the underlying buffer. -     * \return a pointer into memory +     * A managed send buffer: +     * Contains a reference to transport-managed memory, +     * and a method to release the memory after writing.       */ -    template <class T> T cast(void) const{ -        return boost::asio::buffer_cast<T>(this->get()); -    } +    class UHD_API managed_send_buffer : boost::noncopyable{ +    public: +        typedef boost::shared_ptr<managed_send_buffer> sptr; + +        /*! +         * Signal to the transport that we are done with the buffer. +         * This should be called to commit the write to the transport object. +         * After calling, the referenced memory should be considered invalid. +         * \param num_bytes the number of bytes written into the buffer +         */ +        virtual void commit(size_t num_bytes) = 0; + +        /*! +         * Get the size of the underlying buffer. +         * \return the number of bytes +         */ +        size_t size(void) const{ +            return boost::asio::buffer_size(this->get()); +        } + +        /*! +         * Get a pointer to the underlying buffer. +         * \return a pointer into memory +         */ +        template <class T> T cast(void) const{ +            return boost::asio::buffer_cast<T>(this->get()); +        } + +    private: +        /*! +         * Get a reference to the internal mutable buffer. +         * The buffer has a reference to memory and a size. +         * \return a boost asio mutable buffer +         */ +        virtual const boost::asio::mutable_buffer &get(void) const = 0; +    }; -private:      /*! -     * Get a reference to the internal mutable buffer. -     * The buffer has a reference to memory and a size. -     * \return a boost asio mutable buffer +     * A zero-copy interface for transport objects. +     * Provides a way to get send and receive buffers +     * with memory managed by the transport object.       */ -    virtual const boost::asio::mutable_buffer &get(void) const = 0; -}; - -/*! - * A zero-copy interface for transport objects. - * Provides a way to get send and receive buffers - * with memory managed by the transport object. - */ -class UHD_API zero_copy_if : boost::noncopyable{ -public: -    typedef boost::shared_ptr<zero_copy_if> sptr; +    class UHD_API zero_copy_if : boost::noncopyable{ +    public: +        typedef boost::shared_ptr<zero_copy_if> sptr; + +        /*! +         * Get a new receive buffer from this transport object. +         */ +        virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; + +        /*! +         * Get the maximum number of receive frames: +         *   The maximum number of valid managed recv buffers, +         *   or the maximum number of frames in the ring buffer, +         *   depending upon the underlying implementation. +         * \return number of frames +         */ +        virtual size_t get_num_recv_frames(void) const = 0; + +        /*! +         * Get a new send buffer from this transport object. +         */ +        virtual managed_send_buffer::sptr get_send_buff(void) = 0; + +        /*! +         * Get the maximum number of send frames: +         *   The maximum number of valid managed send buffers, +         *   or the maximum number of frames in the ring buffer, +         *   depending upon the underlying implementation. +         * \return number of frames +         */ +        virtual size_t get_num_send_frames(void) const = 0; + +    };      /*! -     * Get a new receive buffer from this transport object. +     * A phony-zero-copy interface for transport objects that +     * provides a zero-copy interface on top of copying transport. +     * This interface implements the get managed recv buffer, +     * the base class must implement the private recv method.       */ -    virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; +    class UHD_API phony_zero_copy_recv_if : public virtual zero_copy_if{ +    public: +        /*! +         * Create a phony zero copy recv interface. +         * \param max_buff_size max buffer size in bytes +         */ +        phony_zero_copy_recv_if(size_t max_buff_size); + +        //! destructor +        virtual ~phony_zero_copy_recv_if(void); + +        /*! +         * Get a new receive buffer from this transport object. +         */ +        managed_recv_buffer::sptr get_recv_buff(void); + +    private: +        /*! +         * Perform a private copying recv. +         * \param buff the buffer to write data into +         * \return the number of bytes written to buff +         */ +        virtual size_t recv(const boost::asio::mutable_buffer &buff) = 0; + +        UHD_PIMPL_DECL(impl) _impl; +    };      /*! -     * Get a new send buffer from this transport object. +     * A phony-zero-copy interface for transport objects that +     * provides a zero-copy interface on top of copying transport. +     * This interface implements the get managed send buffer, +     * the base class must implement the private send method.       */ -    virtual managed_send_buffer::sptr get_send_buff(void) = 0; -}; +    class UHD_API phony_zero_copy_send_if : public virtual zero_copy_if{ +    public: +        /*! +         * Create a phony zero copy send interface. +         * \param max_buff_size max buffer size in bytes +         */ +        phony_zero_copy_send_if(size_t max_buff_size); + +        //! destructor +        virtual ~phony_zero_copy_send_if(void); + +        /*! +         * Get a new send buffer from this transport object. +         */ +        managed_send_buffer::sptr get_send_buff(void); + +    private: +        /*! +         * Perform a private copying send. +         * \param buff the buffer to read data from +         * \return the number of bytes read from buff +         */ +        virtual size_t send(const boost::asio::const_buffer &buff) = 0; + +        UHD_PIMPL_DECL(impl) _impl; +    };  }} //namespace diff --git a/host/include/uhd/usrp/dboard_base.hpp b/host/include/uhd/usrp/dboard_base.hpp index 28bf2ae66..e88d39876 100644 --- a/host/include/uhd/usrp/dboard_base.hpp +++ b/host/include/uhd/usrp/dboard_base.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/wax.hpp> +#include <uhd/utils/pimpl.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp>  #include <uhd/usrp/dboard_id.hpp> @@ -58,8 +59,7 @@ protected:      dboard_id_t get_tx_id(void);  private: -    struct dboard_base_impl; -    dboard_base_impl *_impl; +    UHD_PIMPL_DECL(impl) _impl;  };  /*! diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index f588c6310..391e684c8 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -20,6 +20,7 @@ INSTALL(FILES      assert.hpp      exception.hpp      gain_handler.hpp +    pimpl.hpp      props.hpp      safe_main.hpp      static.hpp diff --git a/host/include/uhd/utils/exception.hpp b/host/include/uhd/utils/exception.hpp index 40e81fae0..e74c19b9c 100644 --- a/host/include/uhd/utils/exception.hpp +++ b/host/include/uhd/utils/exception.hpp @@ -35,4 +35,11 @@      "  at " + std::string(__FILE__) + ":" + BOOST_STRINGIZE(__LINE__) + "\n" \  ) +/*! + * Throws an invalid code path exception with throw-site information. + * Use this macro in places that code execution is not supposed to go. + */ +#define UHD_THROW_INVALID_CODE_PATH() \ +    throw std::runtime_error(UHD_THROW_SITE_INFO("invalid code path")) +  #endif /* INCLUDED_UHD_UTILS_EXCEPTION_HPP */ diff --git a/host/include/uhd/utils/pimpl.hpp b/host/include/uhd/utils/pimpl.hpp new file mode 100644 index 000000000..09bf0c0a2 --- /dev/null +++ b/host/include/uhd/utils/pimpl.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2010 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_UHD_UTILS_PIMPL_HPP +#define INCLUDED_UHD_UTILS_PIMPL_HPP + +#include <uhd/config.hpp> +#include <boost/shared_ptr.hpp> + +/*! \file pimpl.hpp + * "Pimpl idiom" (pointer to implementation idiom). + * The UHD_PIMPL_* macros simplify code overhead for declaring and making pimpls. + * + * Each pimpl is implemented as a shared pointer to the implementation: + * - The container class will not have to deallocate the pimpl. + * - The container class will use the pimpl as a regular pointer. + * - Usage: _impl->method(arg0, arg1) + * - Usage: _impl->member = value; + * + * \see http://en.wikipedia.org/wiki/Opaque_pointer + */ + +/*! + * Make a declaration for a pimpl in a header file. + * - Usage: UHD_PIMPL_DECL(impl) _impl; + * \param _name the name of the pimpl class + */ +#define UHD_PIMPL_DECL(_name) \ +    struct _name; boost::shared_ptr<_name> + +/*! + * Make an instance of a pimpl in a source file. + * - Usage: _impl = UHD_PIMPL_MAKE(impl, ()); + * - Usage: _impl = UHD_PIMPL_MAKE(impl, (a0, a1)); + * \param _name the name of the pimpl class + * \param _args the constructor args for the pimpl + */ +#define UHD_PIMPL_MAKE(_name, _args) \ +    boost::shared_ptr<_name>(new _name _args) + +#endif /* INCLUDED_UHD_UTILS_PIMPL_HPP */ diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index ed8c35225..a74f7d527 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -50,4 +50,5 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_SOURCE_DIR}/lib/transport/udp_simple.cpp      ${CMAKE_SOURCE_DIR}/lib/transport/udp_zero_copy_asio.cpp      ${CMAKE_SOURCE_DIR}/lib/transport/vrt_packet_handler.hpp +    ${CMAKE_SOURCE_DIR}/lib/transport/zero_copy.cpp  ) diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index f8a222475..ced606777 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -28,58 +28,8 @@ using namespace uhd::transport;   * Constants   **********************************************************************/  static const size_t MIN_SOCK_BUFF_SIZE = size_t(100e3); -static const size_t MAX_DGRAM_SIZE = 2048; //assume max size on send and recv -static const double RECV_TIMEOUT = 0.1; // 100 ms - -/*********************************************************************** - * Managed receive buffer implementation for udp zero-copy asio: - **********************************************************************/ -class managed_recv_buffer_impl : public managed_recv_buffer{ -public: -    managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ -        /* NOP */ -    } - -    ~managed_recv_buffer_impl(void){ -        delete [] this->cast<const boost::uint8_t *>(); -    } - -private: -    const boost::asio::const_buffer &get(void) const{ -        return _buff; -    } - -    const boost::asio::const_buffer _buff; -}; - -/*********************************************************************** - * Managed send buffer implementation for udp zero-copy asio: - **********************************************************************/ -class managed_send_buffer_impl : public managed_send_buffer{ -public: -    managed_send_buffer_impl( -        const boost::asio::mutable_buffer &buff, -        boost::asio::ip::udp::socket *socket -    ) : _buff(buff), _socket(socket){ -        /* NOP */ -    } - -    ~managed_send_buffer_impl(void){ -        /* NOP */ -    } - -    void commit(size_t num_bytes){ -        _socket->send(boost::asio::buffer(_buff, num_bytes)); -    } - -private: -    const boost::asio::mutable_buffer &get(void) const{ -        return _buff; -    } - -    const boost::asio::mutable_buffer _buff; -    boost::asio::ip::udp::socket      *_socket; -}; +static const size_t MAX_DGRAM_SIZE = 1500; //assume max size on send and recv +static const double RECV_TIMEOUT = 0.1; //100 ms  /***********************************************************************   * Zero Copy UDP implementation with ASIO: @@ -88,95 +38,108 @@ private:   *   However, it is not a true zero copy implementation as each   *   send and recv requires a copy operation to/from userspace.   **********************************************************************/ -class udp_zero_copy_impl : public udp_zero_copy{ +class udp_zero_copy_impl: +    public phony_zero_copy_recv_if, +    public phony_zero_copy_send_if, +    public udp_zero_copy +{  public:      typedef boost::shared_ptr<udp_zero_copy_impl> sptr; -    //structors -    udp_zero_copy_impl(const std::string &addr, const std::string &port); -    ~udp_zero_copy_impl(void); +    udp_zero_copy_impl( +        const std::string &addr, +        const std::string &port +    ): +        phony_zero_copy_recv_if(MAX_DGRAM_SIZE), +        phony_zero_copy_send_if(MAX_DGRAM_SIZE) +    { +        //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + +        // resolve the address +        boost::asio::ip::udp::resolver resolver(_io_service); +        boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); +        boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + +        // create, open, and connect the socket +        _socket = new boost::asio::ip::udp::socket(_io_service); +        _socket->open(boost::asio::ip::udp::v4()); +        _socket->connect(receiver_endpoint); +        _sock_fd = _socket->native(); +    } -    //send/recv -    managed_recv_buffer::sptr get_recv_buff(void); -    managed_send_buffer::sptr get_send_buff(void); +    ~udp_zero_copy_impl(void){ +        delete _socket; +    } -    //manage buffer -    template <typename Opt> size_t get_buff_size(void){ +    //get size for internal socket buffer +    template <typename Opt> size_t get_buff_size(void) const{          Opt option;          _socket->get_option(option);          return option.value();      } +    //set size for internal socket buffer      template <typename Opt> size_t resize_buff(size_t num_bytes){          Opt option(num_bytes);          _socket->set_option(option);          return get_buff_size<Opt>();      } -private: -    boost::asio::ip::udp::socket   *_socket; -    boost::asio::io_service        _io_service; - -    //send and recv buffer memory (allocated once) -    boost::uint8_t _send_mem[MIN_SOCK_BUFF_SIZE]; - -    managed_send_buffer::sptr _send_buff; -}; -udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ -    //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - -    // resolve the address -    boost::asio::ip::udp::resolver resolver(_io_service); -    boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); -    boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); - -    // create, open, and connect the socket -    _socket = new boost::asio::ip::udp::socket(_io_service); -    _socket->open(boost::asio::ip::udp::v4()); -    _socket->connect(receiver_endpoint); - -    // create the managed send buff (just once) -    _send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( -        boost::asio::buffer(_send_mem, MIN_SOCK_BUFF_SIZE), _socket -    )); - -    // set recv timeout -    timeval tv; -    tv.tv_sec = 0; -    tv.tv_usec = size_t(RECV_TIMEOUT*1e6); -    UHD_ASSERT_THROW(setsockopt( -        _socket->native(), -        SOL_SOCKET, SO_RCVTIMEO, -        (const char *)&tv, sizeof(timeval) -    ) == 0); -} +    //The number of frames is approximately the buffer size divided by the max datagram size. +    //In reality, this is a phony zero-copy interface and the number of frames is infinite. +    //However, its sensible to advertise a frame count that is approximate to buffer size. +    //This way, the transport caller will have an idea about how much buffering to create. -udp_zero_copy_impl::~udp_zero_copy_impl(void){ -    delete _socket; -} - -managed_recv_buffer::sptr udp_zero_copy_impl::get_recv_buff(void){ -    //allocate memory -    boost::uint8_t *recv_mem = new boost::uint8_t[MAX_DGRAM_SIZE]; +    size_t get_num_recv_frames(void) const{ +        return this->get_buff_size<boost::asio::socket_base::receive_buffer_size>()/MAX_DGRAM_SIZE; +    } -    //call recv() with timeout option -    size_t num_bytes = _socket->receive(boost::asio::buffer(recv_mem, MIN_SOCK_BUFF_SIZE)); +    size_t get_num_send_frames(void) const{ +        return this->get_buff_size<boost::asio::socket_base::send_buffer_size>()/MAX_DGRAM_SIZE; +    } -    //create a new managed buffer to house the data -    return managed_recv_buffer::sptr( -        new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) -    ); -} +private: +    boost::asio::ip::udp::socket   *_socket; +    boost::asio::io_service        _io_service; +    int                            _sock_fd; + +    size_t recv(const boost::asio::mutable_buffer &buff){ +        //setup timeval for timeout +        timeval tv; +        tv.tv_sec = 0; +        tv.tv_usec = int(RECV_TIMEOUT*1e6); + +        //setup rset for timeout +        fd_set rset; +        FD_ZERO(&rset); +        FD_SET(_sock_fd, &rset); + +        //call select to perform timed wait +        if (::select(_sock_fd+1, &rset, NULL, NULL, &tv) <= 0) return 0; + +        return ::recv( +            _sock_fd, +            boost::asio::buffer_cast<char *>(buff), +            boost::asio::buffer_size(buff), +            0 +        ); +    } -managed_send_buffer::sptr udp_zero_copy_impl::get_send_buff(void){ -    return _send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these -} +    size_t send(const boost::asio::const_buffer &buff){ +        return ::send( +            _sock_fd, +            boost::asio::buffer_cast<const char *>(buff), +            boost::asio::buffer_size(buff), +            0 +        ); +    } +};  /***********************************************************************   * UDP zero copy make function   **********************************************************************/ -template<typename Opt> static inline void resize_buff_helper( +template<typename Opt> static void resize_buff_helper(      udp_zero_copy_impl::sptr udp_trans,      size_t target_size,      const std::string &name diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp index e64e3383d..d6b863040 100644 --- a/host/lib/transport/vrt_packet_handler.hpp +++ b/host/lib/transport/vrt_packet_handler.hpp @@ -54,6 +54,8 @@ namespace vrt_packet_handler{          }      }; +    typedef boost::function<uhd::transport::managed_recv_buffer::sptr(void)> get_recv_buff_t; +      typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> recv_cb_t;      static UHD_INLINE void recv_cb_nop(uhd::transport::managed_recv_buffer::sptr){ @@ -112,15 +114,16 @@ namespace vrt_packet_handler{          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          double tick_rate, -        uhd::transport::zero_copy_if::sptr zc_iface, +        const get_recv_buff_t &get_recv_buff,          //use these two params to handle a layer above vrt          size_t vrt_header_offset_words32, -        const recv_cb_t& recv_cb +        const recv_cb_t &recv_cb      ){          //perform a receive if no rx data is waiting to be copied          if (boost::asio::buffer_size(state.copy_buff) == 0){              state.fragment_offset_in_samps = 0; -            state.managed_buff = zc_iface->get_recv_buff(); +            state.managed_buff = get_recv_buff(); +            if (state.managed_buff.get() == NULL) return 0;              recv_cb(state.managed_buff); //callback before vrt unpack              try{                  _recv1_helper( @@ -169,7 +172,7 @@ namespace vrt_packet_handler{          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          double tick_rate, -        uhd::transport::zero_copy_if::sptr zc_iface, +        const get_recv_buff_t &get_recv_buff,          //use these two params to handle a layer above vrt          size_t vrt_header_offset_words32 = 0,          const recv_cb_t& recv_cb = &recv_cb_nop @@ -189,7 +192,7 @@ namespace vrt_packet_handler{                  metadata,                  io_type, otw_type,                  tick_rate, -                zc_iface, +                get_recv_buff,                  vrt_header_offset_words32,                  recv_cb              ); @@ -208,7 +211,7 @@ namespace vrt_packet_handler{                      (accum_num_samps == 0)? metadata : tmp_md, //only the first metadata gets kept                      io_type, otw_type,                      tick_rate, -                    zc_iface, +                    get_recv_buff,                      vrt_header_offset_words32,                      recv_cb                  ); @@ -234,6 +237,8 @@ namespace vrt_packet_handler{          }      }; +    typedef boost::function<uhd::transport::managed_send_buffer::sptr(void)> get_send_buff_t; +      typedef boost::function<void(uhd::transport::managed_send_buffer::sptr)> send_cb_t;      static UHD_INLINE void send_cb_nop(uhd::transport::managed_send_buffer::sptr){ @@ -252,12 +257,12 @@ namespace vrt_packet_handler{          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          double tick_rate, -        uhd::transport::zero_copy_if::sptr zc_iface, +        const get_send_buff_t &get_send_buff,          size_t vrt_header_offset_words32,          const send_cb_t& send_cb      ){          //get a new managed send buffer -        uhd::transport::managed_send_buffer::sptr send_buff = zc_iface->get_send_buff(); +        uhd::transport::managed_send_buffer::sptr send_buff = get_send_buff();          boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>() + vrt_header_offset_words32;          size_t num_header_words32, num_packet_words32; @@ -298,7 +303,7 @@ namespace vrt_packet_handler{          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          double tick_rate, -        uhd::transport::zero_copy_if::sptr zc_iface, +        const get_send_buff_t &get_send_buff,          size_t max_samples_per_packet,          //use these two params to handle a layer above vrt          size_t vrt_header_offset_words32 = 0, @@ -319,7 +324,7 @@ namespace vrt_packet_handler{                  metadata,                  io_type, otw_type,                  tick_rate, -                zc_iface, +                get_send_buff,                  vrt_header_offset_words32,                  send_cb              ); @@ -353,7 +358,7 @@ namespace vrt_packet_handler{                      md,                      io_type, otw_type,                      tick_rate, -                    zc_iface, +                    get_send_buff,                      vrt_header_offset_words32,                      send_cb                  ); diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp new file mode 100644 index 000000000..27f41329b --- /dev/null +++ b/host/lib/transport/zero_copy.cpp @@ -0,0 +1,139 @@ +// +// Copyright 2010 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 <uhd/transport/zero_copy.hpp> +#include <boost/cstdint.hpp> +#include <boost/function.hpp> +#include <boost/bind.hpp> + +using namespace uhd::transport; + +/*********************************************************************** + * The pure-virtual deconstructor needs an implementation to be happy + **********************************************************************/ +managed_recv_buffer::~managed_recv_buffer(void){ +    /* NOP */ +} + +/*********************************************************************** + * Phony zero-copy recv interface implementation + **********************************************************************/ + +//! phony zero-copy recv buffer implementation +class managed_recv_buffer_impl : public managed_recv_buffer{ +public: +    managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ +        /* NOP */ +    } + +    ~managed_recv_buffer_impl(void){ +        delete [] this->cast<const boost::uint8_t *>(); +    } + +private: +    const boost::asio::const_buffer &get(void) const{ +        return _buff; +    } + +    const boost::asio::const_buffer _buff; +}; + +//! phony zero-copy recv interface implementation +struct phony_zero_copy_recv_if::impl{ +    size_t max_buff_size; +}; + +phony_zero_copy_recv_if::phony_zero_copy_recv_if(size_t max_buff_size){ +    _impl = UHD_PIMPL_MAKE(impl, ()); +    _impl->max_buff_size = max_buff_size; +} + +phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){ +    /* NOP */ +} + +managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(void){ +    //allocate memory +    boost::uint8_t *recv_mem = new boost::uint8_t[_impl->max_buff_size]; + +    //call recv() with timeout option +    size_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size)); + +    //create a new managed buffer to house the data +    return managed_recv_buffer::sptr( +        new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) +    ); +} + +/*********************************************************************** + * Phony zero-copy send interface implementation + **********************************************************************/ + +//! phony zero-copy send buffer implementation +class managed_send_buffer_impl : public managed_send_buffer{ +public: +    typedef boost::function<size_t(const boost::asio::const_buffer &)> send_fcn_t; + +    managed_send_buffer_impl( +        const boost::asio::mutable_buffer &buff, +        const send_fcn_t &send_fcn +    ): +        _buff(buff), +        _send_fcn(send_fcn) +    { +        /* NOP */ +    } + +    ~managed_send_buffer_impl(void){ +        /* NOP */ +    } + +    void commit(size_t num_bytes){ +        _send_fcn(boost::asio::buffer(_buff, num_bytes)); +    } + +private: +    const boost::asio::mutable_buffer &get(void) const{ +        return _buff; +    } + +    const boost::asio::mutable_buffer _buff; +    const send_fcn_t                  _send_fcn; +}; + +//! phony zero-copy send interface implementation +struct phony_zero_copy_send_if::impl{ +    boost::uint8_t *send_mem; +    managed_send_buffer::sptr send_buff; +}; + +phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){ +    _impl = UHD_PIMPL_MAKE(impl, ()); +    _impl->send_mem = new boost::uint8_t[max_buff_size]; +    _impl->send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( +        boost::asio::buffer(_impl->send_mem, max_buff_size), +        boost::bind(&phony_zero_copy_send_if::send, this, _1) +    )); +} + +phony_zero_copy_send_if::~phony_zero_copy_send_if(void){ +    delete [] _impl->send_mem; +} + +managed_send_buffer::sptr phony_zero_copy_send_if::get_send_buff(void){ +    return _impl->send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these +} diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 89e707718..17fc00d24 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -91,7 +91,7 @@ private:      uhd::dict<dboard_iface::unit_t, bool> _div2;      double       _rx_lo_freq, _tx_lo_freq;      std::string  _rx_ant; -    float        _rx_pga0_gain; +    uhd::dict<std::string, float> _rx_gains;      void set_rx_lo_freq(double freq);      void set_tx_lo_freq(double freq); @@ -100,8 +100,6 @@ private:      void set_rx_gain(float gain, const std::string &name);      void set_tx_gain(float gain, const std::string &name); -    void set_rx_pga0_gain(float gain); -      /*!       * Set the LO frequency for the particular dboard unit.       * \param unit which unit rx or tx @@ -198,7 +196,10 @@ rfx_xcvr::rfx_xcvr(      set_rx_lo_freq((_freq_range.min + _freq_range.max)/2.0);      set_tx_lo_freq((_freq_range.min + _freq_range.max)/2.0);      set_rx_ant("RX2"); -    set_rx_pga0_gain(0); + +    BOOST_FOREACH(const std::string &name, rfx_rx_gain_ranges.keys()){ +        set_rx_gain(rfx_rx_gain_ranges[name].min, name); +    }  }  rfx_xcvr::~rfx_xcvr(void){ @@ -230,20 +231,7 @@ void rfx_xcvr::set_tx_ant(const std::string &ant){  /***********************************************************************   * Gain Handling   **********************************************************************/ -void rfx_xcvr::set_tx_gain(float, const std::string &name){ -    assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); -    UHD_ASSERT_THROW(false); //no gains to set -} - -void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ -    assert_has(rfx_rx_gain_ranges.keys(), name, "rfx rx gain name"); -    if(name == "PGA0"){ -        this->set_rx_pga0_gain(gain); -    } -    else UHD_ASSERT_THROW(false); -} - -void rfx_xcvr::set_rx_pga0_gain(float gain){ +static float rx_pga0_gain_to_dac_volts(float &gain){      //voltage level constants (negative slope)      static const float max_volts = float(.2), min_volts = float(1.2);      static const float slope = (max_volts-min_volts)/45; @@ -251,11 +239,27 @@ void rfx_xcvr::set_rx_pga0_gain(float gain){      //calculate the voltage for the aux dac      float dac_volts = std::clip<float>(gain*slope + min_volts, max_volts, min_volts); -    //write the new voltage to the aux dac -    this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, 1, dac_volts); +    //the actual gain setting +    gain = (dac_volts - min_volts)/slope; + +    return dac_volts; +} + +void rfx_xcvr::set_tx_gain(float, const std::string &name){ +    assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); +    UHD_THROW_INVALID_CODE_PATH(); //no gains to set +} + +void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ +    assert_has(rfx_rx_gain_ranges.keys(), name, "rfx rx gain name"); +    if(name == "PGA0"){ +        float dac_volts = rx_pga0_gain_to_dac_volts(gain); +        _rx_gains[name] = gain; -    //shadow the actual gain setting -    _rx_pga0_gain = (dac_volts - min_volts)/slope; +        //write the new voltage to the aux dac +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, 1, dac_volts); +    } +    else UHD_THROW_INVALID_CODE_PATH();  }  /*********************************************************************** @@ -397,8 +401,8 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          return;      case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(name == "PGA0"); -        val = _rx_pga0_gain; +        assert_has(_rx_gains.keys(), name, "rfx rx gain name"); +        val = _rx_gains[name];          return;      case SUBDEV_PROP_GAIN_RANGE: diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp index 23654860f..95dcb3802 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -15,8 +15,6 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -static const bool wbx_debug = false; -  // Common IO Pins  #define ANTSW_IO        ((1 << 5)|(1 << 15))    // on UNIT_TX, 0 = TX, 1 = RX, on UNIT_RX 0 = main ant, 1 = RX2  #define ADF4350_CE      (1 << 3) @@ -84,17 +82,30 @@ using namespace uhd::usrp;  using namespace boost::assign;  /*********************************************************************** - * The WBX dboard + * The WBX dboard constants   **********************************************************************/ -static const float _max_rx_pga0_gain = 31.5; -static const float _max_tx_pga0_gain = 25; +static const bool wbx_debug = false; + +static const freq_range_t wbx_freq_range(50e6, 2.22e9); + +static const prop_names_t wbx_tx_antennas = list_of("TX/RX"); +static const prop_names_t wbx_rx_antennas = list_of("TX/RX")("RX2"); + +static const uhd::dict<std::string, gain_range_t> wbx_tx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 25, float(0.05))) +; + +static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 31.5, float(0.5))) +; + +/*********************************************************************** + * The WBX dboard + **********************************************************************/  class wbx_xcvr : public xcvr_dboard_base{  public: -    wbx_xcvr( -        ctor_args_t args, -        const freq_range_t &freq_range -    ); +    wbx_xcvr(ctor_args_t args);      ~wbx_xcvr(void);      void rx_get(const wax::obj &key, wax::obj &val); @@ -104,20 +115,16 @@ public:      void tx_set(const wax::obj &key, const wax::obj &val);  private: -    freq_range_t _freq_range; -    uhd::dict<dboard_iface::unit_t, bool> _div2; +    uhd::dict<std::string, float> _tx_gains, _rx_gains;      double       _rx_lo_freq, _tx_lo_freq; -    std::string  _rx_ant; -    int          _rx_pga0_attn_iobits; -    float        _rx_pga0_gain; -    float        _tx_pga0_gain; +    std::string  _tx_ant, _rx_ant;      void set_rx_lo_freq(double freq);      void set_tx_lo_freq(double freq);      void set_rx_ant(const std::string &ant); -    void set_rx_pga0_gain(float gain); -    void set_rx_pga0_attn(float attn); -    void set_tx_pga0_gain(float gain); +    void set_tx_ant(const std::string &ant); +    void set_rx_gain(float gain, const std::string &name); +    void set_tx_gain(float gain, const std::string &name);      void update_atr(void); @@ -143,7 +150,7 @@ private:   * Register the WBX dboard (min freq, max freq, rx div2, tx div2)   **********************************************************************/  static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){ -    return dboard_base::sptr(new wbx_xcvr(args, freq_range_t(50e6, 2220e6))); +    return dboard_base::sptr(new wbx_xcvr(args));  }  UHD_STATIC_BLOCK(reg_wbx_dboards){ @@ -154,11 +161,7 @@ UHD_STATIC_BLOCK(reg_wbx_dboards){  /***********************************************************************   * Structors   **********************************************************************/ -wbx_xcvr::wbx_xcvr( -    ctor_args_t args, -    const freq_range_t &freq_range -) : xcvr_dboard_base(args){ -    _freq_range = freq_range; +wbx_xcvr::wbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      //enable the clocks that we need      this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); @@ -174,11 +177,16 @@ wbx_xcvr::wbx_xcvr(      ) % RXIO_MASK % TXIO_MASK << std::endl;      //set some default values -    set_rx_lo_freq((_freq_range.min + _freq_range.max)/2.0); -    set_tx_lo_freq((_freq_range.min + _freq_range.max)/2.0); +    set_rx_lo_freq((wbx_freq_range.min + wbx_freq_range.max)/2.0); +    set_tx_lo_freq((wbx_freq_range.min + wbx_freq_range.max)/2.0);      set_rx_ant("RX2"); -    set_rx_pga0_gain(0); -    set_tx_pga0_gain(0); + +    BOOST_FOREACH(const std::string &name, wbx_tx_gain_ranges.keys()){ +        set_tx_gain(wbx_tx_gain_ranges[name].min, name); +    } +    BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){ +        set_rx_gain(wbx_rx_gain_ranges[name].min, name); +    }  }  wbx_xcvr::~wbx_xcvr(void){ @@ -186,10 +194,81 @@ wbx_xcvr::~wbx_xcvr(void){  }  /*********************************************************************** - * Helper Methods + * Gain Handling + **********************************************************************/ +static int rx_pga0_gain_to_iobits(float &gain){ +    //clip the input +    gain = std::clip<float>(gain, wbx_rx_gain_ranges["PGA0"].min, wbx_rx_gain_ranges["PGA0"].max); + +    //convert to attenuation and update iobits for atr +    float attn = wbx_rx_gain_ranges["PGA0"].max - gain; + +    //calculate the attenuation +    int attn_code = int(floor(attn*2)); +    int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; + +     +    if (wbx_debug) std::cerr << boost::format( +        "WBX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" +    ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; + +    //the actual gain setting +    gain = wbx_rx_gain_ranges["PGA0"].max - float(attn_code)/2; + +    return iobits; +} + +static float tx_pga0_gain_to_dac_volts(float &gain){ +    //clip the input +    gain = std::clip<float>(gain, wbx_tx_gain_ranges["PGA0"].min, wbx_tx_gain_ranges["PGA0"].max); + +    //voltage level constants +    static const float max_volts = float(0.5), min_volts = float(1.4); +    static const float slope = (max_volts-min_volts)/wbx_tx_gain_ranges["PGA0"].max; + +    //calculate the voltage for the aux dac +    float dac_volts = gain*slope + min_volts; + +    if (wbx_debug) std::cerr << boost::format( +        "WBX TX Gain: %f dB, dac_volts: %f V" +    ) % gain % dac_volts << std::endl; + +    //the actual gain setting +    gain = (dac_volts - min_volts)/slope; + +    return dac_volts; +} + +void wbx_xcvr::set_tx_gain(float gain, const std::string &name){ +    assert_has(wbx_tx_gain_ranges.keys(), name, "wbx tx gain name"); +    if(name == "PGA0"){ +        float dac_volts = tx_pga0_gain_to_dac_volts(gain); +        _tx_gains[name] = gain; + +        //write the new voltage to the aux dac +        this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, 0, dac_volts); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +} + +void wbx_xcvr::set_rx_gain(float gain, const std::string &name){ +    assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name"); +    if(name == "PGA0"){ +        rx_pga0_gain_to_iobits(gain); +        _rx_gains[name] = gain; + +        //write the new gain to atr regs +        update_atr(); +    } +    else UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Antenna Handling   **********************************************************************/  void wbx_xcvr::update_atr(void){      //calculate atr pins +    int pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]);      //setup the tx atr (this does not change with antenna)      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        TX_POWER_UP | ANT_XX | TX_MIXER_DIS); @@ -199,31 +278,23 @@ void wbx_xcvr::update_atr(void){      //setup the rx atr (this does not change with antenna)      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, -        _rx_pga0_attn_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); +        pga0_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, -        _rx_pga0_attn_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); +        pga0_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS);      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, -        _rx_pga0_attn_iobits | RX_POWER_UP | ANT_RX2| RX_MIXER_ENB); +        pga0_iobits | RX_POWER_UP | ANT_RX2| RX_MIXER_ENB);      //set the rx atr regs that change with antenna setting      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, -        _rx_pga0_attn_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); +        pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2));      if (wbx_debug) std::cerr << boost::format(          "WBX RXONLY ATR REG: 0x%08x" -    ) % (_rx_pga0_attn_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; -} - -void wbx_xcvr::set_rx_lo_freq(double freq){ -    _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); -} - -void wbx_xcvr::set_tx_lo_freq(double freq){ -    _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); +    ) % (pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl;  }  void wbx_xcvr::set_rx_ant(const std::string &ant){      //validate input -    UHD_ASSERT_THROW(ant == "TX/RX" or ant == "RX2"); +    assert_has(wbx_rx_antennas, ant, "wbx rx antenna name");      //shadow the setting      _rx_ant = ant; @@ -232,49 +303,20 @@ void wbx_xcvr::set_rx_ant(const std::string &ant){      update_atr();  } -void wbx_xcvr::set_rx_pga0_gain(float gain){ -    //clip the input -    gain = std::clip<float>(gain, 0, _max_rx_pga0_gain); - -    //shadow the setting (does not account for precision loss) -    _rx_pga0_gain = gain; - -    //convert to attenuation and update iobits for atr -    set_rx_pga0_attn(_max_rx_pga0_gain - gain); - -    //write the new gain to atr regs -    update_atr(); +void wbx_xcvr::set_tx_ant(const std::string &ant){ +    assert_has(wbx_tx_antennas, ant, "wbx tx antenna name"); +    //only one antenna option, do nothing  } -void wbx_xcvr::set_rx_pga0_attn(float attn) -{ -    int attn_code = int(floor(attn*2)); -    _rx_pga0_attn_iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; -    if (wbx_debug) std::cerr << boost::format( -        "WBX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" -    ) % attn % attn_code % (_rx_pga0_attn_iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; +/*********************************************************************** + * Tuning + **********************************************************************/ +void wbx_xcvr::set_rx_lo_freq(double freq){ +    _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq);  } -void wbx_xcvr::set_tx_pga0_gain(float gain){ -    //clip the input -    gain = std::clip<float>(gain, 0, _max_tx_pga0_gain); - -    //voltage level constants -    static const float max_volts = float(0.5), min_volts = float(1.4); -    static const float slope = (max_volts-min_volts)/_max_rx_pga0_gain; - -    //calculate the voltage for the aux dac -    float dac_volts = gain*slope + min_volts; - -    if (wbx_debug) std::cerr << boost::format( -        "WBX TX Gain: %f dB, dac_volts: %f V" -    ) % gain % dac_volts << std::endl; - -    //write the new voltage to the aux dac -    this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, 0, dac_volts); - -    //shadow the setting (does not account for precision loss) -    _tx_pga0_gain = gain; +void wbx_xcvr::set_tx_lo_freq(double freq){ +    _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq);  }  double wbx_xcvr::set_lo_freq( @@ -286,7 +328,7 @@ double wbx_xcvr::set_lo_freq(      ) % (target_freq/1e6) << std::endl;      //clip the input -    target_freq = std::clip(target_freq, _freq_range.min, _freq_range.max); +    target_freq = std::clip(target_freq, wbx_freq_range.min, wbx_freq_range.max);      //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)      static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of @@ -439,17 +481,17 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          return;      case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(name == "PGA0"); -        val = _rx_pga0_gain; +        assert_has(_rx_gains.keys(), name, "wbx rx gain name"); +        val = _rx_gains[name];          return;      case SUBDEV_PROP_GAIN_RANGE: -        UHD_ASSERT_THROW(name == "PGA0"); -        val = gain_range_t(0, _max_rx_pga0_gain, float(0.5)); +        assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name"); +        val = wbx_rx_gain_ranges[name];          return;      case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(1, "PGA0"); +        val = prop_names_t(wbx_rx_gain_ranges.keys());          return;      case SUBDEV_PROP_FREQ: @@ -457,17 +499,15 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){          return;      case SUBDEV_PROP_FREQ_RANGE: -        val = _freq_range; +        val = wbx_freq_range;          return;      case SUBDEV_PROP_ANTENNA:          val = _rx_ant;          return; -    case SUBDEV_PROP_ANTENNA_NAMES:{ -            prop_names_t ants = list_of("TX/RX")("RX2"); -            val = ants; -        } +    case SUBDEV_PROP_ANTENNA_NAMES: +        val = wbx_rx_antennas;          return;      case SUBDEV_PROP_QUADRATURE: @@ -502,16 +542,15 @@ void wbx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){      switch(key.as<subdev_prop_t>()){      case SUBDEV_PROP_FREQ: -        set_rx_lo_freq(val.as<double>()); +        this->set_rx_lo_freq(val.as<double>());          return;      case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(name == "PGA0"); -        set_rx_pga0_gain(val.as<float>()); +        this->set_rx_gain(val.as<float>(), name);          return;      case SUBDEV_PROP_ANTENNA: -        set_rx_ant(val.as<std::string>()); +        this->set_rx_ant(val.as<std::string>());          return;      default: UHD_THROW_PROP_SET_ERROR(); @@ -536,17 +575,17 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          return;      case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(name == "PGA0"); -        val = _tx_pga0_gain; +        assert_has(_tx_gains.keys(), name, "wbx tx gain name"); +        val = _tx_gains[name];          return;      case SUBDEV_PROP_GAIN_RANGE: -        UHD_ASSERT_THROW(name == "PGA0"); -        val = gain_range_t(0, _max_tx_pga0_gain, float(0.05)); +        assert_has(wbx_tx_gain_ranges.keys(), name, "wbx tx gain name"); +        val = wbx_tx_gain_ranges[name];          return;      case SUBDEV_PROP_GAIN_NAMES: -        val = prop_names_t(1, "PGA0"); +        val = prop_names_t(wbx_tx_gain_ranges.keys());          return;      case SUBDEV_PROP_FREQ: @@ -554,7 +593,7 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          return;      case SUBDEV_PROP_FREQ_RANGE: -        val = _freq_range; +        val = wbx_freq_range;          return;      case SUBDEV_PROP_ANTENNA: @@ -562,7 +601,7 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){          return;      case SUBDEV_PROP_ANTENNA_NAMES: -        val = prop_names_t(1, "TX/RX"); +        val = wbx_tx_antennas;          return;      case SUBDEV_PROP_QUADRATURE: @@ -597,17 +636,15 @@ void wbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){      switch(key.as<subdev_prop_t>()){      case SUBDEV_PROP_FREQ: -        set_tx_lo_freq(val.as<double>()); +        this->set_tx_lo_freq(val.as<double>());          return;      case SUBDEV_PROP_GAIN: -        UHD_ASSERT_THROW(name == "PGA0"); -        set_tx_pga0_gain(val.as<float>()); +        this->set_tx_gain(val.as<float>(), name);          return;      case SUBDEV_PROP_ANTENNA: -        //its always set to tx/rx, so we only allow this value -        UHD_ASSERT_THROW(val.as<std::string>() == "TX/RX"); +        this->set_tx_ant(val.as<std::string>());          return;      default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index d4d5f184e..974a378bd 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -375,7 +375,7 @@ static max2829_regs_t::tx_baseband_gain_t gain_to_tx_bb_reg(float &gain){          gain = 5;          return max2829_regs_t::TX_BASEBAND_GAIN_5DB;      } -    UHD_ASSERT_THROW(false); +    UHD_THROW_INVALID_CODE_PATH();  }  /*! @@ -417,7 +417,7 @@ void xcvr2450::set_tx_gain(float gain, const std::string &name){          _max2829_regs.tx_baseband_gain = gain_to_tx_bb_reg(gain);          send_reg(0x9);      } -    else UHD_ASSERT_THROW(false); +    else UHD_THROW_INVALID_CODE_PATH();      _tx_gains[name] = gain;  } @@ -431,7 +431,7 @@ void xcvr2450::set_rx_gain(float gain, const std::string &name){          _max2829_regs.rx_lna_gain = gain_to_rx_lna_reg(gain);          send_reg(0xB);      } -    else UHD_ASSERT_THROW(false); +    else UHD_THROW_INVALID_CODE_PATH();      _rx_gains[name] = gain;  } diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp index bd4b37ef3..eafb8897f 100644 --- a/host/lib/usrp/dboard_base.cpp +++ b/host/lib/usrp/dboard_base.cpp @@ -25,17 +25,17 @@ using namespace uhd::usrp;  /***********************************************************************   * dboard_base dboard dboard_base class   **********************************************************************/ -struct dboard_base::dboard_base_impl{ +struct dboard_base::impl{      ctor_args_impl args; -    dboard_base_impl(ctor_args_t args) : args(*args){} +    impl(ctor_args_t args) : args(*args){}  };  dboard_base::dboard_base(ctor_args_t args){ -    _impl = new dboard_base_impl(args); +    _impl = UHD_PIMPL_MAKE(impl, (args));  }  dboard_base::~dboard_base(void){ -   delete _impl; +   /* NOP */  }  std::string dboard_base::get_subdev_name(void){ diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 01352039e..35ddfc4ee 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -181,7 +181,7 @@ static args_t get_dboard_args(          switch(unit){          case dboard_iface::UNIT_RX: return get_dboard_args(unit, 0x0001);          case dboard_iface::UNIT_TX: return get_dboard_args(unit, 0x0000); -        default: UHD_ASSERT_THROW(false); +        default: UHD_THROW_INVALID_CODE_PATH();          }      } diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index 0a2e4b550..2621d43b4 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -136,7 +136,7 @@ void usrp2_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value){      //calculate the new selection mux setting      boost::uint32_t new_sels = 0x0;      for(size_t i = 0; i < 16; i++){ -        bool is_bit_set = bool(value & (0x1 << i)); +        bool is_bit_set = (value & (0x1 << i)) != 0;          new_sels |= ((is_bit_set)? FRF_GPIO_SEL_ATR : FRF_GPIO_SEL_GPIO) << (i*2);      } diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 79b18fb63..18f2d013f 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -15,11 +15,15 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#include "../../transport/vrt_packet_handler.hpp"  #include "usrp2_impl.hpp"  #include "usrp2_regs.hpp"  #include <uhd/transport/convert_types.hpp> +#include <uhd/transport/bounded_buffer.hpp>  #include <boost/format.hpp>  #include <boost/asio.hpp> //htonl and ntohl +#include <boost/bind.hpp> +#include <boost/thread.hpp>  #include <iostream>  using namespace uhd; @@ -28,6 +32,60 @@ using namespace uhd::transport;  namespace asio = boost::asio;  /*********************************************************************** + * io impl details (internal to this file) + **********************************************************************/ +struct usrp2_impl::io_impl{ + +    io_impl(zero_copy_if::sptr zc_if); +    ~io_impl(void); + +    managed_recv_buffer::sptr get_recv_buff(void); + +    //state management for the vrt packet handler code +    vrt_packet_handler::recv_state packet_handler_recv_state; +    vrt_packet_handler::send_state packet_handler_send_state; + +    //methods and variables for the recv pirate +    void recv_pirate_loop(zero_copy_if::sptr zc_if); +    boost::thread *recv_pirate_thread; bool recv_pirate_running; +    bounded_buffer<managed_recv_buffer::sptr>::sptr recv_pirate_booty; +}; + +usrp2_impl::io_impl::io_impl(zero_copy_if::sptr zc_if){ +    //create a large enough booty +    size_t num_frames = zc_if->get_num_recv_frames(); +    std::cout << "Recv pirate num frames: " << num_frames << std::endl; +    recv_pirate_booty = bounded_buffer<managed_recv_buffer::sptr>::make(num_frames); + +    //create a new pirate thread (yarr!!) +    recv_pirate_thread = new boost::thread( +        boost::bind(&usrp2_impl::io_impl::recv_pirate_loop, this, zc_if) +    ); +} + +usrp2_impl::io_impl::~io_impl(void){ +    recv_pirate_running = false; +    recv_pirate_thread->interrupt(); +    recv_pirate_thread->join(); +    delete recv_pirate_thread; +} + +managed_recv_buffer::sptr usrp2_impl::io_impl::get_recv_buff(void){ +    managed_recv_buffer::sptr buff; +    boost::this_thread::disable_interruption di; //disable because the wait can throw +    recv_pirate_booty->pop_with_timed_wait(buff, boost::posix_time::milliseconds(100)); +    return buff; //a timeout means that we return a null sptr... +} + +void usrp2_impl::io_impl::recv_pirate_loop(zero_copy_if::sptr zc_if){ +    recv_pirate_running = true; +    while(recv_pirate_running){ +        managed_recv_buffer::sptr buff = zc_if->get_recv_buff(); +        if (buff->size()) recv_pirate_booty->push_with_pop_on_full(buff); +    } +} + +/***********************************************************************   * Helper Functions   **********************************************************************/  void usrp2_impl::io_init(void){ @@ -60,6 +118,11 @@ void usrp2_impl::io_init(void){      );      _iface->poke32(FR_RX_CTRL_VRT_STREAM_ID, 0);      _iface->poke32(FR_RX_CTRL_VRT_TRAILER, 0); + +    std::cout << "TX samples per packet: " << get_max_send_samps_per_packet() << std::endl; + +    //create new io impl +    _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport));  }  /*********************************************************************** @@ -72,11 +135,11 @@ size_t usrp2_impl::send(      send_mode_t send_mode  ){      return vrt_packet_handler::send( -        _packet_handler_send_state, //last state of the send handler +        _io_impl->packet_handler_send_state, //last state of the send handler          buff, metadata, send_mode,  //buffer to empty and samples metadata          io_type, _tx_otw_type,      //input and output types to convert          get_master_clock_freq(),    //master clock tick rate -        _data_transport,            //zero copy interface +        boost::bind(&zero_copy_if::get_send_buff, _data_transport),          get_max_send_samps_per_packet()      );  } @@ -91,10 +154,10 @@ size_t usrp2_impl::recv(      recv_mode_t recv_mode  ){      return vrt_packet_handler::recv( -        _packet_handler_recv_state, //last state of the recv handler +        _io_impl->packet_handler_recv_state, //last state of the recv handler          buff, metadata, recv_mode,  //buffer to fill and samples metadata          io_type, _rx_otw_type,      //input and output types to convert          get_master_clock_freq(),    //master clock tick rate -        _data_transport             //zero copy interface +        boost::bind(&usrp2_impl::io_impl::get_recv_buff, _io_impl)      );  } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 7948a2069..40c193866 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -23,6 +23,7 @@  #include "codec_ctrl.hpp"  #include "serdes_ctrl.hpp"  #include <uhd/usrp/usrp2.hpp> +#include <uhd/utils/pimpl.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/types/otw_type.hpp>  #include <uhd/types/stream_cmd.hpp> @@ -33,7 +34,6 @@  #include <uhd/transport/vrt.hpp>  #include <uhd/transport/udp_zero_copy.hpp>  #include <uhd/usrp/dboard_manager.hpp> -#include "../../transport/vrt_packet_handler.hpp"  /*!   * Make a usrp2 dboard interface. @@ -153,9 +153,8 @@ private:          uhd::transport::vrt::max_header_words32*sizeof(boost::uint32_t)      ; -    vrt_packet_handler::recv_state _packet_handler_recv_state; -    vrt_packet_handler::send_state _packet_handler_send_state;      uhd::otw_type_t _rx_otw_type, _tx_otw_type; +    UHD_PIMPL_DECL(io_impl) _io_impl;      void io_init(void);      //udp transports for control and data diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index 61b0b503d..24778d13e 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -21,6 +21,7 @@  ADD_EXECUTABLE(main_test      main_test.cpp      addr_test.cpp +    buffer_test.cpp      dict_test.cpp      error_test.cpp      gain_handler_test.cpp diff --git a/host/test/buffer_test.cpp b/host/test/buffer_test.cpp new file mode 100644 index 000000000..aadb3f951 --- /dev/null +++ b/host/test/buffer_test.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/test/unit_test.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/alignment_buffer.hpp> +#include <boost/assign/list_of.hpp> + +using namespace boost::assign; +using namespace uhd::transport; + +static const boost::posix_time::milliseconds timeout(10); + +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_timed_wait){ +    bounded_buffer<int>::sptr bb(bounded_buffer<int>::make(3)); + +    //push elements, check for timeout +    BOOST_CHECK(bb->push_with_timed_wait(0, timeout)); +    BOOST_CHECK(bb->push_with_timed_wait(1, timeout)); +    BOOST_CHECK(bb->push_with_timed_wait(2, timeout)); +    BOOST_CHECK(not bb->push_with_timed_wait(3, timeout)); + +    int val; +    //pop elements, check for timeout and check values +    BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); +    BOOST_CHECK_EQUAL(val, 0); +    BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); +    BOOST_CHECK_EQUAL(val, 1); +    BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); +    BOOST_CHECK_EQUAL(val, 2); +    BOOST_CHECK(not bb->pop_with_timed_wait(val, timeout)); +} + +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_pop_on_full){ +    bounded_buffer<int>::sptr bb(bounded_buffer<int>::make(3)); + +    //push elements, check for timeout +    BOOST_CHECK(bb->push_with_pop_on_full(0)); +    BOOST_CHECK(bb->push_with_pop_on_full(1)); +    BOOST_CHECK(bb->push_with_pop_on_full(2)); +    BOOST_CHECK(not bb->push_with_pop_on_full(3)); + +    int val; +    //pop elements, check for timeout and check values +    BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); +    BOOST_CHECK_EQUAL(val, 1); +    BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); +    BOOST_CHECK_EQUAL(val, 2); +    BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); +    BOOST_CHECK_EQUAL(val, 3); +} + +BOOST_AUTO_TEST_CASE(test_alignment_buffer){ +    alignment_buffer<int, size_t>::sptr ab(alignment_buffer<int, size_t>::make(7, 3)); +    //load index 0 with all good seq numbers +    BOOST_CHECK(ab->push_with_pop_on_full(0, 0, 0)); +    BOOST_CHECK(ab->push_with_pop_on_full(1, 1, 0)); +    BOOST_CHECK(ab->push_with_pop_on_full(2, 2, 0)); +    BOOST_CHECK(ab->push_with_pop_on_full(3, 3, 0)); +    BOOST_CHECK(ab->push_with_pop_on_full(4, 4, 0)); + +    //load index 1 with some skipped seq numbers +    BOOST_CHECK(ab->push_with_pop_on_full(10, 0, 1)); +    BOOST_CHECK(ab->push_with_pop_on_full(11, 1, 1)); +    BOOST_CHECK(ab->push_with_pop_on_full(14, 4, 1)); +    BOOST_CHECK(ab->push_with_pop_on_full(15, 5, 1)); +    BOOST_CHECK(ab->push_with_pop_on_full(16, 6, 1)); + +    //load index 2 with all good seq numbers +    BOOST_CHECK(ab->push_with_pop_on_full(20, 0, 2)); +    BOOST_CHECK(ab->push_with_pop_on_full(21, 1, 2)); +    BOOST_CHECK(ab->push_with_pop_on_full(22, 2, 2)); +    BOOST_CHECK(ab->push_with_pop_on_full(23, 3, 2)); +    BOOST_CHECK(ab->push_with_pop_on_full(24, 4, 2)); + +    //readback aligned values +    std::vector<int> aligned_elems(3); + +    static const std::vector<int> expected_elems0 = list_of(0)(10)(20); +    BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); +    BOOST_CHECK_EQUAL_COLLECTIONS( +        aligned_elems.begin(), aligned_elems.end(), +        expected_elems0.begin(), expected_elems0.end() +    ); + +    static const std::vector<int> expected_elems1 = list_of(1)(11)(21); +    BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); +    BOOST_CHECK_EQUAL_COLLECTIONS( +        aligned_elems.begin(), aligned_elems.end(), +        expected_elems1.begin(), expected_elems1.end() +    ); + +    //there was a skip now find 4 + +    static const std::vector<int> expected_elems4 = list_of(4)(14)(24); +    BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); +    BOOST_CHECK_EQUAL_COLLECTIONS( +        aligned_elems.begin(), aligned_elems.end(), +        expected_elems4.begin(), expected_elems4.end() +    ); +} | 
