diff options
| -rw-r--r-- | host/include/uhd/transport/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | host/include/uhd/transport/convert_types.hpp | 59 | ||||
| -rw-r--r-- | host/include/uhd/transport/smart_buffer.hpp | 45 | ||||
| -rw-r--r-- | host/include/uhd/transport/udp_zero_copy.hpp | 28 | ||||
| -rw-r--r-- | host/include/uhd/transport/vrt.hpp | 2 | ||||
| -rw-r--r-- | host/include/uhd/transport/zero_copy.hpp | 133 | ||||
| -rw-r--r-- | host/lib/CMakeLists.txt | 1 | ||||
| -rwxr-xr-x | host/lib/ic_reg_maps/gen_adf4360_regs.py | 4 | ||||
| -rw-r--r-- | host/lib/transport/convert_types.cpp | 144 | ||||
| -rwxr-xr-x | host/lib/transport/gen_vrt.py | 13 | ||||
| -rw-r--r-- | host/lib/transport/udp_zero_copy_asio.cpp | 106 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/io_impl.cpp | 142 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.hpp | 4 | 
13 files changed, 462 insertions, 222 deletions
| diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 14b5ccd29..4cefffa24 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -17,10 +17,11 @@  INSTALL(FILES +    convert_types.hpp      if_addrs.hpp -    smart_buffer.hpp      udp_simple.hpp      udp_zero_copy.hpp      vrt.hpp +    zero_copy.hpp      DESTINATION ${INCLUDE_DIR}/uhd/transport  ) diff --git a/host/include/uhd/transport/convert_types.hpp b/host/include/uhd/transport/convert_types.hpp new file mode 100644 index 000000000..a4d999240 --- /dev/null +++ b/host/include/uhd/transport/convert_types.hpp @@ -0,0 +1,59 @@ +// +// 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_CONVERT_TYPES_HPP +#define INCLUDED_UHD_TRANSPORT_CONVERT_TYPES_HPP + +#include <uhd/config.hpp> +#include <uhd/types/io_type.hpp> +#include <uhd/types/otw_type.hpp> + +namespace uhd{ namespace transport{ + +/*! + * Convert IO samples to OWT samples. + * + * \param io_buff memory containing samples + * \param io_type the type of these samples + * \param otw_buff memory to write converted samples + * \param otw_type the type of these samples + * \param num_samps the number of samples in io_buff + */ +UHD_API void convert_io_type_to_otw_type( +    const void *io_buff, const io_type_t &io_type, +    void *otw_buff, const otw_type_t &otw_type, +    size_t num_samps +); + +/*! + * Convert OTW samples to IO samples. + * + * \param otw_buff memory containing samples + * \param otw_type the type of these samples + * \param io_buff memory to write converted samples + * \param io_type the type of these samples + * \param num_samps the number of samples in io_buff + */ +UHD_API void convert_otw_type_to_io_type( +    const void *otw_buff, const otw_type_t &otw_type, +    void *io_buff, const io_type_t &io_type, +    size_t num_samps +); + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_CONVERT_TYPES_HPP */ diff --git a/host/include/uhd/transport/smart_buffer.hpp b/host/include/uhd/transport/smart_buffer.hpp deleted file mode 100644 index a9bc259e9..000000000 --- a/host/include/uhd/transport/smart_buffer.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// -// 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_SMART_BUFFER_HPP -#define INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP - -#include <boost/asio/buffer.hpp> -#include <boost/utility.hpp> -#include <boost/shared_ptr.hpp> - -namespace uhd{ namespace transport{ - -/*! - * A buffer that knows how to free itself: - * - * This is just the smart buffer interface. - * A transport implementation will have its own - * internal (custom) smart buffer implementation. - * - * A smart buffer contains a boost asio const buffer. - * On destruction, the buffer contents will be freed. - */ -class smart_buffer : boost::noncopyable{ -public: -    typedef boost::shared_ptr<smart_buffer> sptr; -    virtual const boost::asio::const_buffer &get(void) const = 0; -}; - -}} //namespace - -#endif /* INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP */ diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index 0441a8e74..fd1cec46e 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -19,24 +19,22 @@  #define INCLUDED_UHD_TRANSPORT_UDP_ZERO_COPY_HPP  #include <uhd/config.hpp> -#include <uhd/transport/smart_buffer.hpp> -#include <boost/asio/buffer.hpp> -#include <boost/utility.hpp> +#include <uhd/transport/zero_copy.hpp>  #include <boost/shared_ptr.hpp>  namespace uhd{ namespace transport{  /*!   * A zero copy udp transport provides an efficient way to handle data. - * by avoiding the extra copy when recv() is called on the socket. - * Rather, the zero copy transport gives the caller a memory reference. + * by avoiding the extra copy when recv() or send() is called on the socket. + * Rather, the zero copy transport gives the caller memory references.   * The caller informs the transport when it is finished with the reference.   *   * On linux systems, the zero copy transport can use a kernel packet ring.   * If no platform specific solution is available, make returns a boost asio - * implementation that wraps the functionality around a standard recv() call. + * implementation that wraps the functionality around a standard send/recv calls.   */ -class UHD_API udp_zero_copy : boost::noncopyable{ +class UHD_API udp_zero_copy : public zero_copy_if{  public:      typedef boost::shared_ptr<udp_zero_copy> sptr; @@ -54,22 +52,6 @@ public:       * \param port a string representing the destination port       */      static sptr make(const std::string &addr, const std::string &port); - -    /*! -     * Send a single buffer. -     * Blocks until the data is sent. -     * \param buff single asio buffer -     * \return the number of bytes sent -     */ -    virtual size_t send(const boost::asio::const_buffer &buff) = 0; - -    /*! -     * Receive a buffer. -     * Blocks until data is received or a timeout occurs. -     * The memory is managed by the implementation. -     * \return a smart buffer (empty on timeout) -     */ -    virtual smart_buffer::sptr recv(void) = 0;  };  }} //namespace diff --git a/host/include/uhd/transport/vrt.hpp b/host/include/uhd/transport/vrt.hpp index 137698e57..f2f42f9eb 100644 --- a/host/include/uhd/transport/vrt.hpp +++ b/host/include/uhd/transport/vrt.hpp @@ -26,7 +26,7 @@ namespace uhd{ namespace transport{  namespace vrt{ -    static const size_t max_header_words32 = 7; +    static const size_t max_header_words32 = 5; //hdr+sid+tsi+tsf (no class id supported)      /*!       * Pack a vrt header from metadata. diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp new file mode 100644 index 000000000..4fc1df9de --- /dev/null +++ b/host/include/uhd/transport/zero_copy.hpp @@ -0,0 +1,133 @@ +// +// 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_ZERO_COPY_HPP +#define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP + +#include <uhd/config.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; + +    /*! +     * 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 void done(void) = 0; + +    /*! +     * Get the size of the underlying buffer. +     * \return the number of bytes +     */ +    size_t size(void){ +        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){ +        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) = 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 done(size_t num_bytes) = 0; + +    /*! +     * Get the size of the underlying buffer. +     * \return the number of bytes +     */ +    size_t size(void){ +        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){ +        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) = 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; + +    /*! +     * Get a new receive buffer from this transport object. +     */ +    virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; + +    /*! +     * Get a new send buffer from this transport object. +     */ +    virtual managed_send_buffer::sptr get_send_buff(void) = 0; +}; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP */ diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 58afe099d..52ed809ab 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -51,6 +51,7 @@ SET(libuhd_sources      load_modules.cpp      types.cpp      wax.cpp +    transport/convert_types.cpp      transport/if_addrs.cpp      transport/udp_simple.cpp      usrp/dboard/db_basic_and_lf.cpp diff --git a/host/lib/ic_reg_maps/gen_adf4360_regs.py b/host/lib/ic_reg_maps/gen_adf4360_regs.py index 702c3060f..e7c2c9435 100755 --- a/host/lib/ic_reg_maps/gen_adf4360_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4360_regs.py @@ -20,10 +20,13 @@  # Boston, MA 02110-1301, USA.  import re +import os  import sys  from Cheetah.Template import Template  def parse_tmpl(_tmpl_text, **kwargs):      return str(Template(_tmpl_text, kwargs)) +def safe_makedirs(path): +    not os.path.isdir(path) and os.makedirs(path)  ########################################################################  # Template for raw text data describing registers @@ -166,4 +169,5 @@ class reg:  if __name__ == '__main__':      regs = map(reg, parse_tmpl(REGS_DATA_TMPL).splitlines()) +    safe_makedirs(os.path.dirname(sys.argv[1]))      open(sys.argv[1], 'w').write(parse_tmpl(HEADER_TEXT, regs=regs, file=__file__)) diff --git a/host/lib/transport/convert_types.cpp b/host/lib/transport/convert_types.cpp new file mode 100644 index 000000000..2a6854f50 --- /dev/null +++ b/host/lib/transport/convert_types.cpp @@ -0,0 +1,144 @@ +// +// 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/convert_types.hpp> +#include <uhd/utils/assert.hpp> +#include <boost/asio.hpp> //endianness conversion +#include <boost/cstdint.hpp> + +using namespace uhd; + +/*********************************************************************** + * Constants + **********************************************************************/ +typedef std::complex<float>          fc32_t; + +static const float shorts_per_float = float(1 << 15); +static const float floats_per_short = float(1.0/shorts_per_float); + +#define unrolled_loop(__inst, __len){ \ +    size_t __i = 0; \ +    for(; __i < (__len & ~0x3); __i+= 4){ \ +        __inst(__i+0); __inst(__i+1); \ +        __inst(__i+2); __inst(__i+3); \ +    } \ +    for(; __i < __len; __i++){ \ +        __inst(__i); \ +    } \ +} + +// set a boolean flag that indicates the endianess +#ifdef HAVE_BIG_ENDIAN +static const bool is_big_endian = true; +#else +static const bool is_big_endian = false; +#endif + +static inline void host_floats_to_usrp2_items( +    boost::uint32_t *usrp2_items, +    const fc32_t *host_floats, +    size_t num_samps +){ +    #define host_floats_to_usrp2_items_i(i){ \ +        boost::uint16_t real = boost::int16_t(host_floats[i].real()*shorts_per_float); \ +        boost::uint16_t imag = boost::int16_t(host_floats[i].imag()*shorts_per_float); \ +        usrp2_items[i] = htonl((real << 16) | (imag << 0)); \ +    } +    unrolled_loop(host_floats_to_usrp2_items_i, num_samps); +} + +static inline void usrp2_items_to_host_floats( +    fc32_t *host_floats, +    const boost::uint32_t *usrp2_items, +    size_t num_samps +){ +    #define usrp2_items_to_host_floats_i(i){ \ +        boost::uint32_t item = ntohl(usrp2_items[i]); \ +        boost::int16_t real = boost::uint16_t(item >> 16); \ +        boost::int16_t imag = boost::uint16_t(item >> 0); \ +        host_floats[i] = fc32_t(float(real*floats_per_short), float(imag*floats_per_short)); \ +    } +    unrolled_loop(usrp2_items_to_host_floats_i, num_samps); +} + +static inline void host_items_to_usrp2_items( +    boost::uint32_t *usrp2_items, +    const boost::uint32_t *host_items, +    size_t num_samps +){ +    #define host_items_to_usrp2_items_i(i) usrp2_items[i] = htonl(host_items[i]) +    if (is_big_endian){ +        std::memcpy(usrp2_items, host_items, num_samps*sizeof(boost::uint32_t)); +    } +    else{ +        unrolled_loop(host_items_to_usrp2_items_i, num_samps); +    } +} + +static inline void usrp2_items_to_host_items( +    boost::uint32_t *host_items, +    const boost::uint32_t *usrp2_items, +    size_t num_samps +){ +    #define usrp2_items_to_host_items_i(i) host_items[i] = ntohl(usrp2_items[i]) +    if (is_big_endian){ +        std::memcpy(host_items, usrp2_items, num_samps*sizeof(boost::uint32_t)); +    } +    else{ +        unrolled_loop(usrp2_items_to_host_items_i, num_samps); +    } +} + +void transport::convert_io_type_to_otw_type( +    const void *io_buff, const io_type_t &io_type, +    void *otw_buff, const otw_type_t &otw_type, +    size_t num_samps +){ +    //all we handle for now: +    ASSERT_THROW(otw_type.width == 16 and otw_type.byteorder == otw_type_t::BO_BIG_ENDIAN); + +    switch(io_type.tid){ +    case io_type_t::COMPLEX_FLOAT32: +        host_floats_to_usrp2_items((boost::uint32_t *)otw_buff, (const fc32_t*)io_buff, num_samps); +        break; +    case io_type_t::COMPLEX_INT16: +        host_items_to_usrp2_items((boost::uint32_t *)otw_buff, (const boost::uint32_t*)io_buff, num_samps); +        break; +    default: +        throw std::runtime_error(str(boost::format("convert_types: cannot handle type \"%c\"") % io_type.tid)); +    } +} + +void transport::convert_otw_type_to_io_type( +    const void *otw_buff, const otw_type_t &otw_type, +    void *io_buff, const io_type_t &io_type, +    size_t num_samps +){ +    //all we handle for now: +    ASSERT_THROW(otw_type.width == 16 and otw_type.byteorder == otw_type_t::BO_BIG_ENDIAN); + +    switch(io_type.tid){ +    case io_type_t::COMPLEX_FLOAT32: +        usrp2_items_to_host_floats((fc32_t*)io_buff, (const boost::uint32_t *)otw_buff, num_samps); +        break; +    case io_type_t::COMPLEX_INT16: +        usrp2_items_to_host_items((boost::uint32_t*)io_buff, (const boost::uint32_t *)otw_buff, num_samps); +        break; +    default: +        throw std::runtime_error(str(boost::format("convert_types: cannot handle type \"%c\"") % io_type.tid)); +    } +} diff --git a/host/lib/transport/gen_vrt.py b/host/lib/transport/gen_vrt.py index 0f961efc2..38a394dee 100755 --- a/host/lib/transport/gen_vrt.py +++ b/host/lib/transport/gen_vrt.py @@ -200,10 +200,15 @@ void vrt::unpack(  }  """ +import os  import sys -from Cheetah import Template -def parse_str(_tmpl_text, **kwargs): return str(Template.Template(_tmpl_text, kwargs)) + +from Cheetah.Template import Template +def parse_tmpl(_tmpl_text, **kwargs): +    return str(Template(_tmpl_text, kwargs)) +def safe_makedirs(path): +    not os.path.isdir(path) and os.makedirs(path)  if __name__ == '__main__': -    from Cheetah import Template -    open(sys.argv[1], 'w').write(parse_str(TMPL_TEXT, file=__file__)) +    safe_makedirs(os.path.dirname(sys.argv[1])) +    open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=__file__)) diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index 1fc8ce14a..7e643abad 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -25,39 +25,74 @@  using namespace uhd::transport;  /*********************************************************************** - * Smart buffer implementation for udp zerocopy none - * - * This smart buffer implemention houses a const buffer. - * When the smart buffer is deleted, the buffer is freed. - * The memory in the const buffer is allocated with new [], - * and so the destructor frees the buffer with delete []. + * Managed receive buffer implementation for udp zero-copy asio: + *   Frees the memory held by the const buffer on done.   **********************************************************************/ -class smart_buffer_impl : public smart_buffer{ +class managed_recv_buffer_impl : public managed_recv_buffer{  public: -    smart_buffer_impl(const boost::asio::const_buffer &buff){ -        _buff = buff; +    managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ +        _done = false;      } -    ~smart_buffer_impl(void){ +    ~managed_recv_buffer_impl(void){ +        if (not _done) this->done(); +    } + +    void done(void){ +        _done = true;          delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff);      } -    const boost::asio::const_buffer &get(void) const{ +private: +    const boost::asio::const_buffer &get(void){          return _buff;      } +    const boost::asio::const_buffer _buff; +    bool _done; +}; + +/*********************************************************************** + * Managed send buffer implementation for udp zero-copy asio: + *   Sends and frees the memory held by the mutable buffer on done. + **********************************************************************/ +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){ +        _done = false; +        _socket = socket; +    } + +    ~managed_send_buffer_impl(void){ +        if (not _done) this->done(0); +    } + +    void done(size_t num_bytes){ +        _done = true; +        boost::uint32_t *mem = boost::asio::buffer_cast<boost::uint32_t *>(_buff); +        _socket->send(boost::asio::buffer(mem, num_bytes)); +        delete [] mem; +    } +  private: -    boost::asio::const_buffer _buff; +    const boost::asio::mutable_buffer &get(void){ +        return _buff; +    } + +    const boost::asio::mutable_buffer _buff; +    boost::asio::ip::udp::socket      *_socket; +    bool _done;  };  /*********************************************************************** - * UDP zero copy implementation class - * - * This is the portable zero copy implementation for systems - * where a faster, platform specific solution is not available. - * - * It uses boost asio udp sockets and the standard recv() class, - * and in-fact, is not actually doing a zero-copy implementation. + * Zero Copy UDP implementation with ASIO: + *   This is the portable zero copy implementation for systems + *   where a faster, platform specific solution is not available. + *   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{  public: @@ -66,8 +101,8 @@ public:      ~udp_zero_copy_impl(void);      //send/recv -    size_t send(const boost::asio::const_buffer &buff); -    smart_buffer::sptr recv(void); +    managed_recv_buffer::sptr get_recv_buff(void); +    managed_send_buffer::sptr get_send_buff(void);  private:      boost::asio::ip::udp::socket   *_socket; @@ -107,31 +142,32 @@ udp_zero_copy_impl::~udp_zero_copy_impl(void){      delete _socket;  } -size_t udp_zero_copy_impl::send(const boost::asio::const_buffer &buff){ -    return _socket->send(boost::asio::buffer(buff)); -} - -smart_buffer::sptr udp_zero_copy_impl::recv(void){ -    size_t available = 0; - +managed_recv_buffer::sptr udp_zero_copy_impl::get_recv_buff(void){      //implement timeout through polling and sleeping +    size_t available = 0;      boost::asio::deadline_timer timer(_socket->get_io_service());      timer.expires_from_now(boost::posix_time::milliseconds(100));      while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){          boost::this_thread::sleep(boost::posix_time::milliseconds(1));      } -    //allocate memory and create buffer -    boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)]; -    boost::asio::mutable_buffer buff(buff_mem, available); -      //receive only if data is available +    boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)];      if (available){ -        _socket->receive(boost::asio::buffer(buff)); +        available = _socket->receive(boost::asio::buffer(buff_mem, available));      } -    //create a new smart buffer to house the data -    return smart_buffer::sptr(new smart_buffer_impl(buff)); +    //create a new managed buffer to house the data +    return managed_recv_buffer::sptr( +        new managed_recv_buffer_impl(boost::asio::buffer(buff_mem, available)) +    ); +} + +managed_send_buffer::sptr udp_zero_copy_impl::get_send_buff(void){ +    boost::uint32_t *buff_mem = new boost::uint32_t[2000/sizeof(boost::uint32_t)]; +    return managed_send_buffer::sptr( +        new managed_send_buffer_impl(boost::asio::buffer(buff_mem, 2000), _socket) +    );  }  size_t udp_zero_copy_impl::get_recv_buff_size(void){ diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index eb6b5f7b9..a760cbe3d 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -16,6 +16,7 @@  //  #include "usrp2_impl.hpp" +#include <uhd/transport/convert_types.hpp>  #include <boost/format.hpp>  #include <complex>  #include <iostream> @@ -26,101 +27,26 @@ using namespace uhd::transport;  namespace asio = boost::asio;  /*********************************************************************** - * Constants - **********************************************************************/ -typedef std::complex<float>          fc32_t; -typedef std::complex<boost::int16_t> sc16_t; - -static const float shorts_per_float = float(1 << 15); -static const float floats_per_short = float(1.0/shorts_per_float); - -/***********************************************************************   * Helper Functions   **********************************************************************/  void usrp2_impl::io_init(void){ +    //setup otw type +    _otw_type.width = 16; +    _otw_type.shift = 0; +    _otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; +      //initially empty copy buffer      _rx_copy_buff = asio::buffer("", 0);      //send a small data packet so the usrp2 knows the udp source port      //and the maximum number of lines (32 bit words) per packet +    managed_send_buffer::sptr send_buff = _data_transport->get_send_buff();      boost::uint32_t data[2] = {          htonl(USRP2_INVALID_VRT_HEADER),          htonl(_max_rx_samples_per_packet)      }; -    _data_transport->send(asio::buffer(&data, sizeof(data))); -} - -#define unrolled_loop(__inst, __len){ \ -    size_t __i = 0; \ -    for(; __i < (__len & ~0x3); __i+= 4){ \ -        __inst(__i+0); __inst(__i+1); \ -        __inst(__i+2); __inst(__i+3); \ -    } \ -    for(; __i < __len; __i++){ \ -        __inst(__i); \ -    } \ -} - -// set a boolean flag that indicates the endianess -#ifdef HAVE_BIG_ENDIAN -static const bool is_big_endian = true; -#else -static const bool is_big_endian = false; -#endif - -static inline void host_floats_to_usrp2_items( -    boost::uint32_t *usrp2_items, -    const fc32_t *host_floats, -    size_t num_samps -){ -    #define host_floats_to_usrp2_items_i(i){ \ -        boost::uint16_t real = boost::int16_t(host_floats[i].real()*shorts_per_float); \ -        boost::uint16_t imag = boost::int16_t(host_floats[i].imag()*shorts_per_float); \ -        usrp2_items[i] = htonl((real << 16) | (imag << 0)); \ -    } -    unrolled_loop(host_floats_to_usrp2_items_i, num_samps); -} - -static inline void usrp2_items_to_host_floats( -    fc32_t *host_floats, -    const boost::uint32_t *usrp2_items, -    size_t num_samps -){ -    #define usrp2_items_to_host_floats_i(i){ \ -        boost::uint32_t item = ntohl(usrp2_items[i]); \ -        boost::int16_t real = boost::uint16_t(item >> 16); \ -        boost::int16_t imag = boost::uint16_t(item >> 0); \ -        host_floats[i] = fc32_t(float(real*floats_per_short), float(imag*floats_per_short)); \ -    } -    unrolled_loop(usrp2_items_to_host_floats_i, num_samps); -} - -static inline void host_items_to_usrp2_items( -    boost::uint32_t *usrp2_items, -    const boost::uint32_t *host_items, -    size_t num_samps -){ -    #define host_items_to_usrp2_items_i(i) usrp2_items[i] = htonl(host_items[i]) -    if (is_big_endian){ -        std::memcpy(usrp2_items, host_items, num_samps*sizeof(boost::uint32_t)); -    } -    else{ -        unrolled_loop(host_items_to_usrp2_items_i, num_samps); -    } -} - -static inline void usrp2_items_to_host_items( -    boost::uint32_t *host_items, -    const boost::uint32_t *usrp2_items, -    size_t num_samps -){ -    #define usrp2_items_to_host_items_i(i) host_items[i] = ntohl(usrp2_items[i]) -    if (is_big_endian){ -        std::memcpy(host_items, usrp2_items, num_samps*sizeof(boost::uint32_t)); -    } -    else{ -        unrolled_loop(usrp2_items_to_host_items_i, num_samps); -    } +    memcpy(send_buff->cast<void*>(), data, sizeof(data)); +    send_buff->done(sizeof(data));  }  /*********************************************************************** @@ -128,15 +54,15 @@ static inline void usrp2_items_to_host_items(   **********************************************************************/  void usrp2_impl::recv_raw(rx_metadata_t &metadata){      //do a receive -    _rx_smart_buff = _data_transport->recv(); +    _rx_smart_buff = _data_transport->get_recv_buff();      //unpack the vrt header -    size_t num_packet_words32 = asio::buffer_size(_rx_smart_buff->get())/sizeof(boost::uint32_t); +    size_t num_packet_words32 = _rx_smart_buff->size()/sizeof(boost::uint32_t);      if (num_packet_words32 == 0){          _rx_copy_buff = boost::asio::buffer("", 0);          return; //must exit here after setting the buffer      } -    const boost::uint32_t *vrt_hdr = asio::buffer_cast<const boost::uint32_t *>(_rx_smart_buff->get()); +    const boost::uint32_t *vrt_hdr = _rx_smart_buff->cast<const boost::uint32_t *>();      size_t num_header_words32_out, num_payload_words32_out, packet_count_out;      try{          vrt::unpack( @@ -178,10 +104,12 @@ size_t usrp2_impl::send(  ){      tx_metadata_t metadata = metadata_; //rw copy to change later -    boost::uint32_t tx_mem[_mtu/sizeof(boost::uint32_t)]; -    size_t num_samps = std::min( +    transport::managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); +    boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>(); +    size_t num_samps = std::min(std::min(          asio::buffer_size(buff)/io_type.size, -        size_t(_max_tx_samples_per_packet) +        size_t(_max_tx_samples_per_packet)), +        send_buff->size()/io_type.size      );      //kill the end of burst flag if this is a fragment @@ -204,20 +132,15 @@ size_t usrp2_impl::send(      boost::uint32_t *items = tx_mem + num_header_words32; //offset for data -    //copy the samples into the send buffer -    switch(io_type.tid){ -    case io_type_t::COMPLEX_FLOAT32: -        host_floats_to_usrp2_items(items, asio::buffer_cast<const fc32_t*>(buff), num_samps); -        break; -    case io_type_t::COMPLEX_INT16: -        host_items_to_usrp2_items(items, asio::buffer_cast<const boost::uint32_t*>(buff), num_samps); -        break; -    default: -        throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%c\"") % io_type.tid)); -    } +    //copy-convert the samples into the send buffer +    convert_io_type_to_otw_type( +        asio::buffer_cast<const void*>(buff), io_type, +        (void*)items, _otw_type, +        num_samps +    );      //send and return number of samples -    _data_transport->send(asio::buffer(tx_mem, num_packet_words32*sizeof(boost::uint32_t))); +    send_buff->done(num_packet_words32*sizeof(boost::uint32_t));      return num_samps;  } @@ -254,17 +177,12 @@ size_t usrp2_impl::recv(      metadata.fragment_offset = _fragment_offset_in_samps;      _fragment_offset_in_samps += num_samps; //set for next time -    //copy the samples from the recv buffer -    switch(io_type.tid){ -    case io_type_t::COMPLEX_FLOAT32: -        usrp2_items_to_host_floats(asio::buffer_cast<fc32_t*>(buff), items, num_samps); -        break; -    case io_type_t::COMPLEX_INT16: -        usrp2_items_to_host_items(asio::buffer_cast<boost::uint32_t*>(buff), items, num_samps); -        break; -    default: -        throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%c\"") % io_type.tid)); -    } +    //copy-convert the samples from the recv buffer +    convert_otw_type_to_io_type( +        (const void*)items, _otw_type, +        asio::buffer_cast<void*>(buff), io_type, +        num_samps +    );      //update the rx copy buffer to reflect the bytes copied      _rx_copy_buff = asio::buffer( diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index baa6530b8..4e7154afa 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -20,6 +20,7 @@  #include <uhd/usrp/usrp2.hpp>  #include <uhd/types/dict.hpp> +#include <uhd/types/otw_type.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/types/clock_config.hpp>  #include <boost/asio.hpp> @@ -136,9 +137,10 @@ private:          (_mtu - _hdrs)/sizeof(boost::uint32_t) -          uhd::transport::vrt::max_header_words32      ; -    uhd::transport::smart_buffer::sptr _rx_smart_buff; +    uhd::transport::managed_recv_buffer::sptr _rx_smart_buff;      boost::asio::const_buffer _rx_copy_buff;      size_t _fragment_offset_in_samps; +    uhd::otw_type_t _otw_type;      void io_init(void);      //udp transports for control and data | 
